summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib')
-rw-r--r--tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp7
-rw-r--r--tests/auto/corelib/global/global.pro1
-rw-r--r--tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp67
-rw-r--r--tests/auto/corelib/global/qrandomgenerator/qrandomgenerator.pro4
-rw-r--r--tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp960
-rw-r--r--tests/auto/corelib/global/qtendian/qtendian.pro2
-rw-r--r--tests/auto/corelib/global/qtendian/tst_qtendian.cpp84
-rw-r--r--tests/auto/corelib/io/io.pro9
-rw-r--r--tests/auto/corelib/io/largefile/tst_largefile.cpp4
-rw-r--r--tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp15
-rw-r--r--tests/auto/corelib/io/qdebug/tst_qdebug.cpp41
-rw-r--r--tests/auto/corelib/io/qfile/BLACKLIST4
-rw-r--r--tests/auto/corelib/io/qfile/tst_qfile.cpp30
-rw-r--r--tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp459
-rw-r--r--tests/auto/corelib/io/qfilesystemmetadata/tst_qfilesystemmetadata.cpp6
-rw-r--r--tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp91
-rw-r--r--tests/auto/corelib/io/qlockfile/qlockfiletesthelper/qlockfile_test_helper.cpp10
-rw-r--r--tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp6
-rw-r--r--tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp2
-rw-r--r--tests/auto/corelib/io/qprocess/testDetached/main.cpp85
-rw-r--r--tests/auto/corelib/io/qprocess/tst_qprocess.cpp68
-rw-r--r--tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp60
-rw-r--r--tests/auto/corelib/io/qsettings/tst_qsettings.cpp76
-rw-r--r--tests/auto/corelib/io/qstandardpaths/BLACKLIST3
-rw-r--r--tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp32
-rw-r--r--tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp105
-rw-r--r--tests/auto/corelib/io/qurl/tst_qurl.cpp17
-rw-r--r--tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro4
-rw-r--r--tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp331
-rw-r--r--tests/auto/corelib/itemmodels/itemmodels.pro1
-rw-r--r--tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp169
-rw-r--r--tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/.gitignore1
-rw-r--r--tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro8
-rw-r--r--tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp723
-rw-r--r--tests/auto/corelib/json/tst_qtjson.cpp120
-rw-r--r--tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro2
-rw-r--r--tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp13
-rw-r--r--tests/auto/corelib/kernel/qmath/tst_qmath.cpp29
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp292
-rw-r--r--tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp10
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp24
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp46
-rw-r--r--tests/auto/corelib/kernel/qtimer/qtimer.pro2
-rw-r--r--tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp15
-rw-r--r--tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp47
-rw-r--r--tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp93
-rw-r--r--tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp155
-rw-r--r--tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp1
-rw-r--r--tests/auto/corelib/mimetypes/qmimetype/tst_qmimetype.cpp40
-rw-r--r--tests/auto/corelib/plugin/quuid/testProcessUniqueness/main.cpp3
-rw-r--r--tests/auto/corelib/plugin/quuid/tst_quuid.cpp56
-rw-r--r--tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm12
-rw-r--r--tests/auto/corelib/thread/qfuture/tst_qfuture.cpp5
-rw-r--r--tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp50
-rw-r--r--tests/auto/corelib/thread/qthread/qthread.pro2
-rw-r--r--tests/auto/corelib/thread/qthread/tst_qthread.cpp259
-rw-r--r--tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp68
-rw-r--r--tests/auto/corelib/tools/containerapisymmetry/.gitignore1
-rw-r--r--tests/auto/corelib/tools/containerapisymmetry/containerapisymmetry.pro7
-rw-r--r--tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp100
-rw-r--r--tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp11
-rw-r--r--tests/auto/corelib/tools/qchar/tst_qchar.cpp29
-rw-r--r--tests/auto/corelib/tools/qdate/tst_qdate.cpp2
-rw-r--r--tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp107
-rw-r--r--tests/auto/corelib/tools/qhash/tst_qhash.cpp55
-rw-r--r--tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp28
-rw-r--r--tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp17
-rw-r--r--tests/auto/corelib/tools/qlocale/tst_qlocale.cpp99
-rw-r--r--tests/auto/corelib/tools/qmacautoreleasepool/qmacautoreleasepool.pro4
-rw-r--r--tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm111
-rw-r--r--tests/auto/corelib/tools/qmap/tst_qmap.cpp54
-rw-r--r--tests/auto/corelib/tools/qrect/tst_qrect.cpp32
-rw-r--r--tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp3
-rw-r--r--tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp2
-rw-r--r--tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp5
-rw-r--r--tests/auto/corelib/tools/qstring/tst_qstring.cpp58
-rw-r--r--tests/auto/corelib/tools/qstringapisymmetry/qstringapisymmetry.pro1
-rw-r--r--tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp751
-rw-r--r--tests/auto/corelib/tools/qstringiterator/tst_qstringiterator.cpp12
-rw-r--r--tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp9
-rw-r--r--tests/auto/corelib/tools/qstringref/tst_qstringref.cpp4
-rw-r--r--tests/auto/corelib/tools/qstringview/.gitignore1
-rw-r--r--tests/auto/corelib/tools/qstringview/qstringview.pro6
-rw-r--r--tests/auto/corelib/tools/qstringview/tst_qstringview.cpp619
-rw-r--r--tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp154
-rw-r--r--tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp2
-rw-r--r--tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp8
-rw-r--r--tests/auto/corelib/tools/tools.pro3
88 files changed, 6206 insertions, 918 deletions
diff --git a/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp b/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp
index 0f2e9b5d68..c3b53a2fc0 100644
--- a/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp
+++ b/tests/auto/corelib/codecs/qtextcodec/tst_qtextcodec.cpp
@@ -126,6 +126,7 @@ void tst_QTextCodec::toUnicode()
}
QVERIFY(!uniString.isEmpty());
QCOMPARE( ba, c->fromUnicode( uniString ) );
+ QCOMPARE(ba, c->fromUnicode(QStringView(uniString)) );
char ch = '\0';
QVERIFY(c->toUnicode(&ch, 1).length() == 1);
@@ -262,7 +263,7 @@ void tst_QTextCodec::fromUnicode()
If the encoding is a superset of ASCII, test that the byte
array is correct (no off by one, no trailing '\0').
*/
- QByteArray result = codec->fromUnicode(QString("abc"));
+ QByteArray result = codec->fromUnicode(QStringViewLiteral("abc"));
if (result.startsWith('a')) {
QCOMPARE(result.size(), 3);
QCOMPARE(result, QByteArray("abc"));
@@ -397,6 +398,7 @@ void tst_QTextCodec::asciiToIscii() const
QVERIFY2(textCodec->canEncode(ascii), qPrintable(QString::fromLatin1("Failed for full string with encoding %1")
.arg(QString::fromLatin1(textCodec->name().constData()))));
+ QVERIFY(textCodec->canEncode(QStringView(ascii)));
}
}
@@ -404,12 +406,11 @@ void tst_QTextCodec::nonFlaggedCodepointFFFF() const
{
//Check that the code point 0xFFFF (=non-character code 0xEFBFBF) is not flagged
const QChar ch(0xFFFF);
- QString input(ch);
QTextCodec *const codec = QTextCodec::codecForMib(106); // UTF-8
QVERIFY(codec);
- const QByteArray asDecoded(codec->fromUnicode(input));
+ const QByteArray asDecoded = codec->fromUnicode(QStringView(&ch, 1));
QCOMPARE(asDecoded, QByteArray("\357\277\277"));
QByteArray ffff("\357\277\277");
diff --git a/tests/auto/corelib/global/global.pro b/tests/auto/corelib/global/global.pro
index b4cc8035e6..139e073644 100644
--- a/tests/auto/corelib/global/global.pro
+++ b/tests/auto/corelib/global/global.pro
@@ -7,6 +7,7 @@ SUBDIRS=\
qnumeric \
qfloat16 \
qrand \
+ qrandomgenerator \
qlogging \
qtendian \
qglobalstatic \
diff --git a/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp b/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp
index 09abb953ba..f02e902468 100644
--- a/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp
+++ b/tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@@ -30,12 +31,16 @@
#include <QtTest/QtTest>
#include <qglobal.h>
+#ifdef Q_OS_WIN
+#include <qt_windows.h>
+#endif
class tst_QGetPutEnv : public QObject
{
Q_OBJECT
private slots:
void getSetCheck();
+ void encoding();
void intValue_data();
void intValue();
};
@@ -53,7 +58,11 @@ void tst_QGetPutEnv::getSetCheck()
QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0);
QVERIFY(!ok);
QByteArray result = qgetenv(varName);
- QCOMPARE(result, QByteArray());
+ QVERIFY(result.isNull());
+ QString sresult = qEnvironmentVariable(varName);
+ QVERIFY(sresult.isNull());
+ sresult = qEnvironmentVariable(varName, "hello");
+ QCOMPARE(sresult, QString("hello"));
#ifndef Q_OS_WIN
QVERIFY(qputenv(varName, "")); // deletes varName instead of making it empty, on Windows
@@ -64,6 +73,16 @@ void tst_QGetPutEnv::getSetCheck()
QCOMPARE(qEnvironmentVariableIntValue(varName), 0);
QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0);
QVERIFY(!ok);
+
+ result = qgetenv(varName);
+ QVERIFY(!result.isNull());
+ QCOMPARE(result, QByteArray());
+ sresult = qEnvironmentVariable(varName);
+ QVERIFY(!sresult.isNull());
+ QCOMPARE(sresult, QString());
+ sresult = qEnvironmentVariable(varName, "hello");
+ QVERIFY(!sresult.isNull());
+ QCOMPARE(sresult, QString());
#endif
QVERIFY(qputenv(varName, QByteArray("supervalue")));
@@ -76,19 +95,61 @@ void tst_QGetPutEnv::getSetCheck()
QVERIFY(!ok);
result = qgetenv(varName);
QCOMPARE(result, QByteArrayLiteral("supervalue"));
+ sresult = qEnvironmentVariable(varName);
+ QCOMPARE(sresult, QString("supervalue"));
+ sresult = qEnvironmentVariable(varName, "hello");
+ QCOMPARE(sresult, QString("supervalue"));
qputenv(varName,QByteArray());
// Now test qunsetenv
QVERIFY(qunsetenv(varName));
- QVERIFY(!qEnvironmentVariableIsSet(varName));
+ QVERIFY(!qEnvironmentVariableIsSet(varName)); // note: might fail on some systems!
QVERIFY(qEnvironmentVariableIsEmpty(varName));
ok = true;
QCOMPARE(qEnvironmentVariableIntValue(varName), 0);
QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0);
QVERIFY(!ok);
+
result = qgetenv(varName);
- QCOMPARE(result, QByteArray());
+ QVERIFY(result.isNull());
+ sresult = qEnvironmentVariable(varName);
+ QVERIFY(sresult.isNull());
+ sresult = qEnvironmentVariable(varName, "hello");
+ QCOMPARE(sresult, QString("hello"));
+}
+
+void tst_QGetPutEnv::encoding()
+{
+ // The test string is:
+ // U+0061 LATIN SMALL LETTER A
+ // U+00E1 LATIN SMALL LETTER A WITH ACUTE
+ // U+03B1 GREEK SMALL LETTER ALPHA
+ // U+0430 CYRILLIC SMALL LETTER A
+ // This has letters in three different scripts, so no locale besides
+ // UTF-8 is able handle them all.
+ // The LATIN SMALL LETTER A WITH ACUTE is NFC for NFD:
+ // U+0061 U+0301 LATIN SMALL LETTER A + COMBINING ACUTE ACCENT
+
+ const char varName[] = "should_not_exist";
+ static const wchar_t rawvalue[] = { 'a', 0x00E1, 0x03B1, 0x0430, 0 };
+ QString value = QString::fromWCharArray(rawvalue);
+
+#if defined(Q_OS_WINRT)
+ QSKIP("Test cannot be run on this platform");
+#elif defined(Q_OS_WIN)
+ const wchar_t wvarName[] = L"should_not_exist";
+ _wputenv_s(wvarName, rawvalue);
+#else
+ // confirm the locale is UTF-8
+ if (value.toLocal8Bit() != "a\xc3\xa1\xce\xb1\xd0\xb0")
+ QSKIP("Locale is not UTF-8, cannot test");
+
+ qputenv(varName, QFile::encodeName(value));
+#endif
+
+ QVERIFY(qEnvironmentVariableIsSet(varName));
+ QCOMPARE(qEnvironmentVariable(varName), value);
}
void tst_QGetPutEnv::intValue_data()
diff --git a/tests/auto/corelib/global/qrandomgenerator/qrandomgenerator.pro b/tests/auto/corelib/global/qrandomgenerator/qrandomgenerator.pro
new file mode 100644
index 0000000000..0307b0c1eb
--- /dev/null
+++ b/tests/auto/corelib/global/qrandomgenerator/qrandomgenerator.pro
@@ -0,0 +1,4 @@
+CONFIG += testcase
+TARGET = tst_qrandomgenerator
+QT = core-private testlib
+SOURCES = tst_qrandomgenerator.cpp
diff --git a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
new file mode 100644
index 0000000000..220ec9a2f8
--- /dev/null
+++ b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
@@ -0,0 +1,960 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <qlinkedlist.h>
+#include <qobject.h>
+#include <qrandom.h>
+#include <qvector.h>
+#include <private/qrandom_p.h>
+
+#include <algorithm>
+#include <random>
+
+#if !QT_CONFIG(getentropy) && (defined(Q_OS_BSD4) || defined(Q_OS_WIN))
+# define HAVE_FALLBACK_ENGINE
+#endif
+
+#define COMMA ,
+#define QVERIFY_3TIMES(statement) \
+ do {\
+ if (!static_cast<bool>(statement))\
+ if (!static_cast<bool>(statement))\
+ if (!QTest::qVerify(static_cast<bool>(statement), #statement, "3rd try", __FILE__, __LINE__))\
+ return;\
+ } while (0)
+
+// values chosen at random
+static const quint32 RandomValue32 = 0x4d1169f1U;
+static const quint64 RandomValue64 = Q_UINT64_C(0x3ce63161b998aa91);
+static const double RandomValueFP = double(0.3010463714599609f);
+
+static void setRNGControl(uint v)
+{
+#ifdef QT_BUILD_INTERNAL
+ qt_randomdevice_control.store(v);
+#else
+ Q_UNUSED(v);
+#endif
+}
+
+class tst_QRandomGenerator : public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void cleanup() { setRNGControl(0); }
+
+private slots:
+ void basics();
+ void knownSequence();
+ void discard();
+ void copying();
+ void copyingGlobal();
+ void copyingSystem();
+ void systemRng();
+ void securelySeeding();
+
+ void generate32_data();
+ void generate32();
+ void generate64_data() { generate32_data(); }
+ void generate64();
+ void quality_data() { generate32_data(); }
+ void quality();
+ void fillRangeUInt_data() { generate32_data(); }
+ void fillRangeUInt();
+ void fillRangeULong_data() { generate32_data(); }
+ void fillRangeULong();
+ void fillRangeULLong_data() { generate32_data(); }
+ void fillRangeULLong();
+ void generateUInt_data() { generate32_data(); }
+ void generateUInt();
+ void generateULLong_data() { generate32_data(); }
+ void generateULLong();
+ void generateNonContiguous_data() { generate32_data(); }
+ void generateNonContiguous();
+
+ void bounded_data();
+ void bounded();
+ void boundedQuality_data() { generate32_data(); }
+ void boundedQuality();
+
+ void generateReal_data() { generate32_data(); }
+ void generateReal();
+ void qualityReal_data() { generate32_data(); }
+ void qualityReal();
+
+ void seedStdRandomEngines();
+ void stdUniformIntDistribution_data();
+ void stdUniformIntDistribution();
+ void stdGenerateCanonical_data() { generateReal_data(); }
+ void stdGenerateCanonical();
+ void stdUniformRealDistribution_data();
+ void stdUniformRealDistribution();
+ void stdRandomDistributions();
+};
+
+// The first 20 results of the sequence:
+static const quint32 defaultRngResults[] = {
+ 853323747U, 2396352728U, 3025954838U, 2985633182U, 2815751046U,
+ 340588426U, 3587208406U, 298087538U, 2912478009U, 3642122814U,
+ 3202916223U, 799257577U, 1872145992U, 639469699U, 3201121432U,
+ 2388658094U, 1735523408U, 2215232359U, 668106566U, 2554687763U
+};
+
+
+using namespace std;
+QT_WARNING_DISABLE_GCC("-Wfloat-equal")
+QT_WARNING_DISABLE_CLANG("-Wfloat-equal")
+
+struct RandomGenerator : public QRandomGenerator
+{
+ RandomGenerator(uint control)
+ : QRandomGenerator(control ?
+ QRandomGenerator(control & RandomDataMask) :
+ *QRandomGenerator::global())
+ {
+ setRNGControl(control);
+ }
+};
+
+void tst_QRandomGenerator::basics()
+{
+ // default constructible
+ QRandomGenerator rng;
+
+ // copyable && movable
+ rng = rng;
+ rng = std::move(rng);
+
+ // 64-bit
+ QRandomGenerator64 rng64;
+ rng64 = rng64;
+ rng64 = std::move(rng64);
+
+ // 32- and 64-bit should be interchangeable:
+ rng = rng64;
+ rng64 = rng;
+ rng = std::move(rng64);
+ rng64 = std::move(rng);
+
+ rng = QRandomGenerator64::securelySeeded();
+ rng64 = QRandomGenerator::securelySeeded();
+
+ // access global
+ QRandomGenerator *global = QRandomGenerator::global();
+ QRandomGenerator globalCopy = *global;
+ globalCopy = *global;
+ QRandomGenerator64 *global64 = QRandomGenerator64::global();
+ QRandomGenerator64 globalCopy64 = *global64;
+ globalCopy64 = *global64;
+
+ // access system
+ QRandomGenerator *system = QRandomGenerator::system();
+ QRandomGenerator systemRng = *system;
+ systemRng = *system;
+
+ QRandomGenerator64 *system64 = QRandomGenerator64::system();
+ QRandomGenerator64 systemRng64 = *system64;
+ systemRng64 = *system64;
+
+ Q_STATIC_ASSERT(std::is_same<decltype(rng64.generate()) COMMA quint64>::value);
+ Q_STATIC_ASSERT(std::is_same<decltype(system64->generate()) COMMA quint64>::value);
+}
+
+void tst_QRandomGenerator::knownSequence()
+{
+ QRandomGenerator rng;
+ for (quint32 x : defaultRngResults)
+ QCOMPARE(rng(), x);
+
+ // should work again if we reseed it
+ rng.seed();
+ for (quint32 x : defaultRngResults)
+ QCOMPARE(rng(), x);
+}
+
+void tst_QRandomGenerator::discard()
+{
+ QRandomGenerator rng;
+ rng.discard(1);
+ QCOMPARE(rng(), defaultRngResults[1]);
+
+ rng.discard(9);
+ QCOMPARE(rng(), defaultRngResults[11]);
+}
+
+void tst_QRandomGenerator::copying()
+{
+ QRandomGenerator rng1;
+ QRandomGenerator rng2 = rng1;
+ QCOMPARE(rng1, rng2);
+
+ quint32 samples[20];
+ rng1.fillRange(samples);
+
+ // not equal anymore
+ QVERIFY(rng1 != rng2);
+
+ // should produce the same sequence, whichever it was
+ for (quint32 x : samples)
+ QCOMPARE(rng2(), x);
+
+ // now they should compare equal again
+ QCOMPARE(rng1, rng2);
+}
+
+void tst_QRandomGenerator::copyingGlobal()
+{
+ QRandomGenerator &global = *QRandomGenerator::global();
+ QRandomGenerator copy = global;
+ QCOMPARE(copy, global);
+ QCOMPARE(global, copy);
+
+ quint32 samples[20];
+ global.fillRange(samples);
+
+ // not equal anymore
+ QVERIFY(copy != global);
+
+ // should produce the same sequence, whichever it was
+ for (quint32 x : samples)
+ QCOMPARE(copy(), x);
+
+ // equal again
+ QCOMPARE(copy, global);
+ QCOMPARE(global, copy);
+}
+
+void tst_QRandomGenerator::copyingSystem()
+{
+ QRandomGenerator &system = *QRandomGenerator::system();
+ QRandomGenerator copy = system;
+ QRandomGenerator copy2 = copy;
+ copy2 = copy;
+ QCOMPARE(system, copy);
+ QCOMPARE(copy, copy2);
+
+ quint32 samples[20];
+ copy2.fillRange(samples);
+
+ // they still compre equally
+ QCOMPARE(system, copy);
+ QCOMPARE(copy, copy2);
+
+ // should NOT produce the same sequence, whichever it was
+ int sameCount = 0;
+ for (quint32 x : samples)
+ sameCount += (copy() == x);
+ QVERIFY(sameCount < 20);
+
+ QCOMPARE(system, copy);
+ QCOMPARE(copy, copy2);
+}
+
+void tst_QRandomGenerator::systemRng()
+{
+ QRandomGenerator *rng = QRandomGenerator::system();
+ rng->generate();
+ rng->generate64();
+ rng->generateDouble();
+ rng->bounded(100);
+ rng->bounded(100U);
+
+#ifdef QT_BUILD_INTERNAL
+ quint32 setpoint = std::numeric_limits<int>::max();
+ ++setpoint;
+ quint64 setpoint64 = quint64(setpoint) << 32 | setpoint;
+ setRNGControl(SetRandomData | setpoint);
+
+ QCOMPARE(rng->generate(), setpoint);
+ QCOMPARE(rng->generate64(), setpoint64);
+ QCOMPARE(rng->generateDouble(), ldexp(setpoint64, -64));
+ QCOMPARE(rng->bounded(100), 50);
+#endif
+}
+
+void tst_QRandomGenerator::securelySeeding()
+{
+ QRandomGenerator rng1 = QRandomGenerator::securelySeeded();
+ QRandomGenerator rng2 = QRandomGenerator::securelySeeded();
+
+ quint32 samples[20];
+ rng1.fillRange(samples);
+
+ // should NOT produce the same sequence, whichever it was
+ int sameCount = 0;
+ for (quint32 x : samples)
+ sameCount += (rng2() == x);
+ QVERIFY(sameCount < 20);
+}
+
+void tst_QRandomGenerator::generate32_data()
+{
+ QTest::addColumn<uint>("control");
+ QTest::newRow("fixed") << (RandomValue32 & RandomDataMask);
+ QTest::newRow("global") << 0U;
+#ifdef QT_BUILD_INTERNAL
+ if (qt_has_hwrng())
+ QTest::newRow("hwrng") << uint(UseSystemRNG);
+ QTest::newRow("system") << uint(UseSystemRNG | SkipHWRNG);
+# ifdef HAVE_FALLBACK_ENGINE
+ QTest::newRow("system-fallback") << uint(UseSystemRNG | SkipHWRNG | SkipSystemRNG);
+# endif
+#endif
+}
+
+void tst_QRandomGenerator::generate32()
+{
+ QFETCH(uint, control);
+ RandomGenerator rng(control);
+
+ for (int i = 0; i < 4; ++i) {
+ QVERIFY_3TIMES([&] {
+ quint32 value = rng.generate();
+ return value != 0 && value != RandomValue32;
+ }());
+ }
+
+ // and should hopefully be different from repeated calls
+ for (int i = 0; i < 4; ++i)
+ QVERIFY_3TIMES(rng.generate() != rng.generate());
+}
+
+void tst_QRandomGenerator::generate64()
+{
+ QFETCH(uint, control);
+ RandomGenerator rng(control);
+
+ QVERIFY_3TIMES(rng.generate64() > std::numeric_limits<quint32>::max());
+ for (int i = 0; i < 4; ++i) {
+ QVERIFY_3TIMES([&] {
+ quint64 value = rng.generate64();
+ return value != 0 && value != RandomValue32 && value != RandomValue64;
+ }());
+ }
+
+ // and should hopefully be different from repeated calls
+ for (int i = 0; i < 4; ++i)
+ QVERIFY_3TIMES(rng.generate64() != rng.generate64());
+ for (int i = 0; i < 4; ++i)
+ QVERIFY_3TIMES(rng.generate() != quint32(rng.generate64()));
+ for (int i = 0; i < 4; ++i)
+ QVERIFY_3TIMES(rng.generate() != (rng.generate64() >> 32));
+}
+
+void tst_QRandomGenerator::quality()
+{
+ enum {
+ BufferSize = 2048,
+ BufferCount = BufferSize / sizeof(quint32),
+
+ // if the distribution were perfect, each byte in the buffer would
+ // appear exactly:
+ PerfectDistribution = BufferSize / (UCHAR_MAX + 1),
+
+ // The chance of a value appearing N times above its perfect
+ // distribution is the same as it appearing N times in a row:
+ // N Probability
+ // 1 100%
+ // 2 0.390625%
+ // 3 15.25 in a million
+ // 4 59.60 in a billion
+ // 8 5.421e-20
+ // 16 2.938e-39
+
+ AcceptableThreshold = 4 * PerfectDistribution,
+ FailureThreshold = 16 * PerfectDistribution
+ };
+ Q_STATIC_ASSERT(FailureThreshold > AcceptableThreshold);
+
+ QFETCH(uint, control);
+ if (control & RandomDataMask)
+ return;
+ RandomGenerator rng(control);
+
+ int histogram[UCHAR_MAX + 1];
+ memset(histogram, 0, sizeof(histogram));
+
+ {
+ // test the quality of the generator
+ quint32 buffer[BufferCount];
+ memset(buffer, 0xcc, sizeof(buffer));
+ generate_n(buffer, +BufferCount, [&] { return rng.generate(); });
+
+ quint8 *ptr = reinterpret_cast<quint8 *>(buffer);
+ quint8 *end = ptr + sizeof(buffer);
+ for ( ; ptr != end; ++ptr)
+ histogram[*ptr]++;
+ }
+
+ for (uint i = 0; i < sizeof(histogram)/sizeof(histogram[0]); ++i) {
+ int v = histogram[i];
+ if (v > AcceptableThreshold)
+ qDebug() << i << "above threshold:" << v;
+ QVERIFY2(v < FailureThreshold, QByteArray::number(i));
+ }
+ qDebug() << "Average:" << (std::accumulate(begin(histogram), end(histogram), 0) / (1. * (UCHAR_MAX + 1)))
+ << "(expected" << int(PerfectDistribution) << "ideally)"
+ << "Max:" << *std::max_element(begin(histogram), end(histogram))
+ << "at" << std::max_element(begin(histogram), end(histogram)) - histogram
+ << "Min:" << *std::min_element(begin(histogram), end(histogram))
+ << "at" << std::min_element(begin(histogram), end(histogram)) - histogram;
+}
+
+template <typename T> void fillRange_template()
+{
+ QFETCH(uint, control);
+ RandomGenerator rng(control);
+
+ for (int i = 0; i < 4; ++i) {
+ QVERIFY_3TIMES([&] {
+ T value[1] = { RandomValue32 };
+ rng.fillRange(value);
+ return value[0] != 0 && value[0] != RandomValue32;
+ }());
+ }
+
+ for (int i = 0; i < 4; ++i) {
+ QVERIFY_3TIMES([&] {
+ T array[2] = {};
+ rng.fillRange(array);
+ return array[0] != array[1];
+ }());
+ }
+
+ if (sizeof(T) > sizeof(quint32)) {
+ // just to shut up a warning about shifting uint more than the width
+ enum { Shift = sizeof(T) / 2 * CHAR_BIT };
+ QVERIFY_3TIMES([&] {
+ T value[1] = { };
+ rng.fillRange(value);
+ return quint32(value[0] >> Shift) != quint32(value[0]);
+ }());
+ }
+
+ // fill in a longer range
+ auto longerArrayCheck = [&] {
+ T array[32];
+ memset(array, 0, sizeof(array));
+ rng.fillRange(array);
+ if (sizeof(T) == sizeof(RandomValue64)
+ && find(begin(array), end(array), RandomValue64) != end(array))
+ return false;
+ return find(begin(array), end(array), 0) == end(array) &&
+ find(begin(array), end(array), RandomValue32) == end(array);
+ };
+ QVERIFY_3TIMES(longerArrayCheck());
+}
+
+void tst_QRandomGenerator::fillRangeUInt() { fillRange_template<uint>(); }
+void tst_QRandomGenerator::fillRangeULong() { fillRange_template<ulong>(); }
+void tst_QRandomGenerator::fillRangeULLong() { fillRange_template<qulonglong>(); }
+
+template <typename T> void generate_template()
+{
+ QFETCH(uint, control);
+ RandomGenerator rng(control);
+
+ // almost the same as fillRange, but limited to 32 bits
+ for (int i = 0; i < 4; ++i) {
+ QVERIFY_3TIMES([&] {
+ T value[1] = { RandomValue32 };
+ QRandomGenerator().generate(begin(value), end(value));
+ return value[0] != 0 && value[0] != RandomValue32
+ && value[0] <= numeric_limits<quint32>::max();
+ }());
+ }
+
+ // fill in a longer range
+ auto longerArrayCheck = [&] {
+ T array[72] = {}; // at least 256 bytes
+ QRandomGenerator().generate(begin(array), end(array));
+ return find_if(begin(array), end(array), [&](T cur) {
+ return cur == 0 || cur == RandomValue32 ||
+ cur == RandomValue64 || cur > numeric_limits<quint32>::max();
+ }) == end(array);
+ };
+ QVERIFY_3TIMES(longerArrayCheck());
+}
+
+void tst_QRandomGenerator::generateUInt() { generate_template<uint>(); }
+void tst_QRandomGenerator::generateULLong() { generate_template<qulonglong>(); }
+
+void tst_QRandomGenerator::generateNonContiguous()
+{
+ QFETCH(uint, control);
+ RandomGenerator rng(control);
+
+ QLinkedList<quint64> list = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ auto longerArrayCheck = [&] {
+ QRandomGenerator().generate(list.begin(), list.end());
+ return find_if(list.begin(), list.end(), [&](quint64 cur) {
+ return cur == 0 || cur == RandomValue32 ||
+ cur == RandomValue64 || cur > numeric_limits<quint32>::max();
+ }) == list.end();
+ };
+ QVERIFY_3TIMES(longerArrayCheck());
+}
+
+void tst_QRandomGenerator::bounded_data()
+{
+#ifndef QT_BUILD_INTERNAL
+ QSKIP("Test only possible in developer builds");
+#endif
+
+ QTest::addColumn<uint>("control");
+ QTest::addColumn<quint32>("sup");
+ QTest::addColumn<quint32>("expected");
+
+ auto newRow = [&](quint32 val, quint32 sup) {
+ // calculate the scaled value
+ quint64 scaled = val;
+ scaled <<= 32;
+ scaled /= sup;
+ unsigned shifted = unsigned(scaled);
+ Q_ASSERT(val < sup);
+ Q_ASSERT((shifted & RandomDataMask) == shifted);
+
+ unsigned control = SetRandomData | shifted;
+ QTest::addRow("%u,%u", val, sup) << control << sup << val;
+ };
+
+ // useless: we can only generate zeroes:
+ newRow(0, 1);
+
+ newRow(25, 200);
+ newRow(50, 200);
+ newRow(75, 200);
+}
+
+void tst_QRandomGenerator::bounded()
+{
+ QFETCH(uint, control);
+ QFETCH(quint32, sup);
+ QFETCH(quint32, expected);
+ RandomGenerator rng(control);
+
+ quint32 value = rng.bounded(sup);
+ QVERIFY(value < sup);
+ QCOMPARE(value, expected);
+
+ int ivalue = rng.bounded(sup);
+ QVERIFY(ivalue < int(sup));
+ QCOMPARE(ivalue, int(expected));
+
+ // confirm only the bound now
+ setRNGControl(control & (SkipHWRNG|SkipSystemRNG|UseSystemRNG));
+ value = rng.bounded(sup);
+ QVERIFY(value < sup);
+
+ value = rng.bounded(sup / 2, 3 * sup / 2);
+ QVERIFY(value >= sup / 2);
+ QVERIFY(value < 3 * sup / 2);
+
+ ivalue = rng.bounded(-int(sup), int(sup));
+ QVERIFY(ivalue >= -int(sup));
+ QVERIFY(ivalue < int(sup));
+
+ // wholly negative range
+ ivalue = rng.bounded(-int(sup), 0);
+ QVERIFY(ivalue >= -int(sup));
+ QVERIFY(ivalue < 0);
+}
+
+void tst_QRandomGenerator::boundedQuality()
+{
+ enum { Bound = 283 }; // a prime number
+ enum {
+ BufferCount = Bound * 32,
+
+ // if the distribution were perfect, each byte in the buffer would
+ // appear exactly:
+ PerfectDistribution = BufferCount / Bound,
+
+ // The chance of a value appearing N times above its perfect
+ // distribution is the same as it appearing N times in a row:
+ // N Probability
+ // 1 100%
+ // 2 0.390625%
+ // 3 15.25 in a million
+ // 4 59.60 in a billion
+ // 8 5.421e-20
+ // 16 2.938e-39
+
+ AcceptableThreshold = 4 * PerfectDistribution,
+ FailureThreshold = 16 * PerfectDistribution
+ };
+ Q_STATIC_ASSERT(FailureThreshold > AcceptableThreshold);
+
+ QFETCH(uint, control);
+ if (control & RandomDataMask)
+ return;
+ RandomGenerator rng(control);
+
+ int histogram[Bound];
+ memset(histogram, 0, sizeof(histogram));
+
+ {
+ // test the quality of the generator
+ QVector<quint32> buffer(BufferCount, 0xcdcdcdcd);
+ generate(buffer.begin(), buffer.end(), [&] { return rng.bounded(Bound); });
+
+ for (quint32 value : qAsConst(buffer)) {
+ QVERIFY(value < Bound);
+ histogram[value]++;
+ }
+ }
+
+ for (unsigned i = 0; i < sizeof(histogram)/sizeof(histogram[0]); ++i) {
+ int v = histogram[i];
+ if (v > AcceptableThreshold)
+ qDebug() << i << "above threshold:" << v;
+ QVERIFY2(v < FailureThreshold, QByteArray::number(i));
+ }
+
+ qDebug() << "Average:" << (std::accumulate(begin(histogram), end(histogram), 0) / qreal(Bound))
+ << "(expected" << int(PerfectDistribution) << "ideally)"
+ << "Max:" << *std::max_element(begin(histogram), end(histogram))
+ << "at" << std::max_element(begin(histogram), end(histogram)) - histogram
+ << "Min:" << *std::min_element(begin(histogram), end(histogram))
+ << "at" << std::min_element(begin(histogram), end(histogram)) - histogram;
+}
+
+void tst_QRandomGenerator::generateReal()
+{
+ QFETCH(uint, control);
+ RandomGenerator rng(control);
+
+ for (int i = 0; i < 4; ++i) {
+ QVERIFY_3TIMES([&] {
+ qreal value = rng.generateDouble();
+ return value >= 0 && value < 1 && value != RandomValueFP;
+ }());
+ }
+
+ // and should hopefully be different from repeated calls
+ for (int i = 0; i < 4; ++i)
+ QVERIFY_3TIMES(rng.generateDouble() != rng.generateDouble());
+}
+
+void tst_QRandomGenerator::qualityReal()
+{
+ QFETCH(uint, control);
+ if (control & RandomDataMask)
+ return;
+ RandomGenerator rng(control);
+
+ enum {
+ SampleSize = 160,
+
+ // Expected value: sample size times proportion of the range:
+ PerfectOctile = SampleSize / 8,
+ PerfectHalf = SampleSize / 2,
+
+ // Variance is (1 - proportion of range) * expected; sqrt() for standard deviations.
+ // Should usually be within twice that and almost never outside four times:
+ RangeHalf = 25, // floor(4 * sqrt((1 - 0.5) * PerfectHalf))
+ RangeOctile = 16 // floor(4 * sqrt((1 - 0.125) * PerfectOctile))
+ };
+
+ double data[SampleSize];
+ std::generate(std::begin(data), std::end(data), [&rng] { return rng.generateDouble(); });
+
+ int aboveHalf = 0;
+ int belowOneEighth = 0;
+ int aboveSevenEighths = 0;
+ for (double x : data) {
+ aboveHalf += x >= 0.5;
+ belowOneEighth += x < 0.125;
+ aboveSevenEighths += x >= 0.875;
+
+ // these are strict requirements
+ QVERIFY(x >= 0);
+ QVERIFY(x < 1);
+ }
+
+ qInfo("Halfway distribution: %.1f - %.1f", 100. * aboveHalf / SampleSize, 100 - 100. * aboveHalf / SampleSize);
+ qInfo("%.1f below 1/8 (expected 12.5%% ideally)", 100. * belowOneEighth / SampleSize);
+ qInfo("%.1f above 7/8 (expected 12.5%% ideally)", 100. * aboveSevenEighths / SampleSize);
+
+ QVERIFY(aboveHalf < PerfectHalf + RangeHalf);
+ QVERIFY(aboveHalf > PerfectHalf - RangeHalf);
+ QVERIFY(aboveSevenEighths < PerfectOctile + RangeOctile);
+ QVERIFY(aboveSevenEighths > PerfectOctile - RangeOctile);
+ QVERIFY(belowOneEighth < PerfectOctile + RangeOctile);
+ QVERIFY(belowOneEighth > PerfectOctile - RangeOctile);
+}
+
+template <typename Engine> void seedStdRandomEngine()
+{
+ {
+ QRandomGenerator &rd = *QRandomGenerator::system();
+ Engine e(rd);
+ QVERIFY_3TIMES(e() != 0);
+
+ e.seed(rd);
+ QVERIFY_3TIMES(e() != 0);
+ }
+ {
+ QRandomGenerator64 &rd = *QRandomGenerator64::system();
+ Engine e(rd);
+ QVERIFY_3TIMES(e() != 0);
+
+ e.seed(rd);
+ QVERIFY_3TIMES(e() != 0);
+ }
+}
+
+void tst_QRandomGenerator::seedStdRandomEngines()
+{
+ seedStdRandomEngine<std::default_random_engine>();
+ seedStdRandomEngine<std::minstd_rand0>();
+ seedStdRandomEngine<std::minstd_rand>();
+ seedStdRandomEngine<std::mt19937>();
+ seedStdRandomEngine<std::mt19937_64>();
+ seedStdRandomEngine<std::ranlux24_base>();
+ seedStdRandomEngine<std::ranlux48_base>();
+ seedStdRandomEngine<std::ranlux24>();
+ seedStdRandomEngine<std::ranlux48>();
+}
+
+void tst_QRandomGenerator::stdUniformIntDistribution_data()
+{
+#ifndef QT_BUILD_INTERNAL
+ QSKIP("Test only possible in developer builds");
+#endif
+
+ QTest::addColumn<uint>("control");
+ QTest::addColumn<quint32>("max");
+
+ auto newRow = [&](quint32 max) {
+#ifdef QT_BUILD_INTERNAL
+ if (qt_has_hwrng())
+ QTest::addRow("hwrng:%u", max) << uint(UseSystemRNG) << max;
+ QTest::addRow("system:%u", max) << uint(UseSystemRNG | SkipHWRNG) << max;
+# ifdef HAVE_FALLBACK_ENGINE
+ QTest::addRow("system-fallback:%u", max) << uint(UseSystemRNG | SkipHWRNG | SkipSystemRNG) << max;
+# endif
+#endif
+ QTest::addRow("global:%u", max) << 0U << max;
+ };
+
+ // useless: we can only generate zeroes:
+ newRow(0);
+
+ newRow(1);
+ newRow(199);
+ newRow(numeric_limits<quint32>::max());
+}
+
+void tst_QRandomGenerator::stdUniformIntDistribution()
+{
+ QFETCH(uint, control);
+ QFETCH(quint32, max);
+ RandomGenerator rng(control);
+
+ {
+ QRandomGenerator rd;
+ {
+ std::uniform_int_distribution<quint32> dist(0, max);
+ quint32 value = dist(rd);
+ QVERIFY(value >= dist.min());
+ QVERIFY(value <= dist.max());
+ }
+ if ((3 * max / 2) > max) {
+ std::uniform_int_distribution<quint32> dist(max / 2, 3 * max / 2);
+ quint32 value = dist(rd);
+ QVERIFY(value >= dist.min());
+ QVERIFY(value <= dist.max());
+ }
+
+ {
+ std::uniform_int_distribution<quint64> dist(0, quint64(max) << 32);
+ quint64 value = dist(rd);
+ QVERIFY(value >= dist.min());
+ QVERIFY(value <= dist.max());
+ }
+ {
+ std::uniform_int_distribution<quint64> dist(max / 2, 3 * quint64(max) / 2);
+ quint64 value = dist(rd);
+ QVERIFY(value >= dist.min());
+ QVERIFY(value <= dist.max());
+ }
+ }
+
+ {
+ QRandomGenerator64 rd;
+ {
+ std::uniform_int_distribution<quint32> dist(0, max);
+ quint32 value = dist(rd);
+ QVERIFY(value >= dist.min());
+ QVERIFY(value <= dist.max());
+ }
+ if ((3 * max / 2) > max) {
+ std::uniform_int_distribution<quint32> dist(max / 2, 3 * max / 2);
+ quint32 value = dist(rd);
+ QVERIFY(value >= dist.min());
+ QVERIFY(value <= dist.max());
+ }
+
+ {
+ std::uniform_int_distribution<quint64> dist(0, quint64(max) << 32);
+ quint64 value = dist(rd);
+ QVERIFY(value >= dist.min());
+ QVERIFY(value <= dist.max());
+ }
+ {
+ std::uniform_int_distribution<quint64> dist(max / 2, 3 * quint64(max) / 2);
+ quint64 value = dist(rd);
+ QVERIFY(value >= dist.min());
+ QVERIFY(value <= dist.max());
+ }
+ }
+}
+
+void tst_QRandomGenerator::stdGenerateCanonical()
+{
+#if defined(Q_CC_MSVC) && Q_CC_MSVC < 1900
+ // see https://connect.microsoft.com/VisualStudio/feedback/details/811611
+ QSKIP("MSVC 2013's std::generate_canonical is broken");
+#else
+ QFETCH(uint, control);
+ RandomGenerator rng(control);
+
+ for (int i = 0; i < 4; ++i) {
+ QVERIFY_3TIMES([&] {
+ qreal value = std::generate_canonical<qreal COMMA 32>(rng);
+ return value > 0 && value < 1 && value != RandomValueFP;
+ }());
+ }
+
+ // and should hopefully be different from repeated calls
+ for (int i = 0; i < 4; ++i)
+ QVERIFY_3TIMES(std::generate_canonical<qreal COMMA 32>(rng) !=
+ std::generate_canonical<qreal COMMA 32>(rng));
+#endif
+}
+
+void tst_QRandomGenerator::stdUniformRealDistribution_data()
+{
+#ifndef QT_BUILD_INTERNAL
+ QSKIP("Test only possible in developer builds");
+#endif
+
+ QTest::addColumn<uint>("control");
+ QTest::addColumn<double>("min");
+ QTest::addColumn<double>("sup");
+
+ auto newRow = [&](double min, double sup) {
+#ifdef QT_BUILD_INTERNAL
+ if (qt_has_hwrng())
+ QTest::addRow("hwrng:%g-%g", min, sup) << uint(UseSystemRNG) << min << sup;
+ QTest::addRow("system:%g-%g", min, sup) << uint(UseSystemRNG | SkipHWRNG) << min << sup;
+# ifdef HAVE_FALLBACK_ENGINE
+ QTest::addRow("system-fallback:%g-%g", min, sup) << uint(UseSystemRNG | SkipHWRNG | SkipSystemRNG) << min << sup;
+# endif
+#endif
+ QTest::addRow("global:%g-%g", min, sup) << 0U << min << sup;
+ };
+
+ newRow(0, 0); // useless: we can only generate zeroes
+ newRow(0, 1); // canonical
+ newRow(0, 200);
+ newRow(0, numeric_limits<quint32>::max() + 1.);
+ newRow(0, numeric_limits<quint64>::max() + 1.);
+ newRow(-1, 1.6);
+}
+
+void tst_QRandomGenerator::stdUniformRealDistribution()
+{
+ QFETCH(uint, control);
+ QFETCH(double, min);
+ QFETCH(double, sup);
+ RandomGenerator rng(control & (SkipHWRNG|SkipSystemRNG|UseSystemRNG));
+
+ {
+ QRandomGenerator rd;
+ {
+ std::uniform_real_distribution<double> dist(min, sup);
+ double value = dist(rd);
+ QVERIFY(value >= dist.min());
+ if (min != sup)
+ QVERIFY(value < dist.max());
+ }
+ }
+
+ {
+ QRandomGenerator64 rd;
+ {
+ std::uniform_real_distribution<double> dist(min, sup);
+ double value = dist(rd);
+ QVERIFY(value >= dist.min());
+ if (min != sup)
+ QVERIFY(value < dist.max());
+ }
+ }
+}
+
+template <typename Generator> void stdRandomDistributions_template()
+{
+ Generator rd;
+
+ std::bernoulli_distribution()(rd);
+
+ std::binomial_distribution<quint32>()(rd);
+ std::binomial_distribution<quint64>()(rd);
+
+ std::negative_binomial_distribution<quint32>()(rd);
+ std::negative_binomial_distribution<quint64>()(rd);
+
+ std::poisson_distribution<int>()(rd);
+ std::poisson_distribution<qint64>()(rd);
+
+ std::normal_distribution<qreal>()(rd);
+
+ {
+ std::discrete_distribution<int> discrete{0, 1, 1, 10000, 2};
+ QVERIFY(discrete(rd) != 0);
+ QVERIFY_3TIMES(discrete(rd) == 3);
+ }
+}
+
+void tst_QRandomGenerator::stdRandomDistributions()
+{
+ // just a compile check for some of the distributions, besides
+ // std::uniform_int_distribution and std::uniform_real_distribution (tested
+ // above)
+
+ stdRandomDistributions_template<QRandomGenerator>();
+ stdRandomDistributions_template<QRandomGenerator64>();
+}
+
+QTEST_APPLESS_MAIN(tst_QRandomGenerator)
+
+#include "tst_qrandomgenerator.moc"
diff --git a/tests/auto/corelib/global/qtendian/qtendian.pro b/tests/auto/corelib/global/qtendian/qtendian.pro
index 214c706ca5..890976d26c 100644
--- a/tests/auto/corelib/global/qtendian/qtendian.pro
+++ b/tests/auto/corelib/global/qtendian/qtendian.pro
@@ -1,4 +1,4 @@
CONFIG += testcase
TARGET = tst_qtendian
-QT = core testlib
+QT = core core-private testlib
SOURCES = tst_qtendian.cpp
diff --git a/tests/auto/corelib/global/qtendian/tst_qtendian.cpp b/tests/auto/corelib/global/qtendian/tst_qtendian.cpp
index 915e40954b..6934818dcf 100644
--- a/tests/auto/corelib/global/qtendian/tst_qtendian.cpp
+++ b/tests/auto/corelib/global/qtendian/tst_qtendian.cpp
@@ -29,6 +29,7 @@
#include <QtTest/QtTest>
#include <QtCore/qendian.h>
+#include <QtCore/private/qendian_p.h>
class tst_QtEndian: public QObject
@@ -41,6 +42,11 @@ private slots:
void toBigEndian();
void toLittleEndian();
+
+ void endianIntegers_data();
+ void endianIntegers();
+
+ void endianBitfields();
};
struct TestData
@@ -129,5 +135,83 @@ void tst_QtEndian::toLittleEndian()
#undef ENDIAN_TEST
+void tst_QtEndian::endianIntegers_data()
+{
+ QTest::addColumn<int>("val");
+
+ QTest::newRow("-30000") << -30000;
+ QTest::newRow("-1") << -1;
+ QTest::newRow("0") << 0;
+ QTest::newRow("1020") << 1020;
+ QTest::newRow("16385") << 16385;
+}
+
+void tst_QtEndian::endianIntegers()
+{
+ QFETCH(int, val);
+
+ qint16 vi16 = val;
+ qint32 vi32 = val;
+ qint64 vi64 = val;
+ quint16 vu16 = val;
+ quint32 vu32 = val;
+ quint64 vu64 = val;
+
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ QCOMPARE(*reinterpret_cast<qint16_be*>(&vi16), vi16);
+ QCOMPARE(*reinterpret_cast<qint32_be*>(&vi32), vi32);
+ QCOMPARE(*reinterpret_cast<qint64_be*>(&vi64), vi64);
+ QCOMPARE(*reinterpret_cast<qint16_le*>(&vi16), qbswap(vi16));
+ QCOMPARE(*reinterpret_cast<qint32_le*>(&vi32), qbswap(vi32));
+ QCOMPARE(*reinterpret_cast<qint64_le*>(&vi64), qbswap(vi64));
+ QCOMPARE(*reinterpret_cast<quint16_be*>(&vu16), vu16);
+ QCOMPARE(*reinterpret_cast<quint32_be*>(&vu32), vu32);
+ QCOMPARE(*reinterpret_cast<quint64_be*>(&vu64), vu64);
+ QCOMPARE(*reinterpret_cast<quint16_le*>(&vu16), qbswap(vu16));
+ QCOMPARE(*reinterpret_cast<quint32_le*>(&vu32), qbswap(vu32));
+ QCOMPARE(*reinterpret_cast<quint64_le*>(&vu64), qbswap(vu64));
+#else
+ QCOMPARE(*reinterpret_cast<qint16_be*>(&vi16), qbswap(vi16));
+ QCOMPARE(*reinterpret_cast<qint32_be*>(&vi32), qbswap(vi32));
+ QCOMPARE(*reinterpret_cast<qint64_be*>(&vi64), qbswap(vi64));
+ QCOMPARE(*reinterpret_cast<qint16_le*>(&vi16), vi16);
+ QCOMPARE(*reinterpret_cast<qint32_le*>(&vi32), vi32);
+ QCOMPARE(*reinterpret_cast<qint64_le*>(&vi64), vi64);
+ QCOMPARE(*reinterpret_cast<quint16_be*>(&vu16), qbswap(vu16));
+ QCOMPARE(*reinterpret_cast<quint32_be*>(&vu32), qbswap(vu32));
+ QCOMPARE(*reinterpret_cast<quint64_be*>(&vu64), qbswap(vu64));
+ QCOMPARE(*reinterpret_cast<quint16_le*>(&vu16), vu16);
+ QCOMPARE(*reinterpret_cast<quint32_le*>(&vu32), vu32);
+ QCOMPARE(*reinterpret_cast<quint64_le*>(&vu64), vu64);
+#endif
+}
+
+void tst_QtEndian::endianBitfields()
+{
+ union {
+ quint32_be_bitfield<21, 11> upper;
+ quint32_be_bitfield<10, 11> lower;
+ qint32_be_bitfield<0, 10> bottom;
+ } u;
+
+ u.upper = 200;
+ QCOMPARE(u.upper, 200U);
+ u.lower = 1000;
+ u.bottom = -8;
+ QCOMPARE(u.lower, 1000U);
+ QCOMPARE(u.upper, 200U);
+
+ u.lower += u.upper;
+ QCOMPARE(u.upper, 200U);
+ QCOMPARE(u.lower, 1200U);
+
+ u.upper = 65536 + 7;
+ u.lower = 65535;
+ QCOMPARE(u.lower, 65535U & ((1<<11) - 1));
+ QCOMPARE(u.upper, 7U);
+
+ QCOMPARE(u.bottom, -8);
+}
+
QTEST_MAIN(tst_QtEndian)
#include "tst_qtendian.moc"
diff --git a/tests/auto/corelib/io/io.pro b/tests/auto/corelib/io/io.pro
index 01ed84fda9..bd2a9f4c8e 100644
--- a/tests/auto/corelib/io/io.pro
+++ b/tests/auto/corelib/io/io.pro
@@ -34,12 +34,6 @@ SUBDIRS=\
qurl \
qurlinternal \
qurlquery \
- qwinoverlappedionotifier \
-
-!win32 {
- SUBDIRS -=\
- qwinoverlappedionotifier
-}
!qtHaveModule(gui): SUBDIRS -= \
qdatastream \
@@ -73,5 +67,4 @@ win32:!qtConfig(private_tests): SUBDIRS -= \
qprocess-noapplication
winrt: SUBDIRS -= \
- qstorageinfo \
- qwinoverlappedionotifier
+ qstorageinfo
diff --git a/tests/auto/corelib/io/largefile/tst_largefile.cpp b/tests/auto/corelib/io/largefile/tst_largefile.cpp
index 5975303ca6..2d13e6166d 100644
--- a/tests/auto/corelib/io/largefile/tst_largefile.cpp
+++ b/tests/auto/corelib/io/largefile/tst_largefile.cpp
@@ -31,6 +31,7 @@
#include <QtAlgorithms>
#include <QFile>
#include <QFileInfo>
+#include <QRandomGenerator>
#include <qplatformdefs.h>
#include <QDebug>
@@ -174,8 +175,7 @@ static inline QByteArray generateDataBlock(int blockSize, QString text, qint64 u
static qint64 counter = 0;
- qint64 randomBits = ((qint64)qrand() << 32)
- | ((qint64)qrand() & 0x00000000ffffffff);
+ qint64 randomBits = QRandomGenerator::global()->generate64();
appendRaw(block, randomBits);
appendRaw(block, userBits);
diff --git a/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp b/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp
index ddfd14550b..1072cb44f9 100644
--- a/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp
+++ b/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp
@@ -341,8 +341,10 @@ public:
if (file) {
QMutexLocker lock(&file->mutex);
switch (time) {
- case CreationTime:
- return file->creation;
+ case BirthTime:
+ return file->birth;
+ case MetadataChangeTime:
+ return file->change;
case ModificationTime:
return file->modification;
case AccessTime:
@@ -353,6 +355,13 @@ public:
return QDateTime();
}
+ bool setFileTime(const QDateTime &newDate, FileTime time)
+ {
+ Q_UNUSED(newDate);
+ Q_UNUSED(time);
+ return false;
+ }
+
void setFileName(const QString &file)
{
if (openForRead_ || openForWrite_)
@@ -449,7 +458,7 @@ protected:
uint userId, groupId;
QAbstractFileEngine::FileFlags fileFlags;
- QDateTime creation, modification, access;
+ QDateTime birth, change, modification, access;
QByteArray content;
};
diff --git a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp
index 7147405f3b..b43ea7cfa5 100644
--- a/tests/auto/corelib/io/qdebug/tst_qdebug.cpp
+++ b/tests/auto/corelib/io/qdebug/tst_qdebug.cpp
@@ -51,6 +51,7 @@ private slots:
void qDebugQChar() const;
void qDebugQString() const;
void qDebugQStringRef() const;
+ void qDebugQStringView() const;
void qDebugQLatin1String() const;
void qDebugQByteArray() const;
void qDebugQFlags() const;
@@ -492,6 +493,46 @@ void tst_QDebug::qDebugQStringRef() const
}
}
+void tst_QDebug::qDebugQStringView() const
+{
+ /* Use a basic string. */
+ {
+ QLatin1String file, function;
+ int line = 0;
+ const QStringView inView = QStringViewLiteral("input");
+
+ MessageHandlerSetter mhs(myMessageHandler);
+ { qDebug() << inView; }
+#ifndef QT_NO_MESSAGELOGCONTEXT
+ file = QLatin1String(__FILE__); line = __LINE__ - 2; function = QLatin1String(Q_FUNC_INFO);
+#endif
+ QCOMPARE(s_msgType, QtDebugMsg);
+ QCOMPARE(s_msg, QLatin1String("\"input\""));
+ QCOMPARE(QLatin1String(s_file), file);
+ QCOMPARE(s_line, line);
+ QCOMPARE(QLatin1String(s_function), function);
+ }
+
+ /* Use a null QStringView. */
+ {
+ QString file, function;
+ int line = 0;
+
+ const QStringView inView;
+
+ MessageHandlerSetter mhs(myMessageHandler);
+ { qDebug() << inView; }
+#ifndef QT_NO_MESSAGELOGCONTEXT
+ file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO;
+#endif
+ QCOMPARE(s_msgType, QtDebugMsg);
+ QCOMPARE(s_msg, QLatin1String("\"\""));
+ QCOMPARE(QLatin1String(s_file), file);
+ QCOMPARE(s_line, line);
+ QCOMPARE(QLatin1String(s_function), function);
+ }
+}
+
void tst_QDebug::qDebugQLatin1String() const
{
QString file, function;
diff --git a/tests/auto/corelib/io/qfile/BLACKLIST b/tests/auto/corelib/io/qfile/BLACKLIST
index 8d636d40b8..8366667166 100644
--- a/tests/auto/corelib/io/qfile/BLACKLIST
+++ b/tests/auto/corelib/io/qfile/BLACKLIST
@@ -5,7 +5,3 @@ msvc-2017 ci
[readLineStdin_lineByLine]
msvc-2015 ci
msvc-2017 ci
-[openStandardStreamsFileDescriptors]
-osx
-[openStandardStreamsBufferedStreams]
-osx
diff --git a/tests/auto/corelib/io/qfile/tst_qfile.cpp b/tests/auto/corelib/io/qfile/tst_qfile.cpp
index 5f0eae6fc3..c173a87a41 100644
--- a/tests/auto/corelib/io/qfile/tst_qfile.cpp
+++ b/tests/auto/corelib/io/qfile/tst_qfile.cpp
@@ -2138,6 +2138,7 @@ public:
uint ownerId(FileOwner) const { return 0; }
QString owner(FileOwner) const { return QString(); }
QDateTime fileTime(FileTime) const { return QDateTime(); }
+ bool setFileTime(const QDateTime &newDate, FileTime time) { return false; }
private:
int number;
@@ -3247,22 +3248,39 @@ static qint64 streamExpectedSize(int fd)
QT_STATBUF sb;
if (QT_FSTAT(fd, &sb) != -1)
return sb.st_size;
+ qErrnoWarning("Could not fstat fd %d", fd);
return 0;
}
static qint64 streamCurrentPosition(int fd)
{
- QT_OFF_T pos = QT_LSEEK(fd, 0, SEEK_CUR);
- if (pos != -1)
- return pos;
+ QT_STATBUF sb;
+ if (QT_FSTAT(fd, &sb) != -1) {
+ QT_OFF_T pos = -1;
+ if ((sb.st_mode & QT_STAT_MASK) == QT_STAT_REG)
+ pos = QT_LSEEK(fd, 0, SEEK_CUR);
+ if (pos != -1)
+ return pos;
+ // failure to lseek() is not a problem
+ } else {
+ qErrnoWarning("Could not fstat fd %d", fd);
+ }
return 0;
}
static qint64 streamCurrentPosition(FILE *f)
{
- QT_OFF_T pos = QT_FTELL(f);
- if (pos != -1)
- return pos;
+ QT_STATBUF sb;
+ if (QT_FSTAT(QT_FILENO(f), &sb) != -1) {
+ QT_OFF_T pos = -1;
+ if ((sb.st_mode & QT_STAT_MASK) == QT_STAT_REG)
+ pos = QT_FTELL(f);
+ if (pos != -1)
+ return pos;
+ // failure to ftell() is not a problem
+ } else {
+ qErrnoWarning("Could not fstat fd %d", QT_FILENO(f));
+ }
return 0;
}
diff --git a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
index 38db4c7b63..87d5675e7a 100644
--- a/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
+++ b/tests/auto/corelib/io/qfileinfo/tst_qfileinfo.cpp
@@ -35,6 +35,7 @@
#include <qtemporarydir.h>
#include <qdir.h>
#include <qfileinfo.h>
+#include <qstorageinfo.h>
#ifdef Q_OS_UNIX
#include <errno.h>
#include <fcntl.h>
@@ -47,7 +48,6 @@
#endif
#ifdef Q_OS_WIN
#include <qt_windows.h>
-#include <qlibrary.h>
#if !defined(Q_OS_WINRT)
#include <lm.h>
#endif
@@ -73,83 +73,38 @@ bool IsUserAdmin();
# endif
#endif
-#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
-inline bool qt_isEvilFsTypeName(const char *name)
-{
- return (qstrncmp(name, "nfs", 3) == 0
- || qstrncmp(name, "autofs", 6) == 0
- || qstrncmp(name, "cachefs", 7) == 0);
-}
-
-#if defined(Q_OS_BSD4) && !defined(Q_OS_NETBSD)
-# include <sys/param.h>
-# include <sys/mount.h>
-
-bool qIsLikelyToBeNfs(int handle)
+inline bool qIsLikelyToBeFat(const QString &path)
{
- struct statfs buf;
- if (fstatfs(handle, &buf) != 0)
- return false;
- return qt_isEvilFsTypeName(buf.f_fstypename);
+ QByteArray name = QStorageInfo(path).fileSystemType().toLower();
+ return name.contains("fat") || name.contains("msdos");
}
-#elif defined(Q_OS_LINUX) || defined(Q_OS_HURD)
-
-# include <sys/vfs.h>
-# ifdef QT_LINUXBASE
- // LSB 3.2 has fstatfs in sys/statfs.h, sys/vfs.h is just an empty dummy header
-# include <sys/statfs.h>
-# endif
-
-# ifndef NFS_SUPER_MAGIC
-# define NFS_SUPER_MAGIC 0x00006969
-# endif
-# ifndef AUTOFS_SUPER_MAGIC
-# define AUTOFS_SUPER_MAGIC 0x00000187
-# endif
-# ifndef AUTOFSNG_SUPER_MAGIC
-# define AUTOFSNG_SUPER_MAGIC 0x7d92b1a0
-# endif
-
-bool qIsLikelyToBeNfs(int handle)
+inline bool qIsLikelyToBeNfs(const QString &path)
{
- struct statfs buf;
- if (fstatfs(handle, &buf) != 0)
- return false;
- return buf.f_type == NFS_SUPER_MAGIC
- || buf.f_type == AUTOFS_SUPER_MAGIC
- || buf.f_type == AUTOFSNG_SUPER_MAGIC;
-}
-
-#elif defined(Q_OS_SOLARIS) || defined(Q_OS_IRIX) || defined(Q_OS_AIX) || defined(Q_OS_HPUX) \
- || defined(Q_OS_OSF) || defined(Q_OS_QNX) || defined(Q_OS_SCO) \
- || defined(Q_OS_UNIXWARE) || defined(Q_OS_RELIANT) || defined(Q_OS_NETBSD)
-
-# include <sys/statvfs.h>
-
-bool qIsLikelyToBeNfs(int handle)
-{
- struct statvfs buf;
- if (fstatvfs(handle, &buf) != 0)
- return false;
-#if defined(Q_OS_NETBSD)
- return qt_isEvilFsTypeName(buf.f_fstypename);
+#ifdef Q_OS_WIN
+ Q_UNUSED(path);
+ return false;
#else
- return qt_isEvilFsTypeName(buf.f_basetype);
+ QByteArray type = QStorageInfo(path).fileSystemType();
+ const char *name = type.constData();
+
+ return (qstrncmp(name, "nfs", 3) == 0
+ || qstrncmp(name, "autofs", 6) == 0
+ || qstrncmp(name, "autofsng", 8) == 0
+ || qstrncmp(name, "cachefs", 7) == 0);
#endif
}
-#else
-inline bool qIsLikelyToBeNfs(int /* handle */)
-{
- return false;
-}
-#endif
-#endif
static QString seedAndTemplate()
{
- qsrand(QDateTime::currentSecsSinceEpoch());
- return QDir::tempPath() + "/tst_qfileinfo-XXXXXX";
+ QString base;
+#if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID)
+ // use XDG_RUNTIME_DIR as it's a fully-capable FS
+ base = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
+#endif
+ if (base.isEmpty())
+ base = QDir::tempPath();
+ return base + "/tst_qfileinfo-XXXXXX";
}
static QByteArray msgDoesNotExist(const QString &name)
@@ -285,8 +240,9 @@ private slots:
#endif
void group();
+ void invalidState_data();
void invalidState();
- void nonExistingFileDates();
+ void nonExistingFile();
private:
const QString m_currentDir;
@@ -736,18 +692,12 @@ void tst_QFileInfo::canonicalFilePath()
#endif
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
- typedef BOOL (WINAPI *PtrCreateSymbolicLink)(LPTSTR, LPTSTR, DWORD);
- PtrCreateSymbolicLink ptrCreateSymbolicLink =
- (PtrCreateSymbolicLink)QLibrary::resolve(QLatin1String("kernel32"), "CreateSymbolicLinkW");
-
- if (!ptrCreateSymbolicLink) {
- QSKIP("Symbolic links aren't supported by FS");
- } else {
+ {
// CreateSymbolicLink can return TRUE & still fail to create the link,
// the error code in that case is ERROR_PRIVILEGE_NOT_HELD (1314)
SetLastError(0);
const QString linkTarget = QStringLiteral("res");
- BOOL ret = ptrCreateSymbolicLink((wchar_t*)linkTarget.utf16(), (wchar_t*)m_resourcesDir.utf16(), 1);
+ BOOL ret = CreateSymbolicLink((wchar_t*)linkTarget.utf16(), (wchar_t*)m_resourcesDir.utf16(), 1);
DWORD dwErr = GetLastError();
if (!ret)
QSKIP("Symbolic links aren't supported by FS");
@@ -1042,6 +992,11 @@ void tst_QFileInfo::systemFiles()
QVERIFY2(fi.exists(), msgDoesNotExist(fi.absoluteFilePath()).constData());
QVERIFY(fi.size() > 0);
QVERIFY(fi.lastModified().isValid());
+ QVERIFY(fi.metadataChangeTime().isValid());
+ QCOMPARE(fi.metadataChangeTime(), fi.lastModified()); // On Windows, they're the same
+ QVERIFY(fi.birthTime().isValid());
+ QVERIFY(fi.birthTime() <= fi.lastModified());
+ QCOMPARE(fi.created(), fi.birthTime()); // On Windows, they're the same
}
void tst_QFileInfo::compare_data()
@@ -1132,43 +1087,92 @@ void tst_QFileInfo::fileTimes_data()
void tst_QFileInfo::fileTimes()
{
- int sleepTime = 2000;
+ auto datePairString = [](const QDateTime &actual, const QDateTime &before) {
+ return (actual.toString(Qt::ISODateWithMs) + " (should be >) " + before.toString(Qt::ISODateWithMs))
+ .toLatin1();
+ };
+
QFETCH(QString, fileName);
+ int sleepTime = 100;
+
+ // on Linux and Windows, the filesystem timestamps may be slightly out of
+ // sync with the system clock (maybe they're using CLOCK_REALTIME_COARSE),
+ // so add a margin of error to our comparisons
+ int fsClockSkew = 10;
+#ifdef Q_OS_WIN
+ fsClockSkew = 500;
+#endif
+
+ // NFS clocks may be WAY out of sync
+ if (qIsLikelyToBeNfs(fileName))
+ QSKIP("This test doesn't work on NFS");
+
+ bool noAccessTime = false;
+ {
+ // try to guess if file times on this filesystem round to the second
+ QFileInfo cwd(".");
+ if (cwd.lastModified().toMSecsSinceEpoch() % 1000 == 0
+ && cwd.lastRead().toMSecsSinceEpoch() % 1000 == 0) {
+ fsClockSkew = sleepTime = 1000;
+
+ noAccessTime = qIsLikelyToBeFat(fileName);
+ if (noAccessTime) {
+ // FAT filesystems (but maybe not exFAT) store timestamps with 2-second
+ // granularity and access time with 1-day granularity
+ fsClockSkew = sleepTime = 2000;
+ }
+ }
+ }
+
if (QFile::exists(fileName)) {
QVERIFY(QFile::remove(fileName));
}
- QTest::qSleep(sleepTime);
+
+ QDateTime beforeBirth, beforeWrite, beforeMetadataChange, beforeRead;
+ QDateTime birthTime, writeTime, metadataChangeTime, readTime;
+
+ // --- Create file and write to it
+ beforeBirth = QDateTime::currentDateTime().addMSecs(-fsClockSkew);
{
QFile file(fileName);
QVERIFY(file.open(QFile::WriteOnly | QFile::Text));
-#if defined(Q_OS_UNIX) && !defined(Q_OS_VXWORKS)
- if (qIsLikelyToBeNfs(file.handle()))
- QSKIP("This Test doesn't work on NFS");
-#endif
+ QFileInfo fileInfo(fileName);
+ birthTime = fileInfo.birthTime();
+ QVERIFY2(!birthTime.isValid() || birthTime > beforeBirth,
+ datePairString(birthTime, beforeBirth));
+
+ QTest::qSleep(sleepTime);
+ beforeWrite = QDateTime::currentDateTime().addMSecs(-fsClockSkew);
QTextStream ts(&file);
ts << fileName << endl;
}
- QTest::qSleep(sleepTime);
- QDateTime beforeWrite = QDateTime::currentDateTime();
- QTest::qSleep(sleepTime);
{
QFileInfo fileInfo(fileName);
- QVERIFY(fileInfo.created() < beforeWrite);
- QFile file(fileName);
- QVERIFY(file.open(QFile::ReadWrite | QFile::Text));
- QTextStream ts(&file);
- ts << fileName << endl;
+ writeTime = fileInfo.lastModified();
+ QVERIFY2(writeTime > beforeWrite, datePairString(writeTime, beforeWrite));
+ QCOMPARE(fileInfo.birthTime(), birthTime); // mustn't have changed
}
+
+ // --- Change the file's metadata
QTest::qSleep(sleepTime);
- QDateTime beforeRead = QDateTime::currentDateTime();
- QTest::qSleep(sleepTime);
+ beforeMetadataChange = QDateTime::currentDateTime().addMSecs(-fsClockSkew);
+ {
+ QFile file(fileName);
+ file.setPermissions(file.permissions());
+ }
{
QFileInfo fileInfo(fileName);
-// On unix created() returns the same as lastModified().
-#if !defined(Q_OS_UNIX)
- QVERIFY(fileInfo.created() < beforeWrite);
-#endif
- QVERIFY(fileInfo.lastModified() > beforeWrite);
+ metadataChangeTime = fileInfo.metadataChangeTime();
+ QVERIFY2(metadataChangeTime > beforeMetadataChange,
+ datePairString(metadataChangeTime, beforeMetadataChange));
+ QVERIFY(metadataChangeTime >= writeTime); // not all filesystems can store both times
+ QCOMPARE(fileInfo.birthTime(), birthTime); // mustn't have changed
+ }
+
+ // --- Read the file
+ QTest::qSleep(sleepTime);
+ beforeRead = QDateTime::currentDateTime().addMSecs(-fsClockSkew);
+ {
QFile file(fileName);
QVERIFY(file.open(QFile::ReadOnly | QFile::Text));
QTextStream ts(&file);
@@ -1177,13 +1181,17 @@ void tst_QFileInfo::fileTimes()
}
QFileInfo fileInfo(fileName);
-#if !defined(Q_OS_UNIX)
- QVERIFY(fileInfo.created() < beforeWrite);
-#endif
+ readTime = fileInfo.lastRead();
+ QCOMPARE(fileInfo.lastModified(), writeTime); // mustn't have changed
+ QCOMPARE(fileInfo.birthTime(), birthTime); // mustn't have changed
+ QVERIFY(readTime.isValid());
+
+#if defined(Q_OS_WINRT) || defined(Q_OS_QNX) || (defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED))
+ noAccessTime = true;
+#elif defined(Q_OS_WIN)
//In Vista the last-access timestamp is not updated when the file is accessed/touched (by default).
//To enable this the HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisableLastAccessUpdate
//is set to 0, in the test machine.
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
HKEY key;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\FileSystem",
0, KEY_READ, &key)) {
@@ -1192,75 +1200,43 @@ void tst_QFileInfo::fileTimes()
LONG error = RegQueryValueEx(key, L"NtfsDisableLastAccessUpdate"
, NULL, NULL, (LPBYTE)&disabledAccessTimes, &size);
if (ERROR_SUCCESS == error && disabledAccessTimes)
- QEXPECT_FAIL("", "File access times are disabled in windows registry (this is the default setting)", Continue);
+ noAccessTime = true;
RegCloseKey(key);
}
#endif
-#if defined(Q_OS_WINRT)
- QEXPECT_FAIL("", "WinRT does not allow timestamp handling change in the filesystem due to sandboxing", Continue);
-#elif defined(Q_OS_QNX)
- QEXPECT_FAIL("", "QNX uses the noatime filesystem option", Continue);
-#elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
- if (fileInfo.lastRead() <= beforeRead)
- QEXPECT_FAIL("", "Android may use relatime or noatime on mounts", Continue);
-#endif
- QVERIFY(fileInfo.lastRead() > beforeRead);
- QVERIFY(fileInfo.lastModified() > beforeWrite);
- QVERIFY(fileInfo.lastModified() < beforeRead);
+ if (noAccessTime)
+ return;
+
+ QVERIFY2(readTime > beforeRead, datePairString(readTime, beforeRead));
+ QVERIFY(writeTime < beforeRead);
}
void tst_QFileInfo::fileTimes_oldFile()
{
- // This is not supported on WinRT
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
- // All files are opened in share mode (both read and write).
- DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
-
- // All files on Windows can be read; there's no such thing as an
- // unreadable file. Add GENERIC_WRITE if WriteOnly is passed.
- int accessRights = GENERIC_READ | GENERIC_WRITE;
-
- SECURITY_ATTRIBUTES securityAtts = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
-
- // Regular file mode. In Unbuffered mode, pass the no-buffering flag.
- DWORD flagsAndAtts = FILE_ATTRIBUTE_NORMAL;
-
- // WriteOnly can create files, ReadOnly cannot.
- DWORD creationDisp = OPEN_ALWAYS;
-
- // Create the file handle.
- HANDLE fileHandle = CreateFile(L"oldfile.txt",
- accessRights,
- shareMode,
- &securityAtts,
- creationDisp,
- flagsAndAtts,
- NULL);
-
- // Set file times back to 1601.
- SYSTEMTIME stime;
- stime.wYear = 1601;
- stime.wMonth = 1;
- stime.wDayOfWeek = 1;
- stime.wDay = 1;
- stime.wHour = 1;
- stime.wMinute = 0;
- stime.wSecond = 0;
- stime.wMilliseconds = 0;
-
- FILETIME ctime;
- QVERIFY(SystemTimeToFileTime(&stime, &ctime));
- FILETIME atime = ctime;
- FILETIME mtime = atime;
- QVERIFY(fileHandle);
- QVERIFY(SetFileTime(fileHandle, &ctime, &atime, &mtime) != 0);
+ // This is 2^{31} seconds before 1970-01-01 15:14:8,
+ // i.e. shortly after the start of time_t, in any time-zone:
+ const QDateTime early(QDate(1901, 12, 14), QTime(12, 0));
+ QFile file("ancientfile.txt");
+ file.open(QIODevice::WriteOnly);
+ file.write("\n", 1);
+ file.close();
- CloseHandle(fileHandle);
+ /*
+ QFile's setFileTime calls QFSFileEngine::setFileTime() which fails unless
+ the file is open at the time. Of course, when writing, close() changes
+ modification time, so need to re-open for read in order to setFileTime().
+ */
+ file.open(QIODevice::ReadOnly);
+ bool ok = file.setFileTime(early, QFileDevice::FileModificationTime);
+ file.close();
- QFileInfo info("oldfile.txt");
- QCOMPARE(info.lastModified(), QDateTime(QDate(1601, 1, 1), QTime(1, 0), Qt::UTC).toLocalTime());
-#endif
+ if (ok) {
+ QFileInfo info(file.fileName());
+ QCOMPARE(info.lastModified(), early);
+ } else {
+ QSKIP("Unable to set file metadata to ancient values");
+ }
}
void tst_QFileInfo::isSymLink_data()
@@ -1400,7 +1376,7 @@ void tst_QFileInfo::isNativePath_data()
QTest::addColumn<bool>("isNativePath");
QTest::newRow("default-constructed") << QString() << false;
- QTest::newRow("empty") << QString("") << true;
+ QTest::newRow("empty") << QString("") << false;
QTest::newRow("local root") << QString::fromLatin1("/") << true;
QTest::newRow("local non-existent file") << QString::fromLatin1("/abrakadabra.boo") << true;
@@ -1469,16 +1445,6 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data()
QDir pwd;
pwd.mkdir("target");
- QLibrary kernel32("kernel32");
- typedef BOOLEAN (WINAPI *PtrCreateSymbolicLink)(LPCWSTR, LPCWSTR, DWORD);
- PtrCreateSymbolicLink createSymbolicLinkW = 0;
- createSymbolicLinkW = (PtrCreateSymbolicLink) kernel32.resolve("CreateSymbolicLinkW");
- if (!createSymbolicLinkW) {
- //we need at least one data set for the test not to fail when skipping _data function
- QDir target("target");
- QTest::newRow("dummy") << target.path() << false << "" << target.canonicalPath();
- QSKIP("symbolic links not supported by operating system");
- }
{
//Directory symlinks
QDir target("target");
@@ -1498,10 +1464,10 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data()
DWORD err = ERROR_SUCCESS ;
if (!pwd.exists("abs_symlink"))
- if (!createSymbolicLinkW((wchar_t*)absSymlink.utf16(),(wchar_t*)absTarget.utf16(),0x1))
+ if (!CreateSymbolicLink((wchar_t*)absSymlink.utf16(),(wchar_t*)absTarget.utf16(),0x1))
err = GetLastError();
if (err == ERROR_SUCCESS && !pwd.exists(relSymlink))
- if (!createSymbolicLinkW((wchar_t*)relSymlink.utf16(),(wchar_t*)relTarget.utf16(),0x1))
+ if (!CreateSymbolicLink((wchar_t*)relSymlink.utf16(),(wchar_t*)relTarget.utf16(),0x1))
err = GetLastError();
if (err != ERROR_SUCCESS) {
wchar_t errstr[0x100];
@@ -1531,10 +1497,9 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data()
QString relSymlink = "rel_symlink.cpp";
QString relToRelTarget = QDir::toNativeSeparators(relativeDir.relativeFilePath(target.absoluteFilePath()));
QString relToRelSymlink = "relative/rel_symlink";
- QVERIFY(pwd.exists("abs_symlink.cpp") || createSymbolicLinkW((wchar_t*)absSymlink.utf16(),(wchar_t*)absTarget.utf16(),0x0));
- QVERIFY(pwd.exists(relSymlink) || createSymbolicLinkW((wchar_t*)relSymlink.utf16(),(wchar_t*)relTarget.utf16(),0x0));
- QVERIFY(pwd.exists(relToRelSymlink)
- || createSymbolicLinkW((wchar_t*)relToRelSymlink.utf16(), (wchar_t*)relToRelTarget.utf16(),0x0));
+ QVERIFY(pwd.exists("abs_symlink.cpp") || CreateSymbolicLink((wchar_t*)absSymlink.utf16(),(wchar_t*)absTarget.utf16(),0x0));
+ QVERIFY(pwd.exists(relSymlink) || CreateSymbolicLink((wchar_t*)relSymlink.utf16(),(wchar_t*)relTarget.utf16(),0x0));
+ QVERIFY(pwd.exists(relToRelSymlink) || CreateSymbolicLink((wchar_t*)relToRelSymlink.utf16(), (wchar_t*)relToRelTarget.utf16(),0x0));
QTest::newRow("absolute file symlink") << absSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath();
QTest::newRow("relative file symlink") << relSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath();
QTest::newRow("relative to relative file symlink") << relToRelSymlink << true << QDir::fromNativeSeparators(absTarget) << target.canonicalFilePath();
@@ -1561,20 +1526,14 @@ void tst_QFileInfo::ntfsJunctionPointsAndSymlinks_data()
QTest::newRow("junction_root") << junction << false << QString() << QString();
//Mountpoint
- typedef BOOLEAN (WINAPI *PtrGetVolumeNameForVolumeMountPointW)(LPCWSTR, LPWSTR, DWORD);
- PtrGetVolumeNameForVolumeMountPointW getVolumeNameForVolumeMountPointW = 0;
- getVolumeNameForVolumeMountPointW = (PtrGetVolumeNameForVolumeMountPointW) kernel32.resolve("GetVolumeNameForVolumeMountPointW");
- if(getVolumeNameForVolumeMountPointW)
- {
- wchar_t buffer[MAX_PATH];
- QString rootPath = QDir::toNativeSeparators(QDir::rootPath());
- QVERIFY(getVolumeNameForVolumeMountPointW((wchar_t*)rootPath.utf16(), buffer, MAX_PATH));
- QString rootVolume = QString::fromWCharArray(buffer);
- junction = "mountpoint";
- rootVolume.replace("\\\\?\\","\\??\\");
- FileSystem::createNtfsJunction(rootVolume, junction);
- QTest::newRow("mountpoint") << junction << false << QString() << QString();
- }
+ wchar_t buffer[MAX_PATH];
+ QString rootPath = QDir::toNativeSeparators(QDir::rootPath());
+ QVERIFY(GetVolumeNameForVolumeMountPoint((wchar_t*)rootPath.utf16(), buffer, MAX_PATH));
+ QString rootVolume = QString::fromWCharArray(buffer);
+ junction = "mountpoint";
+ rootVolume.replace("\\\\?\\","\\??\\");
+ FileSystem::createNtfsJunction(rootVolume, junction);
+ QTest::newRow("mountpoint") << junction << false << QString() << QString();
}
void tst_QFileInfo::ntfsJunctionPointsAndSymlinks()
@@ -1941,56 +1900,96 @@ void tst_QFileInfo::group()
QCOMPARE(fi.group(), expected);
}
-void tst_QFileInfo::invalidState()
+static void stateCheck(const QFileInfo &info, const QString &dirname, const QString &filename)
{
- // Shouldn't crash;
-
- {
- QFileInfo info;
- QCOMPARE(info.size(), qint64(0));
- QVERIFY(!info.exists());
-
- info.setCaching(false);
+ QCOMPARE(info.size(), qint64(0));
+ QVERIFY(!info.exists());
- info.created();
- info.lastRead();
- info.lastModified();
+ QString path;
+ QString abspath;
+ if (!dirname.isEmpty()) {
+ path = ".";
+ abspath = dirname + '/' + filename;
}
- {
- QFileInfo info("");
- QCOMPARE(info.size(), qint64(0));
- QVERIFY(!info.exists());
-
- info.setCaching(false);
+ QCOMPARE(info.filePath(), filename);
+ QCOMPARE(info.absoluteFilePath(), abspath);
+ QCOMPARE(info.canonicalFilePath(), QString());
+ QCOMPARE(info.fileName(), filename);
+ QCOMPARE(info.baseName(), filename);
+ QCOMPARE(info.completeBaseName(), filename);
+ QCOMPARE(info.suffix(), QString());
+ QCOMPARE(info.bundleName(), QString());
+ QCOMPARE(info.completeSuffix(), QString());
+
+ QVERIFY(info.isRelative());
+ QCOMPARE(info.path(), path);
+ QCOMPARE(info.absolutePath(), dirname);
+ QCOMPARE(info.dir().path(), ".");
+
+ // these don't look right
+ QCOMPARE(info.canonicalPath(), path);
+ QCOMPARE(info.absoluteDir().path(), dirname.isEmpty() ? "." : dirname);
+
+ QVERIFY(!info.isReadable());
+ QVERIFY(!info.isWritable());
+ QVERIFY(!info.isExecutable());
+ QVERIFY(!info.isHidden());
+ QVERIFY(!info.isFile());
+ QVERIFY(!info.isDir());
+ QVERIFY(!info.isSymLink());
+ QVERIFY(!info.isBundle());
+ QVERIFY(!info.isRoot());
+ QCOMPARE(info.isNativePath(), !filename.isEmpty());
+
+ QCOMPARE(info.readLink(), QString());
+ QCOMPARE(info.ownerId(), uint(-2));
+ QCOMPARE(info.groupId(), uint(-2));
+ QCOMPARE(info.owner(), QString());
+ QCOMPARE(info.group(), QString());
+
+ QCOMPARE(info.permissions(), QFile::Permissions());
- info.created();
- info.lastRead();
- info.lastModified();
- }
+ QVERIFY(!info.created().isValid());
+ QVERIFY(!info.birthTime().isValid());
+ QVERIFY(!info.metadataChangeTime().isValid());
+ QVERIFY(!info.lastRead().isValid());
+ QVERIFY(!info.lastModified().isValid());
+};
- {
- QFileInfo info("file-doesn't-really-exist.txt");
- QCOMPARE(info.size(), qint64(0));
- QVERIFY(!info.exists());
+void tst_QFileInfo::invalidState_data()
+{
+ QTest::addColumn<int>("mode");
+ QTest::newRow("default") << 0;
+ QTest::newRow("empty") << 1;
+ QTest::newRow("copy-of-default") << 2;
+ QTest::newRow("copy-of-empty") << 3;
+}
- info.setCaching(false);
+void tst_QFileInfo::invalidState()
+{
+ // Shouldn't crash or produce warnings
+ QFETCH(int, mode);
+ const QFileInfo &info = (mode & 1 ? QFileInfo("") : QFileInfo());
- info.created();
- info.lastRead();
- info.lastModified();
+ if (mode & 2) {
+ QFileInfo copy(info);
+ stateCheck(copy, QString(), QString());
+ } else {
+ stateCheck(info, QString(), QString());
}
-
- QVERIFY(true);
}
-void tst_QFileInfo::nonExistingFileDates()
+void tst_QFileInfo::nonExistingFile()
{
- QFileInfo info("non-existing-file.foobar");
- QVERIFY(!info.exists());
- QVERIFY(!info.created().isValid());
- QVERIFY(!info.lastRead().isValid());
- QVERIFY(!info.lastModified().isValid());
+ QString dirname = QDir::currentPath();
+ QString cdirname = QFileInfo(dirname).canonicalFilePath();
+ if (dirname != cdirname)
+ QDir::setCurrent(cdirname); // chdir() to our canonical path
+
+ QString filename = "non-existing-file-foobar";
+ QFileInfo info(filename);
+ stateCheck(info, dirname, filename);
}
QTEST_MAIN(tst_QFileInfo)
diff --git a/tests/auto/corelib/io/qfilesystemmetadata/tst_qfilesystemmetadata.cpp b/tests/auto/corelib/io/qfilesystemmetadata/tst_qfilesystemmetadata.cpp
index 1c104688a5..ea4da39cd5 100644
--- a/tests/auto/corelib/io/qfilesystemmetadata/tst_qfilesystemmetadata.cpp
+++ b/tests/auto/corelib/io/qfilesystemmetadata/tst_qfilesystemmetadata.cpp
@@ -66,14 +66,16 @@ void tst_QFileSystemMetaData::timeSinceEpoch()
/* data.ftLastAccessTime = data.ftLastWriteTime = */
data.ftCreationTime = epochToFileTime(afterEpochUtc);
meta.fillFromFindData(data);
+ QCOMPARE(meta.birthTime().toUTC(),
+ QDateTime::fromMSecsSinceEpoch(afterEpochUtc * qint64(1000), Qt::UTC));
#else
QT_STATBUF data;
memset(&data, 0, sizeof(data));
data.st_ctime = afterEpochUtc;
meta.fillFromStatBuf(data);
-#endif
- QCOMPARE(meta.creationTime().toUTC(),
+ QCOMPARE(meta.metadataChangeTime().toUTC(),
QDateTime::fromMSecsSinceEpoch(afterEpochUtc * qint64(1000), Qt::UTC));
+#endif
}
#else // i.e. no Q_AUTOTEST_EXPORT
void tst_QFileSystemMetaData::timeSinceEpoch()
diff --git a/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp b/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp
index 9e0b9c2329..618b7f338e 100644
--- a/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp
+++ b/tests/auto/corelib/io/qiodevice/tst_qiodevice.cpp
@@ -55,6 +55,10 @@ private slots:
void readAllKeepPosition();
void writeInTextMode();
+ void skip_data();
+ void skip();
+ void skipAfterPeek_data();
+ void skipAfterPeek();
void transaction_data();
void transaction();
@@ -628,6 +632,93 @@ void tst_QIODevice::writeInTextMode()
#endif
}
+void tst_QIODevice::skip_data()
+{
+ QTest::addColumn<bool>("sequential");
+ QTest::addColumn<QByteArray>("data");
+ QTest::addColumn<int>("read");
+ QTest::addColumn<int>("skip");
+ QTest::addColumn<int>("skipped");
+ QTest::addColumn<char>("expect");
+
+ QByteArray bigData;
+ bigData.fill('a', 20000);
+ bigData[10001] = 'x';
+
+ bool sequential = true;
+ do {
+ QByteArray devName(sequential ? "sequential" : "random-access");
+
+ QTest::newRow(qPrintable(devName + "-small_data")) << true << QByteArray("abcdefghij")
+ << 3 << 6 << 6 << 'j';
+ QTest::newRow(qPrintable(devName + "-big_data")) << true << bigData
+ << 1 << 10000 << 10000 << 'x';
+ QTest::newRow(qPrintable(devName + "-beyond_the_end")) << true << bigData
+ << 1 << 20000 << 19999 << '\0';
+
+ sequential = !sequential;
+ } while (!sequential);
+}
+
+void tst_QIODevice::skip()
+{
+ QFETCH(bool, sequential);
+ QFETCH(QByteArray, data);
+ QFETCH(int, read);
+ QFETCH(int, skip);
+ QFETCH(int, skipped);
+ QFETCH(char, expect);
+ char lastChar = 0;
+
+ QScopedPointer<QIODevice> dev(sequential ? (QIODevice *) new SequentialReadBuffer(&data)
+ : (QIODevice *) new QBuffer(&data));
+ dev->open(QIODevice::ReadOnly);
+
+ for (int i = 0; i < read; ++i)
+ dev->getChar(nullptr);
+
+ QCOMPARE(dev->skip(skip), skipped);
+ dev->getChar(&lastChar);
+ QCOMPARE(lastChar, expect);
+}
+
+void tst_QIODevice::skipAfterPeek_data()
+{
+ QTest::addColumn<bool>("sequential");
+ QTest::addColumn<QByteArray>("data");
+
+ QByteArray bigData;
+ for (int i = 0; i < 1000; ++i)
+ bigData += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ QTest::newRow("sequential") << true << bigData;
+ QTest::newRow("random-access") << false << bigData;
+}
+
+void tst_QIODevice::skipAfterPeek()
+{
+ QFETCH(bool, sequential);
+ QFETCH(QByteArray, data);
+
+ QScopedPointer<QIODevice> dev(sequential ? (QIODevice *) new SequentialReadBuffer(&data)
+ : (QIODevice *) new QBuffer(&data));
+ int readSoFar = 0;
+ qint64 bytesToSkip = 1;
+
+ dev->open(QIODevice::ReadOnly);
+ forever {
+ QByteArray chunk = dev->peek(bytesToSkip);
+ if (chunk.isEmpty())
+ break;
+
+ QCOMPARE(dev->skip(bytesToSkip), qint64(chunk.size()));
+ QCOMPARE(chunk, data.mid(readSoFar, chunk.size()));
+ readSoFar += chunk.size();
+ bytesToSkip <<= 1;
+ }
+ QCOMPARE(readSoFar, data.size());
+}
+
void tst_QIODevice::transaction_data()
{
QTest::addColumn<bool>("sequential");
diff --git a/tests/auto/corelib/io/qlockfile/qlockfiletesthelper/qlockfile_test_helper.cpp b/tests/auto/corelib/io/qlockfile/qlockfiletesthelper/qlockfile_test_helper.cpp
index f10c2a7c98..e086bf1904 100644
--- a/tests/auto/corelib/io/qlockfile/qlockfiletesthelper/qlockfile_test_helper.cpp
+++ b/tests/auto/corelib/io/qlockfile/qlockfiletesthelper/qlockfile_test_helper.cpp
@@ -31,6 +31,12 @@
#include <QLockFile>
#include <QThread>
+#ifdef Q_OS_UNIX
+# include <unistd.h>
+#else
+# include <stdlib.h>
+#endif
+
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
@@ -44,11 +50,11 @@ int main(int argc, char *argv[])
if (argc > 2)
option = QString::fromLocal8Bit(argv[2]);
- if (option == "-crash") {
+ if (option == "-uncleanexit") {
QLockFile lockFile(lockName);
lockFile.lock();
// exit on purpose, so that the lock remains!
- exit(0);
+ _exit(0);
} else if (option == "-busy") {
QLockFile lockFile(lockName);
lockFile.lock();
diff --git a/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp b/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp
index 59196dc40b..be523c8c0d 100644
--- a/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp
+++ b/tests/auto/corelib/io/qlockfile/tst_qlockfile.cpp
@@ -262,7 +262,7 @@ void tst_QLockFile::staleLockFromCrashedProcess()
QFETCH(int, staleLockTime);
const QString fileName = dir.path() + "/staleLockFromCrashedProcess";
- int ret = QProcess::execute(m_helperApp, QStringList() << fileName << "-crash");
+ int ret = QProcess::execute(m_helperApp, QStringList() << fileName << "-uncleanexit");
QCOMPARE(ret, int(QLockFile::NoError));
QTRY_VERIFY(QFile::exists(fileName));
@@ -288,7 +288,7 @@ void tst_QLockFile::staleLockFromCrashedProcessReusedPid()
#else
const QString fileName = dir.path() + "/staleLockFromCrashedProcessReusedPid";
- int ret = QProcess::execute(m_helperApp, QStringList() << fileName << "-crash");
+ int ret = QProcess::execute(m_helperApp, QStringList() << fileName << "-uncleanexit");
QCOMPARE(ret, int(QLockFile::NoError));
QVERIFY(QFile::exists(fileName));
QVERIFY(overwritePidInLockFile(fileName, QCoreApplication::applicationPid()));
@@ -397,7 +397,7 @@ void tst_QLockFile::staleLockRace()
// Only one thread should delete it, otherwise a race will ensue
const QString fileName = dir.path() + "/sharedFile";
const QString lockName = fileName + ".lock";
- int ret = QProcess::execute(m_helperApp, QStringList() << lockName << "-crash");
+ int ret = QProcess::execute(m_helperApp, QStringList() << lockName << "-uncleanexit");
QCOMPARE(ret, int(QLockFile::NoError));
QTRY_VERIFY(QFile::exists(lockName));
diff --git a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp
index b0acb1c58d..b78fa29fb6 100644
--- a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp
+++ b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp
@@ -64,7 +64,7 @@ void tst_QNoDebug::noDebugOutput() const
void tst_QNoDebug::streaming() const
{
QDateTime dt(QDate(1,2,3),QTime(4,5,6));
- const QByteArray debugString = dt.toString(QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz t")).toLatin1();
+ const QByteArray debugString = dt.toString(QStringViewLiteral("yyyy-MM-dd HH:mm:ss.zzz t")).toLatin1();
const QByteArray message = "QDateTime(" + debugString + " Qt::TimeSpec(LocalTime))";
QTest::ignoreMessage(QtWarningMsg, message.constData());
qWarning() << dt;
diff --git a/tests/auto/corelib/io/qprocess/testDetached/main.cpp b/tests/auto/corelib/io/qprocess/testDetached/main.cpp
index 702cabe873..c10e32d584 100644
--- a/tests/auto/corelib/io/qprocess/testDetached/main.cpp
+++ b/tests/auto/corelib/io/qprocess/testDetached/main.cpp
@@ -40,32 +40,89 @@
#include <windows.h>
#endif
+static void writeStuff(QFile &f)
+{
+ f.write(QDir::currentPath().toUtf8());
+ f.putChar('\n');
+#if defined(Q_OS_UNIX)
+ f.write(QByteArray::number(quint64(getpid())));
+#elif defined(Q_OS_WIN)
+ f.write(QByteArray::number(quint64(GetCurrentProcessId())));
+#endif
+ f.putChar('\n');
+ f.write(qgetenv("tst_QProcess"));
+ f.putChar('\n');
+}
+
+struct Args
+{
+ int exitCode = 0;
+ QByteArray errorMessage;
+ QString fileName;
+ FILE *channel = nullptr;
+ QByteArray channelName;
+};
+
+static Args parseArguments(const QStringList &args)
+{
+ Args result;
+ if (args.count() < 2) {
+ result.exitCode = 128;
+ result.errorMessage = "Usage: testDetached [--out-channel={stdout|stderr}] filename.txt\n";
+ return result;
+ }
+ for (const QString &arg : args) {
+ if (arg.startsWith("--")) {
+ if (!arg.startsWith("--out-channel=")) {
+ result.exitCode = 2;
+ result.errorMessage = "Unknown argument " + arg.toLocal8Bit();
+ return result;
+ }
+ result.channelName = arg.mid(14).toLocal8Bit();
+ if (result.channelName == "stdout") {
+ result.channel = stdout;
+ } else if (result.channelName == "stderr") {
+ result.channel = stderr;
+ } else {
+ result.exitCode = 3;
+ result.errorMessage = "Unknown channel " + result.channelName;
+ return result;
+ }
+ } else {
+ result.fileName = arg;
+ }
+ }
+ return result;
+}
+
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
- QStringList args = app.arguments();
- if (args.count() != 2) {
- fprintf(stderr, "Usage: testDetached filename.txt\n");
- return 128;
+ const Args args = parseArguments(app.arguments());
+ if (args.exitCode) {
+ fprintf(stderr, "testDetached: %s\n", args.errorMessage.constData());
+ return args.exitCode;
}
- QFile f(args.at(1));
+ if (args.channel) {
+ QFile channel;
+ if (!channel.open(args.channel, QIODevice::WriteOnly | QIODevice::Text)) {
+ fprintf(stderr, "Cannot open channel %s for writing: %s\n",
+ qPrintable(args.channelName), qPrintable(channel.errorString()));
+ return 4;
+ }
+ writeStuff(channel);
+ }
+
+ QFile f(args.fileName);
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
fprintf(stderr, "Cannot open %s for writing: %s\n",
qPrintable(f.fileName()), qPrintable(f.errorString()));
return 1;
}
- f.write(QDir::currentPath().toUtf8());
- f.putChar('\n');
-#if defined(Q_OS_UNIX)
- f.write(QByteArray::number(quint64(getpid())));
-#elif defined(Q_OS_WIN)
- f.write(QByteArray::number(quint64(GetCurrentProcessId())));
-#endif
- f.putChar('\n');
-
+ writeStuff(f);
f.close();
return 0;
diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
index f4d6d5cb40..de6eb28503 100644
--- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
+++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp
@@ -127,7 +127,8 @@ private slots:
void systemEnvironment();
void lockupsInStartDetached();
void waitForReadyReadForNonexistantProcess();
- void detachedWorkingDirectoryAndPid();
+ void detachedProcessParameters_data();
+ void detachedProcessParameters();
void startFinishStartFinish();
void invalidProgramString_data();
void invalidProgramString();
@@ -2072,21 +2073,54 @@ void tst_QProcess::fileWriterProcess()
} while (stopWatch.elapsed() < 3000);
}
-void tst_QProcess::detachedWorkingDirectoryAndPid()
+void tst_QProcess::detachedProcessParameters_data()
{
+ QTest::addColumn<QString>("outChannel");
+ QTest::newRow("none") << QString();
+ QTest::newRow("stdout") << QString("stdout");
+ QTest::newRow("stderr") << QString("stderr");
+}
+
+void tst_QProcess::detachedProcessParameters()
+{
+ QFETCH(QString, outChannel);
qint64 pid;
QFile infoFile(m_temporaryDir.path() + QLatin1String("/detachedinfo.txt"));
if (infoFile.exists())
QVERIFY(infoFile.remove());
+ QFile channelFile(m_temporaryDir.path() + QLatin1String("detachedinfo2.txt"));
+ if (channelFile.exists())
+ QVERIFY(channelFile.remove());
QString workingDir = QDir::currentPath() + "/testDetached";
QVERIFY(QFile::exists(workingDir));
- QStringList args;
- args << infoFile.fileName();
- QVERIFY(QProcess::startDetached(QDir::currentPath() + QLatin1String("/testDetached/testDetached"), args, workingDir, &pid));
+ QVERIFY(qgetenv("tst_QProcess").isEmpty());
+ QByteArray envVarValue("foobarbaz");
+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
+ environment.insert(QStringLiteral("tst_QProcess"), QString::fromUtf8(envVarValue));
+
+ QProcess process;
+ process.setProgram(QDir::currentPath() + QLatin1String("/testDetached/testDetached"));
+#ifdef Q_OS_WIN
+ int modifierCalls = 0;
+ process.setCreateProcessArgumentsModifier(
+ [&modifierCalls] (QProcess::CreateProcessArguments *) { modifierCalls++; });
+#endif
+ QStringList args(infoFile.fileName());
+ if (!outChannel.isEmpty()) {
+ args << QStringLiteral("--out-channel=") + outChannel;
+ if (outChannel == "stdout")
+ process.setStandardOutputFile(channelFile.fileName());
+ else if (outChannel == "stderr")
+ process.setStandardErrorFile(channelFile.fileName());
+ }
+ process.setArguments(args);
+ process.setWorkingDirectory(workingDir);
+ process.setProcessEnvironment(environment);
+ QVERIFY(process.startDetached(&pid));
QFileInfo fi(infoFile);
fi.setCaching(false);
@@ -2097,19 +2131,35 @@ void tst_QProcess::detachedWorkingDirectoryAndPid()
}
QVERIFY(infoFile.open(QIODevice::ReadOnly | QIODevice::Text));
- QString actualWorkingDir = QString::fromUtf8(infoFile.readLine());
- actualWorkingDir.chop(1); // strip off newline
- QByteArray processIdString = infoFile.readLine();
- processIdString.chop(1);
+ QString actualWorkingDir = QString::fromUtf8(infoFile.readLine()).trimmed();
+ QByteArray processIdString = infoFile.readLine().trimmed();
+ QByteArray actualEnvVarValue = infoFile.readLine().trimmed();
+ QByteArray infoFileContent;
+ if (!outChannel.isEmpty()) {
+ infoFile.seek(0);
+ infoFileContent = infoFile.readAll();
+ }
infoFile.close();
infoFile.remove();
+ if (!outChannel.isEmpty()) {
+ QVERIFY(channelFile.open(QIODevice::ReadOnly | QIODevice::Text));
+ QByteArray channelContent = channelFile.readAll();
+ channelFile.close();
+ channelFile.remove();
+ QCOMPARE(channelContent, infoFileContent);
+ }
+
bool ok = false;
qint64 actualPid = processIdString.toLongLong(&ok);
QVERIFY(ok);
QCOMPARE(actualWorkingDir, workingDir);
QCOMPARE(actualPid, pid);
+ QCOMPARE(actualEnvVarValue, envVarValue);
+#ifdef Q_OS_WIN
+ QCOMPARE(modifierCalls, 1);
+#endif
}
void tst_QProcess::switchReadChannels()
diff --git a/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp b/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp
index 9573f3078b..96970421d3 100644
--- a/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp
+++ b/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp
@@ -82,6 +82,11 @@ private slots:
void transactionalWriteErrorRenaming();
void symlink();
void directory();
+
+#ifdef Q_OS_WIN
+ void alternateDataStream_data();
+ void alternateDataStream();
+#endif
};
static inline QByteArray msgCannotOpen(const QFileDevice &f)
@@ -474,5 +479,60 @@ void tst_QSaveFile::directory()
#endif
}
+#ifdef Q_OS_WIN
+void tst_QSaveFile::alternateDataStream_data()
+{
+ QTest::addColumn<bool>("directWriteFallback");
+ QTest::addColumn<bool>("success");
+
+ QTest::newRow("default") << false << false;
+ QTest::newRow("directWriteFallback") << true << true;
+}
+
+void tst_QSaveFile::alternateDataStream()
+{
+ QFETCH(bool, directWriteFallback);
+ QFETCH(bool, success);
+ static const char newContent[] = "New content\r\n";
+
+ QTemporaryDir dir;
+ QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
+ QString baseName = dir.path() + QLatin1String("/base");
+ {
+ QFile baseFile(baseName);
+ QVERIFY2(baseFile.open(QIODevice::ReadWrite), qPrintable(baseFile.errorString()));
+ }
+
+ // First, create a file with old content
+ QString adsName = baseName + QLatin1String(":outfile");
+ {
+ QFile targetFile(adsName);
+ if (!targetFile.open(QIODevice::ReadWrite))
+ QSKIP("Failed to ceate ADS file (" + targetFile.errorString().toUtf8()
+ + "). Temp dir is FAT?");
+ targetFile.write("Old content\r\n");
+ }
+
+ // And write to it again using QSaveFile; only works if directWriteFallback is enabled
+ QSaveFile file(adsName);
+ file.setDirectWriteFallback(directWriteFallback);
+ QCOMPARE(file.directWriteFallback(), directWriteFallback);
+
+ if (success) {
+ QVERIFY2(file.open(QIODevice::WriteOnly), qPrintable(file.errorString()));
+ file.write(newContent);
+ QVERIFY2(file.commit(), qPrintable(file.errorString()));
+
+ // check the contents
+ QFile targetFile(adsName);
+ QVERIFY2(targetFile.open(QIODevice::ReadOnly), qPrintable(targetFile.errorString()));
+ QByteArray contents = targetFile.readAll();
+ QCOMPARE(contents, QByteArray(newContent));
+ } else {
+ QVERIFY(!file.open(QIODevice::WriteOnly));
+ }
+}
+#endif
+
QTEST_MAIN(tst_QSaveFile)
#include "tst_qsavefile.moc"
diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp
index 332c2dcc01..012ce5f2f5 100644
--- a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp
+++ b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp
@@ -118,6 +118,10 @@ private slots:
void remove();
void contains();
void sync();
+ void syncNonWriteableDir();
+#ifdef Q_OS_WIN
+ void syncAlternateDataStream();
+#endif
void setFallbacksEnabled();
void setFallbacksEnabled_data();
void fromFile_data();
@@ -1750,6 +1754,78 @@ void tst_QSettings::sync()
QCOMPARE(settings1.allKeys().count(), 11);
}
+void tst_QSettings::syncNonWriteableDir()
+{
+ QTemporaryDir tempDir;
+ QVERIFY2(tempDir.isValid(), qUtf8Printable(tempDir.errorString()));
+
+ // first, create a file
+ QString filename = tempDir.path() + "/config.ini";
+ {
+ QFile f(filename);
+ QVERIFY2(f.open(QIODevice::WriteOnly), qUtf8Printable(f.errorString()));
+ }
+
+ // second, make the dir unwriteable
+ QVERIFY(QFile::setPermissions(tempDir.path(), QFile::ReadUser | QFile::ExeUser));
+ struct UndoSetPermissions {
+ QString name;
+ UndoSetPermissions(const QString &name) : name(name) {}
+ ~UndoSetPermissions()
+ { QFile::setPermissions(name, QFile::ReadUser | QFile::WriteUser | QFile::ExeUser); }
+ };
+ UndoSetPermissions undo(tempDir.path()); // otherwise, QTemporaryDir will fail
+
+ {
+ QSettings settings(filename, QSettings::IniFormat);
+ QVERIFY(settings.isAtomicSyncRequired());
+ settings.setAtomicSyncRequired(false);
+ settings.setValue("alpha/beta", 1);
+ settings.sync();
+ QCOMPARE(settings.status(), QSettings::NoError);
+ }
+
+ QVERIFY(QFileInfo(filename).size() != 0);
+ QSettings settings(filename, QSettings::IniFormat);
+ QCOMPARE(settings.value("alpha/beta"), QVariant(1));
+}
+
+#ifdef Q_OS_WIN
+void tst_QSettings::syncAlternateDataStream()
+{
+ QTemporaryDir tempDir;
+ QVERIFY2(tempDir.isValid(), qUtf8Printable(tempDir.errorString()));
+
+ // first, create a file
+ QString filename = tempDir.path() + "/file";
+ {
+ QFile f(filename);
+ QVERIFY2(f.open(QIODevice::WriteOnly), qUtf8Printable(f.errorString()));
+ }
+
+ // then create an ADS
+ filename += ":config.ini";
+ {
+ QFile f(filename);
+ if (!f.open(QIODevice::WriteOnly))
+ QSKIP("Could not create ADS file (" + f.errorString().toUtf8() + ") - FAT drive?");
+ }
+
+ {
+ QSettings settings(filename, QSettings::IniFormat);
+ QVERIFY(settings.isAtomicSyncRequired());
+ settings.setAtomicSyncRequired(false);
+ settings.setValue("alpha/beta", 1);
+ settings.sync();
+ QCOMPARE(settings.status(), QSettings::NoError);
+ }
+
+ QVERIFY(QFileInfo(filename).size() != 0);
+ QSettings settings(filename, QSettings::IniFormat);
+ QCOMPARE(settings.value("alpha/beta"), QVariant(1));
+}
+#endif
+
void tst_QSettings::setFallbacksEnabled_data()
{
populateWithFormats();
diff --git a/tests/auto/corelib/io/qstandardpaths/BLACKLIST b/tests/auto/corelib/io/qstandardpaths/BLACKLIST
new file mode 100644
index 0000000000..d5ee9650cd
--- /dev/null
+++ b/tests/auto/corelib/io/qstandardpaths/BLACKLIST
@@ -0,0 +1,3 @@
+[testFindExecutable]
+# QTBUG-64404
+b2qt 64bit
diff --git a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp
index 21936faca5..6dbb8ddd0d 100644
--- a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp
+++ b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp
@@ -139,25 +139,24 @@ void tst_QTemporaryDir::fileTemplate_data()
{
QTest::addColumn<QString>("constructorTemplate");
QTest::addColumn<QString>("prefix");
+ QTest::addColumn<QString>("suffix");
+
+ QTest::newRow("default") << "" << "tst_qtemporarydir-" << "";
- QTest::newRow("constructor default") << "" << "tst_qtemporarydir-";
-
- QTest::newRow("constructor with xxx sufix") << "qt_XXXXXXxxx" << "qt_XXXXXXxxx";
- QTest::newRow("constructor with xXx sufix") << "qt_XXXXXXxXx" << "qt_XXXXXXxXx";
- QTest::newRow("constructor with no suffix") << "qt_XXXXXX" << "qt_";
- QTest::newRow("constructor with >6 X's, no suffix") << "qt_XXXXXXXXXX" << "qt_";
- // When more than 6 X are present at the end, linux and windows will only replace the last 6,
- // while Mac OS will actually replace all of them so we can only expect "qt_" (and check isValid).
- QTest::newRow("constructor with XXXX suffix") << "qt_XXXXXX_XXXX" << "qt_";
- QTest::newRow("constructor with XXXX prefix") << "qt_XXXX" << "qt_";
- QTest::newRow("constructor with XXXXX prefix") << "qt_XXXXX" << "qt_";
+ QTest::newRow("xxx-suffix") << "qt_XXXXXXxxx" << "qt_" << "xxx";
+ QTest::newRow("xXx-suffix") << "qt_XXXXXXxXx" << "qt_" << "xXx";
+ QTest::newRow("no-suffix") << "qt_XXXXXX" << "qt_" << "";
+ QTest::newRow("10X") << "qt_XXXXXXXXXX" << "qt_" << "";
+ QTest::newRow("4Xsuffix") << "qt_XXXXXX_XXXX" << "qt_" << "_XXXX";
+ QTest::newRow("4Xprefix") << "qt_XXXX" << "qt_XXXX" << "";
+ QTest::newRow("5Xprefix") << "qt_XXXXX" << "qt_XXXXX" << "";
if (QTestPrivate::canHandleUnicodeFileNames()) {
// Test Umlauts (contained in Latin1)
QString prefix = "qt_" + umlautTestText();
- QTest::newRow("Umlauts") << (prefix + "XXXXXX") << prefix;
- // Test Chinese
+ QTest::newRow("Umlauts") << (prefix + "XXXXXX") << prefix << "";
+ // test non-Latin1
prefix = "qt_" + hanTestText();
- QTest::newRow("Chinese characters") << (prefix + "XXXXXX") << prefix;
+ QTest::newRow("Chinese") << (prefix + "XXXXXX" + umlautTestText()) << prefix << umlautTestText();
}
}
@@ -165,14 +164,17 @@ void tst_QTemporaryDir::fileTemplate()
{
QFETCH(QString, constructorTemplate);
QFETCH(QString, prefix);
+ QFETCH(QString, suffix);
QTemporaryDir tempDir(constructorTemplate);
QVERIFY(tempDir.isValid());
QString dirName = QDir(tempDir.path()).dirName();
- if (prefix.length())
+ if (prefix.length()) {
QCOMPARE(dirName.left(prefix.length()), prefix);
+ QCOMPARE(dirName.right(suffix.length()), suffix);
+ }
}
diff --git a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp
index a4e7aa0316..dbc3d68e93 100644
--- a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp
+++ b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@@ -32,6 +33,7 @@
#include <qtemporarydir.h>
#include <qtemporaryfile.h>
#include <qfile.h>
+#include <qdatetime.h>
#include <qdir.h>
#include <qset.h>
#include <qtextcodec.h>
@@ -65,9 +67,10 @@ private slots:
void fileNameIsEmpty();
void autoRemove();
void nonWritableCurrentDir();
- void write();
+ void io();
void openCloseOpenClose();
void removeAndReOpen();
+ void removeUnnamed();
void size();
void resize();
void openOnRootDrives();
@@ -278,6 +281,18 @@ void tst_QTemporaryFile::autoRemove()
fileName = file.fileName();
file.close();
}
+ QVERIFY(!fileName.isEmpty());
+ QVERIFY(!QFile::exists(fileName));
+
+ // same, but gets the file name after closing
+ {
+ QTemporaryFile file("tempXXXXXX");
+ file.setAutoRemove(true);
+ QVERIFY(file.open());
+ file.close();
+ fileName = file.fileName();
+ }
+ QVERIFY(!fileName.isEmpty());
QVERIFY(!QFile::exists(fileName));
// Test if disabling auto remove works.
@@ -288,6 +303,19 @@ void tst_QTemporaryFile::autoRemove()
fileName = file.fileName();
file.close();
}
+ QVERIFY(!fileName.isEmpty());
+ QVERIFY(QFile::exists(fileName));
+ QVERIFY(QFile::remove(fileName));
+
+ // same, but gets the file name after closing
+ {
+ QTemporaryFile file("tempXXXXXX");
+ file.setAutoRemove(false);
+ QVERIFY(file.open());
+ file.close();
+ fileName = file.fileName();
+ }
+ QVERIFY(!fileName.isEmpty());
QVERIFY(QFile::exists(fileName));
QVERIFY(QFile::remove(fileName));
@@ -338,17 +366,51 @@ void tst_QTemporaryFile::nonWritableCurrentDir()
#endif
}
-void tst_QTemporaryFile::write()
+void tst_QTemporaryFile::io()
{
QByteArray data("OLE\nOLE\nOLE");
QTemporaryFile file;
+ QDateTime before = QDateTime::currentDateTimeUtc().addMSecs(-250);
+
+ // discard msec component (round down) - not all FSs and OSs support them
+ before.setSecsSinceEpoch(before.toSecsSinceEpoch());
+
QVERIFY(file.open());
+ QVERIFY(file.readLink().isEmpty()); // it's not a link!
+ QFile::Permissions perm = file.permissions();
+ QVERIFY(perm & QFile::ReadOwner);
+ QVERIFY(file.setPermissions(perm));
+
+ QCOMPARE(int(file.size()), 0);
+ QVERIFY(file.resize(data.size()));
+ QCOMPARE(int(file.size()), data.size());
QCOMPARE((int)file.write(data), data.size());
+ QCOMPARE(int(file.size()), data.size());
+
+ QDateTime mtime = file.fileTime(QFile::FileModificationTime).toUTC();
+ QDateTime btime = file.fileTime(QFile::FileBirthTime).toUTC();
+ QDateTime ctime = file.fileTime(QFile::FileMetadataChangeTime).toUTC();
+ QDateTime atime = file.fileTime(QFile::FileAccessTime).toUTC();
+
+ QDateTime after = QDateTime::currentDateTimeUtc().toUTC().addMSecs(250);
+ // round msecs up
+ after.setSecsSinceEpoch(after.toSecsSinceEpoch() + 1);
+
+ // mtime must be valid, the rest could fail
+ QVERIFY(mtime <= after && mtime >= before);
+ QVERIFY(!btime.isValid() || (btime <= after && btime >= before));
+ QVERIFY(!ctime.isValid() || (ctime <= after && ctime >= before));
+ QVERIFY(!btime.isValid() || (btime <= after && btime >= before));
+
+ QVERIFY(file.setFileTime(before.addSecs(-10), QFile::FileModificationTime));
+ mtime = file.fileTime(QFile::FileModificationTime).toUTC();
+ QCOMPARE(mtime, before.addSecs(-10));
+
file.reset();
QFile compare(file.fileName());
compare.open(QIODevice::ReadOnly);
QCOMPARE(compare.readAll() , data);
- file.close();
+ QCOMPARE(compare.fileTime(QFile::FileModificationTime), mtime);
}
void tst_QTemporaryFile::openCloseOpenClose()
@@ -381,11 +443,13 @@ void tst_QTemporaryFile::removeAndReOpen()
{
QTemporaryFile file;
file.open();
- fileName = file.fileName();
+ fileName = file.fileName(); // materializes any unnamed file
QVERIFY(QFile::exists(fileName));
- file.remove();
+ QVERIFY(file.remove());
+ QVERIFY(file.fileName().isEmpty());
QVERIFY(!QFile::exists(fileName));
+ QVERIFY(!file.remove());
QVERIFY(file.open());
QCOMPARE(QFileInfo(file.fileName()).path(), QFileInfo(fileName).path());
@@ -395,21 +459,36 @@ void tst_QTemporaryFile::removeAndReOpen()
QVERIFY(!QFile::exists(fileName));
}
+void tst_QTemporaryFile::removeUnnamed()
+{
+ QTemporaryFile file;
+ file.open();
+
+ // we did not call fileName(), so the file name may not have a name
+ QVERIFY(file.remove());
+ QVERIFY(file.fileName().isEmpty());
+
+ // if it was unnamed, this will succeed again, so we can't check the result
+ file.remove();
+}
+
void tst_QTemporaryFile::size()
{
QTemporaryFile file;
QVERIFY(file.open());
- QVERIFY(file.exists());
QVERIFY(!file.isSequential());
QByteArray str("foobar");
file.write(str);
- QVERIFY(QFile::exists(file.fileName()));
+
// On CE it takes more time for the filesystem to update
// the information. Usually you have to close it or seek
// to get latest information. flush() does not help either.
QCOMPARE(file.size(), qint64(6));
file.seek(0);
QCOMPARE(file.size(), qint64(6));
+
+ QVERIFY(QFile::exists(file.fileName()));
+ QVERIFY(file.exists());
}
void tst_QTemporaryFile::resize()
@@ -806,6 +885,14 @@ void tst_QTemporaryFile::QTBUG_4796()
QCOMPARE(file5.open(), openResult);
QCOMPARE(file6.open(), openResult);
+ // force the files to exist, if they are supposed to
+ QCOMPARE(!file1.fileName().isEmpty(), openResult);
+ QCOMPARE(!file2.fileName().isEmpty(), openResult);
+ QCOMPARE(!file3.fileName().isEmpty(), openResult);
+ QCOMPARE(!file4.fileName().isEmpty(), openResult);
+ QCOMPARE(!file5.fileName().isEmpty(), openResult);
+ QCOMPARE(!file6.fileName().isEmpty(), openResult);
+
QCOMPARE(file1.exists(), openResult);
QCOMPARE(file2.exists(), openResult);
QCOMPARE(file3.exists(), openResult);
@@ -861,8 +948,6 @@ void tst_QTemporaryFile::guaranteeUnique()
// First pass. See which filename QTemporaryFile will try first.
{
- // Fix the random seed.
- qsrand(1135);
QTemporaryFile tmpFile("testFile1.XXXXXX");
tmpFile.open();
takenFileName = tmpFile.fileName();
@@ -876,8 +961,6 @@ void tst_QTemporaryFile::guaranteeUnique()
// Second pass, now we have blocked its first attempt with a directory.
{
- // Fix the random seed.
- qsrand(1135);
QTemporaryFile tmpFile("testFile1.XXXXXX");
QVERIFY(tmpFile.open());
QString uniqueFileName = tmpFile.fileName();
diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp
index 0bbd1b9f15..20282068cb 100644
--- a/tests/auto/corelib/io/qurl/tst_qurl.cpp
+++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp
@@ -1384,18 +1384,6 @@ void tst_QUrl::compat_constructor_01_data()
void tst_QUrl::compat_constructor_01()
{
- /* The following should work as expected:
- *
- * QUrlOperator op;
- * op.copy( QString( "Makefile" ),
- * QString("ftp://rms:grmpf12@nibbler/home/rms/tmp"),
- * false );
- *
- * as well as the following:
- *
- * QUrlOperator op;
- * op.copy(QString("ftp://ftp.qt-project.org/qt/INSTALL"), ".");
- */
QFETCH( QString, urlStr );
{
@@ -1425,11 +1413,6 @@ void tst_QUrl::compat_constructor_02_data()
void tst_QUrl::compat_constructor_02()
{
- /* The following should work as expected:
- *
- * QUrlOperator op( "ftp://ftp.qt-project.org/qt" );
- * op.copy(QString("INSTALL"), ".");
- */
QFETCH( QString, urlStr );
QFETCH( QString, fileName );
diff --git a/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro b/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro
deleted file mode 100644
index 4f0e9da3c2..0000000000
--- a/tests/auto/corelib/io/qwinoverlappedionotifier/qwinoverlappedionotifier.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qwinoverlappedionotifier
-QT = core-private testlib
-SOURCES = tst_qwinoverlappedionotifier.cpp
diff --git a/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp b/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp
deleted file mode 100644
index 7034c2c9fd..0000000000
--- a/tests/auto/corelib/io/qwinoverlappedionotifier/tst_qwinoverlappedionotifier.cpp
+++ /dev/null
@@ -1,331 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** 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 The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
-#include <private/qwinoverlappedionotifier_p.h>
-#include <qbytearray.h>
-#include <qt_windows.h>
-
-#ifndef PIPE_REJECT_REMOTE_CLIENTS
-#define PIPE_REJECT_REMOTE_CLIENTS 0x08
-#endif
-
-class tst_QWinOverlappedIoNotifier : public QObject
-{
- Q_OBJECT
-
-private slots:
- void initTestCase();
- void readFile_data();
- void readFile();
- void waitForNotified_data();
- void waitForNotified();
- void waitForAnyNotified();
- void brokenPipe();
- void multipleOperations();
-
-private:
- QFileInfo sourceFileInfo;
- DWORD notifiedBytesRead;
- DWORD notifiedErrorCode;
-};
-
-class NotifierSink : public QObject
-{
- Q_OBJECT
-public:
- NotifierSink(QWinOverlappedIoNotifier *notifier)
- : QObject(notifier),
- threshold(1)
- {
- connect(notifier, &QWinOverlappedIoNotifier::notified, this, &NotifierSink::notified);
- }
-
-protected slots:
- void notified(DWORD bytesRead, DWORD errorCode, OVERLAPPED *overlapped)
- {
- IOResult ioResult;
- ioResult.bytes = bytesRead;
- ioResult.errorCode = errorCode;
- ioResult.overlapped = overlapped;
- notifications.append(ioResult);
- if (notifications.count() >= threshold)
- emit notificationReceived();
- }
-
-signals:
- void notificationReceived();
-
-public:
- int threshold;
-
- struct IOResult
- {
- IOResult()
- : bytes(0), errorCode(ERROR_SUCCESS), overlapped(0)
- {}
- DWORD bytes;
- DWORD errorCode;
- OVERLAPPED *overlapped;
- };
-
- QList<IOResult> notifications;
-};
-
-void tst_QWinOverlappedIoNotifier::initTestCase()
-{
- sourceFileInfo.setFile(QFINDTESTDATA("tst_qwinoverlappedionotifier.cpp"));
- QVERIFY2(sourceFileInfo.exists(), "File tst_qwinoverlappedionotifier.cpp not found.");
-}
-
-void tst_QWinOverlappedIoNotifier::readFile_data()
-{
- QTest::addColumn<QString>("fileName");
- QTest::addColumn<int>("readBufferSize");
- QTest::addColumn<DWORD>("expectedBytesRead");
-
- QString sourceFileName = QDir::toNativeSeparators(sourceFileInfo.absoluteFilePath());
- int sourceFileSize = sourceFileInfo.size();
-
- QTest::newRow("read file, less than available")
- << sourceFileName << sourceFileSize / 2 << DWORD(sourceFileSize / 2);
- QTest::newRow("read file, more than available")
- << sourceFileName << sourceFileSize * 2 << DWORD(sourceFileSize);
-}
-
-void tst_QWinOverlappedIoNotifier::readFile()
-{
- QFETCH(QString, fileName);
- QFETCH(int, readBufferSize);
- QFETCH(DWORD, expectedBytesRead);
-
- QWinOverlappedIoNotifier notifier;
- NotifierSink sink(&notifier);
- connect(&sink, &NotifierSink::notificationReceived, &QTestEventLoop::instance(), &QTestEventLoop::exitLoop);
-
- HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()),
- GENERIC_READ, FILE_SHARE_READ,
- NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
- notifier.setHandle(hFile);
- notifier.setEnabled(true);
-
- OVERLAPPED overlapped;
- ZeroMemory(&overlapped, sizeof(OVERLAPPED));
- QByteArray buffer(readBufferSize, 0);
- BOOL readSuccess = ReadFile(hFile, buffer.data(), buffer.size(), NULL, &overlapped);
- QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING);
-
- QTestEventLoop::instance().enterLoop(3);
- CloseHandle(hFile);
- QCOMPARE(sink.notifications.count(), 1);
- QCOMPARE(sink.notifications.last().bytes, expectedBytesRead);
- QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_SUCCESS));
- QCOMPARE(sink.notifications.last().overlapped, &overlapped);
-}
-
-void tst_QWinOverlappedIoNotifier::waitForNotified_data()
-{
- readFile_data();
-}
-
-void tst_QWinOverlappedIoNotifier::waitForNotified()
-{
- QFETCH(QString, fileName);
- QFETCH(int, readBufferSize);
- QFETCH(DWORD, expectedBytesRead);
-
- QWinOverlappedIoNotifier notifier;
- NotifierSink sink(&notifier);
- HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()),
- GENERIC_READ, FILE_SHARE_READ,
- NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
- notifier.setHandle(hFile);
- notifier.setEnabled(true);
- QCOMPARE(notifier.waitForNotified(100, 0), false);
-
- OVERLAPPED overlapped;
- ZeroMemory(&overlapped, sizeof(OVERLAPPED));
- QByteArray buffer(readBufferSize, 0);
- BOOL readSuccess = ReadFile(hFile, buffer.data(), buffer.size(), NULL, &overlapped);
- QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING);
-
- QCOMPARE(notifier.waitForNotified(3000, &overlapped), true);
- CloseHandle(hFile);
- QCOMPARE(sink.notifications.count(), 1);
- QCOMPARE(sink.notifications.last().bytes, expectedBytesRead);
- QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_SUCCESS));
- QCOMPARE(sink.notifications.last().overlapped, &overlapped);
- QCOMPARE(notifier.waitForNotified(100, &overlapped), false);
-}
-
-void tst_QWinOverlappedIoNotifier::waitForAnyNotified()
-{
- const QString fileName = QDir::toNativeSeparators(sourceFileInfo.absoluteFilePath());
- const int readBufferSize = sourceFileInfo.size();
-
- QWinOverlappedIoNotifier notifier;
- HANDLE hFile = CreateFile(reinterpret_cast<const wchar_t*>(fileName.utf16()),
- GENERIC_READ, FILE_SHARE_READ,
- NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
- notifier.setHandle(hFile);
- notifier.setEnabled(true);
- QVERIFY(!notifier.waitForAnyNotified(100));
-
- OVERLAPPED overlapped1;
- ZeroMemory(&overlapped1, sizeof(OVERLAPPED));
- QByteArray buffer1(readBufferSize, 0);
- BOOL readSuccess = ReadFile(hFile, buffer1.data(), buffer1.size(), NULL, &overlapped1);
- QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING);
-
- OVERLAPPED overlapped2;
- ZeroMemory(&overlapped2, sizeof(OVERLAPPED));
- QByteArray buffer2(readBufferSize, 0);
- readSuccess = ReadFile(hFile, buffer2.data(), buffer2.size(), NULL, &overlapped2);
- QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING);
-
- QSet<OVERLAPPED *> overlappedObjects;
- overlappedObjects << &overlapped1 << &overlapped2;
-
- for (int i = 1; i <= 2; ++i) {
- OVERLAPPED *notifiedOverlapped = notifier.waitForAnyNotified(3000);
- QVERIFY(overlappedObjects.contains(notifiedOverlapped));
- overlappedObjects.remove(notifiedOverlapped);
- }
-
- CloseHandle(hFile);
- QVERIFY(overlappedObjects.isEmpty());
- QVERIFY(!notifier.waitForAnyNotified(100));
-}
-
-void tst_QWinOverlappedIoNotifier::brokenPipe()
-{
- QWinOverlappedIoNotifier notifier;
- NotifierSink sink(&notifier);
- connect(&sink, &NotifierSink::notificationReceived, &QTestEventLoop::instance(), &QTestEventLoop::exitLoop);
-
- wchar_t pipeName[] = L"\\\\.\\pipe\\tst_QWinOverlappedIoNotifier_brokenPipe";
- HANDLE hPipe = CreateNamedPipe(pipeName,
- PIPE_ACCESS_DUPLEX,
- PIPE_TYPE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS,
- 1, 0, 0, 0, NULL);
- QVERIFY(hPipe != INVALID_HANDLE_VALUE);
- HANDLE hReadEnd = CreateFile(pipeName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
- QVERIFY(hReadEnd != INVALID_HANDLE_VALUE);
- notifier.setHandle(hReadEnd);
- notifier.setEnabled(true);
-
- OVERLAPPED overlapped;
- ZeroMemory(&overlapped, sizeof(OVERLAPPED));
- QByteArray buffer(1024, 0);
- BOOL readSuccess = ReadFile(hReadEnd, buffer.data(), buffer.size(), NULL, &overlapped);
- QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING);
-
- // close the write end of the pipe
- CloseHandle(hPipe);
-
- QTestEventLoop::instance().enterLoop(3);
- CloseHandle(hReadEnd);
- QCOMPARE(sink.notifications.count(), 1);
- QCOMPARE(sink.notifications.last().bytes, DWORD(0));
- QCOMPARE(sink.notifications.last().errorCode, DWORD(ERROR_BROKEN_PIPE));
- QCOMPARE(sink.notifications.last().overlapped, &overlapped);
-}
-
-void tst_QWinOverlappedIoNotifier::multipleOperations()
-{
- QWinOverlappedIoNotifier clientNotifier;
- NotifierSink sink(&clientNotifier);
- sink.threshold = 2;
- connect(&sink, &NotifierSink::notificationReceived,
- &QTestEventLoop::instance(), &QTestEventLoop::exitLoop);
-
- wchar_t pipeName[] = L"\\\\.\\pipe\\tst_QWinOverlappedIoNotifier_multipleOperations";
- HANDLE hServer = CreateNamedPipe(pipeName,
- PIPE_ACCESS_DUPLEX,
- PIPE_TYPE_BYTE | PIPE_NOWAIT | PIPE_REJECT_REMOTE_CLIENTS,
- 1, 0, 0, 0, NULL);
- QVERIFY(hServer != INVALID_HANDLE_VALUE);
- HANDLE hClient = CreateFile(pipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL,
- OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
- QVERIFY(hClient != INVALID_HANDLE_VALUE);
- clientNotifier.setHandle(hClient);
- clientNotifier.setEnabled(true);
-
- // start async read on client
- QByteArray clientReadBuffer(377, Qt::Uninitialized);
- OVERLAPPED clientReadOverlapped;
- ZeroMemory(&clientReadOverlapped, sizeof(clientReadOverlapped));
- BOOL readSuccess = ReadFile(hClient, clientReadBuffer.data(), clientReadBuffer.size(),
- NULL, &clientReadOverlapped);
- QVERIFY(readSuccess || GetLastError() == ERROR_IO_PENDING);
-
- // start async write client -> server
- QByteArray clientDataToWrite(233, 'B');
- OVERLAPPED clientWriteOverlapped;
- ZeroMemory(&clientWriteOverlapped, sizeof(clientWriteOverlapped));
- BOOL writeSuccess = WriteFile(hClient, clientDataToWrite.data(), clientDataToWrite.size(),
- NULL, &clientWriteOverlapped);
- QVERIFY(writeSuccess || GetLastError() == ERROR_IO_PENDING);
-
- // start async write server -> client
- QByteArray serverDataToWrite(144, 'A');
- OVERLAPPED serverOverlapped;
- ZeroMemory(&serverOverlapped, sizeof(serverOverlapped));
- writeSuccess = WriteFile(hServer, serverDataToWrite.data(), serverDataToWrite.size(),
- NULL, &serverOverlapped);
- QVERIFY(writeSuccess || GetLastError() == ERROR_IO_PENDING);
-
- // read synchronously on server to complete the client -> server write
- QByteArray serverReadBuffer(610, Qt::Uninitialized);
- DWORD dwBytesRead = 0;
- readSuccess = ReadFile(hServer, serverReadBuffer.data(), serverReadBuffer.size(),
- &dwBytesRead, NULL);
- QVERIFY(readSuccess);
- QCOMPARE(int(dwBytesRead), clientDataToWrite.size());
- serverReadBuffer.resize(dwBytesRead);
- QCOMPARE(serverReadBuffer, clientDataToWrite);
-
- QTestEventLoop::instance().enterLoop(3);
- QTRY_COMPARE(sink.notifications.count(), 2);
- foreach (const NotifierSink::IOResult &r, sink.notifications) {
- QCOMPARE(r.errorCode, DWORD(ERROR_SUCCESS));
- if (r.bytes == DWORD(serverDataToWrite.count()))
- QCOMPARE(r.overlapped, &clientReadOverlapped);
- else if (r.bytes == DWORD(clientDataToWrite.count()))
- QCOMPARE(r.overlapped, &clientWriteOverlapped);
- else
- QVERIFY2(false, "Unexpected number of bytes received.");
- }
-
- CloseHandle(hClient);
- CloseHandle(hServer);
-}
-
-QTEST_MAIN(tst_QWinOverlappedIoNotifier)
-
-#include "tst_qwinoverlappedionotifier.moc"
diff --git a/tests/auto/corelib/itemmodels/itemmodels.pro b/tests/auto/corelib/itemmodels/itemmodels.pro
index c1d75cc2cb..a09f03a7b4 100644
--- a/tests/auto/corelib/itemmodels/itemmodels.pro
+++ b/tests/auto/corelib/itemmodels/itemmodels.pro
@@ -7,6 +7,7 @@ qtHaveModule(gui): SUBDIRS += \
qabstractproxymodel \
qidentityproxymodel \
qitemselectionmodel \
+ qsortfilterproxymodel_recursive \
qtHaveModule(widgets) {
SUBDIRS += \
diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
index bc8f0c1c51..1fb51490db 100644
--- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
+++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp
@@ -94,6 +94,7 @@ private slots:
void changeSourceDataKeepsStableSorting_qtbug1548();
void changeSourceDataForwardsRoles_qtbug35440();
void resortingDoesNotBreakTreeModels();
+ void dynamicFilterWithoutSort();
void sortFilterRole();
void selectionFilteredOut();
void match_data();
@@ -148,6 +149,9 @@ private slots:
void sourceLayoutChangeLeavesValidPersistentIndexes();
void rowMoveLeavesValidPersistentIndexes();
+ void emitLayoutChangedOnlyIfSortingChanged_data();
+ void emitLayoutChangedOnlyIfSortingChanged();
+
protected:
void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
void checkHierarchy(const QStringList &data, const QAbstractItemModel *model);
@@ -2057,8 +2061,6 @@ static void checkSortedTableModel(const QAbstractItemModel *model, const QString
void tst_QSortFilterProxyModel::changeSourceDataKeepsStableSorting_qtbug1548()
{
- QSKIP("This test will fail, see QTBUG-1548");
-
// Check that emitting dataChanged from the source model
// for a change of a role which is not the sorting role
// doesn't alter the sorting. In this case, we sort on the DisplayRole,
@@ -3568,6 +3570,13 @@ void tst_QSortFilterProxyModel::testParentLayoutChanged()
parentItem = item;
}
}
+ // item 0
+ // item 10
+ // - item 1
+ // - item 11
+ // - item 2
+ // - item 12
+ // ...
QSortFilterProxyModel proxy;
proxy.sort(0, Qt::AscendingOrder);
@@ -3609,11 +3618,12 @@ void tst_QSortFilterProxyModel::testParentLayoutChanged()
QVERIFY(proxy2ParentsChangedSpy.isValid());
QStandardItem *item = model.invisibleRootItem()->child(1)->child(1);
+ QCOMPARE(item->text(), QStringLiteral("item 11"));
// Ensure mapped:
proxy.mapFromSource(model.indexFromItem(item));
- item->setData("Changed");
+ item->setText("Changed");
QCOMPARE(dataChangedSpy.size(), 1);
QCOMPARE(layoutAboutToBeChangedSpy.size(), 1);
@@ -3883,7 +3893,7 @@ void tst_QSortFilterProxyModel::hierarchyFilterInvalidation()
view.setCurrentIndex(proxy.index(2, 0).child(0, 0));
view.show();
- QTest::qWaitForWindowExposed(&view);
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
proxy.setMode(true);
}
@@ -3940,7 +3950,7 @@ void tst_QSortFilterProxyModel::simpleFilterInvalidation()
view.setModel(&proxy);
view.show();
- QTest::qWaitForWindowExposed(&view);
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
proxy.setMode(true);
model.insertRow(0, new QStandardItem("extra"));
@@ -4026,7 +4036,7 @@ public:
}
- QModelIndex mapToSource(const QModelIndex &proxyIndex) const
+ QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
{
Q_ASSERT(sourceModel());
return QSortFilterProxyModel::mapToSource(proxyIndex);
@@ -4353,5 +4363,152 @@ void tst_QSortFilterProxyModel::rowMoveLeavesValidPersistentIndexes()
QVERIFY(persistentIndex.parent().isValid());
}
+void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged_data()
+{
+ QTest::addColumn<int>("changedRow");
+ QTest::addColumn<Qt::ItemDataRole>("changedRole");
+ QTest::addColumn<QString>("newData");
+ QTest::addColumn<QString>("expectedSourceRowTexts");
+ QTest::addColumn<QString>("expectedProxyRowTexts");
+ QTest::addColumn<int>("expectedLayoutChanged");
+
+ // Starting point:
+ // a source model with 8,7,6,5,4,3,2,1
+ // a proxy model keeping only even rows and sorting them, therefore showing 2,4,6,8
+
+ // When setData changes ordering, layoutChanged should be emitted
+ QTest::newRow("ordering_change") << 0 << Qt::DisplayRole << "0" << "07654321" << "0246" << 1;
+
+ // When setData on visible row doesn't change ordering, layoutChanged should not be emitted
+ QTest::newRow("no_ordering_change") << 6 << Qt::DisplayRole << "0" << "87654301" << "0468" << 0;
+
+ // When setData happens on a filtered out row, layoutChanged should not be emitted
+ QTest::newRow("filtered_out") << 1 << Qt::DisplayRole << "9" << "89654321" << "2468" << 0;
+
+ // When setData makes a row visible, layoutChanged should not be emitted (rowsInserted is emitted instead)
+ QTest::newRow("make_row_visible") << 7 << Qt::DisplayRole << "0" << "87654320" << "02468" << 0;
+
+ // When setData makes a row hidden, layoutChanged should not be emitted (rowsRemoved is emitted instead)
+ QTest::newRow("make_row_hidden") << 4 << Qt::DisplayRole << "1" << "87651321" << "268" << 0;
+
+ // When setData happens on an unrelated role, layoutChanged should not be emitted
+ QTest::newRow("unrelated_role") << 0 << Qt::DecorationRole << "" << "87654321" << "2468" << 0;
+
+ // When many changes happen together... and trigger removal, insertion, and layoutChanged
+ QTest::newRow("many_changes") << -1 << Qt::DisplayRole << "3,4,2,5,6,0,7,9" << "34256079" << "0246" << 1;
+
+ // When many changes happen together... and trigger removal, insertion, but no change in ordering of visible rows => no layoutChanged
+ QTest::newRow("many_changes_no_layoutChanged") << -1 << Qt::DisplayRole << "7,5,4,3,2,1,0,8" << "75432108" << "0248" << 0;
+}
+
+void tst_QSortFilterProxyModel::emitLayoutChangedOnlyIfSortingChanged()
+{
+ QFETCH(int, changedRow);
+ QFETCH(QString, newData);
+ QFETCH(Qt::ItemDataRole, changedRole);
+ QFETCH(QString, expectedSourceRowTexts);
+ QFETCH(QString, expectedProxyRowTexts);
+ QFETCH(int, expectedLayoutChanged);
+
+ // Custom version of QStringListModel which supports emitting dataChanged for many rows at once
+ class CustomStringListModel : public QAbstractListModel
+ {
+ public:
+ bool setData(const QModelIndex &index, const QVariant &value, int role) override
+ {
+ if (index.row() >= 0 && index.row() < lst.size()
+ && (role == Qt::EditRole || role == Qt::DisplayRole)) {
+ lst.replace(index.row(), value.toString());
+ emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
+ return true;
+ }
+ return false;
+ }
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
+ {
+ if (role == Qt::DisplayRole || role == Qt::EditRole)
+ return lst.at(index.row());
+ return QVariant();
+ }
+ int rowCount(const QModelIndex & = QModelIndex()) const override
+ {
+ return lst.count();
+ }
+
+ void replaceData(const QStringList &newData)
+ {
+ lst = newData;
+ emit dataChanged(index(0, 0), index(rowCount()-1, 0), {Qt::DisplayRole, Qt::EditRole});
+ }
+
+ void emitDecorationChangedSignal()
+ {
+ const QModelIndex idx = index(0, 0);
+ emit dataChanged(idx, idx, {Qt::DecorationRole});
+ }
+ private:
+ QStringList lst;
+ };
+ CustomStringListModel model;
+ QStringList strings;
+ for (auto i = 8; i >= 1; --i)
+ strings.append(QString::number(i));
+ model.replaceData(strings);
+ QCOMPARE(rowTexts(&model), QStringLiteral("87654321"));
+
+ class FilterEvenRowsProxyModel : public QSortFilterProxyModel
+ {
+ public:
+ bool filterAcceptsRow(int srcRow, const QModelIndex& srcParent) const override
+ {
+ return sourceModel()->index(srcRow, 0, srcParent).data().toInt() % 2 == 0;
+ }
+ };
+
+ FilterEvenRowsProxyModel proxy;
+ proxy.sort(0);
+ proxy.setSourceModel(&model);
+ QCOMPARE(rowTexts(&proxy), QStringLiteral("2468"));
+
+ QSignalSpy modelDataChangedSpy(&model, &QAbstractItemModel::dataChanged);
+ QSignalSpy proxyLayoutChangedSpy(&proxy, &QAbstractItemModel::layoutChanged);
+
+ if (changedRole == Qt::DecorationRole)
+ model.emitDecorationChangedSignal();
+ else if (changedRow == -1)
+ model.replaceData(newData.split(QLatin1Char(',')));
+ else
+ model.setData(model.index(changedRow, 0), newData, changedRole);
+
+ QCOMPARE(rowTexts(&model), expectedSourceRowTexts);
+ QCOMPARE(rowTexts(&proxy), expectedProxyRowTexts);
+ QCOMPARE(modelDataChangedSpy.size(), 1);
+ QCOMPARE(proxyLayoutChangedSpy.size(), expectedLayoutChanged);
+}
+
+void tst_QSortFilterProxyModel::dynamicFilterWithoutSort()
+{
+ QStringListModel model;
+ const QStringList initial = QString("bravo charlie delta echo").split(QLatin1Char(' '));
+ model.setStringList(initial);
+ QSortFilterProxyModel proxy;
+ proxy.setDynamicSortFilter(true);
+ proxy.setSourceModel(&model);
+
+ QSignalSpy layoutChangeSpy(&proxy, &QAbstractItemModel::layoutChanged);
+ QSignalSpy resetSpy(&proxy, &QAbstractItemModel::modelReset);
+
+ QVERIFY(layoutChangeSpy.isValid());
+ QVERIFY(resetSpy.isValid());
+
+ model.setStringList(QStringList() << "Monday" << "Tuesday" << "Wednesday" << "Thursday" << "Friday");
+
+ QVERIFY(layoutChangeSpy.isEmpty());
+
+ QCOMPARE(model.stringList(), QStringList() << "Monday" << "Tuesday" << "Wednesday" << "Thursday" << "Friday");
+
+ QCOMPARE(resetSpy.count(), 1);
+}
+
QTEST_MAIN(tst_QSortFilterProxyModel)
#include "tst_qsortfilterproxymodel.moc"
diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/.gitignore b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/.gitignore
new file mode 100644
index 0000000000..2007aaabbd
--- /dev/null
+++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/.gitignore
@@ -0,0 +1 @@
+tst_qsortfilterproxymodel_recursive
diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro
new file mode 100644
index 0000000000..a8b793dbc6
--- /dev/null
+++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro
@@ -0,0 +1,8 @@
+CONFIG += testcase
+CONFIG += parallel_test
+TARGET = tst_qsortfilterproxymodel_recursive
+
+QT += testlib
+
+SOURCES += tst_qsortfilterproxymodel_recursive.cpp
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp
new file mode 100644
index 0000000000..852d9adb46
--- /dev/null
+++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp
@@ -0,0 +1,723 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, authors Filipe Azevedo <filipe.azevedo@kdab.com> and David Faure <david.faure@kdab.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QTest>
+#include <QSignalSpy>
+
+#include <QtCore/QSortFilterProxyModel>
+#include <QtGui/QStandardItem>
+
+Q_DECLARE_METATYPE(QModelIndex)
+
+class ModelSignalSpy : public QObject {
+ Q_OBJECT
+public:
+ explicit ModelSignalSpy(QAbstractItemModel &model) {
+ connect(&model, &QAbstractItemModel::rowsInserted, this, &ModelSignalSpy::onRowsInserted);
+ connect(&model, &QAbstractItemModel::rowsRemoved, this, &ModelSignalSpy::onRowsRemoved);
+ connect(&model, &QAbstractItemModel::rowsAboutToBeInserted, this, &ModelSignalSpy::onRowsAboutToBeInserted);
+ connect(&model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelSignalSpy::onRowsAboutToBeRemoved);
+ connect(&model, &QAbstractItemModel::rowsMoved, this, &ModelSignalSpy::onRowsMoved);
+ connect(&model, &QAbstractItemModel::dataChanged, this, &ModelSignalSpy::onDataChanged);
+ connect(&model, &QAbstractItemModel::layoutChanged, this, &ModelSignalSpy::onLayoutChanged);
+ connect(&model, &QAbstractItemModel::modelReset, this, &ModelSignalSpy::onModelReset);
+ }
+
+ QStringList mSignals;
+
+private Q_SLOTS:
+ void onRowsInserted(QModelIndex p, int start, int end) {
+ mSignals << QLatin1String("rowsInserted(") + textForRowSpy(p, start, end) + ')';
+ }
+ void onRowsRemoved(QModelIndex p, int start, int end) {
+ mSignals << QLatin1String("rowsRemoved(") + textForRowSpy(p, start, end) + ')';
+ }
+ void onRowsAboutToBeInserted(QModelIndex p, int start, int end) {
+ mSignals << QLatin1String("rowsAboutToBeInserted(") + textForRowSpy(p, start, end) + ')';
+ }
+ void onRowsAboutToBeRemoved(QModelIndex p, int start, int end) {
+ mSignals << QLatin1String("rowsAboutToBeRemoved(") + textForRowSpy(p, start, end) + ')';
+ }
+ void onRowsMoved(QModelIndex,int,int,QModelIndex,int) {
+ mSignals << QStringLiteral("rowsMoved");
+ }
+ void onDataChanged(const QModelIndex &from, const QModelIndex& ) {
+ mSignals << QStringLiteral("dataChanged(%1)").arg(from.data().toString());
+ }
+ void onLayoutChanged() {
+ mSignals << QStringLiteral("layoutChanged");
+ }
+ void onModelReset() {
+ mSignals << QStringLiteral("modelReset");
+ }
+private:
+ QString textForRowSpy(const QModelIndex &parent, int start, int end)
+ {
+ QString txt = parent.data().toString();
+ if (!txt.isEmpty())
+ txt += QLatin1Char('.');
+ txt += QString::number(start+1);
+ if (start != end)
+ txt += QLatin1Char('-') + QString::number(end+1);
+ return txt;
+ }
+};
+
+class TestModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+ TestModel(QAbstractItemModel *sourceModel)
+ : QSortFilterProxyModel()
+ {
+ setRecursiveFilteringEnabled(true);
+ setSourceModel(sourceModel);
+ }
+
+ virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
+ {
+ return sourceModel()->index(sourceRow, 0, sourceParent).data(Qt::UserRole +1).toBool()
+ && QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
+ }
+};
+
+// Represents this tree
+// - A
+// - - B
+// - - - C
+// - - - D
+// - - E
+// as a single string, englobing children in brackets, like this:
+// [A[B[C D] E]]
+// In addition, items that match the filtering (data(UserRole+1) == true) have a * after their value.
+static QString treeAsString(const QAbstractItemModel &model, const QModelIndex &parent = QModelIndex())
+{
+ QString ret;
+ const int rowCount = model.rowCount(parent);
+ if (rowCount > 0) {
+ ret += QLatin1Char('[');
+ for (int row = 0 ; row < rowCount; ++row) {
+ if (row > 0) {
+ ret += ' ';
+ }
+ const QModelIndex child = model.index(row, 0, parent);
+ ret += child.data().toString();
+ if (child.data(Qt::UserRole+1).toBool())
+ ret += QLatin1Char('*');
+ ret += treeAsString(model, child);
+ }
+ ret += QLatin1Char(']');
+ }
+ return ret;
+}
+
+// Fill a tree model based on a string representation (see treeAsString)
+static void fillModel(QStandardItemModel &model, const QString &str)
+{
+ QCOMPARE(str.count('['), str.count(']'));
+ QStandardItem *item = 0;
+ QString data;
+ for ( int i = 0 ; i < str.length() ; ++i ) {
+ const QChar ch = str.at(i);
+ if ((ch == '[' || ch == ']' || ch == ' ') && !data.isEmpty()) {
+ if (data.endsWith('*')) {
+ item->setData(true, Qt::UserRole + 1);
+ data.chop(1);
+ }
+ item->setText(data);
+ data.clear();
+ }
+ if (ch == '[') {
+ // Create new child
+ QStandardItem *child = new QStandardItem;
+ if (item)
+ item->appendRow(child);
+ else
+ model.appendRow(child);
+ item = child;
+ } else if (ch == ']') {
+ // Go up to parent
+ item = item->parent();
+ } else if (ch == ' ') {
+ // Create new sibling
+ QStandardItem *child = new QStandardItem;
+ QStandardItem *parent = item->parent();
+ if (parent)
+ parent->appendRow(child);
+ else
+ model.appendRow(child);
+ item = child;
+ } else {
+ data += ch;
+ }
+ }
+}
+
+class tst_QSortFilterProxyModel_Recursive : public QObject
+{
+ Q_OBJECT
+private:
+private Q_SLOTS:
+ void testInitialFiltering_data()
+ {
+ QTest::addColumn<QString>("sourceStr");
+ QTest::addColumn<QString>("proxyStr");
+
+ QTest::newRow("empty") << "[]" << "";
+ QTest::newRow("no") << "[1]" << "";
+ QTest::newRow("yes") << "[1*]" << "[1*]";
+ QTest::newRow("second") << "[1 2*]" << "[2*]";
+ QTest::newRow("child_yes") << "[1 2[2.1*]]" << "[2[2.1*]]";
+ QTest::newRow("grandchild_yes") << "[1 2[2.1[2.1.1*]]]" << "[2[2.1[2.1.1*]]]";
+ // 1, 3.1 and 4.2.1 match, so their parents are in the model
+ QTest::newRow("more") << "[1* 2[2.1] 3[3.1*] 4[4.1 4.2[4.2.1*]]]" << "[1* 3[3.1*] 4[4.2[4.2.1*]]]";
+ }
+
+ void testInitialFiltering()
+ {
+ QFETCH(QString, sourceStr);
+ QFETCH(QString, proxyStr);
+
+ QStandardItemModel model;
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QVERIFY(proxy.isRecursiveFilteringEnabled());
+ QCOMPARE(treeAsString(proxy), proxyStr);
+ }
+
+ // Test changing a role that is unrelated to the filtering.
+ void testUnrelatedDataChange()
+ {
+ QStandardItemModel model;
+ const QString sourceStr = QStringLiteral("[1[1.1[1.1.1*]]]");
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), sourceStr);
+
+ ModelSignalSpy spy(proxy);
+ QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0);
+
+ // When changing the text on the item
+ item_1_1_1->setText(QStringLiteral("ME"));
+
+ QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[ME*]]]"));
+
+ QCOMPARE(spy.mSignals, QStringList()
+ << QStringLiteral("dataChanged(ME)")
+ << QStringLiteral("dataChanged(1.1)")
+ << QStringLiteral("dataChanged(1)"));
+ }
+
+ // Test changing a role that is unrelated to the filtering, in a hidden item.
+ void testHiddenDataChange()
+ {
+ QStandardItemModel model;
+ const QString sourceStr = QStringLiteral("[1[1.1[1.1.1]]]");
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), QString());
+
+ ModelSignalSpy spy(proxy);
+ QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0);
+
+ // When changing the text on a hidden item
+ item_1_1_1->setText(QStringLiteral("ME"));
+
+ QCOMPARE(treeAsString(proxy), QString());
+ QCOMPARE(spy.mSignals, QStringList());
+ }
+
+ // Test that we properly react to a data-changed signal in a descendant and include all required rows
+ void testDataChangeIn_data()
+ {
+ QTest::addColumn<QString>("sourceStr");
+ QTest::addColumn<QString>("initialProxyStr");
+ QTest::addColumn<QString>("add"); // set the flag on this item
+ QTest::addColumn<QString>("expectedProxyStr");
+ QTest::addColumn<QStringList>("expectedSignals");
+
+ QTest::newRow("toplevel") << "[1]" << "" << "1" << "[1*]"
+ << (QStringList() << QStringLiteral("rowsAboutToBeInserted(1)") << QStringLiteral("rowsInserted(1)"));
+ QTest::newRow("show_parents") << "[1[1.1[1.1.1]]]" << "" << "1.1.1" << "[1[1.1[1.1.1*]]]"
+ << (QStringList() << QStringLiteral("rowsAboutToBeInserted(1)") << QStringLiteral("rowsInserted(1)"));
+
+ const QStringList insert_1_1_1 = QStringList()
+ << QStringLiteral("rowsAboutToBeInserted(1.1.1)")
+ << QStringLiteral("rowsInserted(1.1.1)")
+ << QStringLiteral("dataChanged(1.1)")
+ << QStringLiteral("dataChanged(1)")
+ ;
+ QTest::newRow("parent_visible") << "[1[1.1*[1.1.1]]]" << "[1[1.1*]]" << "1.1.1" << "[1[1.1*[1.1.1*]]]"
+ << insert_1_1_1;
+
+ QTest::newRow("sibling_visible") << "[1[1.1[1.1.1 1.1.2*]]]" << "[1[1.1[1.1.2*]]]" << "1.1.1" << "[1[1.1[1.1.1* 1.1.2*]]]"
+ << insert_1_1_1;
+
+ QTest::newRow("visible_cousin") << "[1[1.1[1.1.1 1.1.2[1.1.2.1*]]]]" << "[1[1.1[1.1.2[1.1.2.1*]]]]" << "1.1.1" << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]"
+ << insert_1_1_1;
+
+ QTest::newRow("show_parent") << "[1[1.1[1.1.1 1.1.2] 1.2*]]" << "[1[1.2*]]" << "1.1.1" << "[1[1.1[1.1.1*] 1.2*]]"
+ << (QStringList()
+ << QStringLiteral("rowsAboutToBeInserted(1.1)")
+ << QStringLiteral("rowsInserted(1.1)")
+ << QStringLiteral("dataChanged(1)"));
+
+ QTest::newRow("with_children") << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "1.1.1" << "[1[1.1[1.1.1*[1.1.1.1*]]] 2*]"
+ << (QStringList()
+ << QStringLiteral("dataChanged(1.1.1)")
+ << QStringLiteral("dataChanged(1.1)")
+ << QStringLiteral("dataChanged(1)"));
+
+ }
+
+ void testDataChangeIn()
+ {
+ QFETCH(QString, sourceStr);
+ QFETCH(QString, initialProxyStr);
+ QFETCH(QString, add);
+ QFETCH(QString, expectedProxyStr);
+ QFETCH(QStringList, expectedSignals);
+
+ QStandardItemModel model;
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), initialProxyStr);
+
+ ModelSignalSpy spy(proxy);
+ // When changing the data on the designated item to show this row
+ QStandardItem *itemToChange = itemByText(model, add);
+ QVERIFY(!itemToChange->data().toBool());
+ itemToChange->setData(true);
+
+ // The proxy should update as expected
+ QCOMPARE(treeAsString(proxy), expectedProxyStr);
+
+ //qDebug() << spy.mSignals;
+ QCOMPARE(spy.mSignals, expectedSignals);
+ }
+
+ void testDataChangeOut_data()
+ {
+ QTest::addColumn<QString>("sourceStr");
+ QTest::addColumn<QString>("initialProxyStr");
+ QTest::addColumn<QString>("remove"); // unset the flag on this item
+ QTest::addColumn<QString>("expectedProxyStr");
+ QTest::addColumn<QStringList>("expectedSignals");
+
+ const QStringList remove1_1_1 = (QStringList()
+ << QStringLiteral("rowsAboutToBeRemoved(1.1.1)")
+ << QStringLiteral("rowsRemoved(1.1.1)")
+ << QStringLiteral("dataChanged(1.1)")
+ << QStringLiteral("dataChanged(1)"));
+
+ QTest::newRow("toplevel") << "[1*]" << "[1*]" << "1" << ""
+ << (QStringList() << QStringLiteral("rowsAboutToBeRemoved(1)") << QStringLiteral("rowsRemoved(1)"));
+
+ QTest::newRow("hide_parent") << "[1[1.1[1.1.1*]]]" << "[1[1.1[1.1.1*]]]" << "1.1.1" << "" <<
+ (QStringList()
+ << QStringLiteral("rowsAboutToBeRemoved(1.1.1)")
+ << QStringLiteral("rowsRemoved(1.1.1)")
+ << QStringLiteral("rowsAboutToBeRemoved(1.1)")
+ << QStringLiteral("rowsRemoved(1.1)")
+ << QStringLiteral("rowsAboutToBeRemoved(1)")
+ << QStringLiteral("rowsRemoved(1)"));
+
+ QTest::newRow("parent_visible") << "[1[1.1*[1.1.1*]]]" << "[1[1.1*[1.1.1*]]]" << "1.1.1" << "[1[1.1*]]"
+ << remove1_1_1;
+
+ QTest::newRow("visible") << "[1[1.1[1.1.1* 1.1.2*]]]" << "[1[1.1[1.1.1* 1.1.2*]]]" << "1.1.1" << "[1[1.1[1.1.2*]]]"
+ << remove1_1_1;
+ QTest::newRow("visible_cousin") << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "1.1.1" << "[1[1.1[1.1.2[1.1.2.1*]]]]"
+ << remove1_1_1;
+
+ // The following tests trigger the removal of an ascendant.
+ QTest::newRow("remove_parent") << "[1[1.1[1.1.1* 1.1.2] 1.2*]]" << "[1[1.1[1.1.1*] 1.2*]]" << "1.1.1" << "[1[1.2*]]"
+ << (QStringList()
+ << QStringLiteral("rowsAboutToBeRemoved(1.1.1)")
+ << QStringLiteral("rowsRemoved(1.1.1)")
+ << QStringLiteral("rowsAboutToBeRemoved(1.1)")
+ << QStringLiteral("rowsRemoved(1.1)")
+ << QStringLiteral("dataChanged(1)"));
+
+ QTest::newRow("with_children") << "[1[1.1[1.1.1*[1.1.1.1*]]] 2*]" << "[1[1.1[1.1.1*[1.1.1.1*]]] 2*]" << "1.1.1" << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]"
+ << (QStringList()
+ << QStringLiteral("dataChanged(1.1.1)")
+ << QStringLiteral("dataChanged(1.1)")
+ << QStringLiteral("dataChanged(1)"));
+
+ QTest::newRow("last_visible") << "[1[1.1[1.1.1* 1.1.2]]]" << "[1[1.1[1.1.1*]]]" << "1.1.1" << ""
+ << (QStringList()
+ << QStringLiteral("rowsAboutToBeRemoved(1.1.1)")
+ << QStringLiteral("rowsRemoved(1.1.1)")
+ << QStringLiteral("rowsAboutToBeRemoved(1.1)")
+ << QStringLiteral("rowsRemoved(1.1)")
+ << QStringLiteral("rowsAboutToBeRemoved(1)")
+ << QStringLiteral("rowsRemoved(1)"));
+
+ }
+
+ void testDataChangeOut()
+ {
+ QFETCH(QString, sourceStr);
+ QFETCH(QString, initialProxyStr);
+ QFETCH(QString, remove);
+ QFETCH(QString, expectedProxyStr);
+ QFETCH(QStringList, expectedSignals);
+
+ QStandardItemModel model;
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), initialProxyStr);
+
+ ModelSignalSpy spy(proxy);
+
+ // When changing the data on the designated item to exclude this row again
+ QStandardItem *itemToChange = itemByText(model, remove);
+ QVERIFY(itemToChange->data().toBool());
+ itemToChange->setData(false);
+
+ // The proxy should update as expected
+ QCOMPARE(treeAsString(proxy), expectedProxyStr);
+
+ //qDebug() << spy.mSignals;
+ QCOMPARE(spy.mSignals, expectedSignals);
+ }
+
+ void testInsert()
+ {
+ QStandardItemModel model;
+ const QString sourceStr = QStringLiteral("[1[1.1[1.1.1]]]");
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), QString());
+
+ ModelSignalSpy spy(proxy);
+ QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0);
+ QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1"));
+ item_1_1_1_1->setData(true);
+ item_1_1_1->appendRow(item_1_1_1_1);
+ QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1[1.1.1.1*]]]]"));
+
+ QCOMPARE(spy.mSignals, QStringList() << QStringLiteral("rowsAboutToBeInserted(1)")
+ << QStringLiteral("rowsInserted(1)"));
+ }
+
+ // Start from [1[1.1[1.1.1 1.1.2[1.1.2.1*]]]]
+ // where 1.1.1 is hidden but 1.1 is shown, we want to insert a shown child in 1.1.1.
+ // The proxy ensures dataChanged is called on 1.1,
+ // so that 1.1.1 and 1.1.1.1 are included in the model.
+ void testInsertCousin()
+ {
+ QStandardItemModel model;
+ const QString sourceStr = QStringLiteral("[1[1.1[1.1.1 1.1.2[1.1.2.1*]]]]");
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.2[1.1.2.1*]]]]"));
+
+ ModelSignalSpy spy(proxy);
+ {
+ QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1"));
+ item_1_1_1_1->setData(true);
+ QStandardItem *item_1_1_1 = model.item(0)->child(0)->child(0);
+ item_1_1_1->appendRow(item_1_1_1_1);
+ }
+
+ QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1[1.1.1.1*] 1.1.2[1.1.2.1*]]]]"));
+ //qDebug() << spy.mSignals;
+ QCOMPARE(spy.mSignals, QStringList()
+ << QStringLiteral("rowsAboutToBeInserted(1.1.1)")
+ << QStringLiteral("rowsInserted(1.1.1)")
+ << QStringLiteral("dataChanged(1.1)")
+ << QStringLiteral("dataChanged(1)"));
+ }
+
+ void testInsertWithChildren()
+ {
+ QStandardItemModel model;
+ const QString sourceStr = QStringLiteral("[1[1.1]]");
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), QString());
+
+ ModelSignalSpy spy(proxy);
+ {
+ QStandardItem *item_1_1_1 = new QStandardItem(QStringLiteral("1.1.1"));
+ QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1"));
+ item_1_1_1_1->setData(true);
+ item_1_1_1->appendRow(item_1_1_1_1);
+
+ QStandardItem *item_1_1 = model.item(0)->child(0);
+ item_1_1->appendRow(item_1_1_1);
+ }
+
+ QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1[1.1.1.1*]]]]"));
+ QCOMPARE(spy.mSignals, QStringList()
+ << QStringLiteral("rowsAboutToBeInserted(1)")
+ << QStringLiteral("rowsInserted(1)"));
+ }
+
+ void testInsertIntoVisibleWithChildren()
+ {
+ QStandardItemModel model;
+ const QString sourceStr = QStringLiteral("[1[1.1[1.1.1*]]]");
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), sourceStr);
+
+ ModelSignalSpy spy(proxy);
+ {
+ QStandardItem *item_1_1_2 = new QStandardItem(QStringLiteral("1.1.2"));
+ QStandardItem *item_1_1_2_1 = new QStandardItem(QStringLiteral("1.1.2.1"));
+ item_1_1_2_1->setData(true);
+ item_1_1_2->appendRow(item_1_1_2_1);
+
+ QStandardItem *item_1_1 = model.item(0)->child(0);
+ item_1_1->appendRow(item_1_1_2);
+ }
+
+ QCOMPARE(treeAsString(proxy), QStringLiteral("[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]"));
+ QCOMPARE(spy.mSignals, QStringList()
+ << QStringLiteral("rowsAboutToBeInserted(1.1.2)")
+ << QStringLiteral("rowsInserted(1.1.2)"));
+ }
+
+ void testInsertBefore()
+ {
+ QStandardItemModel model;
+ const QString sourceStr = "[1[1.1[1.1.2*]]]";
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), sourceStr);
+
+ ModelSignalSpy spy(proxy);
+ {
+ QStandardItem *item_1_1_1 = new QStandardItem("1.1.1");
+
+ QStandardItem *item_1_1 = model.item(0)->child(0);
+ item_1_1->insertRow(0, item_1_1_1);
+ }
+
+ QCOMPARE(treeAsString(proxy), QString("[1[1.1[1.1.2*]]]"));
+ QCOMPARE(spy.mSignals, QStringList());
+ }
+
+ void testInsertHidden() // inserting filtered-out rows shouldn't emit anything
+ {
+ QStandardItemModel model;
+ const QString sourceStr = QStringLiteral("[1[1.1]]");
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), QString());
+
+ ModelSignalSpy spy(proxy);
+ {
+ QStandardItem *item_1_1_1 = new QStandardItem(QStringLiteral("1.1.1"));
+ QStandardItem *item_1_1_1_1 = new QStandardItem(QStringLiteral("1.1.1.1"));
+ item_1_1_1->appendRow(item_1_1_1_1);
+
+ QStandardItem *item_1_1 = model.item(0)->child(0);
+ item_1_1->appendRow(item_1_1_1);
+ }
+
+ QCOMPARE(treeAsString(proxy), QString());
+ QCOMPARE(spy.mSignals, QStringList());
+ }
+
+ void testConsecutiveInserts_data()
+ {
+ testInitialFiltering_data();
+ }
+
+ void testConsecutiveInserts()
+ {
+ QFETCH(QString, sourceStr);
+ QFETCH(QString, proxyStr);
+
+ QStandardItemModel model;
+ TestModel proxy(&model); // this time the proxy listens to the model while we fill it
+
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+ QCOMPARE(treeAsString(proxy), proxyStr);
+ }
+
+ void testRemove_data()
+ {
+ QTest::addColumn<QString>("sourceStr");
+ QTest::addColumn<QString>("initialProxyStr");
+ QTest::addColumn<QString>("remove"); // remove this item
+ QTest::addColumn<QString>("expectedProxyStr");
+ QTest::addColumn<QStringList>("expectedSignals");
+
+ const QStringList remove1_1_1 = (QStringList() << QStringLiteral("rowsAboutToBeRemoved(1.1.1)") << QStringLiteral("rowsRemoved(1.1.1)"));
+
+ QTest::newRow("toplevel") << "[1* 2* 3*]" << "[1* 2* 3*]" << "1" << "[2* 3*]"
+ << (QStringList() << QStringLiteral("rowsAboutToBeRemoved(1)") << QStringLiteral("rowsRemoved(1)"));
+
+ QTest::newRow("remove_hidden") << "[1 2* 3*]" << "[2* 3*]" << "1" << "[2* 3*]" << QStringList();
+
+ QTest::newRow("parent_hidden") << "[1[1.1[1.1.1]]]" << "" << "1.1.1" << "" << QStringList();
+
+ QTest::newRow("child_hidden") << "[1[1.1*[1.1.1]]]" << "[1[1.1*]]" << "1.1.1" << "[1[1.1*]]" << QStringList();
+
+ QTest::newRow("parent_visible") << "[1[1.1*[1.1.1*]]]" << "[1[1.1*[1.1.1*]]]" << "1.1.1" << "[1[1.1*]]"
+ << remove1_1_1;
+
+ QTest::newRow("visible") << "[1[1.1[1.1.1* 1.1.2*]]]" << "[1[1.1[1.1.1* 1.1.2*]]]" << "1.1.1" << "[1[1.1[1.1.2*]]]"
+ << remove1_1_1;
+ QTest::newRow("visible_cousin") << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "[1[1.1[1.1.1* 1.1.2[1.1.2.1*]]]]" << "1.1.1" << "[1[1.1[1.1.2[1.1.2.1*]]]]"
+ << remove1_1_1;
+
+ // The following tests trigger the removal of an ascendant.
+ // We could optimize the rows{AboutToBe,}Removed(1.1.1) away...
+
+ QTest::newRow("remove_parent") << "[1[1.1[1.1.1* 1.1.2] 1.2*]]" << "[1[1.1[1.1.1*] 1.2*]]" << "1.1.1" << "[1[1.2*]]"
+ << (QStringList()
+ << QStringLiteral("rowsAboutToBeRemoved(1.1.1)")
+ << QStringLiteral("rowsRemoved(1.1.1)")
+ << QStringLiteral("rowsAboutToBeRemoved(1.1)")
+ << QStringLiteral("rowsRemoved(1.1)")
+ << QStringLiteral("dataChanged(1)"));
+
+ QTest::newRow("with_children") << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "[1[1.1[1.1.1[1.1.1.1*]]] 2*]" << "1.1.1" << "[2*]"
+ << (QStringList()
+ << QStringLiteral("rowsAboutToBeRemoved(1.1.1)")
+ << QStringLiteral("rowsRemoved(1.1.1)")
+ << QStringLiteral("rowsAboutToBeRemoved(1)")
+ << QStringLiteral("rowsRemoved(1)"));
+
+ QTest::newRow("last_visible") << "[1[1.1[1.1.1* 1.1.2]]]" << "[1[1.1[1.1.1*]]]" << "1.1.1" << ""
+ << (QStringList()
+ << QStringLiteral("rowsAboutToBeRemoved(1.1.1)")
+ << QStringLiteral("rowsRemoved(1.1.1)")
+ << QStringLiteral("rowsAboutToBeRemoved(1)")
+ << QStringLiteral("rowsRemoved(1)"));
+
+
+ }
+
+ void testRemove()
+ {
+ QFETCH(QString, sourceStr);
+ QFETCH(QString, initialProxyStr);
+ QFETCH(QString, remove);
+ QFETCH(QString, expectedProxyStr);
+ QFETCH(QStringList, expectedSignals);
+
+ QStandardItemModel model;
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), initialProxyStr);
+
+ ModelSignalSpy spy(proxy);
+ QStandardItem *itemToRemove = itemByText(model, remove);
+ QVERIFY(itemToRemove);
+ if (itemToRemove->parent())
+ itemToRemove->parent()->removeRow(itemToRemove->row());
+ else
+ model.removeRow(itemToRemove->row());
+ QCOMPARE(treeAsString(proxy), expectedProxyStr);
+
+ //qDebug() << spy.mSignals;
+ QCOMPARE(spy.mSignals, expectedSignals);
+ }
+
+ void testStandardFiltering_data()
+ {
+ QTest::addColumn<QString>("sourceStr");
+ QTest::addColumn<QString>("initialProxyStr");
+ QTest::addColumn<QString>("filter");
+ QTest::addColumn<QString>("expectedProxyStr");
+
+ QTest::newRow("select_child") << "[1[1.1[1.1.1* 1.1.2*]]]" << "[1[1.1[1.1.1* 1.1.2*]]]"
+ << "1.1.2" << "[1[1.1[1.1.2*]]]";
+
+ QTest::newRow("filter_all_out") << "[1[1.1[1.1.1*]]]" << "[1[1.1[1.1.1*]]]"
+ << "test" << "";
+
+ QTest::newRow("select_parent") << "[1[1.1[1.1.1*[child*] 1.1.2*]]]" << "[1[1.1[1.1.1*[child*] 1.1.2*]]]"
+ << "1.1.1" << "[1[1.1[1.1.1*]]]";
+
+ }
+
+ void testStandardFiltering()
+ {
+ QFETCH(QString, sourceStr);
+ QFETCH(QString, initialProxyStr);
+ QFETCH(QString, filter);
+ QFETCH(QString, expectedProxyStr);
+
+ QStandardItemModel model;
+ fillModel(model, sourceStr);
+ QCOMPARE(treeAsString(model), sourceStr);
+
+ TestModel proxy(&model);
+ QCOMPARE(treeAsString(proxy), initialProxyStr);
+
+ ModelSignalSpy spy(proxy);
+
+ //qDebug() << "setFilterFixedString";
+ proxy.setFilterFixedString(filter);
+
+ QCOMPARE(treeAsString(proxy), expectedProxyStr);
+
+ }
+
+private:
+ QStandardItem *itemByText(const QStandardItemModel& model, const QString &text) const {
+ QModelIndexList list = model.match(model.index(0, 0), Qt::DisplayRole, text, 1, Qt::MatchRecursive);
+ return list.isEmpty() ? 0 : model.itemFromIndex(list.first());
+ }
+};
+
+QTEST_GUILESS_MAIN(tst_QSortFilterProxyModel_Recursive)
+#include "tst_qsortfilterproxymodel_recursive.moc"
diff --git a/tests/auto/corelib/json/tst_qtjson.cpp b/tests/auto/corelib/json/tst_qtjson.cpp
index b215364f0e..1e3604ac9e 100644
--- a/tests/auto/corelib/json/tst_qtjson.cpp
+++ b/tests/auto/corelib/json/tst_qtjson.cpp
@@ -80,7 +80,10 @@ private Q_SLOTS:
void undefinedValues();
+ void fromVariant_data();
void fromVariant();
+ void toVariant_data();
+ void toVariant();
void fromVariantMap();
void fromVariantHash();
void toVariantMap();
@@ -145,6 +148,9 @@ private Q_SLOTS:
void parseErrorOffset_data();
void parseErrorOffset();
+ void implicitValueType();
+ void implicitDocumentType();
+
private:
QString testDataDir;
};
@@ -1092,8 +1098,11 @@ void tst_QtJson::undefinedValues()
QCOMPARE(array.at(-1).type(), QJsonValue::Undefined);
}
-void tst_QtJson::fromVariant()
+void tst_QtJson::fromVariant_data()
{
+ QTest::addColumn<QVariant>("variant");
+ QTest::addColumn<QJsonValue>("jsonvalue");
+
bool boolValue = true;
int intValue = -1;
uint uintValue = 1;
@@ -1116,44 +1125,66 @@ void tst_QtJson::fromVariant()
variantList.append(doubleValue);
variantList.append(stringValue);
variantList.append(stringList);
- variantList.append(QVariant());
+ variantList.append(QVariant::fromValue(nullptr));
QJsonArray jsonArray_variant;
jsonArray_variant.append(boolValue);
jsonArray_variant.append(floatValue);
jsonArray_variant.append(doubleValue);
jsonArray_variant.append(stringValue);
jsonArray_variant.append(jsonArray_string);
- jsonArray_variant.append(QJsonValue());
+ jsonArray_variant.append(QJsonValue(QJsonValue::Null));
QVariantMap variantMap;
variantMap["bool"] = boolValue;
variantMap["float"] = floatValue;
variantMap["string"] = stringValue;
variantMap["array"] = variantList;
+ QVariantHash variantHash;
+ variantHash["bool"] = boolValue;
+ variantHash["float"] = floatValue;
+ variantHash["string"] = stringValue;
+ variantHash["array"] = variantList;
QJsonObject jsonObject;
jsonObject["bool"] = boolValue;
jsonObject["float"] = floatValue;
jsonObject["string"] = stringValue;
jsonObject["array"] = jsonArray_variant;
- QCOMPARE(QJsonValue::fromVariant(QVariant::fromValue(nullptr)), QJsonValue(QJsonValue::Null));
- QCOMPARE(QJsonValue::fromVariant(QVariant(boolValue)), QJsonValue(boolValue));
- QCOMPARE(QJsonValue::fromVariant(QVariant(intValue)), QJsonValue(intValue));
- QCOMPARE(QJsonValue::fromVariant(QVariant(uintValue)), QJsonValue(static_cast<double>(uintValue)));
- QCOMPARE(QJsonValue::fromVariant(QVariant(longlongValue)), QJsonValue(longlongValue));
- QCOMPARE(QJsonValue::fromVariant(QVariant(ulonglongValue)), QJsonValue(static_cast<double>(ulonglongValue)));
- QCOMPARE(QJsonValue::fromVariant(QVariant(floatValue)), QJsonValue(static_cast<double>(floatValue)));
- QCOMPARE(QJsonValue::fromVariant(QVariant(doubleValue)), QJsonValue(doubleValue));
- QCOMPARE(QJsonValue::fromVariant(QVariant(stringValue)), QJsonValue(stringValue));
- QCOMPARE(QJsonValue::fromVariant(QVariant(stringList)), QJsonValue(jsonArray_string));
- QCOMPARE(QJsonValue::fromVariant(QVariant(variantList)), QJsonValue(jsonArray_variant));
- QCOMPARE(QJsonValue::fromVariant(QVariant(variantMap)), QJsonValue(jsonObject));
-
- QVERIFY(QJsonValue::fromVariant(QVariant(QJsonValue(true))).isBool());
- QVERIFY(QJsonValue::fromVariant(QVariant(jsonArray_string)).isArray());
- QVERIFY(QJsonValue::fromVariant(QVariant(QJsonDocument(jsonArray_string))).isArray());
- QVERIFY(QJsonValue::fromVariant(QVariant(jsonObject)).isObject());
- QVERIFY(QJsonValue::fromVariant(QVariant(QJsonDocument(jsonObject))).isObject());
+ QTest::newRow("nullptr") << QVariant::fromValue(nullptr) << QJsonValue(QJsonValue::Null);
+ QTest::newRow("bool") << QVariant(boolValue) << QJsonValue(boolValue);
+ QTest::newRow("int") << QVariant(intValue) << QJsonValue(intValue);
+ QTest::newRow("uint") << QVariant(uintValue) << QJsonValue(static_cast<double>(uintValue));
+ QTest::newRow("longlong") << QVariant(longlongValue) << QJsonValue(longlongValue);
+ QTest::newRow("ulonglong") << QVariant(ulonglongValue) << QJsonValue(static_cast<double>(ulonglongValue));
+ QTest::newRow("float") << QVariant(floatValue) << QJsonValue(floatValue);
+ QTest::newRow("double") << QVariant(doubleValue) << QJsonValue(doubleValue);
+ QTest::newRow("string") << QVariant(stringValue) << QJsonValue(stringValue);
+ QTest::newRow("stringList") << QVariant(stringList) << QJsonValue(jsonArray_string);
+ QTest::newRow("variantList") << QVariant(variantList) << QJsonValue(jsonArray_variant);
+ QTest::newRow("variantMap") << QVariant(variantMap) << QJsonValue(jsonObject);
+ QTest::newRow("variantHash") << QVariant(variantHash) << QJsonValue(jsonObject);
+}
+
+void tst_QtJson::fromVariant()
+{
+ QFETCH( QVariant, variant );
+ QFETCH( QJsonValue, jsonvalue );
+
+ QCOMPARE(QJsonValue::fromVariant(variant), jsonvalue);
+ QCOMPARE(variant.toJsonValue(), jsonvalue);
+}
+
+void tst_QtJson::toVariant_data()
+{
+ fromVariant_data();
+}
+
+void tst_QtJson::toVariant()
+{
+ QFETCH( QVariant, variant );
+ QFETCH( QJsonValue, jsonvalue );
+
+ QCOMPARE(jsonvalue.toVariant(), variant);
}
void tst_QtJson::fromVariantMap()
@@ -2908,5 +2939,52 @@ void tst_QtJson::parseErrorOffset()
QCOMPARE(error.offset, errorOffset);
}
+void tst_QtJson::implicitValueType()
+{
+ QJsonObject rootObject{
+ {"object", QJsonObject{{"value", 42}}},
+ {"array", QJsonArray{665, 666, 667}}
+ };
+
+ QJsonValue objectValue = rootObject["object"];
+ QCOMPARE(objectValue["value"].toInt(), 42);
+ QCOMPARE(objectValue["missingValue"], QJsonValue(QJsonValue::Undefined));
+ QCOMPARE(objectValue[123], QJsonValue(QJsonValue::Undefined));
+ QCOMPARE(objectValue["missingValue"].toInt(123), 123);
+
+ QJsonValue arrayValue = rootObject["array"];
+ QCOMPARE(arrayValue[1].toInt(), 666);
+ QCOMPARE(arrayValue[-1], QJsonValue(QJsonValue::Undefined));
+ QCOMPARE(arrayValue["asObject"], QJsonValue(QJsonValue::Undefined));
+ QCOMPARE(arrayValue[-1].toInt(123), 123);
+
+ const QJsonObject constObject = rootObject;
+ QCOMPARE(constObject["object"]["value"].toInt(), 42);
+ QCOMPARE(constObject["array"][1].toInt(), 666);
+
+ QJsonValue objectAsValue(rootObject);
+ QCOMPARE(objectAsValue["object"]["value"].toInt(), 42);
+ QCOMPARE(objectAsValue["array"][1].toInt(), 666);
+}
+
+void tst_QtJson::implicitDocumentType()
+{
+ QJsonDocument emptyDocument;
+ QCOMPARE(emptyDocument["asObject"], QJsonValue(QJsonValue::Undefined));
+ QCOMPARE(emptyDocument[123], QJsonValue(QJsonValue::Undefined));
+
+ QJsonDocument objectDocument(QJsonObject{{"value", 42}});
+ QCOMPARE(objectDocument["value"].toInt(), 42);
+ QCOMPARE(objectDocument["missingValue"], QJsonValue(QJsonValue::Undefined));
+ QCOMPARE(objectDocument[123], QJsonValue(QJsonValue::Undefined));
+ QCOMPARE(objectDocument["missingValue"].toInt(123), 123);
+
+ QJsonDocument arrayDocument(QJsonArray{665, 666, 667});
+ QCOMPARE(arrayDocument[1].toInt(), 666);
+ QCOMPARE(arrayDocument[-1], QJsonValue(QJsonValue::Undefined));
+ QCOMPARE(arrayDocument["asObject"], QJsonValue(QJsonValue::Undefined));
+ QCOMPARE(arrayDocument[-1].toInt(123), 123);
+}
+
QTEST_MAIN(tst_QtJson)
#include "tst_qtjson.moc"
diff --git a/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro b/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro
index e37542be65..1a76085c1b 100644
--- a/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro
+++ b/tests/auto/corelib/kernel/qcoreapplication/qcoreapplication.pro
@@ -5,5 +5,5 @@ SOURCES = tst_qcoreapplication.cpp
HEADERS = tst_qcoreapplication.h
win32: VERSION = 1.2.3.4
else: VERSION = 1.2.3
-darwin: QMAKE_LFLAGS += -Wl,-sectcreate,__TEXT,__info_plist,$$shell_quote($$PWD/Info.plist)
+QMAKE_INFO_PLIST = $$PWD/Info.plist
requires(qtConfig(private_tests))
diff --git a/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp
index 0b4f76ef70..109f5b77f6 100644
--- a/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp
+++ b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp
@@ -637,11 +637,6 @@ void tst_QEventLoop::testQuitLock()
{
QEventLoop eventLoop;
- QTimer timer;
- timer.setInterval(100);
- QSignalSpy timerSpy(&timer, &QTimer::timeout);
- timer.start();
-
QEventLoopPrivate* privateClass = static_cast<QEventLoopPrivate*>(QObjectPrivate::get(&eventLoop));
QCOMPARE(privateClass->quitLockRef.load(), 0);
@@ -655,9 +650,6 @@ void tst_QEventLoop::testQuitLock()
QCOMPARE(privateClass->quitLockRef.load(), 0);
- // The job takes long enough that the timer times out several times.
- QVERIFY(timerSpy.count() > 3);
- timerSpy.clear();
job1 = new JobObject(&eventLoop, this);
job1->start(200);
@@ -670,11 +662,6 @@ void tst_QEventLoop::testQuitLock()
}
eventLoop.exec();
-
- qDebug() << timerSpy.count();
- // The timer times out more if it has more subjobs to do.
- // We run 10 jobs in sequence here of about 200ms each.
- QVERIFY(timerSpy.count() > 17);
}
QTEST_MAIN(tst_QEventLoop)
diff --git a/tests/auto/corelib/kernel/qmath/tst_qmath.cpp b/tests/auto/corelib/kernel/qmath/tst_qmath.cpp
index ad40b0140d..feb704e0aa 100644
--- a/tests/auto/corelib/kernel/qmath/tst_qmath.cpp
+++ b/tests/auto/corelib/kernel/qmath/tst_qmath.cpp
@@ -30,8 +30,6 @@
#include <QtTest/QtTest>
#include <qmath.h>
-static const double PI = 3.14159265358979323846264338327950288;
-
class tst_QMath : public QObject
{
Q_OBJECT
@@ -55,8 +53,9 @@ void tst_QMath::fastSinCos()
{
// Test evenly spaced angles from 0 to 2pi radians.
const int LOOP_COUNT = 100000;
+ const qreal loopAngle = 2 * M_PI / (LOOP_COUNT - 1);
for (int i = 0; i < LOOP_COUNT; ++i) {
- qreal angle = i * 2 * PI / (LOOP_COUNT - 1);
+ qreal angle = i * loopAngle;
QVERIFY(qAbs(qSin(angle) - qFastSin(angle)) < 1e-5);
QVERIFY(qAbs(qCos(angle) - qFastCos(angle)) < 1e-5);
}
@@ -69,18 +68,18 @@ void tst_QMath::degreesToRadians_data()
QTest::addColumn<double>("degreesDouble");
QTest::addColumn<double>("radiansDouble");
- QTest::newRow( "pi" ) << 180.0f << float(M_PI) << 180.0 << PI;
- QTest::newRow( "doublepi" ) << 360.0f << float(2*M_PI) << 360.0 << 2*PI;
- QTest::newRow( "halfpi" ) << 90.0f << float(M_PI_2) << 90.0 << PI/2;
+ QTest::newRow( "pi" ) << 180.0f << float(M_PI) << 180.0 << M_PI;
+ QTest::newRow( "doublepi" ) << 360.0f << float(2 * M_PI) << 360.0 << 2 * M_PI;
+ QTest::newRow( "halfpi" ) << 90.0f << float(M_PI_2) << 90.0 << M_PI_2;
QTest::newRow( "random" ) << 123.1234567f << 2.1489097058516724f << 123.123456789123456789 << 2.148909707407169856192285627;
QTest::newRow( "bigrandom" ) << 987654321.9876543f << 17237819.79023679f << 987654321987654321.987654321987654321 << 17237819790236794.0;
QTest::newRow( "zero" ) << 0.0f << 0.0f << 0.0 << 0.0;
- QTest::newRow( "minuspi" ) << -180.0f << float(-M_PI) << 180.0 << PI;
- QTest::newRow( "minusdoublepi" ) << -360.0f << float(-2*M_PI) << -360.0 << -2*PI;
- QTest::newRow( "minushalfpi" ) << -90.0f << float(-M_PI_2) << -90.0 << -PI/2;
+ QTest::newRow( "minuspi" ) << -180.0f << float(-M_PI) << 180.0 << M_PI;
+ QTest::newRow( "minusdoublepi" ) << -360.0f << float(-2 * M_PI) << -360.0 << -2 * M_PI;
+ QTest::newRow( "minushalfpi" ) << -90.0f << float(-M_PI_2) << -90.0 << -M_PI_2;
QTest::newRow( "minusrandom" ) << -123.1234567f << -2.1489097058516724f << -123.123456789123456789 << -2.148909707407169856192285627;
QTest::newRow( "minusbigrandom" ) << -987654321.9876543f << -17237819.79023679f << -987654321987654321.987654321987654321 << -17237819790236794.0;
@@ -104,18 +103,18 @@ void tst_QMath::radiansToDegrees_data()
QTest::addColumn<double>("radiansDouble");
QTest::addColumn<double>("degreesDouble");
- QTest::newRow( "pi" ) << float(M_PI) << 180.0f << PI << 180.0;
- QTest::newRow( "doublepi" ) << float(2*M_PI) << 360.0f << 2*PI << 360.0;
- QTest::newRow( "halfpi" ) << float(M_PI_2) << 90.0f<< PI/2 << 90.0;
+ QTest::newRow( "pi" ) << float(M_PI) << 180.0f << M_PI << 180.0;
+ QTest::newRow( "doublepi" ) << float(2 * M_PI) << 360.0f << 2 * M_PI << 360.0;
+ QTest::newRow( "halfpi" ) << float(M_PI_2) << 90.0f << M_PI_2 << 90.0;
QTest::newRow( "random" ) << 123.1234567f << 7054.454427971739f << 123.123456789123456789 << 7054.4544330781363896676339209079742431640625;
QTest::newRow( "bigrandom" ) << 987654321.9876543f << 56588424267.74745f << 987654321987654321.987654321987654321 << 56588424267747450880.0;
QTest::newRow( "zero" ) << 0.0f << 0.0f << 0.0 << 0.0;
- QTest::newRow( "minuspi" ) << float(-M_PI) << -180.0f << -PI << -180.0;
- QTest::newRow( "minusdoublepi" ) << float(-2*M_PI) << -360.0f << -2*PI << -360.0;
- QTest::newRow( "minushalfpi" ) << float(-M_PI_2) << -90.0f << -PI/2 << -90.0;
+ QTest::newRow( "minuspi" ) << float(-M_PI) << -180.0f << -M_PI << -180.0;
+ QTest::newRow( "minusdoublepi" ) << float(-2 * M_PI) << -360.0f << -2 * M_PI << -360.0;
+ QTest::newRow( "minushalfpi" ) << float(-M_PI_2) << -90.0f << -M_PI_2 << -90.0;
QTest::newRow( "minusrandom" ) << -123.1234567f << -7054.454427971739f << -123.123456789123456789 << -7054.4544330781363896676339209079742431640625;
QTest::newRow( "minusbigrandom" ) << -987654321.9876543f << -56588424267.74745f << -987654321987654321.987654321987654321 << -56588424267747450880.0;
diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
index e793d71fe2..431a9ebdea 100644
--- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
+++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
@@ -147,6 +147,77 @@ namespace MyNamespace {
{
Q_OBJECT
};
+
+ class ClassWithSetterGetterSignals : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ int value1() const { return m_value1; }
+ void setValue1(int v) {
+ if (v != m_value1) {
+ m_value1 = v;
+ Q_EMIT value1Changed();
+ }
+ }
+
+ int value2() const { return m_value2; }
+ void setValue2(int v) {
+ if (v != m_value2) {
+ m_value2 = v;
+ Q_EMIT value2Changed();
+ }
+ }
+
+ Q_SIGNALS:
+ void value1Changed();
+ void value2Changed();
+
+ private:
+ int m_value1 = 0;
+ int m_value2 = 0;
+ };
+
+ class ClassWithSetterGetterSignalsAddsProperties : public ClassWithSetterGetterSignals
+ {
+ Q_OBJECT
+ Q_PROPERTY(int value1 READ value1 WRITE setValue1 NOTIFY value1Changed)
+ Q_PROPERTY(int value2 READ value2 WRITE setValue2 NOTIFY value2Changed)
+ };
+
+ class ClassWithChangedSignal : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ int value1() const { return m_value1; }
+ void setValue1(int v) {
+ if (v != m_value1) {
+ m_value1 = v;
+ Q_EMIT propertiesChanged();
+ }
+ }
+
+ void thisIsNotASignal() { }
+
+ Q_SIGNALS:
+ void propertiesChanged();
+
+ private:
+ int m_value1 = 0;
+ };
+
+ class ClassWithChangedSignalNewValue : public ClassWithChangedSignal
+ {
+ Q_OBJECT
+
+ Q_PROPERTY(int value2 MEMBER m_value2 NOTIFY propertiesChanged)
+ Q_PROPERTY(int value3 MEMBER m_value3 NOTIFY thisIsNotASignal)
+
+ private:
+ int m_value2 = 0;
+ int m_value3 = 0;
+ };
}
@@ -200,8 +271,11 @@ public:
private slots:
void connectSlotsByName();
void invokeMetaMember();
+ void invokePointer();
void invokeQueuedMetaMember();
+ void invokeQueuedPointer();
void invokeBlockingQueuedMetaMember();
+ void invokeBlockingQueuedPointer();
void invokeCustomTypes();
void invokeMetaConstructor();
void invokeTypedefTypes();
@@ -242,6 +316,8 @@ private slots:
void inherits_data();
void inherits();
+ void notifySignalsInParentClass();
+
signals:
void value6Changed();
void value7Changed(const QString &);
@@ -427,6 +503,10 @@ public slots:
+ QString::number(o6.size());
}
+public:
+ static void staticFunction0();
+ static qint64 staticFunction1();
+
signals:
void sig0();
QString sig1(QString s1);
@@ -440,8 +520,11 @@ private:
public:
QString slotResult;
+ static QString staticResult;
};
+QString QtTestObject::staticResult;
+
QtTestObject::QtTestObject()
{
connect(this, SIGNAL(sig0()), this, SLOT(sl0()));
@@ -500,6 +583,13 @@ void QtTestObject::testSender()
void QtTestObject::slotWithUnregisteredParameterType(MyUnregisteredType)
{ slotResult = "slotWithUnregisteredReturnType"; }
+void QtTestObject::staticFunction0()
+{
+ staticResult = "staticFunction0";
+}
+
+qint64 QtTestObject::staticFunction1()
+{ staticResult = "staticFunction1"; return Q_INT64_C(123456789)*123456789; }
void tst_QMetaObject::invokeMetaMember()
{
@@ -508,9 +598,18 @@ void tst_QMetaObject::invokeMetaMember()
QString t1("1"); QString t2("2"); QString t3("3"); QString t4("4"); QString t5("5");
QString t6("6"); QString t7("7"); QString t8("8"); QString t9("9"); QString t10("X");
- QVERIFY(!QMetaObject::invokeMethod(0, 0));
- QVERIFY(!QMetaObject::invokeMethod(0, "sl0"));
- QVERIFY(!QMetaObject::invokeMethod(&obj, 0));
+ // Test nullptr
+ char *nullCharArray = nullptr;
+ const char *nullConstCharArray = nullptr;
+ QVERIFY(!QMetaObject::invokeMethod(nullptr, nullCharArray));
+ QVERIFY(!QMetaObject::invokeMethod(nullptr, nullConstCharArray));
+ QVERIFY(!QMetaObject::invokeMethod(nullptr, "sl0"));
+ QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray));
+ QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray));
+ QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection));
+ QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, Qt::AutoConnection));
+ QVERIFY(!QMetaObject::invokeMethod(&obj, nullCharArray, Qt::AutoConnection, QGenericReturnArgument()));
+ QVERIFY(!QMetaObject::invokeMethod(&obj, nullConstCharArray, Qt::AutoConnection, QGenericReturnArgument()));
QVERIFY(QMetaObject::invokeMethod(&obj, "sl0"));
QCOMPARE(obj.slotResult, QString("sl0"));
@@ -639,6 +738,65 @@ void tst_QMetaObject::invokeMetaMember()
QCOMPARE(obj.slotResult, QString("sl1:hehe"));
}
+void testFunction(){}
+
+
+void tst_QMetaObject::invokePointer()
+{
+ QtTestObject obj;
+ QtTestObject *const nullTestObject = nullptr;
+
+ QString t1("1");
+
+ // Test member functions
+ QVERIFY(!QMetaObject::invokeMethod(nullTestObject, &QtTestObject::sl0));
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl0));
+ QCOMPARE(obj.slotResult, QString("sl0"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::testSender));
+ QCOMPARE(obj.slotResult, QString("0x0"));
+
+ qint64 return64 = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl14, &return64));
+ QCOMPARE(return64, Q_INT64_C(123456789)*123456789);
+ QCOMPARE(obj.slotResult, QString("sl14"));
+
+ // signals
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sig0));
+ QCOMPARE(obj.slotResult, QString("sl0"));
+
+ // Test function pointers
+ QVERIFY(!QMetaObject::invokeMethod(0, &testFunction));
+ QVERIFY(QMetaObject::invokeMethod(&obj, &testFunction));
+
+ QVERIFY(!QMetaObject::invokeMethod(0, &QtTestObject::staticFunction0));
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction0));
+ QCOMPARE(QtTestObject::staticResult, QString("staticFunction0"));
+
+ return64 = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction1, &return64));
+ QCOMPARE(return64, Q_INT64_C(123456789)*123456789);
+ QCOMPARE(QtTestObject::staticResult, QString("staticFunction1"));
+
+ // Test lambdas
+ QCOMPARE(countedStructObjectsCount, 0);
+ {
+ CountedStruct str;
+ QVERIFY(QMetaObject::invokeMethod(&obj, [str, &t1, &obj]() { obj.sl1(t1); }));
+ QCOMPARE(obj.slotResult, QString("sl1:1"));
+ }
+ QCOMPARE(countedStructObjectsCount, 0);
+ {
+ CountedStruct str;
+ QString exp;
+ QVERIFY(QMetaObject::invokeMethod(
+ &obj, [str, &obj]() -> QString { return obj.sl1("bubu"); }, &exp));
+ QCOMPARE(exp, QString("yessir"));
+ QCOMPARE(obj.slotResult, QString("sl1:bubu"));
+ }
+ QCOMPARE(countedStructObjectsCount, 0);
+}
+
void tst_QMetaObject::invokeQueuedMetaMember()
{
QtTestObject obj;
@@ -699,6 +857,56 @@ void tst_QMetaObject::invokeQueuedMetaMember()
}
}
+void tst_QMetaObject::invokeQueuedPointer()
+{
+ QtTestObject obj;
+
+ // Test member function
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl0, Qt::QueuedConnection));
+ QVERIFY(obj.slotResult.isEmpty());
+ qApp->processEvents(QEventLoop::AllEvents);
+ QCOMPARE(obj.slotResult, QString("sl0"));
+
+ // signals
+ obj.slotResult.clear();
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sig0, Qt::QueuedConnection));
+ QVERIFY(obj.slotResult.isEmpty());
+ qApp->processEvents(QEventLoop::AllEvents);
+ QCOMPARE(obj.slotResult, QString("sl0"));
+
+ // Test function pointers
+ QtTestObject::staticResult.clear();
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction0, Qt::QueuedConnection));
+ QVERIFY(QtTestObject::staticResult.isEmpty());
+ qApp->processEvents(QEventLoop::AllEvents);
+ QCOMPARE(QtTestObject::staticResult, QString("staticFunction0"));
+
+ // Test lambda
+ QCOMPARE(countedStructObjectsCount, 0);
+ {
+ CountedStruct str;
+ obj.slotResult.clear();
+ QVERIFY(
+ QMetaObject::invokeMethod(&obj, [str, &obj]() { obj.sl0(); }, Qt::QueuedConnection));
+ QVERIFY(obj.slotResult.isEmpty());
+ qApp->processEvents(QEventLoop::AllEvents);
+ QCOMPARE(obj.slotResult, QString("sl0"));
+ }
+ QCOMPARE(countedStructObjectsCount, 0);
+ {
+ CountedStruct str;
+ qint32 var = 0;
+ QTest::ignoreMessage(QtWarningMsg,
+ "QMetaObject::invokeMethod: Unable to invoke methods with return "
+ "values in queued connections");
+ QVERIFY(!QMetaObject::invokeMethod(&obj, [str]() -> qint32 { return 1; },
+ Qt::QueuedConnection, &var));
+ QCOMPARE(var, 0);
+ }
+ QCOMPARE(countedStructObjectsCount, 0);
+}
+
+
void tst_QMetaObject::invokeBlockingQueuedMetaMember()
{
QThread t;
@@ -832,6 +1040,71 @@ void tst_QMetaObject::invokeBlockingQueuedMetaMember()
}
+void tst_QMetaObject::invokeBlockingQueuedPointer()
+{
+ QtTestObject *const nullTestObject = nullptr;
+
+ QThread t;
+ t.start();
+ QtTestObject obj;
+ obj.moveToThread(&t);
+
+ QString t1("1");
+
+ // Test member functions
+ QVERIFY(!QMetaObject::invokeMethod(nullTestObject, &QtTestObject::sl0, Qt::BlockingQueuedConnection));
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl0, Qt::BlockingQueuedConnection));
+ QCOMPARE(obj.slotResult, QString("sl0"));
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::testSender, Qt::BlockingQueuedConnection));
+ QCOMPARE(obj.slotResult, QString("0x0"));
+
+ // return qint64
+ qint64 return64 = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl14, Qt::BlockingQueuedConnection,
+ &return64));
+ QCOMPARE(return64, Q_INT64_C(123456789)*123456789);
+ QCOMPARE(obj.slotResult, QString("sl14"));
+
+ //test signals
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sig0, Qt::BlockingQueuedConnection));
+ QCOMPARE(obj.slotResult, QString("sl0"));
+
+ // Test function pointers
+ QVERIFY(!QMetaObject::invokeMethod(0, &testFunction, Qt::BlockingQueuedConnection));
+ QVERIFY(QMetaObject::invokeMethod(&obj, &testFunction, Qt::BlockingQueuedConnection));
+
+ QVERIFY(!QMetaObject::invokeMethod(0, &QtTestObject::staticFunction0, Qt::BlockingQueuedConnection));
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction0, Qt::BlockingQueuedConnection));
+ QCOMPARE(QtTestObject::staticResult, QString("staticFunction0"));
+
+ return64 = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::staticFunction1, Qt::BlockingQueuedConnection, &return64));
+ QCOMPARE(return64, Q_INT64_C(123456789)*123456789);
+ QCOMPARE(QtTestObject::staticResult, QString("staticFunction1"));
+
+ // Test lambdas
+ QCOMPARE(countedStructObjectsCount, 0);
+ {
+ CountedStruct str;
+ QVERIFY(QMetaObject::invokeMethod(&obj, [str, &obj, &t1]() { obj.sl1(t1); },
+ Qt::BlockingQueuedConnection));
+ QCOMPARE(obj.slotResult, QString("sl1:1"));
+ }
+ {
+ CountedStruct str;
+ QString exp;
+ QVERIFY(QMetaObject::invokeMethod(&obj,
+ [&obj, str]() -> QString { return obj.sl1("bubu"); },
+ Qt::BlockingQueuedConnection, &exp));
+ QCOMPARE(exp, QString("yessir"));
+ QCOMPARE(obj.slotResult, QString("sl1:bubu"));
+ }
+ QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.moveToThread(QThread::currentThread());}, Qt::BlockingQueuedConnection));
+ t.quit();
+ QVERIFY(t.wait());
+ QCOMPARE(countedStructObjectsCount, 0);
+}
void tst_QMetaObject::qtMetaObjectInheritance()
@@ -1502,5 +1775,18 @@ void tst_QMetaObject::inherits()
QCOMPARE(derivedMetaObject->inherits(baseMetaObject), inheritsResult);
}
+void tst_QMetaObject::notifySignalsInParentClass()
+{
+ MyNamespace::ClassWithSetterGetterSignalsAddsProperties obj;
+ QCOMPARE(obj.metaObject()->property(obj.metaObject()->indexOfProperty("value1")).notifySignal().name(), QByteArray("value1Changed"));
+ QCOMPARE(obj.metaObject()->property(obj.metaObject()->indexOfProperty("value2")).notifySignal().name(), QByteArray("value2Changed"));
+
+ MyNamespace::ClassWithChangedSignalNewValue obj2;
+ QCOMPARE(obj2.metaObject()->property(obj2.metaObject()->indexOfProperty("value2")).notifySignal().name(), QByteArray("propertiesChanged"));
+
+ QTest::ignoreMessage(QtWarningMsg, "QMetaProperty::notifySignal: cannot find the NOTIFY signal thisIsNotASignal in class MyNamespace::ClassWithChangedSignalNewValue for property 'value3'");
+ obj2.metaObject()->property(obj2.metaObject()->indexOfProperty("value3")).notifySignal();
+}
+
QTEST_MAIN(tst_QMetaObject)
#include "tst_qmetaobject.moc"
diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
index 9ae39449f8..6bb031e357 100644
--- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
+++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
@@ -806,6 +806,7 @@ void tst_QMetaObjectBuilder::enumerator()
QMetaEnumBuilder enum1 = builder.addEnumerator("foo");
QCOMPARE(enum1.name(), QByteArray("foo"));
QVERIFY(!enum1.isFlag());
+ QVERIFY(!enum1.isScoped());
QCOMPARE(enum1.keyCount(), 0);
QCOMPARE(enum1.index(), 0);
QCOMPARE(builder.enumeratorCount(), 1);
@@ -814,6 +815,7 @@ void tst_QMetaObjectBuilder::enumerator()
QMetaEnumBuilder enum2 = builder.addEnumerator("bar");
QCOMPARE(enum2.name(), QByteArray("bar"));
QVERIFY(!enum2.isFlag());
+ QVERIFY(!enum2.isScoped());
QCOMPARE(enum2.keyCount(), 0);
QCOMPARE(enum2.index(), 1);
QCOMPARE(builder.enumeratorCount(), 2);
@@ -827,6 +829,7 @@ void tst_QMetaObjectBuilder::enumerator()
// Modify the attributes on enum1.
enum1.setIsFlag(true);
+ enum1.setIsScoped(true);
QCOMPARE(enum1.addKey("ABC", 0), 0);
QCOMPARE(enum1.addKey("DEF", 1), 1);
QCOMPARE(enum1.addKey("GHI", -1), 2);
@@ -834,6 +837,7 @@ void tst_QMetaObjectBuilder::enumerator()
// Check that enum1 is changed, but enum2 is not.
QCOMPARE(enum1.name(), QByteArray("foo"));
QVERIFY(enum1.isFlag());
+ QVERIFY(enum1.isScoped());
QCOMPARE(enum1.keyCount(), 3);
QCOMPARE(enum1.index(), 0);
QCOMPARE(enum1.key(0), QByteArray("ABC"));
@@ -845,6 +849,7 @@ void tst_QMetaObjectBuilder::enumerator()
QCOMPARE(enum1.value(2), -1);
QCOMPARE(enum2.name(), QByteArray("bar"));
QVERIFY(!enum2.isFlag());
+ QVERIFY(!enum2.isScoped());
QCOMPARE(enum2.keyCount(), 0);
QCOMPARE(enum2.index(), 1);
@@ -856,6 +861,7 @@ void tst_QMetaObjectBuilder::enumerator()
// This time check that only method2 changed.
QCOMPARE(enum1.name(), QByteArray("foo"));
QVERIFY(enum1.isFlag());
+ QVERIFY(enum1.isScoped());
QCOMPARE(enum1.keyCount(), 3);
QCOMPARE(enum1.index(), 0);
QCOMPARE(enum1.key(0), QByteArray("ABC"));
@@ -867,6 +873,7 @@ void tst_QMetaObjectBuilder::enumerator()
QCOMPARE(enum1.value(2), -1);
QCOMPARE(enum2.name(), QByteArray("bar"));
QVERIFY(enum2.isFlag());
+ QVERIFY(!enum2.isScoped());
QCOMPARE(enum2.keyCount(), 2);
QCOMPARE(enum2.index(), 1);
QCOMPARE(enum2.key(0), QByteArray("XYZ"));
@@ -879,6 +886,7 @@ void tst_QMetaObjectBuilder::enumerator()
enum1.removeKey(2);
QCOMPARE(enum1.name(), QByteArray("foo"));
QVERIFY(enum1.isFlag());
+ QVERIFY(enum1.isScoped());
QCOMPARE(enum1.keyCount(), 2);
QCOMPARE(enum1.index(), 0);
QCOMPARE(enum1.key(0), QByteArray("ABC"));
@@ -889,6 +897,7 @@ void tst_QMetaObjectBuilder::enumerator()
QCOMPARE(enum1.value(2), -1);
QCOMPARE(enum2.name(), QByteArray("bar"));
QVERIFY(enum2.isFlag());
+ QVERIFY(!enum2.isScoped());
QCOMPARE(enum2.keyCount(), 2);
QCOMPARE(enum2.index(), 1);
QCOMPARE(enum2.key(0), QByteArray("XYZ"));
@@ -903,6 +912,7 @@ void tst_QMetaObjectBuilder::enumerator()
enum2 = builder.enumerator(0);
QCOMPARE(enum2.name(), QByteArray("bar"));
QVERIFY(enum2.isFlag());
+ QVERIFY(!enum2.isScoped());
QCOMPARE(enum2.keyCount(), 2);
QCOMPARE(enum2.index(), 0);
QCOMPARE(enum2.key(0), QByteArray("XYZ"));
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
index f9ddd59aaa..076610a0c5 100644
--- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
@@ -155,7 +155,9 @@ void tst_QMetaType::defined()
QCOMPARE(int(QMetaTypeId2<int*>::Defined), 0);
QCOMPARE(int(QMetaTypeId2<CustomQObject::CustomQEnum>::Defined), 1);
QCOMPARE(int(QMetaTypeId2<CustomGadget>::Defined), 1);
+ QCOMPARE(int(QMetaTypeId2<CustomGadget*>::Defined), 1);
QVERIFY(!QMetaTypeId2<GadgetDerived>::Defined);
+ QVERIFY(!QMetaTypeId2<GadgetDerived*>::Defined);
QVERIFY(int(QMetaTypeId2<CustomQObject*>::Defined));
QVERIFY(!QMetaTypeId2<CustomQObject>::Defined);
QVERIFY(!QMetaTypeId2<CustomNonQObject>::Defined);
@@ -397,6 +399,7 @@ void tst_QMetaType::typeName_data()
QTest::newRow("CustomQObject*") << ::qMetaTypeId<CustomQObject*>() << QString::fromLatin1("CustomQObject*");
QTest::newRow("CustomGadget") << ::qMetaTypeId<CustomGadget>() << QString::fromLatin1("CustomGadget");
+ QTest::newRow("CustomGadget*") << ::qMetaTypeId<CustomGadget*>() << QString::fromLatin1("CustomGadget*");
QTest::newRow("CustomQObject::CustomQEnum") << ::qMetaTypeId<CustomQObject::CustomQEnum>() << QString::fromLatin1("CustomQObject::CustomQEnum");
QTest::newRow("Qt::ArrowType") << ::qMetaTypeId<Qt::ArrowType>() << QString::fromLatin1("Qt::ArrowType");
}
@@ -1684,6 +1687,7 @@ public:
};
Q_DECLARE_METATYPE(MyGadget);
+Q_DECLARE_METATYPE(MyGadget*);
Q_DECLARE_METATYPE(const QMetaObject *);
Q_DECLARE_METATYPE(Qt::ScrollBarPolicy);
Q_DECLARE_METATYPE(MyGadget::MyEnum);
@@ -1693,16 +1697,18 @@ void tst_QMetaType::metaObject_data()
QTest::addColumn<int>("type");
QTest::addColumn<const QMetaObject*>("result");
QTest::addColumn<bool>("isGadget");
+ QTest::addColumn<bool>("isGadgetPtr");
QTest::addColumn<bool>("isQObjectPtr");
- QTest::newRow("QObject") << int(QMetaType::QObjectStar) << &QObject::staticMetaObject << false << true;
- QTest::newRow("QFile*") << ::qMetaTypeId<QFile*>() << &QFile::staticMetaObject << false << true;
- QTest::newRow("MyObject*") << ::qMetaTypeId<MyObject*>() << &MyObject::staticMetaObject << false << true;
- QTest::newRow("int") << int(QMetaType::Int) << static_cast<const QMetaObject *>(0) << false << false;
- QTest::newRow("QEasingCurve") << ::qMetaTypeId<QEasingCurve>() << &QEasingCurve::staticMetaObject << true << false;
- QTest::newRow("MyGadget") << ::qMetaTypeId<MyGadget>() << &MyGadget::staticMetaObject << true << false;
- QTest::newRow("MyEnum") << ::qMetaTypeId<MyGadget::MyEnum>() << &MyGadget::staticMetaObject << false << false;
- QTest::newRow("Qt::ScrollBarPolicy") << ::qMetaTypeId<Qt::ScrollBarPolicy>() << &QObject::staticQtMetaObject << false << false;
+ QTest::newRow("QObject") << int(QMetaType::QObjectStar) << &QObject::staticMetaObject << false << false << true;
+ QTest::newRow("QFile*") << ::qMetaTypeId<QFile*>() << &QFile::staticMetaObject << false << false << true;
+ QTest::newRow("MyObject*") << ::qMetaTypeId<MyObject*>() << &MyObject::staticMetaObject << false << false << true;
+ QTest::newRow("int") << int(QMetaType::Int) << static_cast<const QMetaObject *>(0) << false << false << false;
+ QTest::newRow("QEasingCurve") << ::qMetaTypeId<QEasingCurve>() << &QEasingCurve::staticMetaObject << true << false << false;
+ QTest::newRow("MyGadget") << ::qMetaTypeId<MyGadget>() << &MyGadget::staticMetaObject << true << false << false;
+ QTest::newRow("MyGadget*") << ::qMetaTypeId<MyGadget*>() << &MyGadget::staticMetaObject << false << true << false;
+ QTest::newRow("MyEnum") << ::qMetaTypeId<MyGadget::MyEnum>() << &MyGadget::staticMetaObject << false << false << false;
+ QTest::newRow("Qt::ScrollBarPolicy") << ::qMetaTypeId<Qt::ScrollBarPolicy>() << &QObject::staticQtMetaObject << false << false << false;
}
@@ -1711,12 +1717,14 @@ void tst_QMetaType::metaObject()
QFETCH(int, type);
QFETCH(const QMetaObject *, result);
QFETCH(bool, isGadget);
+ QFETCH(bool, isGadgetPtr);
QFETCH(bool, isQObjectPtr);
QCOMPARE(QMetaType::metaObjectForType(type), result);
QMetaType mt(type);
QCOMPARE(mt.metaObject(), result);
QCOMPARE(!!(mt.flags() & QMetaType::IsGadget), isGadget);
+ QCOMPARE(!!(mt.flags() & QMetaType::PointerToGadget), isGadgetPtr);
QCOMPARE(!!(mt.flags() & QMetaType::PointerToQObject), isQObjectPtr);
}
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index f1e58921c0..1f98f64340 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -140,6 +140,7 @@ private slots:
void connectFunctorWithContext();
void connectFunctorWithContextUnique();
void connectFunctorDeadlock();
+ void connectFunctorMoveOnly();
void connectStaticSlotWithObject();
void disconnectDoesNotLeakFunctor();
void contextDoesNotLeakFunctor();
@@ -2362,8 +2363,8 @@ void tst_QObject::testUserData()
// Randomize the table a bit
for (int i=0; i<100; ++i) {
- int p1 = rand() % USER_DATA_COUNT;
- int p2 = rand() % USER_DATA_COUNT;
+ int p1 = QRandomGenerator::global()->bounded(USER_DATA_COUNT);
+ int p2 = QRandomGenerator::global()->bounded(USER_DATA_COUNT);
int tmp = user_data_ids[p1];
user_data_ids[p1] = user_data_ids[p2];
@@ -6238,6 +6239,47 @@ void tst_QObject::connectFunctorDeadlock()
sender.emitSignal1();
}
+void tst_QObject::connectFunctorMoveOnly()
+{
+ struct MoveOnlyFunctor {
+ Q_DISABLE_COPY(MoveOnlyFunctor)
+ MoveOnlyFunctor(int *status) : status(status) {}
+ MoveOnlyFunctor(MoveOnlyFunctor &&o) : status(o.status) { o.status = nullptr; };
+ void operator()(int i) { *status = i; }
+ void operator()() { *status = -8; }
+ int *status;
+ };
+
+ int status = 1;
+ SenderObject obj;
+ QEventLoop e;
+
+ connect(&obj, &SenderObject::signal1, MoveOnlyFunctor(&status));
+ QCOMPARE(status, 1);
+ obj.signal1();
+ QCOMPARE(status, -8);
+
+ connect(&obj, &SenderObject::signal7, MoveOnlyFunctor(&status));
+ QCOMPARE(status, -8);
+ obj.signal7(7888, "Hello");
+ QCOMPARE(status, 7888);
+
+ // With a context
+ status = 1;
+ connect(&obj, &SenderObject::signal2, this, MoveOnlyFunctor(&status));
+ QCOMPARE(status, 1);
+ obj.signal2();
+ QCOMPARE(status, -8);
+
+ // QueuedConnection
+ status = 1;
+ connect(&obj, &SenderObject::signal3, this, MoveOnlyFunctor(&status), Qt::QueuedConnection);
+ obj.signal3();
+ QCOMPARE(status, 1);
+ QCoreApplication::processEvents();
+ QCOMPARE(status, -8);
+}
+
static int s_static_slot_checker = 1;
class StaticSlotChecker : public QObject
diff --git a/tests/auto/corelib/kernel/qtimer/qtimer.pro b/tests/auto/corelib/kernel/qtimer/qtimer.pro
index b27d862bc5..710dfea682 100644
--- a/tests/auto/corelib/kernel/qtimer/qtimer.pro
+++ b/tests/auto/corelib/kernel/qtimer/qtimer.pro
@@ -1,6 +1,6 @@
CONFIG += testcase
TARGET = tst_qtimer
-QT = core testlib
+QT = core core-private testlib
SOURCES = tst_qtimer.cpp
# Force C++17 if available
diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
index fd704f582d..b921c0f13d 100644
--- a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
+++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
@@ -33,11 +33,11 @@
# include <QtCore/QCoreApplication>
#endif
+#include <QtCore/private/qglobal_p.h>
#include <QtTest/QtTest>
#include <qtimer.h>
#include <qthread.h>
-#include <qoperatingsystemversion.h>
#if defined Q_OS_UNIX
#include <unistd.h>
@@ -500,7 +500,7 @@ void tst_QTimer::moveToThread()
#if defined(Q_OS_WIN32)
QSKIP("Does not work reliably on Windows :(");
#elif defined(Q_OS_MACOS)
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSSierra)
+ if (__builtin_available(macOS 10.12, *))
QSKIP("Does not work reliably on macOS 10.12 (QTBUG-59679)");
#endif
QTimer ti1;
@@ -836,7 +836,6 @@ void tst_QTimer::singleShotToFunctors()
QTest::qWait(800);
QCOMPARE(count, 2);
-#if defined(Q_COMPILER_LAMBDA)
QTimer::singleShot(0, [&count] { ++count; });
QCoreApplication::processEvents();
QCOMPARE(count, 3);
@@ -855,7 +854,15 @@ void tst_QTimer::singleShotToFunctors()
thread.quit();
thread.wait();
-#endif
+
+ struct MoveOnly : CountedStruct {
+ Q_DISABLE_COPY(MoveOnly);
+ MoveOnly(MoveOnly &&o) : CountedStruct(std::move(o)) {};
+ MoveOnly(int *c) : CountedStruct(c) {}
+ };
+ QTimer::singleShot(0, MoveOnly(&count));
+ QCoreApplication::processEvents();
+ QCOMPARE(count, 5);
_e.reset();
_t = Q_NULLPTR;
diff --git a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp
index e3eee6dbdd..451f96339e 100644
--- a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp
+++ b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp
@@ -50,6 +50,7 @@ private slots:
void loadFromResource();
void loadDirectory();
void dependencies();
+ void translationInThreadWhileInstallingTranslator();
private:
int languageChangeEventCounter;
@@ -287,6 +288,52 @@ void tst_QTranslator::dependencies()
}
}
+struct TranslateThread : public QThread
+{
+ bool ok = false;
+ QAtomicInt terminate;
+ QMutex startupLock;
+ QWaitCondition runningCondition;
+
+ void run() {
+ bool startSignalled = false;
+
+ while (terminate.load() == 0) {
+ const QString result = QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 0);
+
+ if (!startSignalled) {
+ QMutexLocker startupLocker(&startupLock);
+ runningCondition.wakeAll();
+ startSignalled = true;
+ }
+
+ ok = (result == QLatin1String("Hallo 0 Welten!"))
+ || (result == QLatin1String("Hello 0 world(s)!"));
+ if (!ok)
+ break;
+ }
+ }
+};
+
+void tst_QTranslator::translationInThreadWhileInstallingTranslator()
+{
+ TranslateThread thread;
+
+ QMutexLocker startupLocker(&thread.startupLock);
+
+ thread.start();
+
+ thread.runningCondition.wait(&thread.startupLock);
+
+ QTranslator *tor = new QTranslator;
+ tor->load("hellotr_la");
+ QCoreApplication::installTranslator(tor);
+
+ ++thread.terminate;
+
+ QVERIFY(thread.wait());
+ QVERIFY(thread.ok);
+}
QTEST_MAIN(tst_QTranslator)
#include "tst_qtranslator.moc"
diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
index e43b7acfb8..0d45159d09 100644
--- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
+++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
@@ -277,6 +277,8 @@ private slots:
void compareSanity();
void compareRich();
+ void nullConvert();
+
void accessSequentialContainerKey();
private:
@@ -369,6 +371,8 @@ void tst_QVariant::copy_constructor()
QVERIFY(var8.isNull());
}
+Q_DECLARE_METATYPE(int*)
+
void tst_QVariant::isNull()
{
QVariant var;
@@ -411,6 +415,18 @@ void tst_QVariant::isNull()
QVERIFY(var9.isNull());
var9 = QVariant::fromValue<QJsonValue>(QJsonValue(QJsonValue::Null));
QVERIFY(var9.isNull());
+
+ QVariant var10(QMetaType::VoidStar, nullptr);
+ QVERIFY(var10.isNull());
+ var10 = QVariant::fromValue<void*>(nullptr);
+ QVERIFY(var10.isNull());
+
+ QVariant var11(QMetaType::QObjectStar, nullptr);
+ QVERIFY(var11.isNull());
+ var11 = QVariant::fromValue<QObject*>(nullptr);
+ QVERIFY(var11.isNull());
+
+ QVERIFY(QVariant::fromValue<int*>(nullptr).isNull());
}
void tst_QVariant::swap()
@@ -1046,6 +1062,7 @@ void tst_QVariant::toByteArray_data()
QTest::newRow( "longlong" ) << QVariant( (qlonglong)34 ) << QByteArray( "34" );
QTest::newRow( "ulonglong" ) << QVariant( (qulonglong)34 ) << QByteArray( "34" );
+ QTest::newRow( "nullptr" ) << QVariant::fromValue(nullptr) << QByteArray();
}
void tst_QVariant::toByteArray()
@@ -1055,7 +1072,13 @@ void tst_QVariant::toByteArray()
QVERIFY( value.isValid() );
QVERIFY( value.canConvert( QVariant::ByteArray ) );
QByteArray ba = value.toByteArray();
+ QCOMPARE( ba.isNull(), result.isNull() );
QCOMPARE( ba, result );
+
+ QVERIFY( value.convert( QVariant::ByteArray ) );
+ QCOMPARE( value.isNull(), result.isNull() );
+ QCOMPARE( value.toByteArray().isNull(), result.isNull() );
+ QCOMPARE( value.toByteArray(), result );
}
void tst_QVariant::toString_data()
@@ -1082,6 +1105,7 @@ void tst_QVariant::toString_data()
QString( "123456789012" );
QTest::newRow("QJsonValue") << QVariant(QJsonValue(QString("hello"))) << QString("hello");
QTest::newRow("QJsonValue(Null)") << QVariant(QJsonValue(QJsonValue::Null)) << QString();
+ QTest::newRow("nullptr") << QVariant::fromValue(nullptr) << QString();
}
void tst_QVariant::toString()
@@ -1091,7 +1115,13 @@ void tst_QVariant::toString()
QVERIFY( value.isValid() );
QVERIFY( value.canConvert( QVariant::String ) );
QString str = value.toString();
+ QCOMPARE( str.isNull(), result.isNull() );
QCOMPARE( str, result );
+
+ QVERIFY( value.convert( QVariant::String ) );
+ QCOMPARE( value.isNull(), result.isNull() );
+ QCOMPARE( value.toString().isNull(), result.isNull() );
+ QCOMPARE( value.toString(), result );
}
void tst_QVariant::toDate_data()
@@ -2648,7 +2678,7 @@ void tst_QVariant::qvariant_cast_QObject_data()
QTest::newRow("null QObject") << QVariant::fromValue<QObject*>(0) << true << true;
QTest::newRow("null derived QObject") << QVariant::fromValue<CustomQObject*>(0) << true << true;
QTest::newRow("null custom object") << QVariant::fromValue<CustomNonQObject*>(0) << false << true;
- QTest::newRow("null int") << QVariant::fromValue<int>(0) << false << true;
+ QTest::newRow("zero int") << QVariant::fromValue<int>(0) << false << false;
}
void tst_QVariant::qvariant_cast_QObject()
@@ -2666,12 +2696,14 @@ void tst_QVariant::qvariant_cast_QObject()
QVERIFY(data.canConvert(QMetaType::QObjectStar));
QVERIFY(data.canConvert(::qMetaTypeId<QObject*>()));
QCOMPARE(data.value<QObject*>() == 0, isNull);
+ QCOMPARE(data.isNull(), isNull);
QVERIFY(data.convert(QMetaType::QObjectStar));
QCOMPARE(data.userType(), int(QMetaType::QObjectStar));
} else {
QVERIFY(!data.canConvert<QObject*>());
QVERIFY(!data.canConvert(QMetaType::QObjectStar));
QVERIFY(!data.canConvert(::qMetaTypeId<QObject*>()));
+ QCOMPARE(data.isNull(), isNull);
QVERIFY(!data.value<QObject*>());
QVERIFY(!data.convert(QMetaType::QObjectStar));
QVERIFY(data.userType() != QMetaType::QObjectStar);
@@ -3403,21 +3435,6 @@ void tst_QVariant::toIntFromDouble() const
QCOMPARE(result, 2147483630);
}
-void tst_QVariant::setValue()
-{
- QJsonDocument t; //we just take a value so that we're sure that it will be shared
- QVariant v1 = QVariant::fromValue(t);
- QVERIFY( v1.isDetached() );
- QVariant v2 = v1;
- QVERIFY( !v1.isDetached() );
- QVERIFY( !v2.isDetached() );
-
- v2.setValue(3); //set an integer value
-
- QVERIFY( v1.isDetached() );
- QVERIFY( v2.isDetached() );
-}
-
void tst_QVariant::fpStringRoundtrip_data() const
{
QTest::addColumn<QVariant>("number");
@@ -3649,6 +3666,20 @@ Q_DECLARE_METATYPE(MyMovable *)
Q_DECLARE_METATYPE(MyNotMovable *)
Q_DECLARE_METATYPE(QSharedDataPointer<MyShared>)
+void tst_QVariant::setValue()
+{
+ MyNotMovable t; //we just take a value so that we're sure that it will be shared
+ QVariant v1 = QVariant::fromValue(t);
+ QVERIFY( v1.isDetached() );
+ QVariant v2 = v1;
+ QVERIFY( !v1.isDetached() );
+ QVERIFY( !v2.isDetached() );
+
+ v2.setValue(3); //set an integer value
+
+ QVERIFY( v1.isDetached() );
+ QVERIFY( v2.isDetached() );
+}
void tst_QVariant::moreCustomTypes()
{
@@ -3751,7 +3782,7 @@ void tst_QVariant::moreCustomTypes()
{
int i = 5;
PLAY_WITH_VARIANT((void *)(&i), false, QString(), 0, false);
- PLAY_WITH_VARIANT((void *)(0), false, QString(), 0, false);
+ PLAY_WITH_VARIANT((void *)(0), true, QString(), 0, false);
}
{
@@ -4862,6 +4893,33 @@ void tst_QVariant::compareRich()
<< QStringLiteral("d"));
}
+void tst_QVariant::nullConvert()
+{
+ // Test quirks with QVariants different types of null states.
+
+ // null variant with no initialized value
+ QVariant nullVar(QVariant::String);
+ QVERIFY(nullVar.isValid());
+ QVERIFY(nullVar.isNull());
+ // We can not convert a variant with no value
+ QVERIFY(!nullVar.convert(QVariant::Url));
+ QCOMPARE(nullVar.type(), QVariant::Url);
+ QVERIFY(nullVar.isNull());
+
+ // variant initialized with null value
+ QVariant nullStr = QVariant::fromValue(QString());
+ QVERIFY(nullStr.isValid());
+ QVERIFY(nullStr.isNull());
+ // We can convert an initialized null value however
+ QVERIFY(nullStr.convert(QVariant::Url));
+ QCOMPARE(nullStr.type(), QVariant::Url);
+ QVERIFY(nullStr.isValid());
+ // QUrl does not have an isNull method
+ QVERIFY(!nullStr.isNull());
+ // The URL is not valid however
+ QVERIFY(!nullStr.toUrl().isValid());
+}
+
void tst_QVariant::accessSequentialContainerKey()
{
QString nameResult;
@@ -4886,6 +4944,5 @@ void tst_QVariant::accessSequentialContainerKey()
QCOMPARE(nameResult, QStringLiteral("Seven"));
}
-
QTEST_MAIN(tst_QVariant)
#include "tst_qvariant.moc"
diff --git a/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp b/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp
index 3221587300..76efa008f7 100644
--- a/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp
+++ b/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp
@@ -29,8 +29,13 @@
#include <QtTest/QtTest>
#include <qwineventnotifier.h>
#include <qtimer.h>
+#include <qvarlengtharray.h>
+#include <qvector.h>
#include <qt_windows.h>
+#include <algorithm>
+#include <memory>
+
class tst_QWinEventNotifier : public QObject
{
Q_OBJECT
@@ -39,7 +44,11 @@ protected slots:
void simple_activated();
void simple_timerSet();
private slots:
+ void simple_data();
void simple();
+ void manyNotifiers();
+ void disableNotifiersInActivatedSlot_data();
+ void disableNotifiersInActivatedSlot();
private:
HANDLE simpleHEvent;
@@ -58,9 +67,17 @@ void tst_QWinEventNotifier::simple_timerSet()
SetEvent((HANDLE)simpleHEvent);
}
+void tst_QWinEventNotifier::simple_data()
+{
+ QTest::addColumn<bool>("resetManually");
+ QTest::newRow("manual_reset") << true;
+ QTest::newRow("auto_reset") << false;
+}
+
void tst_QWinEventNotifier::simple()
{
- simpleHEvent = CreateEvent(0, true, false, 0);
+ QFETCH(bool, resetManually);
+ simpleHEvent = CreateEvent(0, resetManually, false, 0);
QVERIFY(simpleHEvent);
QWinEventNotifier n(simpleHEvent);
@@ -87,6 +104,142 @@ void tst_QWinEventNotifier::simple()
QVERIFY(simpleActivated);
}
+class EventWithNotifier : public QObject
+{
+ Q_OBJECT
+public:
+ EventWithNotifier()
+ {
+ connect(&notifier, &QWinEventNotifier::activated,
+ this, &EventWithNotifier::onNotifierActivated);
+ notifier.setHandle(CreateEvent(0, TRUE, FALSE, 0));
+ notifier.setEnabled(true);
+ }
+
+ ~EventWithNotifier()
+ {
+ notifier.setEnabled(false);
+ CloseHandle(notifier.handle());
+ }
+
+ HANDLE eventHandle() const { return notifier.handle(); }
+ int numberOfTimesActivated() const { return activatedCount; }
+ void setEnabled(bool b) { notifier.setEnabled(b); }
+
+signals:
+ void activated();
+
+public slots:
+ void onNotifierActivated()
+ {
+ ResetEvent(notifier.handle());
+ activatedCount++;
+ emit activated();
+ }
+
+private:
+ QWinEventNotifier notifier;
+ int activatedCount = 0;
+};
+
+void tst_QWinEventNotifier::manyNotifiers()
+{
+ const size_t maxEvents = 100;
+ const size_t middleEvenEvent = maxEvents / 2;
+ Q_ASSERT(middleEvenEvent % 2 == 0);
+ using EventWithNotifierPtr = std::unique_ptr<EventWithNotifier>;
+ std::vector<EventWithNotifierPtr> events(maxEvents);
+ std::generate(events.begin(), events.end(), [] () {
+ return EventWithNotifierPtr(new EventWithNotifier);
+ });
+
+ QTestEventLoop loop;
+ auto connection = connect(events.at(8).get(), &EventWithNotifier::activated, &loop, &QTestEventLoop::exitLoop);
+ for (const auto &ewn : events) {
+ connect(ewn.get(), &EventWithNotifier::activated, [&events, &loop] () {
+ if (std::all_of(events.cbegin(), events.cend(),
+ [] (const EventWithNotifierPtr &ewn) {
+ return ewn->numberOfTimesActivated() > 0; })) {
+ loop.exitLoop();
+ }
+ });
+ }
+
+ // Activate all even events before running the event loop.
+ for (size_t i = 0; i < events.size(); i += 2)
+ SetEvent(events.at(i)->eventHandle());
+
+ // Wait until event notifier with index 8 has been activated.
+ loop.enterLoop(30);
+ QObject::disconnect(connection);
+
+ // Activate all odd events after the event loop has run for a bit.
+ for (size_t i = 1; i < events.size(); i += 2)
+ SetEvent(events.at(i)->eventHandle());
+
+ // Wait until all event notifiers have fired.
+ loop.enterLoop(30);
+
+ // All notifiers must have been activated exactly once.
+ QVERIFY(std::all_of(events.cbegin(), events.cend(), [] (const EventWithNotifierPtr &ewn) {
+ return ewn->numberOfTimesActivated() == 1;
+ }));
+}
+
+using Indices = QVector<int>;
+
+void tst_QWinEventNotifier::disableNotifiersInActivatedSlot_data()
+{
+ QTest::addColumn<int>("count");
+ QTest::addColumn<Indices>("notifiersToSignal");
+ QTest::addColumn<Indices>("notifiersToDisable");
+ QTest::addColumn<bool>("deleteNotifiers");
+ QTest::newRow("disable_signaled") << 3 << Indices{1} << Indices{1} << false;
+ QTest::newRow("disable_signaled2") << 3 << Indices{1, 2} << Indices{1} << false;
+ QTest::newRow("disable_before_signaled") << 3 << Indices{1} << Indices{0, 1} << false;
+ QTest::newRow("disable_after_signaled") << 3 << Indices{1} << Indices{1, 2} << false;
+ QTest::newRow("delete_signaled") << 3 << Indices{1} << Indices{1} << true;
+ QTest::newRow("delete_before_signaled1") << 3 << Indices{1} << Indices{0} << true;
+ QTest::newRow("delete_before_signaled2") << 3 << Indices{1} << Indices{0, 1} << true;
+ QTest::newRow("delete_before_signaled3") << 4 << Indices{3, 1} << Indices{0, 1} << true;
+ QTest::newRow("delete_after_signaled1") << 3 << Indices{1} << Indices{1, 2} << true;
+ QTest::newRow("delete_after_signaled2") << 4 << Indices{1, 3} << Indices{1, 2} << true;
+ QTest::newRow("delete_after_signaled3") << 5 << Indices{1} << Indices{1, 4} << true;
+}
+
+void tst_QWinEventNotifier::disableNotifiersInActivatedSlot()
+{
+ QFETCH(int, count);
+ QFETCH(Indices, notifiersToSignal);
+ QFETCH(Indices, notifiersToDisable);
+ QFETCH(bool, deleteNotifiers);
+
+ QVarLengthArray<std::unique_ptr<EventWithNotifier>, 10> events(count);
+ for (int i = 0; i < count; ++i)
+ events[i].reset(new EventWithNotifier);
+
+ auto isActivatedOrNull = [&events](int i) {
+ return !events.at(i) || events.at(i)->numberOfTimesActivated() > 0;
+ };
+
+ for (auto &e : events) {
+ connect(e.get(), &EventWithNotifier::activated, [&]() {
+ for (int i : notifiersToDisable) {
+ if (deleteNotifiers)
+ events[i].reset();
+ else
+ events.at(i)->setEnabled(false);
+ }
+ if (std::all_of(notifiersToSignal.begin(), notifiersToSignal.end(), isActivatedOrNull))
+ QTimer::singleShot(0, &QTestEventLoop::instance(), SLOT(exitLoop()));
+ });
+ }
+ for (int i : notifiersToSignal)
+ SetEvent(events.at(i)->eventHandle());
+ QTestEventLoop::instance().enterLoop(30);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+}
+
QTEST_MAIN(tst_QWinEventNotifier)
#include "tst_qwineventnotifier.moc"
diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp
index 8a46bc1c55..8883b6360f 100644
--- a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp
+++ b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp
@@ -115,7 +115,6 @@ Q_CONSTRUCTOR_FUNCTION(initializeLang)
static QString seedAndTemplate()
{
- qsrand(QDateTime::currentSecsSinceEpoch());
return QDir::tempPath() + "/tst_qmimedatabase-XXXXXX";
}
diff --git a/tests/auto/corelib/mimetypes/qmimetype/tst_qmimetype.cpp b/tests/auto/corelib/mimetypes/qmimetype/tst_qmimetype.cpp
index d93603f641..c74bce3b5b 100644
--- a/tests/auto/corelib/mimetypes/qmimetype/tst_qmimetype.cpp
+++ b/tests/auto/corelib/mimetypes/qmimetype/tst_qmimetype.cpp
@@ -46,6 +46,7 @@ private slots:
void genericIconName();
void iconName();
void suffixes();
+ void gadget();
};
// ------------------------------------------------------------------------------------------------
@@ -201,5 +202,44 @@ void tst_qmimetype::suffixes()
// ------------------------------------------------------------------------------------------------
+void tst_qmimetype::gadget()
+{
+ QMimeType instantiatedQMimeType (
+ buildQMimeType (
+ qMimeTypeName(),
+ qMimeTypeGenericIconName(),
+ qMimeTypeIconName(),
+ qMimeTypeGlobPatterns()
+ )
+ );
+
+ const QMetaObject *metaObject = &instantiatedQMimeType.staticMetaObject;
+
+ QCOMPARE(metaObject->className(), "QMimeType");
+ QVariantMap properties;
+ for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); i++) {
+ QMetaProperty property = metaObject->property(i);
+ properties[property.name()] = property.readOnGadget(&instantiatedQMimeType);
+ }
+
+ QCOMPARE(properties["valid"].toBool(), instantiatedQMimeType.isValid());
+ QCOMPARE(properties["isDefault"].toBool(), instantiatedQMimeType.isDefault());
+ QCOMPARE(properties["name"].toString(), instantiatedQMimeType.name());
+ QCOMPARE(properties["comment"].toString(), instantiatedQMimeType.comment());
+ QCOMPARE(properties["genericIconName"].toString(), instantiatedQMimeType.genericIconName());
+ QCOMPARE(properties["iconName"].toString(), instantiatedQMimeType.iconName());
+ QCOMPARE(properties["globPatterns"].toStringList(), instantiatedQMimeType.globPatterns());
+ QCOMPARE(properties["parentMimeTypes"].toStringList(), instantiatedQMimeType.parentMimeTypes());
+ QCOMPARE(properties["allAncestors"].toStringList(), instantiatedQMimeType.allAncestors());
+ QCOMPARE(properties["aliases"].toStringList(), instantiatedQMimeType.aliases());
+ QCOMPARE(properties["suffixes"].toStringList(), instantiatedQMimeType.suffixes());
+ QCOMPARE(properties["preferredSuffix"].toString(), instantiatedQMimeType.preferredSuffix());
+ QCOMPARE(properties["filterString"].toString(), instantiatedQMimeType.filterString());
+
+ QVERIFY(metaObject->indexOfMethod("inherits(QString)") >= 0);
+}
+
+// ------------------------------------------------------------------------------------------------
+
QTEST_GUILESS_MAIN(tst_qmimetype)
#include "tst_qmimetype.moc"
diff --git a/tests/auto/corelib/plugin/quuid/testProcessUniqueness/main.cpp b/tests/auto/corelib/plugin/quuid/testProcessUniqueness/main.cpp
index daf9b1579a..d1e138d8eb 100644
--- a/tests/auto/corelib/plugin/quuid/testProcessUniqueness/main.cpp
+++ b/tests/auto/corelib/plugin/quuid/testProcessUniqueness/main.cpp
@@ -35,9 +35,6 @@ int main(int argc, char **argv)
Q_UNUSED(argc)
Q_UNUSED(argv)
- // First, break QUuid.
- qrand();
-
// Now print a few uuids.
printf("%s", qPrintable(QUuid::createUuid().toString()));
printf("%s", qPrintable(QUuid::createUuid().toString()));
diff --git a/tests/auto/corelib/plugin/quuid/tst_quuid.cpp b/tests/auto/corelib/plugin/quuid/tst_quuid.cpp
index d3102c7ee5..9e3edc96ec 100644
--- a/tests/auto/corelib/plugin/quuid/tst_quuid.cpp
+++ b/tests/auto/corelib/plugin/quuid/tst_quuid.cpp
@@ -41,6 +41,7 @@ private slots:
void fromChar();
void toString();
+ void fromString_data();
void fromString();
void toByteArray();
void fromByteArray();
@@ -127,15 +128,58 @@ void tst_QUuid::toString()
QCOMPARE(uuidB.toString(), QString("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}"));
}
+void tst_QUuid::fromString_data()
+{
+ QTest::addColumn<QUuid>("expected");
+ QTest::addColumn<QString>("input");
+
+ QUuid invalid = {};
+
+#define ROW(which, string) \
+ QTest::addRow("%-38s -> %s", string, #which) << which << string
+ ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5}");
+ ROW(uuidA, "fc69b59e-cc34-4436-a43c-ee95d128b8c5}");
+ ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5" );
+ ROW(uuidA, "fc69b59e-cc34-4436-a43c-ee95d128b8c5" );
+
+ ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c56"); // too long (not an error!)
+ ROW(invalid, "{fc69b59e-cc34-4436-a43c-ee95d128b8c" ); // premature end (within length limits)
+ ROW(invalid, " fc69b59e-cc34-4436-a43c-ee95d128b8c5}"); // leading space
+ ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5 "); // trailing space (not an error!)
+ ROW(invalid, "{gc69b59e-cc34-4436-a43c-ee95d128b8c5}"); // non-hex digit in 1st group
+ ROW(invalid, "{fc69b59e-cp34-4436-a43c-ee95d128b8c5}"); // non-hex digit in 2nd group
+ ROW(invalid, "{fc69b59e-cc34-44r6-a43c-ee95d128b8c5}"); // non-hex digit in 3rd group
+ ROW(invalid, "{fc69b59e-cc34-4436-a4yc-ee95d128b8c5}"); // non-hex digit in 4th group
+ ROW(invalid, "{fc69b59e-cc34-4436-a43c-ee95d128j8c5}"); // non-hex digit in last group
+ ROW(invalid, "(fc69b59e-cc34-4436-a43c-ee95d128b8c5}"); // wrong initial character
+ ROW(invalid, "{fc69b59e+cc34-4436-a43c-ee95d128b8c5}"); // wrong 1st separator
+ ROW(invalid, "{fc69b59e-cc34*4436-a43c-ee95d128b8c5}"); // wrong 2nd separator
+ ROW(invalid, "{fc69b59e-cc34-44366a43c-ee95d128b8c5}"); // wrong 3rd separator
+ ROW(invalid, "{fc69b59e-cc34-4436-a43c\303\244ee95d128b8c5}"); // wrong 4th separator (&auml;)
+ ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5)"); // wrong final character (not an error!)
+
+ ROW(uuidB, "{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}");
+#undef ROW
+}
+
void tst_QUuid::fromString()
{
- QCOMPARE(uuidA, QUuid(QString("{fc69b59e-cc34-4436-a43c-ee95d128b8c5}")));
- QCOMPARE(uuidA, QUuid(QString("fc69b59e-cc34-4436-a43c-ee95d128b8c5}")));
- QCOMPARE(uuidA, QUuid(QString("{fc69b59e-cc34-4436-a43c-ee95d128b8c5")));
- QCOMPARE(uuidA, QUuid(QString("fc69b59e-cc34-4436-a43c-ee95d128b8c5")));
- QCOMPARE(QUuid(), QUuid(QString("{fc69b59e-cc34-4436-a43c-ee95d128b8c")));
+ QFETCH(const QUuid, expected);
+ QFETCH(const QString, input);
- QCOMPARE(uuidB, QUuid(QString("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}")));
+ const auto inputL1 = input.toLatin1();
+ const auto inputU8 = input.toUtf8();
+
+ QCOMPARE(expected, QUuid(input));
+ QCOMPARE(expected, QUuid(inputU8));
+ QCOMPARE(expected, QUuid(inputL1));
+
+ QCOMPARE(expected, QUuid::fromString(input));
+
+ // for QLatin1String, construct one whose data() is not NUL-terminated:
+ const auto longerInputL1 = inputL1 + '5'; // the '5' makes the premature end check incorrectly succeed
+ const auto inputL1S = QLatin1String(longerInputL1.data(), inputL1.size());
+ QCOMPARE(expected, QUuid::fromString(inputL1S));
}
void tst_QUuid::toByteArray()
diff --git a/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm b/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm
index d90bff65b3..41ccece115 100644
--- a/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm
+++ b/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm
@@ -36,7 +36,7 @@ void tst_QUuid_darwinTypes()
{
// QUuid <-> CFUUID
{
- QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef"));
+ const auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef"));
const CFUUIDRef cfuuid = qtUuid.toCFUUID();
QCOMPARE(QUuid::fromCFUUID(cfuuid), qtUuid);
CFStringRef cfstring = CFUUIDCreateString(0, cfuuid);
@@ -45,10 +45,10 @@ void tst_QUuid_darwinTypes()
CFRelease(cfuuid);
}
{
- QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef"));
+ auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef"));
const CFUUIDRef cfuuid = qtUuid.toCFUUID();
QUuid qtUuidCopy(qtUuid);
- qtUuid = QUuid(QByteArrayLiteral("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify
+ qtUuid = QUuid::fromString(QLatin1String("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify
QCOMPARE(QUuid::fromCFUUID(cfuuid), qtUuidCopy);
CFStringRef cfstring = CFUUIDCreateString(0, cfuuid);
QCOMPARE(QString::fromCFString(cfstring), qtUuidCopy.toString().mid(1, 36).toUpper());
@@ -59,7 +59,7 @@ void tst_QUuid_darwinTypes()
{
QMacAutoReleasePool pool;
- QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef"));
+ const auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef"));
const NSUUID *nsuuid = qtUuid.toNSUUID();
QCOMPARE(QUuid::fromNSUUID(nsuuid), qtUuid);
QCOMPARE(QString::fromNSString([nsuuid UUIDString]), qtUuid.toString().mid(1, 36).toUpper());
@@ -67,10 +67,10 @@ void tst_QUuid_darwinTypes()
{
QMacAutoReleasePool pool;
- QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef"));
+ auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef"));
const NSUUID *nsuuid = qtUuid.toNSUUID();
QUuid qtUuidCopy(qtUuid);
- qtUuid = QUuid(QByteArrayLiteral("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify
+ qtUuid = QUuid::fromString(QLatin1String("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify
QCOMPARE(QUuid::fromNSUUID(nsuuid), qtUuidCopy);
QCOMPARE(QString::fromNSString([nsuuid UUIDString]), qtUuidCopy.toString().mid(1, 36).toUpper());
}
diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
index a546cad225..58bebe19ac 100644
--- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
+++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
@@ -36,6 +36,7 @@
#include <qresultstore.h>
#include <qthreadpool.h>
#include <qexception.h>
+#include <qrandom.h>
#include <private/qfutureinterface_p.h>
// COM interface macro.
@@ -1205,8 +1206,6 @@ void tst_QFuture::pause()
Interface.reportFinished();
}
-const int resultCount = 1000;
-
class ResultObject : public QObject
{
Q_OBJECT
@@ -1458,7 +1457,7 @@ void tst_QFuture::nonGlobalThreadPool()
void run() Q_DECL_OVERRIDE
{
- const int ms = 100 + (qrand() % 100 - 100/2);
+ const int ms = 100 + (QRandomGenerator::global()->bounded(100) - 100/2);
QThread::msleep(ms);
reportResult(Answer);
reportFinished();
diff --git a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
index ce093f769c..a67ecc2471 100644
--- a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
+++ b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
@@ -44,6 +44,7 @@ private slots:
void tryAcquireWithTimeoutForever_data();
void tryAcquireWithTimeoutForever();
void producerConsumer();
+ void raii();
};
static QSemaphore *semaphore = 0;
@@ -480,5 +481,54 @@ void tst_QSemaphore::producerConsumer()
consumer.wait();
}
+void tst_QSemaphore::raii()
+{
+ QSemaphore sem;
+
+ QCOMPARE(sem.available(), 0);
+
+ // basic operation:
+ {
+ QSemaphoreReleaser r0;
+ const QSemaphoreReleaser r1(sem);
+ const QSemaphoreReleaser r2(sem, 2);
+
+ QCOMPARE(r0.semaphore(), nullptr);
+ QCOMPARE(r1.semaphore(), &sem);
+ QCOMPARE(r2.semaphore(), &sem);
+ }
+
+ QCOMPARE(sem.available(), 3);
+
+ // cancel:
+ {
+ const QSemaphoreReleaser r1(sem);
+ QSemaphoreReleaser r2(sem, 2);
+
+ QCOMPARE(r2.cancel(), &sem);
+ QCOMPARE(r2.semaphore(), nullptr);
+ }
+
+ QCOMPARE(sem.available(), 4);
+
+ // move-assignment:
+ {
+ const QSemaphoreReleaser r1(sem);
+ QSemaphoreReleaser r2(sem, 2);
+
+ QCOMPARE(sem.available(), 4);
+
+ r2 = QSemaphoreReleaser();
+
+ QCOMPARE(sem.available(), 6);
+
+ r2 = QSemaphoreReleaser(sem, 42);
+
+ QCOMPARE(sem.available(), 6);
+ }
+
+ QCOMPARE(sem.available(), 49);
+}
+
QTEST_MAIN(tst_QSemaphore)
#include "tst_qsemaphore.moc"
diff --git a/tests/auto/corelib/thread/qthread/qthread.pro b/tests/auto/corelib/thread/qthread/qthread.pro
index e0ef506d2c..37552f1fca 100644
--- a/tests/auto/corelib/thread/qthread/qthread.pro
+++ b/tests/auto/corelib/thread/qthread/qthread.pro
@@ -2,6 +2,8 @@ CONFIG += testcase
TARGET = tst_qthread
QT = core testlib
SOURCES = tst_qthread.cpp
+qtConfig(c++14):CONFIG += c++14
+qtConfig(c++1z):CONFIG += c++1z
INCLUDEPATH += ../../../../shared/
HEADERS += ../../../../shared/emulationdetector.h
diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
index 5c8522a313..0405896ca7 100644
--- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp
+++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
@@ -47,6 +47,10 @@
#endif
#endif
+#ifndef QT_NO_EXCEPTIONS
+#include <exception>
+#endif
+
#include "emulationdetector.h"
class tst_QThread : public QObject
@@ -100,6 +104,8 @@ private slots:
void stressTest();
void quitLock();
+
+ void create();
};
enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute };
@@ -1330,6 +1336,259 @@ void tst_QThread::quitLock()
QVERIFY(exitThreadCalled);
}
+void tst_QThread::create()
+{
+#if !QT_CONFIG(cxx11_future)
+ QSKIP("This test requires QThread::create");
+#else
+ {
+ const auto &function = [](){};
+ QScopedPointer<QThread> thread(QThread::create(function));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ thread->start();
+ QVERIFY(thread->wait());
+ }
+
+ {
+ // no side effects before starting
+ int i = 0;
+ const auto &function = [&i]() { i = 42; };
+ QScopedPointer<QThread> thread(QThread::create(function));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ QCOMPARE(i, 0);
+ thread->start();
+ QVERIFY(thread->wait());
+ QCOMPARE(i, 42);
+ }
+
+ {
+ // control thread progress
+ QSemaphore semaphore1;
+ QSemaphore semaphore2;
+
+ const auto &function = [&semaphore1, &semaphore2]() -> void
+ {
+ semaphore1.acquire();
+ semaphore2.release();
+ };
+
+ QScopedPointer<QThread> thread(QThread::create(function));
+
+ QVERIFY(thread);
+ thread->start();
+ QTRY_VERIFY(thread->isRunning());
+ semaphore1.release();
+ semaphore2.acquire();
+ QVERIFY(thread->wait());
+ QVERIFY(!thread->isRunning());
+ }
+
+ {
+ // ignore return values
+ const auto &function = []() { return 42; };
+ QScopedPointer<QThread> thread(QThread::create(function));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ thread->start();
+ QVERIFY(thread->wait());
+ }
+
+ {
+ // return value of create
+ QScopedPointer<QThread> thread;
+ QSemaphore s;
+ const auto &function = [&thread, &s]() -> void
+ {
+ s.acquire();
+ QCOMPARE(thread.data(), QThread::currentThread());
+ };
+
+ thread.reset(QThread::create(function));
+ QVERIFY(thread);
+ thread->start();
+ QTRY_VERIFY(thread->isRunning());
+ s.release();
+ QVERIFY(thread->wait());
+ }
+
+ {
+ // move-only parameters
+ struct MoveOnlyValue {
+ explicit MoveOnlyValue(int v) : v(v) {}
+ ~MoveOnlyValue() = default;
+ MoveOnlyValue(const MoveOnlyValue &) = delete;
+ MoveOnlyValue(MoveOnlyValue &&) = default;
+ MoveOnlyValue &operator=(const MoveOnlyValue &) = delete;
+ MoveOnlyValue &operator=(MoveOnlyValue &&) = default;
+ int v;
+ };
+
+ struct MoveOnlyFunctor {
+ explicit MoveOnlyFunctor(int *i) : i(i) {}
+ ~MoveOnlyFunctor() = default;
+ MoveOnlyFunctor(const MoveOnlyFunctor &) = delete;
+ MoveOnlyFunctor(MoveOnlyFunctor &&) = default;
+ MoveOnlyFunctor &operator=(const MoveOnlyFunctor &) = delete;
+ MoveOnlyFunctor &operator=(MoveOnlyFunctor &&) = default;
+ int operator()() { return (*i = 42); }
+ int *i;
+ };
+
+ {
+ int i = 0;
+ MoveOnlyFunctor f(&i);
+ QScopedPointer<QThread> thread(QThread::create(std::move(f)));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ thread->start();
+ QVERIFY(thread->wait());
+ QCOMPARE(i, 42);
+ }
+
+#if defined(__cpp_init_captures) && __cpp_init_captures >= 201304
+ {
+ int i = 0;
+ MoveOnlyValue mo(123);
+ auto moveOnlyFunction = [&i, mo = std::move(mo)]() { i = mo.v; };
+ QScopedPointer<QThread> thread(QThread::create(std::move(moveOnlyFunction)));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ thread->start();
+ QVERIFY(thread->wait());
+ QCOMPARE(i, 123);
+ }
+#endif // __cpp_init_captures
+
+#ifdef QTHREAD_HAS_VARIADIC_CREATE
+ {
+ int i = 0;
+ const auto &function = [&i](MoveOnlyValue &&mo) { i = mo.v; };
+ QScopedPointer<QThread> thread(QThread::create(function, MoveOnlyValue(123)));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ thread->start();
+ QVERIFY(thread->wait());
+ QCOMPARE(i, 123);
+ }
+
+ {
+ int i = 0;
+ const auto &function = [&i](MoveOnlyValue &&mo) { i = mo.v; };
+ MoveOnlyValue mo(-1);
+ QScopedPointer<QThread> thread(QThread::create(function, std::move(mo)));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ thread->start();
+ QVERIFY(thread->wait());
+ QCOMPARE(i, -1);
+ }
+#endif // QTHREAD_HAS_VARIADIC_CREATE
+ }
+
+#ifdef QTHREAD_HAS_VARIADIC_CREATE
+ {
+ // simple parameter passing
+ int i = 0;
+ const auto &function = [&i](int j, int k) { i = j * k; };
+ QScopedPointer<QThread> thread(QThread::create(function, 3, 4));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ QCOMPARE(i, 0);
+ thread->start();
+ QVERIFY(thread->wait());
+ QCOMPARE(i, 12);
+ }
+
+ {
+ // ignore return values (with parameters)
+ const auto &function = [](double d) { return d * 2.0; };
+ QScopedPointer<QThread> thread(QThread::create(function, 3.14));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ thread->start();
+ QVERIFY(thread->wait());
+ }
+
+ {
+ // handling of pointers to member functions, std::ref, etc.
+ struct S {
+ S() : v(0) {}
+ void doSomething() { ++v; }
+ int v;
+ };
+
+ S object;
+
+ QCOMPARE(object.v, 0);
+
+ QScopedPointer<QThread> thread;
+ thread.reset(QThread::create(&S::doSomething, object));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ thread->start();
+ QVERIFY(thread->wait());
+
+ QCOMPARE(object.v, 0); // a copy was passed, this should still be 0
+
+ thread.reset(QThread::create(&S::doSomething, std::ref(object)));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ thread->start();
+ QVERIFY(thread->wait());
+
+ QCOMPARE(object.v, 1);
+
+ thread.reset(QThread::create(&S::doSomething, &object));
+ QVERIFY(thread);
+ QVERIFY(!thread->isRunning());
+ thread->start();
+ QVERIFY(thread->wait());
+
+ QCOMPARE(object.v, 2);
+ }
+
+ {
+ // std::ref into ordinary reference
+ int i = 42;
+ const auto &function = [](int &i) { i *= 2; };
+ QScopedPointer<QThread> thread(QThread::create(function, std::ref(i)));
+ QVERIFY(thread);
+ thread->start();
+ QVERIFY(thread->wait());
+ QCOMPARE(i, 84);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ {
+ // exceptions when copying/decaying the arguments are thrown at build side and won't terminate
+ class ThreadException : public std::exception
+ {
+ };
+
+ struct ThrowWhenCopying
+ {
+ ThrowWhenCopying() = default;
+ ThrowWhenCopying(const ThrowWhenCopying &)
+ {
+ throw ThreadException();
+ }
+ ~ThrowWhenCopying() = default;
+ ThrowWhenCopying &operator=(const ThrowWhenCopying &) = default;
+ };
+
+ const auto &function = [](const ThrowWhenCopying &){};
+ QScopedPointer<QThread> thread;
+ ThrowWhenCopying t;
+ QVERIFY_EXCEPTION_THROWN(thread.reset(QThread::create(function, t)), ThreadException);
+ QVERIFY(!thread);
+ }
+#endif // QT_NO_EXCEPTIONS
+#endif // QTHREAD_HAS_VARIADIC_CREATE
+#endif // QT_CONFIG(cxx11_future)
+}
+
class StopableJob : public QObject
{
Q_OBJECT
diff --git a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
index 0eaf8a4b77..1092216fb7 100644
--- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
+++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
@@ -92,6 +92,7 @@ private slots:
void tryTake();
void waitForDoneTimeout();
void destroyingWaitsForTasksToFinish();
+ void stackSize();
void stressTest();
void takeAllAndIncreaseMaxThreadCount();
void waitForDoneAfterTake();
@@ -963,21 +964,6 @@ void tst_QThreadPool::cancel()
QSemaphore sem(0);
QSemaphore startedThreads(0);
- class SemaphoreReleaser
- {
- QSemaphore &sem;
- int n;
- Q_DISABLE_COPY(SemaphoreReleaser)
- public:
- explicit SemaphoreReleaser(QSemaphore &sem, int n)
- : sem(sem), n(n) {}
-
- ~SemaphoreReleaser()
- {
- sem.release(n);
- }
- };
-
class BlockingRunnable : public QRunnable
{
public:
@@ -1016,7 +1002,7 @@ void tst_QThreadPool::cancel()
// ensure that the QThreadPool doesn't deadlock if any of the checks fail
// and cause an early return:
- const SemaphoreReleaser semReleaser(sem, runs);
+ const QSemaphoreReleaser semReleaser(sem, runs);
count.store(0);
QAtomicInt dtorCounter = 0;
@@ -1050,21 +1036,6 @@ void tst_QThreadPool::tryTake()
QSemaphore sem(0);
QSemaphore startedThreads(0);
- class SemaphoreReleaser
- {
- QSemaphore &sem;
- int n;
- Q_DISABLE_COPY(SemaphoreReleaser)
- public:
- explicit SemaphoreReleaser(QSemaphore &sem, int n)
- : sem(sem), n(n) {}
-
- ~SemaphoreReleaser()
- {
- sem.release(n);
- }
- };
-
class BlockingRunnable : public QRunnable
{
public:
@@ -1103,7 +1074,7 @@ void tst_QThreadPool::tryTake()
// ensure that the QThreadPool doesn't deadlock if any of the checks fail
// and cause an early return:
- const SemaphoreReleaser semReleaser(sem, Runs);
+ const QSemaphoreReleaser semReleaser(sem, Runs);
count.store(0);
QAtomicInt dtorCounter = 0;
@@ -1168,6 +1139,39 @@ void tst_QThreadPool::destroyingWaitsForTasksToFinish()
}
}
+// Verify that QThreadPool::stackSize is used when creating
+// new threads. Note that this tests the Qt property only
+// since QThread::stackSize() does not reflect the actual
+// stack size used by the native thread.
+void tst_QThreadPool::stackSize()
+{
+ uint targetStackSize = 512 * 1024;
+ uint threadStackSize = 1; // impossible value
+
+ class StackSizeChecker : public QRunnable
+ {
+ public:
+ uint *stackSize;
+
+ StackSizeChecker(uint *stackSize)
+ :stackSize(stackSize)
+ {
+
+ }
+
+ void run()
+ {
+ *stackSize = QThread::currentThread()->stackSize();
+ }
+ };
+
+ QThreadPool threadPool;
+ threadPool.setStackSize(targetStackSize);
+ threadPool.start(new StackSizeChecker(&threadStackSize));
+ QVERIFY(threadPool.waitForDone(30000)); // 30s timeout
+ QCOMPARE(threadStackSize, targetStackSize);
+}
+
void tst_QThreadPool::stressTest()
{
class Task : public QRunnable
diff --git a/tests/auto/corelib/tools/containerapisymmetry/.gitignore b/tests/auto/corelib/tools/containerapisymmetry/.gitignore
new file mode 100644
index 0000000000..172ca970f2
--- /dev/null
+++ b/tests/auto/corelib/tools/containerapisymmetry/.gitignore
@@ -0,0 +1 @@
+tst_containerapisymmetry
diff --git a/tests/auto/corelib/tools/containerapisymmetry/containerapisymmetry.pro b/tests/auto/corelib/tools/containerapisymmetry/containerapisymmetry.pro
new file mode 100644
index 0000000000..30dc8026ef
--- /dev/null
+++ b/tests/auto/corelib/tools/containerapisymmetry/containerapisymmetry.pro
@@ -0,0 +1,7 @@
+CONFIG += testcase
+TARGET = tst_containerapisymmetry
+SOURCES += tst_containerapisymmetry.cpp
+QT = core testlib
+
+# This test does not work with strict iterators
+DEFINES -= QT_STRICT_ITERATORS
diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
new file mode 100644
index 0000000000..3b8111f1a3
--- /dev/null
+++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include "qbytearray.h"
+#include "qlinkedlist.h"
+#include "qlist.h"
+#include "qstring.h"
+#include "qvarlengtharray.h"
+#include "qvector.h"
+
+#include <vector> // for reference
+
+class tst_ContainerApiSymmetry : public QObject
+{
+ Q_OBJECT
+
+private:
+ template <typename Container>
+ void front_back_impl() const;
+
+private Q_SLOTS:
+ void front_back_std_vector() { front_back_impl<std::vector<int>>(); }
+ void front_back_QVector() { front_back_impl<QVector<int>>(); }
+ void front_back_QList() { front_back_impl<QList<qintptr>>(); }
+ void front_back_QLinkedList() { front_back_impl<QLinkedList<int>>(); }
+ void front_back_QVarLengthArray() { front_back_impl<QVarLengthArray<int>>(); }
+ void front_back_QString() { front_back_impl<QString>(); }
+ void front_back_QStringRef() { front_back_impl<QStringRef>(); }
+ void front_back_QStringView() { front_back_impl<QStringView>(); }
+ void front_back_QLatin1String() { front_back_impl<QLatin1String>(); }
+ void front_back_QByteArray() { front_back_impl<QByteArray>(); }
+};
+
+template <typename Container>
+Container make(int size)
+{
+ Container c;
+ int i = 1;
+ while (size--)
+ c.push_back(typename Container::value_type(i++));
+ return c;
+}
+
+static QString s_string = QStringLiteral("\1\2\3\4\5\6\7");
+
+template <> QStringRef make(int size) { return s_string.leftRef(size); }
+template <> QStringView make(int size) { return QStringView(s_string).left(size); }
+template <> QLatin1String make(int size) { return QLatin1String("\1\2\3\4\5\6\7", size); }
+
+template <typename T> T clean(T &&t) { return std::forward<T>(t); }
+inline QChar clean(QCharRef ch) { return ch; }
+inline char clean(QByteRef ch) { return ch; }
+inline char clean(QLatin1Char ch) { return ch.toLatin1(); }
+
+template <typename Container>
+void tst_ContainerApiSymmetry::front_back_impl() const
+{
+ using V = typename Container::value_type;
+ auto c1 = make<Container>(1);
+ QCOMPARE(clean(c1.front()), V(1));
+ QCOMPARE(clean(c1.back()), V(1));
+ QCOMPARE(clean(qAsConst(c1).front()), V(1));
+ QCOMPARE(clean(qAsConst(c1).back()), V(1));
+
+ auto c2 = make<Container>(2);
+ QCOMPARE(clean(c2.front()), V(1));
+ QCOMPARE(clean(c2.back()), V(2));
+ QCOMPARE(clean(qAsConst(c2).front()), V(1));
+ QCOMPARE(clean(qAsConst(c2).back()), V(2));
+}
+
+QTEST_APPLESS_MAIN(tst_ContainerApiSymmetry)
+#include "tst_containerapisymmetry.moc"
diff --git a/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp b/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp
index e13c2894af..72299402f0 100644
--- a/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp
+++ b/tests/auto/corelib/tools/qalgorithms/tst_qalgorithms.cpp
@@ -37,6 +37,7 @@
#include <qalgorithms.h>
#include <QStringList>
#include <QString>
+#include <QRandomGenerator>
#include <QVector>
#define Q_TEST_PERFORMANCE 0
@@ -133,7 +134,7 @@ QVector<DataType> generateData(QString dataSetType, const int length)
QVector<DataType> container;
if (dataSetType == "Random") {
for (int i = 0; i < length; ++i)
- container.append(rand());
+ container.append(QRandomGenerator::global()->generate());
} else if (dataSetType == "Ascending") {
for (int i = 0; i < length; ++i)
container.append(i);
@@ -1082,12 +1083,12 @@ void tst_QAlgorithms::popCount_data_impl(size_t sizeof_T_Int)
// 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());
+ const quint64 input = QRandomGenerator::global()->generate64();
QTest::addRow("0x%016llx", input) << input << bitsSetInInt64(input);
}
else if (sizeof_T_Int >= 2)
for (size_t i = 0; i < 1000 ; ++i) {
- const quint32 input = qrand();
+ const quint32 input = QRandomGenerator::global()->generate();
if (sizeof_T_Int >= 4)
QTest::addRow("0x%08x", input) << quint64(input) << bitsSetInInt(input);
else
@@ -1129,7 +1130,7 @@ void tst_QAlgorithms::countTrailing_data_impl(size_t sizeof_T_Int)
// and some random ones:
for (uint i = 0; i < sizeof_T_Int*8; ++i) {
for (uint j = 0; j < sizeof_T_Int*3; ++j) { // 3 is arbitrary
- const quint64 r = quint64(qrand()) << 32 | quint32(qrand());
+ const quint64 r = QRandomGenerator::global()->generate64();
const quint64 b = Q_UINT64_C(1) << i;
const quint64 mask = ((~(b-1)) ^ b) & type_mask;
const quint64 input = (r&mask) | b;
@@ -1166,7 +1167,7 @@ void tst_QAlgorithms::countLeading_data_impl(size_t sizeof_T_Int)
// and some random ones:
for (uint i = 0; i < sizeof_T_Int*8; ++i) {
for (uint j = 0; j < sizeof_T_Int*3; ++j) { // 3 is arbitrary
- const quint64 r = quint64(qrand()) << 32 | quint32(qrand());
+ const quint64 r = QRandomGenerator::global()->generate64();
const quint64 b = Q_UINT64_C(1) << i;
const quint64 mask = b-1;
const quint64 input = (r&mask) | b;
diff --git a/tests/auto/corelib/tools/qchar/tst_qchar.cpp b/tests/auto/corelib/tools/qchar/tst_qchar.cpp
index e5a6e953d3..cf4f6d21e2 100644
--- a/tests/auto/corelib/tools/qchar/tst_qchar.cpp
+++ b/tests/auto/corelib/tools/qchar/tst_qchar.cpp
@@ -36,6 +36,8 @@ class tst_QChar : public QObject
{
Q_OBJECT
private slots:
+ void fromChar16_t();
+ void fromWchar_t();
void operator_eqeq_null();
void operators_data();
void operators();
@@ -72,6 +74,33 @@ private slots:
void unicodeVersion();
};
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+
+void tst_QChar::fromChar16_t()
+{
+#if defined(Q_COMPILER_UNICODE_STRINGS)
+ QChar aUmlaut = u'\u00E4'; // German small letter a-umlaut
+ QCOMPARE(aUmlaut, QChar(0xE4));
+ QChar replacementCharacter = u'\uFFFD';
+ QCOMPARE(replacementCharacter, QChar(QChar::ReplacementCharacter));
+#else
+ QSKIP("This test requires C++11 char16_t support enabled in the compiler.");
+#endif
+}
+
+void tst_QChar::fromWchar_t()
+{
+#if defined(Q_OS_WIN)
+ QChar aUmlaut = L'\u00E4'; // German small letter a-umlaut
+ QCOMPARE(aUmlaut, QChar(0xE4));
+ QChar replacementCharacter = L'\uFFFD';
+ QCOMPARE(replacementCharacter, QChar(QChar::ReplacementCharacter));
+#else
+ QSKIP("This is a Windows-only test.");
+#endif
+}
+
void tst_QChar::operator_eqeq_null()
{
{
diff --git a/tests/auto/corelib/tools/qdate/tst_qdate.cpp b/tests/auto/corelib/tools/qdate/tst_qdate.cpp
index f88eac1a9f..ff31f01d7c 100644
--- a/tests/auto/corelib/tools/qdate/tst_qdate.cpp
+++ b/tests/auto/corelib/tools/qdate/tst_qdate.cpp
@@ -1490,7 +1490,7 @@ void tst_QDate::roundtrip() const
void tst_QDate::qdebug() const
{
- QTest::ignoreMessage(QtDebugMsg, "QDate(\"\")");
+ QTest::ignoreMessage(QtDebugMsg, "QDate(Invalid)");
qDebug() << QDate();
QTest::ignoreMessage(QtDebugMsg, "QDate(\"1983-08-07\")");
qDebug() << QDate(1983, 8, 7);
diff --git a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp
index 91b5966fa8..d2ffe50ce8 100644
--- a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp
+++ b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp
@@ -131,6 +131,8 @@ private slots:
void setOffsetFromUtc();
void toOffsetFromUtc();
+ void zoneAtTime_data();
+ void zoneAtTime();
void timeZoneAbbreviation();
void getDate();
@@ -1378,11 +1380,13 @@ void tst_QDateTime::toTimeSpec()
QCOMPARE(utcToLocal.date(), fromLocal.date());
QCOMPARE(utcToLocal.time(), fromLocal.time());
QCOMPARE(utcToLocal.timeSpec(), Qt::LocalTime);
+ QCOMPARE(utcToLocal.toTimeSpec(Qt::UTC), fromUtc);
QCOMPARE(localToUtc, fromUtc);
QCOMPARE(localToUtc.date(), fromUtc.date());
QCOMPARE(localToUtc.time(), fromUtc.time());
QCOMPARE(localToUtc.timeSpec(), Qt::UTC);
+ QCOMPARE(localToUtc.toTimeSpec(Qt::LocalTime), fromLocal);
QCOMPARE(utcToUtc, localToUtc);
QCOMPARE(utcToUtc.date(), localToUtc.date());
@@ -1399,11 +1403,13 @@ void tst_QDateTime::toTimeSpec()
QCOMPARE(utcToOffset.date(), fromUtc.date());
QCOMPARE(utcToOffset.time(), fromUtc.time());
QCOMPARE(utcToOffset.timeSpec(), Qt::UTC);
+ QCOMPARE(utcToOffset.toTimeSpec(Qt::UTC), fromUtc);
QCOMPARE(localToOffset, fromUtc);
QCOMPARE(localToOffset.date(), fromUtc.date());
QCOMPARE(localToOffset.time(), fromUtc.time());
QCOMPARE(localToOffset.timeSpec(), Qt::UTC);
+ QCOMPARE(localToOffset.toTimeSpec(Qt::LocalTime), fromLocal);
} else {
QSKIP("Not tested with timezone other than Central European (CET/CEST)");
}
@@ -1906,7 +1912,7 @@ void tst_QDateTime::operator_eqeq_data()
QDateTime dateTime1a = dateTime1.addMSecs(1);
QDateTime dateTime2(QDate(2012, 20, 6), QTime(14, 33, 2, 500));
QDateTime dateTime2a = dateTime2.addMSecs(-1);
- QDateTime dateTime3(QDate(1970, 1, 1), QTime(0, 0, 0, 0), Qt::UTC);
+ QDateTime dateTime3(QDate(1970, 1, 1), QTime(0, 0, 0, 0), Qt::UTC); // UTC epoch
QDateTime dateTime3a = dateTime3.addDays(1);
QDateTime dateTime3b = dateTime3.addDays(-1);
// Ensure that different times may be equal when considering timezone.
@@ -1914,8 +1920,7 @@ void tst_QDateTime::operator_eqeq_data()
dateTime3c.setOffsetFromUtc(3600);
QDateTime dateTime3d(dateTime3.addSecs(-3600));
dateTime3d.setOffsetFromUtc(-3600);
- // Convert from UTC to local.
- QDateTime dateTime3e(dateTime3.date(), dateTime3.time());
+ QDateTime dateTime3e(dateTime3.date(), dateTime3.time()); // Local time's epoch
QTest::newRow("data0") << dateTime1 << dateTime1 << true << false;
QTest::newRow("data1") << dateTime2 << dateTime2 << true << false;
@@ -1930,8 +1935,11 @@ void tst_QDateTime::operator_eqeq_data()
QTest::newRow("data10") << dateTime3 << dateTime3c << true << false;
QTest::newRow("data11") << dateTime3 << dateTime3d << true << false;
QTest::newRow("data12") << dateTime3c << dateTime3d << true << false;
- QTest::newRow("data13") << dateTime3 << dateTime3e
- << (localTimeType == LocalTimeIsUtc) << false;
+ if (localTimeType == LocalTimeIsUtc)
+ QTest::newRow("data13") << dateTime3 << dateTime3e << true << false;
+ // ... but a zone (sometimes) ahead of or behind UTC (e.g. Europe/London)
+ // might agree with UTC about the epoch, all the same.
+
QTest::newRow("invalid == invalid") << invalidDateTime() << invalidDateTime() << true << false;
QTest::newRow("invalid == valid #1") << invalidDateTime() << dateTime1 << false << false;
@@ -2391,8 +2399,8 @@ void tst_QDateTime::fromStringStringFormat_data()
QTest::newRow("data14") << QString("32.01.2004") << QString("dd.MM.yyyy") << invalidDateTime();
QTest::newRow("data15") << QString("Thu January 2004") << QString("ddd MMMM yyyy") << QDateTime(QDate(2004, 1, 1), QTime());
QTest::newRow("data16") << QString("2005-06-28T07:57:30.001Z")
- << QString("yyyy-MM-ddThh:mm:ss.zZ")
- << QDateTime(QDate(2005, 06, 28), QTime(07, 57, 30, 1));
+ << QString("yyyy-MM-ddThh:mm:ss.zt")
+ << QDateTime(QDate(2005, 06, 28), QTime(07, 57, 30, 1), Qt::UTC);
}
void tst_QDateTime::fromStringStringFormat()
@@ -2480,21 +2488,19 @@ void tst_QDateTime::fromStringToStringLocale()
QLocale def;
QLocale::setDefault(QLocale(QLocale::French, QLocale::France));
+#define ROUNDTRIP(format) \
+ QCOMPARE(QDateTime::fromString(dateTime.toString(format), format), dateTime)
- QCOMPARE(QDateTime::fromString(dateTime.toString(Qt::DefaultLocaleShortDate), Qt::DefaultLocaleShortDate), dateTime);
- QCOMPARE(QDateTime::fromString(dateTime.toString(Qt::SystemLocaleShortDate), Qt::SystemLocaleShortDate), dateTime);
+ ROUNDTRIP(Qt::DefaultLocaleShortDate);
+ ROUNDTRIP(Qt::SystemLocaleShortDate);
// obsolete
- QCOMPARE(QDateTime::fromString(dateTime.toString(Qt::SystemLocaleDate), Qt::SystemLocaleDate), dateTime);
- QCOMPARE(QDateTime::fromString(dateTime.toString(Qt::LocaleDate), Qt::LocaleDate), dateTime);
-
- QEXPECT_FAIL("data0", "This format is apparently failing because of a bug in the datetime parser. (QTBUG-22833)", Continue);
- QCOMPARE(QDateTime::fromString(dateTime.toString(Qt::DefaultLocaleLongDate), Qt::DefaultLocaleLongDate), dateTime);
-#ifndef Q_OS_WIN
- QEXPECT_FAIL("data0", "This format is apparently failing because of a bug in the datetime parser. (QTBUG-22833)", Continue);
-#endif
- QCOMPARE(QDateTime::fromString(dateTime.toString(Qt::SystemLocaleLongDate), Qt::SystemLocaleLongDate), dateTime);
+ ROUNDTRIP(Qt::SystemLocaleDate);
+ ROUNDTRIP(Qt::LocaleDate);
+ ROUNDTRIP(Qt::DefaultLocaleLongDate);
+ ROUNDTRIP(Qt::SystemLocaleLongDate);
+#undef ROUNDTRIP
QLocale::setDefault(def);
}
@@ -2629,6 +2635,71 @@ void tst_QDateTime::toOffsetFromUtc()
QCOMPARE(dt2.time(), QTime(0, 0, 0));
}
+void tst_QDateTime::zoneAtTime_data()
+{
+ QTest::addColumn<QByteArray>("ianaID");
+ QTest::addColumn<QDate>("date");
+ QTest::addColumn<int>("offset");
+#define ADDROW(name, zone, date, offset) \
+ QTest::newRow(name) << QByteArray(zone) << (date) << (offset)
+
+ // Check DST handling around epoch:
+ {
+ QDate epoch(1970, 1, 1);
+ ADDROW("epoch:UTC", "UTC", epoch, 0);
+ // Paris and Berlin skipped DST around 1970; but Rome used it.
+ ADDROW("epoch:CET", "Europe/Rome", epoch, 3600);
+ ADDROW("epoch:PST", "America/Vancouver", epoch, -8 * 3600);
+ ADDROW("epoch:EST", "America/New_York", epoch, -5 * 3600);
+ }
+ {
+ // QDateTime deliberately ignores DST before the epoch.
+ QDate summer69(1969, 8, 15); // Woodstock started
+ ADDROW("summer69:UTC", "UTC", summer69, 0);
+ ADDROW("summer69:CET", "Europe/Rome", summer69, 3600);
+ ADDROW("summer69:PST", "America/Vancouver", summer69, -8 * 3600);
+ ADDROW("summer69:EST", "America/New_York", summer69, -5 * 3600);
+ }
+ {
+ // ... but takes it into account after:
+ QDate summer70(1970, 8, 26); // Isle of Wight festival
+ ADDROW("summer70:UTC", "UTC", summer70, 0);
+ ADDROW("summer70:CET", "Europe/Rome", summer70, 2 * 3600);
+ ADDROW("summer70:PST", "America/Vancouver", summer70, -7 * 3600);
+ ADDROW("summer70:EST", "America/New_York", summer70, -4 * 3600);
+ }
+
+#ifndef Q_OS_WIN
+ // Bracket a few noteworthy transitions:
+ ADDROW("before:ACWST", "Australia/Eucla", QDate(1974, 10, 26), 31500); // 8:45
+ ADDROW("after:ACWST", "Australia/Eucla", QDate(1974, 10, 27), 35100); // 9:45
+ ADDROW("before:NPT", "Asia/Kathmandu", QDate(1985, 12, 31), 19800); // 5:30
+ ADDROW("after:NPT", "Asia/Kathmandu", QDate(1986, 1, 1), 20700); // 5:45
+ // The two that have skipped a day (each):
+ ADDROW("before:LINT", "Pacific/Kiritimati", QDate(1994, 12, 31), -36000);
+ ADDROW("after:LINT", "Pacific/Kiritimati", QDate(1995, 2, 1), 14 * 3600);
+ ADDROW("after:WST", "Pacific/Apia", QDate(2011, 12, 31), 14 * 3600);
+#endif // MS lacks ACWST, NPT; doesn't grok date-line crossings; and Windows 7 lacks LINT.
+ ADDROW("before:WST", "Pacific/Apia", QDate(2011, 12, 29), -36000);
+#undef ADDROW
+}
+
+void tst_QDateTime::zoneAtTime()
+{
+ QFETCH(QByteArray, ianaID);
+ QFETCH(QDate, date);
+ QFETCH(int, offset);
+ const QTime noon(12, 0);
+
+ QTimeZone zone(ianaID);
+ QVERIFY(zone.isValid());
+ QCOMPARE(QDateTime(date, noon, zone).offsetFromUtc(), offset);
+ if (date.year() < 1970)
+ QCOMPARE(zone.standardTimeOffset(QDateTime(date, noon, zone)), offset);
+ else // zone.offsetFromUtc *does* include DST, even before epoch
+ QCOMPARE(zone.offsetFromUtc(QDateTime(date, noon, zone)), offset);
+}
+
void tst_QDateTime::timeZoneAbbreviation()
{
QDateTime dt1(QDate(2013, 1, 1), QTime(1, 0, 0), Qt::OffsetFromUTC, 60 * 60);
diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp
index 943b4316ff..0015efacfa 100644
--- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp
+++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp
@@ -60,6 +60,7 @@ private slots:
void compare2();
void iterators(); // sligthly modified from tst_QMap
void keyIterator();
+ void keyValueIterator();
void keys_values_uniqueKeys(); // slightly modified from tst_QMap
void noNeedlessRehashes();
@@ -1124,6 +1125,60 @@ void tst_QHash::keyIterator()
Q_STATIC_ASSERT(std::is_default_constructible<keyIterator>::value);
}
+void tst_QHash::keyValueIterator()
+{
+ QHash<int, int> hash;
+ typedef QHash<int, int>::const_key_value_iterator::value_type entry_type;
+
+ for (int i = 0; i < 100; ++i)
+ hash.insert(i, i * 100);
+
+ auto key_value_it = hash.constKeyValueBegin();
+ auto it = hash.cbegin();
+
+
+ for (int i = 0; i < hash.size(); ++i) {
+ QVERIFY(key_value_it != hash.constKeyValueEnd());
+ QVERIFY(it != hash.cend());
+
+ entry_type pair(it.key(), it.value());
+ QCOMPARE(*key_value_it, pair);
+ ++key_value_it;
+ ++it;
+ }
+
+ QVERIFY(key_value_it == hash.constKeyValueEnd());
+ QVERIFY(it == hash.cend());
+
+ int key = 50;
+ int value = 50 * 100;
+ entry_type pair(key, value);
+ key_value_it = std::find(hash.constKeyValueBegin(), hash.constKeyValueEnd(), pair);
+ it = std::find(hash.cbegin(), hash.cend(), value);
+
+ QVERIFY(key_value_it != hash.constKeyValueEnd());
+ QCOMPARE(*key_value_it, entry_type(it.key(), it.value()));
+
+ ++it;
+ ++key_value_it;
+ QCOMPARE(*key_value_it, entry_type(it.key(), it.value()));
+
+ --it;
+ --key_value_it;
+ QCOMPARE(*key_value_it, entry_type(it.key(), it.value()));
+
+ ++it;
+ ++key_value_it;
+ QCOMPARE(*key_value_it, entry_type(it.key(), it.value()));
+
+ --it;
+ --key_value_it;
+ QCOMPARE(*key_value_it, entry_type(it.key(), it.value()));
+ key = 99;
+ value = 99 * 100;
+ QCOMPARE(std::count(hash.constKeyValueBegin(), hash.constKeyValueEnd(), entry_type(key, value)), 1);
+}
+
void tst_QHash::rehash_isnt_quadratic()
{
// this test should be incredibly slow if rehash() is quadratic
diff --git a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp
index 4a81adf9fe..124e3cdf00 100644
--- a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp
+++ b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp
@@ -49,6 +49,7 @@ public slots:
void init();
private Q_SLOTS:
+ void consistent();
void qhash();
void qhash_of_empty_and_null_qstring();
void qhash_of_empty_and_null_qbytearray();
@@ -61,6 +62,17 @@ private Q_SLOTS:
void setGlobalQHashSeed();
};
+void tst_QHashFunctions::consistent()
+{
+ // QString-like
+ {
+ const QString s = QStringLiteral("abcdefghijklmnopqrstuvxyz").repeated(16);
+
+ QCOMPARE(qHash(s), qHash(QStringRef(&s)));
+ QCOMPARE(qHash(s), qHash(QStringView(s)));
+ }
+}
+
void tst_QHashFunctions::initTestCase()
{
Q_STATIC_ASSERT(int(RandomSeed) > 0);
@@ -160,6 +172,14 @@ void tst_QHashFunctions::qhash_of_empty_and_null_qstring()
QString null, empty("");
QCOMPARE(null, empty);
QCOMPARE(qHash(null, seed), qHash(empty, seed));
+
+ QStringRef nullRef, emptyRef(&empty);
+ QCOMPARE(nullRef, emptyRef);
+ QCOMPARE(qHash(nullRef, seed), qHash(emptyRef, seed));
+
+ QStringView nullView, emptyView(empty);
+ QCOMPARE(nullView, emptyView);
+ QCOMPARE(qHash(nullView, seed), qHash(emptyView, seed));
}
void tst_QHashFunctions::qhash_of_empty_and_null_qbytearray()
@@ -264,17 +284,17 @@ void tst_QHashFunctions::rangeCommutative()
void tst_QHashFunctions::setGlobalQHashSeed()
{
// Setter works as advertised
- qSetGlobalQHashSeed(0x10101010);
- QCOMPARE(qGlobalQHashSeed(), 0x10101010);
+ qSetGlobalQHashSeed(0);
+ QCOMPARE(qGlobalQHashSeed(), 0);
// Creating a new QHash doesn't reset the seed
QHash<QString, int> someHash;
someHash.insert("foo", 42);
- QCOMPARE(qGlobalQHashSeed(), 0x10101010);
+ QCOMPARE(qGlobalQHashSeed(), 0);
// Reset works as advertised
qSetGlobalQHashSeed(-1);
- QVERIFY(qGlobalQHashSeed() != -1);
+ QVERIFY(qGlobalQHashSeed() > 0);
}
QTEST_APPLESS_MAIN(tst_QHashFunctions)
diff --git a/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp b/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp
index a68671d899..c8373b6ae9 100644
--- a/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp
+++ b/tests/auto/corelib/tools/qlatin1string/tst_qlatin1string.cpp
@@ -48,6 +48,7 @@ private Q_SLOTS:
void midLeftRight();
void nullString();
void emptyString();
+ void iterators();
void relationalOperators_data();
void relationalOperators();
};
@@ -155,6 +156,22 @@ void tst_QLatin1String::emptyString()
}
}
+void tst_QLatin1String::iterators()
+{
+ QLatin1String hello("hello");
+ QLatin1String olleh("olleh");
+
+ QVERIFY(std::equal(hello.begin(), hello.end(),
+ olleh.rbegin()));
+ QVERIFY(std::equal(hello.rbegin(), hello.rend(),
+ QT_MAKE_CHECKED_ARRAY_ITERATOR(olleh.begin(), olleh.size())));
+
+ QVERIFY(std::equal(hello.cbegin(), hello.cend(),
+ olleh.rbegin()));
+ QVERIFY(std::equal(hello.crbegin(), hello.crend(),
+ QT_MAKE_CHECKED_ARRAY_ITERATOR(olleh.begin(), olleh.size())));
+}
+
void tst_QLatin1String::relationalOperators_data()
{
QTest::addColumn<QLatin1StringContainer>("lhs");
diff --git a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp
index 10d78b1f2f..d424e6086d 100644
--- a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp
+++ b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp
@@ -137,6 +137,8 @@ private slots:
void textDirection_data();
void textDirection();
+ void formattedDataSize_data();
+ void formattedDataSize();
void bcp47Name();
private:
@@ -1221,6 +1223,9 @@ void tst_QLocale::dayOfWeek()
QCOMPARE(QLocale::c().toString(date, "ddd"), shortName);
QCOMPARE(QLocale::c().toString(date, "dddd"), longName);
+
+ QCOMPARE(QLocale::c().toString(date, QStringViewLiteral("ddd")), shortName);
+ QCOMPARE(QLocale::c().toString(date, QStringViewLiteral("dddd")), longName);
}
void tst_QLocale::formatDate_data()
@@ -1263,6 +1268,7 @@ void tst_QLocale::formatDate()
QLocale l(QLocale::C);
QCOMPARE(l.toString(date, format), result);
+ QCOMPARE(l.toString(date, QStringView(format)), result);
}
@@ -1319,6 +1325,7 @@ void tst_QLocale::formatTime()
QLocale l(QLocale::C);
QCOMPARE(l.toString(time, format), result);
+ QCOMPARE(l.toString(time, QStringView(format)), result);
}
@@ -1480,6 +1487,7 @@ void tst_QLocale::formatDateTime()
QLocale l(localeName);
QCOMPARE(l.toString(dateTime, format), result);
+ QCOMPARE(l.toString(dateTime, QStringView(format)), result);
}
void tst_QLocale::formatTimeZone()
@@ -2203,9 +2211,9 @@ void tst_QLocale::timeFormat()
QCOMPARE(c.timeFormat(QLocale::NarrowFormat), c.timeFormat(QLocale::ShortFormat));
const QLocale no("no_NO");
- QCOMPARE(no.timeFormat(QLocale::NarrowFormat), QLatin1String("HH.mm"));
- QCOMPARE(no.timeFormat(QLocale::ShortFormat), QLatin1String("HH.mm"));
- QCOMPARE(no.timeFormat(QLocale::LongFormat), QLatin1String("HH.mm.ss t"));
+ QCOMPARE(no.timeFormat(QLocale::NarrowFormat), QLatin1String("HH:mm"));
+ QCOMPARE(no.timeFormat(QLocale::ShortFormat), QLatin1String("HH:mm"));
+ QCOMPARE(no.timeFormat(QLocale::LongFormat), QLatin1String("HH:mm:ss t"));
const QLocale id("id_ID");
QCOMPARE(id.timeFormat(QLocale::ShortFormat), QLatin1String("HH.mm"));
@@ -2227,9 +2235,9 @@ void tst_QLocale::dateTimeFormat()
QCOMPARE(c.dateTimeFormat(QLocale::NarrowFormat), c.dateTimeFormat(QLocale::ShortFormat));
const QLocale no("no_NO");
- QCOMPARE(no.dateTimeFormat(QLocale::NarrowFormat), QLatin1String("dd.MM.yyyy HH.mm"));
- QCOMPARE(no.dateTimeFormat(QLocale::ShortFormat), QLatin1String("dd.MM.yyyy HH.mm"));
- QCOMPARE(no.dateTimeFormat(QLocale::LongFormat), QLatin1String("dddd d. MMMM yyyy HH.mm.ss t"));
+ QCOMPARE(no.dateTimeFormat(QLocale::NarrowFormat), QLatin1String("dd.MM.yyyy HH:mm"));
+ QCOMPARE(no.dateTimeFormat(QLocale::ShortFormat), QLatin1String("dd.MM.yyyy HH:mm"));
+ QCOMPARE(no.dateTimeFormat(QLocale::LongFormat), QLatin1String("dddd d. MMMM yyyy HH:mm:ss t"));
}
void tst_QLocale::monthName()
@@ -2510,8 +2518,8 @@ void tst_QLocale::textDirection_data()
default:
break;
}
- QString testName = QLocalePrivate::languageToCode(QLocale::Language(language));
- QTest::newRow(testName.toLatin1().constData()) << language << int(QLocale::AnyScript) << rightToLeft;
+ const QLatin1String testName = QLocalePrivate::languageToCode(QLocale::Language(language));
+ QTest::newRow(qPrintable(testName)) << language << int(QLocale::AnyScript) << rightToLeft;
}
QTest::newRow("pa_Arab") << int(QLocale::Punjabi) << int(QLocale::ArabicScript) << true;
QTest::newRow("uz_Arab") << int(QLocale::Uzbek) << int(QLocale::ArabicScript) << true;
@@ -2527,6 +2535,81 @@ void tst_QLocale::textDirection()
QCOMPARE(locale.textDirection() == Qt::RightToLeft, rightToLeft);
}
+void tst_QLocale::formattedDataSize_data()
+{
+ QTest::addColumn<QLocale::Language>("language");
+ QTest::addColumn<int>("decimalPlaces");
+ QTest::addColumn<QLocale::DataSizeFormats>("units");
+ QTest::addColumn<int>("bytes");
+ QTest::addColumn<QString>("output");
+
+ struct {
+ const char *name;
+ QLocale::Language lang;
+ const char *bytes;
+ const char abbrev;
+ const char sep; // decimal separator
+ } data[] = {
+ { "English", QLocale::English, "bytes", 'B', '.' },
+ { "French", QLocale::French, "octets", 'o', ',' },
+ { "C", QLocale::C, "bytes", 'B', '.' }
+ };
+
+ for (const auto row : data) {
+#define ROWB(id, deci, num, text) \
+ QTest::addRow("%s-%s", row.name, id) \
+ << row.lang << deci << format \
+ << num << (QString(text) + QChar(' ') + QString(row.bytes))
+#define ROWQ(id, deci, num, head, tail) \
+ QTest::addRow("%s-%s", row.name, id) \
+ << row.lang << deci << format \
+ << num << (QString(head) + QChar(row.sep) + QString(tail) + QChar(row.abbrev))
+
+ // Metatype system fails to handle raw enum members as format; needs variable
+ {
+ const QLocale::DataSizeFormats format = QLocale::DataSizeIecFormat;
+ ROWB("IEC-0", 2, 0, "0");
+ ROWB("IEC-10", 2, 10, "10");
+ ROWQ("IEC-12Ki", 2, 12345, "12", "06 Ki");
+ ROWQ("IEC-16Ki", 2, 16384, "16", "00 Ki");
+ ROWQ("IEC-1235k", 2, 1234567, "1", "18 Mi");
+ ROWQ("IEC-1374k", 2, 1374744, "1", "31 Mi");
+ ROWQ("IEC-1234M", 2, 1234567890, "1", "15 Gi");
+ }
+ {
+ const QLocale::DataSizeFormats format = QLocale::DataSizeTraditionalFormat;
+ ROWB("Trad-0", 2, 0, "0");
+ ROWB("Trad-10", 2, 10, "10");
+ ROWQ("Trad-12Ki", 2, 12345, "12", "06 k");
+ ROWQ("Trad-16Ki", 2, 16384, "16", "00 k");
+ ROWQ("Trad-1235k", 2, 1234567, "1", "18 M");
+ ROWQ("Trad-1374k", 2, 1374744, "1", "31 M");
+ ROWQ("Trad-1234M", 2, 1234567890, "1", "15 G");
+ }
+ {
+ const QLocale::DataSizeFormats format = QLocale::DataSizeSIFormat;
+ ROWB("Decimal-0", 2, 0, "0");
+ ROWB("Decimal-10", 2, 10, "10");
+ ROWQ("Decimal-16Ki", 2, 16384, "16", "38 k");
+ ROWQ("Decimal-1234k", 2, 1234567, "1", "23 M");
+ ROWQ("Decimal-1374k", 2, 1374744, "1", "37 M");
+ ROWQ("Decimal-1234M", 2, 1234567890, "1", "23 G");
+ }
+#undef ROWQ
+#undef ROWB
+ }
+}
+
+void tst_QLocale::formattedDataSize()
+{
+ QFETCH(QLocale::Language, language);
+ QFETCH(int, decimalPlaces);
+ QFETCH(QLocale::DataSizeFormats, units);
+ QFETCH(int, bytes);
+ QFETCH(QString, output);
+ QCOMPARE(QLocale(language).formattedDataSize(bytes, decimalPlaces, units), output);
+}
+
void tst_QLocale::bcp47Name()
{
QCOMPARE(QLocale("C").bcp47Name(), QStringLiteral("en"));
diff --git a/tests/auto/corelib/tools/qmacautoreleasepool/qmacautoreleasepool.pro b/tests/auto/corelib/tools/qmacautoreleasepool/qmacautoreleasepool.pro
new file mode 100644
index 0000000000..26b3a47472
--- /dev/null
+++ b/tests/auto/corelib/tools/qmacautoreleasepool/qmacautoreleasepool.pro
@@ -0,0 +1,4 @@
+CONFIG += testcase
+TARGET = tst_qmacautoreleasepool
+QT = core testlib
+SOURCES = tst_qmacautoreleasepool.mm
diff --git a/tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm b/tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm
new file mode 100644
index 0000000000..8f1069f419
--- /dev/null
+++ b/tests/auto/corelib/tools/qmacautoreleasepool/tst_qmacautoreleasepool.mm
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <Foundation/Foundation.h>
+
+class tst_QMacAutoreleasePool : public QObject
+{
+ Q_OBJECT
+private slots:
+ void noPool();
+ void rootLevelPool();
+ void stackAllocatedPool();
+ void heapAllocatedPool();
+};
+
+static id lastDeallocedObject = nil;
+
+@interface DeallocTracker : NSObject @end
+@implementation DeallocTracker
+-(void)dealloc
+{
+ lastDeallocedObject = self;
+ [super dealloc];
+}
+@end
+
+void tst_QMacAutoreleasePool::noPool()
+{
+ // No pool, will not be released, but should not crash
+
+ [[[DeallocTracker alloc] init] autorelease];
+}
+
+void tst_QMacAutoreleasePool::rootLevelPool()
+{
+ // The root level case, no NSAutoreleasePool since we're not in the main
+ // runloop, and objects autoreleased as part of main.
+
+ NSObject *allocedObject = nil;
+ {
+ QMacAutoReleasePool qtPool;
+ allocedObject = [[[DeallocTracker alloc] init] autorelease];
+ }
+ QCOMPARE(lastDeallocedObject, allocedObject);
+}
+
+void tst_QMacAutoreleasePool::stackAllocatedPool()
+{
+ // The normal case, other pools surrounding our pool, draining
+ // our pool before any other pool.
+
+ NSObject *allocedObject = nil;
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ {
+ QMacAutoReleasePool qtPool;
+ allocedObject = [[[DeallocTracker alloc] init] autorelease];
+ }
+ QCOMPARE(lastDeallocedObject, allocedObject);
+ [pool drain];
+}
+
+void tst_QMacAutoreleasePool::heapAllocatedPool()
+{
+ // The special case, a pool allocated on the heap, or as a member of a
+ // heap allocated object. This is not a supported use of QMacAutoReleasePool,
+ // and will result in warnings if the pool is prematurely drained.
+
+ NSObject *allocedObject = nil;
+ {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ QMacAutoReleasePool *qtPool = nullptr;
+ {
+ qtPool = new QMacAutoReleasePool;
+ allocedObject = [[[DeallocTracker alloc] init] autorelease];
+ }
+ [pool drain];
+ delete qtPool;
+ }
+ QCOMPARE(lastDeallocedObject, allocedObject);
+}
+
+QTEST_APPLESS_MAIN(tst_QMacAutoreleasePool)
+
+#include "tst_qmacautoreleasepool.moc"
diff --git a/tests/auto/corelib/tools/qmap/tst_qmap.cpp b/tests/auto/corelib/tools/qmap/tst_qmap.cpp
index f42ffc0471..b39444e76f 100644
--- a/tests/auto/corelib/tools/qmap/tst_qmap.cpp
+++ b/tests/auto/corelib/tools/qmap/tst_qmap.cpp
@@ -61,6 +61,7 @@ private slots:
void iterators();
void keyIterator();
+ void keyValueIterator();
void keys_values_uniqueKeys();
void qmultimap_specific();
@@ -863,6 +864,59 @@ void tst_QMap::keyIterator()
Q_STATIC_ASSERT(std::is_default_constructible<keyIterator>::value);
}
+void tst_QMap::keyValueIterator()
+{
+ QMap<int, int> map;
+ typedef QMap<int, int>::const_key_value_iterator::value_type entry_type;
+
+ for (int i = 0; i < 100; ++i)
+ map.insert(i, i * 100);
+
+ auto key_value_it = map.constKeyValueBegin();
+ auto it = map.cbegin();
+
+ for (int i = 0; i < map.size(); ++i) {
+ QVERIFY(key_value_it != map.constKeyValueEnd());
+ QVERIFY(it != map.cend());
+
+ entry_type pair(it.key(), it.value());
+ QCOMPARE(*key_value_it, pair);
+ ++key_value_it;
+ ++it;
+ }
+
+ QVERIFY(key_value_it == map.constKeyValueEnd());
+ QVERIFY(it == map.cend());
+
+ int key = 50;
+ int value = 50 * 100;
+ entry_type pair(key, value);
+ key_value_it = std::find(map.constKeyValueBegin(), map.constKeyValueEnd(), pair);
+ it = std::find(map.cbegin(), map.cend(), value);
+
+ QVERIFY(key_value_it != map.constKeyValueEnd());
+ QCOMPARE(*key_value_it, entry_type(it.key(), it.value()));
+
+ ++it;
+ ++key_value_it;
+ QCOMPARE(*key_value_it, entry_type(it.key(), it.value()));
+
+ --it;
+ --key_value_it;
+ QCOMPARE(*key_value_it, entry_type(it.key(), it.value()));
+
+ ++it;
+ ++key_value_it;
+ QCOMPARE(*key_value_it, entry_type(it.key(), it.value()));
+
+ --it;
+ --key_value_it;
+ QCOMPARE(*key_value_it, entry_type(it.key(), it.value()));
+ key = 99;
+ value = 99 * 100;
+ QCOMPARE(std::count(map.constKeyValueBegin(), map.constKeyValueEnd(), entry_type(key, value)), 1);
+}
+
void tst_QMap::keys_values_uniqueKeys()
{
QMap<QString, int> map;
diff --git a/tests/auto/corelib/tools/qrect/tst_qrect.cpp b/tests/auto/corelib/tools/qrect/tst_qrect.cpp
index d3c6412b0d..1c2221ec29 100644
--- a/tests/auto/corelib/tools/qrect/tst_qrect.cpp
+++ b/tests/auto/corelib/tools/qrect/tst_qrect.cpp
@@ -171,6 +171,7 @@ private slots:
void containsPointF_data();
void containsPointF();
void smallRects() const;
+ void toRect();
};
// Used to work around some floating point precision problems.
@@ -4331,5 +4332,36 @@ void tst_QRect::smallRects() const
QVERIFY(r1 != r2);
}
+void tst_QRect::toRect()
+{
+ for (qreal x = 1.0; x < 2.0; x += 0.25) {
+ for (qreal y = 1.0; y < 2.0; y += 0.25) {
+ for (qreal w = 1.0; w < 2.0; w += 0.25) {
+ for (qreal h = 1.0; h < 2.0; h += 0.25) {
+ const QRectF rectf(x, y, w, h);
+ const QRectF rect = rectf.toRect();
+ QVERIFY(qAbs(rect.x() - rectf.x()) < 1.0);
+ QVERIFY(qAbs(rect.y() - rectf.y()) < 1.0);
+ QVERIFY(qAbs(rect.width() - rectf.width()) < 1.0);
+ QVERIFY(qAbs(rect.height() - rectf.height()) < 1.0);
+ QVERIFY(qAbs(rect.right() - rectf.right()) < 1.0);
+ QVERIFY(qAbs(rect.bottom() - rectf.bottom()) < 1.0);
+
+ const QRectF arect = rectf.toAlignedRect();
+ QVERIFY(qAbs(arect.x() - rectf.x()) < 1.0);
+ QVERIFY(qAbs(arect.y() - rectf.y()) < 1.0);
+ QVERIFY(qAbs(arect.width() - rectf.width()) < 2.0);
+ QVERIFY(qAbs(arect.height() - rectf.height()) < 2.0);
+ QVERIFY(qAbs(arect.right() - rectf.right()) < 1.0);
+ QVERIFY(qAbs(arect.bottom() - rectf.bottom()) < 1.0);
+
+ QVERIFY(arect.contains(rectf));
+ QVERIFY(arect.contains(rect));
+ }
+ }
+ }
+ }
+}
+
QTEST_MAIN(tst_QRect)
#include "tst_qrect.moc"
diff --git a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp
index 2a93250ba5..c828551e44 100644
--- a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp
+++ b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp
@@ -169,6 +169,7 @@ void consistencyCheck(const QRegularExpressionMatch &match)
int length = match.capturedLength(i);
QString captured = match.captured(i);
QStringRef capturedRef = match.capturedRef(i);
+ QStringView capturedView = match.capturedView(i);
if (!captured.isNull()) {
QVERIFY(startPos >= 0);
@@ -177,11 +178,13 @@ void consistencyCheck(const QRegularExpressionMatch &match)
QVERIFY(endPos >= startPos);
QVERIFY((endPos - startPos) == length);
QVERIFY(captured == capturedRef);
+ QVERIFY(captured == capturedView);
} else {
QVERIFY(startPos == -1);
QVERIFY(endPos == -1);
QVERIFY((endPos - startPos) == length);
QVERIFY(capturedRef.isNull());
+ QVERIFY(capturedView.isNull());
}
}
}
diff --git a/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp b/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp
index 21efaede00..a4b06d1b3b 100644
--- a/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp
+++ b/tests/auto/corelib/tools/qscopedpointer/tst_qscopedpointer.cpp
@@ -88,7 +88,7 @@ class MySubClass : public MyClass
void tst_QScopedPointer::useSubClassInConstructor()
{
/* Use a syntax which users typically would do. */
- QScopedPointer<MyClass> p(new MyClass());
+ QScopedPointer<MyClass> p(new MySubClass());
}
void tst_QScopedPointer::dataOnValue()
diff --git a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp
index 442d4d089c..e1dcdb8407 100644
--- a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp
+++ b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp
@@ -1884,7 +1884,7 @@ class StrongThread: public QThread
protected:
void run()
{
- usleep(rand() % 2000);
+ usleep(QRandomGenerator::global()->bounded(2000));
ptr->ref();
ptr.clear();
}
@@ -1897,7 +1897,7 @@ class WeakThread: public QThread
protected:
void run()
{
- usleep(rand() % 2000);
+ usleep(QRandomGenerator::global()->bounded(2000));
QSharedPointer<ThreadData> ptr = weak;
if (ptr)
ptr->ref();
@@ -1959,7 +1959,6 @@ void tst_QSharedPointer::threadStressTest()
base.clear();
- srand(time(NULL));
// start threads
for (int i = 0; i < allThreads.count(); ++i)
if (allThreads[i]) allThreads[i]->start();
diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp
index 24f16b9911..70ccc72630 100644
--- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp
+++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp
@@ -4726,8 +4726,8 @@ void tst_QString::arg()
QString s14( "%1%2%3" );
QCOMPARE( s4.arg("foo"), QLatin1String("[foo]") );
- QCOMPARE( s5.arg("foo"), QLatin1String("[foo]") );
- QCOMPARE( s6.arg("foo"), QLatin1String("[foo]") );
+ QCOMPARE( s5.arg(QLatin1String("foo")), QLatin1String("[foo]") );
+ QCOMPARE( s6.arg(QStringViewLiteral("foo")), QLatin1String("[foo]") );
QCOMPARE( s7.arg("foo"), QLatin1String("[foo]") );
QCOMPARE( s8.arg("foo"), QLatin1String("[foo %1]") );
QCOMPARE( s8.arg("foo").arg("bar"), QLatin1String("[foo bar]") );
@@ -4788,11 +4788,11 @@ void tst_QString::arg()
QCOMPARE( QString("%%%1%%%2").arg("foo").arg("bar"), QLatin1String("%%foo%%bar") );
QCOMPARE( QString("%1").arg("hello", -10), QLatin1String("hello ") );
- QCOMPARE( QString("%1").arg("hello", -5), QLatin1String("hello") );
- QCOMPARE( QString("%1").arg("hello", -2), QLatin1String("hello") );
+ QCOMPARE( QString("%1").arg(QLatin1String("hello"), -5), QLatin1String("hello") );
+ QCOMPARE( QString("%1").arg(QStringViewLiteral("hello"), -2), QLatin1String("hello") );
QCOMPARE( QString("%1").arg("hello", 0), QLatin1String("hello") );
- QCOMPARE( QString("%1").arg("hello", 2), QLatin1String("hello") );
- QCOMPARE( QString("%1").arg("hello", 5), QLatin1String("hello") );
+ QCOMPARE( QString("%1").arg(QLatin1String("hello"), 2), QLatin1String("hello") );
+ QCOMPARE( QString("%1").arg(QStringViewLiteral("hello"), 5), QLatin1String("hello") );
QCOMPARE( QString("%1").arg("hello", 10), QLatin1String(" hello") );
QCOMPARE( QString("%1%1").arg("hello"), QLatin1String("hellohello") );
QCOMPARE( QString("%2%1").arg("hello"), QLatin1String("%2hello") );
@@ -6067,14 +6067,6 @@ void tst_QString::compare_data()
lower += QChar(QChar::lowSurrogate(0x10428));
QTest::newRow("data8") << upper << lower << -1 << 0;
- QTest::newRow("vectorized-boundaries-7") << QString("1234567") << QString("abcdefg") << -1 << -1;
- QTest::newRow("vectorized-boundaries-8") << QString("12345678") << QString("abcdefgh") << -1 << -1;
- QTest::newRow("vectorized-boundaries-9") << QString("123456789") << QString("abcdefghi") << -1 << -1;
-
- QTest::newRow("vectorized-boundaries-15") << QString("123456789012345") << QString("abcdefghiklmnop") << -1 << -1;
- QTest::newRow("vectorized-boundaries-16") << QString("1234567890123456") << QString("abcdefghiklmnopq") << -1 << -1;
- QTest::newRow("vectorized-boundaries-17") << QString("12345678901234567") << QString("abcdefghiklmnopqr") << -1 << -1;
-
// embedded nulls
// These don't work as of now. It's OK that these don't work since \0 is not a valid unicode
/*QTest::newRow("data10") << QString(QByteArray("\0", 1)) << QString(QByteArray("\0", 1)) << 0 << 0;
@@ -6083,6 +6075,44 @@ void tst_QString::compare_data()
QTest::newRow("data13") << QString("ab\0c") << QString(QByteArray("ab\0c", 4)) << 0 << 0;
QTest::newRow("data14") << QString(QByteArray("ab\0c", 4)) << QString("abc") << -1 << -1;
QTest::newRow("data15") << QString("abc") << QString(QByteArray("ab\0c", 4)) << 1 << 1;*/
+
+ // All tests below (generated by the 3 for-loops) are meant to excercise the vectorized versions
+ // of ucstrncmp.
+
+ QString in1, in2;
+ for (int i = 0; i < 70; ++i) {
+ in1 += QString::number(i % 10);
+ in2 += QString::number((70 - i + 1) % 10);
+ }
+ Q_ASSERT(in1.length() == in2.length());
+ Q_ASSERT(in1 != in2);
+ Q_ASSERT(in1.at(0) < in2.at(0));
+ for (int i = 0; i < in1.length(); ++i) {
+ Q_ASSERT(in1.at(i) != in2.at(i));
+ }
+
+ for (int i = 1; i <= 65; ++i) {
+ QString inp1 = in1.left(i);
+ QString inp2 = in2.left(i);
+ QTest::addRow("all-different-%d", i) << inp1 << inp2 << -1 << -1;
+ }
+
+ for (int i = 1; i <= 65; ++i) {
+ QString start(i - 1, 'a');
+
+ QString in = start + QLatin1Char('a');
+ QTest::addRow("all-same-%d", i) << in << in << 0 << 0;
+
+ QString in2 = start + QLatin1Char('b');
+ QTest::addRow("last-different-%d", i) << in << in2 << -1 << -1;
+ }
+
+ for (int i = 0; i < 16; ++i) {
+ QString in1(16, 'a');
+ QString in2 = in1;
+ in2[i] = 'b';
+ QTest::addRow("all-same-except-char-%d", i) << in1 << in2 << -1 << -1;
+ }
}
static bool isLatin(const QString &s)
diff --git a/tests/auto/corelib/tools/qstringapisymmetry/qstringapisymmetry.pro b/tests/auto/corelib/tools/qstringapisymmetry/qstringapisymmetry.pro
index bc38b17949..a4e91e38bd 100644
--- a/tests/auto/corelib/tools/qstringapisymmetry/qstringapisymmetry.pro
+++ b/tests/auto/corelib/tools/qstringapisymmetry/qstringapisymmetry.pro
@@ -3,3 +3,4 @@ TARGET = tst_qstringapisymmetry
QT = core testlib
SOURCES = tst_qstringapisymmetry.cpp
qtConfig(c++14): CONFIG += c++14
+qtConfig(c++1z): CONFIG += c++1z
diff --git a/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp b/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp
index 9d9b47b61e..61d1f86f00 100644
--- a/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp
+++ b/tests/auto/corelib/tools/qstringapisymmetry/tst_qstringapisymmetry.cpp
@@ -31,6 +31,7 @@
#undef QT_ASCII_CAST_WARNINGS
#include <QString>
+#include <QStringView>
#include <QChar>
#include <QStringRef>
#include <QLatin1String>
@@ -44,6 +45,7 @@ Q_DECLARE_METATYPE(QStringRef)
template <typename T>
QString toQString(const T &t) { return QString(t); }
QString toQString(const QStringRef &ref) { return ref.toString(); }
+QString toQString(QStringView view) { return view.toString(); }
// FIXME: these are missing at the time of writing, add them, then remove the dummies here:
#define MAKE_RELOP(op, A1, A2) \
@@ -81,13 +83,15 @@ class tst_QStringApiSymmetry : public QObject
void compare_impl() const;
private Q_SLOTS:
- // test all combinations of {QChar, QStringRef, QString, QLatin1String, QByteArray, const char*}
+ // test all combinations of {QChar, QStringRef, QString, QStringView, QLatin1String, QByteArray, const char*}
void compare_QChar_QChar_data() { compare_data(false); }
void compare_QChar_QChar() { compare_impl<QChar, QChar>(); }
void compare_QChar_QStringRef_data() { compare_data(false); }
void compare_QChar_QStringRef() { compare_impl<QChar, QStringRef>(); }
void compare_QChar_QString_data() { compare_data(false); }
void compare_QChar_QString() { compare_impl<QChar, QString>(); }
+ void compare_QChar_QStringView_data() { compare_data(false); }
+ void compare_QChar_QStringView() { compare_impl<QChar, QStringView>(); }
void compare_QChar_QLatin1String_data() { compare_data(false); }
void compare_QChar_QLatin1String() { compare_impl<QChar, QLatin1String>(); }
void compare_QChar_QByteArray_data() { compare_data(false); }
@@ -101,6 +105,8 @@ private Q_SLOTS:
void compare_QStringRef_QStringRef() { compare_impl<QStringRef, QStringRef>(); }
void compare_QStringRef_QString_data() { compare_data(); }
void compare_QStringRef_QString() { compare_impl<QStringRef, QString>(); }
+ void compare_QStringRef_QStringView_data() { compare_data(); }
+ void compare_QStringRef_QStringView() { compare_impl<QStringRef, QStringView>(); }
void compare_QStringRef_QLatin1String_data() { compare_data(); }
void compare_QStringRef_QLatin1String() { compare_impl<QStringRef, QLatin1String>(); }
void compare_QStringRef_QByteArray_data() { compare_data(); }
@@ -114,6 +120,8 @@ private Q_SLOTS:
void compare_QString_QStringRef() { compare_impl<QString, QStringRef>(); }
void compare_QString_QString_data() { compare_data(); }
void compare_QString_QString() { compare_impl<QString, QString>(); }
+ void compare_QString_QStringView_data() { compare_data(); }
+ void compare_QString_QStringView() { compare_impl<QString, QStringView>(); }
void compare_QString_QLatin1String_data() { compare_data(); }
void compare_QString_QLatin1String() { compare_impl<QString, QLatin1String>(); }
void compare_QString_QByteArray_data() { compare_data(); }
@@ -121,12 +129,25 @@ private Q_SLOTS:
void compare_QString_const_char_star_data() { compare_data(); }
void compare_QString_const_char_star() { compare_impl<QString, const char *>(); }
+ void compare_QStringView_QChar_data() { compare_data(false); }
+ void compare_QStringView_QChar() { compare_impl<QStringView, QChar>(); }
+ void compare_QStringView_QStringRef_data() { compare_data(); }
+ void compare_QStringView_QStringRef() { compare_impl<QStringView, QStringRef>(); }
+ void compare_QStringView_QString_data() { compare_data(); }
+ void compare_QStringView_QString() { compare_impl<QStringView, QString>(); }
+ void compare_QStringView_QStringView_data() { compare_data(); }
+ void compare_QStringView_QStringView() { compare_impl<QStringView, QStringView>(); }
+ void compare_QStringView_QLatin1String_data() { compare_data(); }
+ void compare_QStringView_QLatin1String() { compare_impl<QStringView, QLatin1String>(); }
+
void compare_QLatin1String_QChar_data() { compare_data(false); }
void compare_QLatin1String_QChar() { compare_impl<QLatin1String, QChar>(); }
void compare_QLatin1String_QStringRef_data() { compare_data(); }
void compare_QLatin1String_QStringRef() { compare_impl<QLatin1String, QStringRef>(); }
void compare_QLatin1String_QString_data() { compare_data(); }
void compare_QLatin1String_QString() { compare_impl<QLatin1String, QString>(); }
+ void compare_QLatin1String_QStringView_data() { compare_data(); }
+ void compare_QLatin1String_QStringView() { compare_impl<QLatin1String, QStringView>(); }
void compare_QLatin1String_QLatin1String_data() { compare_data(); }
void compare_QLatin1String_QLatin1String() { compare_impl<QLatin1String, QLatin1String>(); }
void compare_QLatin1String_QByteArray_data() { compare_data(); }
@@ -160,6 +181,181 @@ private Q_SLOTS:
//void compare_const_char_star_const_char_star_data() { compare_data(); }
//void compare_const_char_star_const_char_star() { compare_impl<const char *, const char *>(); }
+private:
+ void startsWith_data(bool rhsIsQChar = false);
+ template <typename Haystack, typename Needle> void startsWith_impl() const;
+
+ void endsWith_data(bool rhsIsQChar = false);
+ template <typename Haystack, typename Needle> void endsWith_impl() const;
+
+private Q_SLOTS:
+ // test all combinations of {QString, QStringRef, QStringView, QLatin1String} x {QString, QStringRef, QStringView, QLatin1String, QChar}:
+ void startsWith_QString_QString_data() { startsWith_data(); }
+ void startsWith_QString_QString() { startsWith_impl<QString, QString>(); }
+ void startsWith_QString_QStringRef_data() { startsWith_data(); }
+ void startsWith_QString_QStringRef() { startsWith_impl<QString, QStringRef>(); }
+ void startsWith_QString_QStringView_data() { startsWith_data(); }
+ void startsWith_QString_QStringView() { startsWith_impl<QString, QStringView>(); }
+ void startsWith_QString_QLatin1String_data() { startsWith_data(); }
+ void startsWith_QString_QLatin1String() { startsWith_impl<QString, QLatin1String>(); }
+ void startsWith_QString_QChar_data() { startsWith_data(false); }
+ void startsWith_QString_QChar() { startsWith_impl<QString, QChar>(); }
+
+ void startsWith_QStringRef_QString_data() { startsWith_data(); }
+ void startsWith_QStringRef_QString() { startsWith_impl<QStringRef, QString>(); }
+ void startsWith_QStringRef_QStringRef_data() { startsWith_data(); }
+ void startsWith_QStringRef_QStringRef() { startsWith_impl<QStringRef, QStringRef>(); }
+ void startsWith_QStringRef_QStringView_data() { startsWith_data(); }
+ void startsWith_QStringRef_QStringView() { startsWith_impl<QStringRef, QStringView>(); }
+ void startsWith_QStringRef_QLatin1String_data() { startsWith_data(); }
+ void startsWith_QStringRef_QLatin1String() { startsWith_impl<QStringRef, QLatin1String>(); }
+ void startsWith_QStringRef_QChar_data() { startsWith_data(false); }
+ void startsWith_QStringRef_QChar() { startsWith_impl<QStringRef, QChar>(); }
+
+ void startsWith_QStringView_QString_data() { startsWith_data(); }
+ void startsWith_QStringView_QString() { startsWith_impl<QStringView, QString>(); }
+ void startsWith_QStringView_QStringRef_data() { startsWith_data(); }
+ void startsWith_QStringView_QStringRef() { startsWith_impl<QStringView, QStringRef>(); }
+ void startsWith_QStringView_QStringView_data() { startsWith_data(); }
+ void startsWith_QStringView_QStringView() { startsWith_impl<QStringView, QStringView>(); }
+ void startsWith_QStringView_QLatin1String_data() { startsWith_data(); }
+ void startsWith_QStringView_QLatin1String() { startsWith_impl<QStringView, QLatin1String>(); }
+ void startsWith_QStringView_QChar_data() { startsWith_data(false); }
+ void startsWith_QStringView_QChar() { startsWith_impl<QStringView, QChar>(); }
+
+ void startsWith_QLatin1String_QString_data() { startsWith_data(); }
+ void startsWith_QLatin1String_QString() { startsWith_impl<QLatin1String, QString>(); }
+ void startsWith_QLatin1String_QStringRef_data() { startsWith_data(); }
+ void startsWith_QLatin1String_QStringRef() { startsWith_impl<QLatin1String, QStringRef>(); }
+ void startsWith_QLatin1String_QStringView_data() { startsWith_data(); }
+ void startsWith_QLatin1String_QStringView() { startsWith_impl<QLatin1String, QStringView>(); }
+ void startsWith_QLatin1String_QLatin1String_data() { startsWith_data(); }
+ void startsWith_QLatin1String_QLatin1String() { startsWith_impl<QLatin1String, QLatin1String>(); }
+ void startsWith_QLatin1String_QChar_data() { startsWith_data(false); }
+ void startsWith_QLatin1String_QChar() { startsWith_impl<QLatin1String, QChar>(); }
+
+ void endsWith_QString_QString_data() { endsWith_data(); }
+ void endsWith_QString_QString() { endsWith_impl<QString, QString>(); }
+ void endsWith_QString_QStringRef_data() { endsWith_data(); }
+ void endsWith_QString_QStringRef() { endsWith_impl<QString, QStringRef>(); }
+ void endsWith_QString_QStringView_data() { endsWith_data(); }
+ void endsWith_QString_QStringView() { endsWith_impl<QString, QStringView>(); }
+ void endsWith_QString_QLatin1String_data() { endsWith_data(); }
+ void endsWith_QString_QLatin1String() { endsWith_impl<QString, QLatin1String>(); }
+ void endsWith_QString_QChar_data() { endsWith_data(false); }
+ void endsWith_QString_QChar() { endsWith_impl<QString, QChar>(); }
+
+ void endsWith_QStringRef_QString_data() { endsWith_data(); }
+ void endsWith_QStringRef_QString() { endsWith_impl<QStringRef, QString>(); }
+ void endsWith_QStringRef_QStringRef_data() { endsWith_data(); }
+ void endsWith_QStringRef_QStringRef() { endsWith_impl<QStringRef, QStringRef>(); }
+ void endsWith_QStringRef_QStringView_data() { endsWith_data(); }
+ void endsWith_QStringRef_QStringView() { endsWith_impl<QStringRef, QStringView>(); }
+ void endsWith_QStringRef_QLatin1String_data() { endsWith_data(); }
+ void endsWith_QStringRef_QLatin1String() { endsWith_impl<QStringRef, QLatin1String>(); }
+ void endsWith_QStringRef_QChar_data() { endsWith_data(false); }
+ void endsWith_QStringRef_QChar() { endsWith_impl<QStringRef, QChar>(); }
+
+ void endsWith_QStringView_QString_data() { endsWith_data(); }
+ void endsWith_QStringView_QString() { endsWith_impl<QStringView, QString>(); }
+ void endsWith_QStringView_QStringRef_data() { endsWith_data(); }
+ void endsWith_QStringView_QStringRef() { endsWith_impl<QStringView, QStringRef>(); }
+ void endsWith_QStringView_QStringView_data() { endsWith_data(); }
+ void endsWith_QStringView_QStringView() { endsWith_impl<QStringView, QStringView>(); }
+ void endsWith_QStringView_QLatin1String_data() { endsWith_data(); }
+ void endsWith_QStringView_QLatin1String() { endsWith_impl<QStringView, QLatin1String>(); }
+ void endsWith_QStringView_QChar_data() { endsWith_data(false); }
+ void endsWith_QStringView_QChar() { endsWith_impl<QStringView, QChar>(); }
+
+ void endsWith_QLatin1String_QString_data() { endsWith_data(); }
+ void endsWith_QLatin1String_QString() { endsWith_impl<QLatin1String, QString>(); }
+ void endsWith_QLatin1String_QStringRef_data() { endsWith_data(); }
+ void endsWith_QLatin1String_QStringRef() { endsWith_impl<QLatin1String, QStringRef>(); }
+ void endsWith_QLatin1String_QStringView_data() { endsWith_data(); }
+ void endsWith_QLatin1String_QStringView() { endsWith_impl<QLatin1String, QStringView>(); }
+ void endsWith_QLatin1String_QLatin1String_data() { endsWith_data(); }
+ void endsWith_QLatin1String_QLatin1String() { endsWith_impl<QLatin1String, QLatin1String>(); }
+ void endsWith_QLatin1String_QChar_data() { endsWith_data(false); }
+ void endsWith_QLatin1String_QChar() { endsWith_impl<QLatin1String, QChar>(); }
+
+private:
+ void mid_data();
+ template <typename String> void mid_impl();
+
+ void left_data();
+ template <typename String> void left_impl();
+
+ void right_data();
+ template <typename String> void right_impl();
+
+ void chop_data();
+ template <typename String> void chop_impl();
+
+ void truncate_data() { left_data(); }
+ template <typename String> void truncate_impl();
+
+private Q_SLOTS:
+
+ void mid_QString_data() { mid_data(); }
+ void mid_QString() { mid_impl<QString>(); }
+ void mid_QStringRef_data() { mid_data(); }
+ void mid_QStringRef() { mid_impl<QStringRef>(); }
+ void mid_QStringView_data() { mid_data(); }
+ void mid_QStringView() { mid_impl<QStringView>(); }
+ void mid_QLatin1String_data() { mid_data(); }
+ void mid_QLatin1String() { mid_impl<QLatin1String>(); }
+ void mid_QByteArray_data() { mid_data(); }
+ void mid_QByteArray() { mid_impl<QByteArray>(); }
+
+ void left_truncate_QString_data() { left_data(); }
+ void left_truncate_QString() { left_impl<QString>(); }
+ void left_truncate_QStringRef_data() { left_data(); }
+ void left_truncate_QStringRef() { left_impl<QStringRef>(); }
+ void left_truncate_QStringView_data() { left_data(); }
+ void left_truncate_QStringView() { left_impl<QStringView>(); }
+ void left_truncate_QLatin1String_data() { left_data(); }
+ void left_truncate_QLatin1String() { left_impl<QLatin1String>(); }
+ void left_truncate_QByteArray_data() { left_data(); }
+ void left_truncate_QByteArray() { left_impl<QByteArray>(); }
+
+ void right_QString_data() { right_data(); }
+ void right_QString() { right_impl<QString>(); }
+ void right_QStringRef_data() { right_data(); }
+ void right_QStringRef() { right_impl<QStringRef>(); }
+ void right_QStringView_data() { right_data(); }
+ void right_QStringView() { right_impl<QStringView>(); }
+ void right_QLatin1String_data() { right_data(); }
+ void right_QLatin1String() { right_impl<QLatin1String>(); }
+ void right_QByteArray_data() { right_data(); }
+ void right_QByteArray() { right_impl<QByteArray>(); }
+
+ void chop_QString_data() { chop_data(); }
+ void chop_QString() { chop_impl<QString>(); }
+ void chop_QStringRef_data() { chop_data(); }
+ void chop_QStringRef() { chop_impl<QStringRef>(); }
+ void chop_QStringView_data() { chop_data(); }
+ void chop_QStringView() { chop_impl<QStringView>(); }
+ void chop_QLatin1String_data() { chop_data(); }
+ void chop_QLatin1String() { chop_impl<QLatin1String>(); }
+ void chop_QByteArray_data() { chop_data(); }
+ void chop_QByteArray() { chop_impl<QByteArray>(); }
+
+private:
+ void trimmed_data();
+ template <typename String> void trimmed_impl();
+
+private Q_SLOTS:
+ void trim_trimmed_QString_data() { trimmed_data(); }
+ void trim_trimmed_QString() { trimmed_impl<QString>(); }
+ void trim_trimmed_QStringRef_data() { trimmed_data(); }
+ void trim_trimmed_QStringRef() { trimmed_impl<QStringRef>(); }
+ void trim_trimmed_QStringView_data() { trimmed_data(); }
+ void trim_trimmed_QStringView() { trimmed_impl<QStringView>(); }
+ void trim_trimmed_QLatin1String_data() { trimmed_data(); }
+ void trim_trimmed_QLatin1String() { trimmed_impl<QLatin1String>(); }
+ void trim_trimmed_QByteArray_data() { trimmed_data(); }
+ void trim_trimmed_QByteArray() { trimmed_impl<QByteArray>(); }
+
//
// UTF-16-only checks:
//
@@ -183,21 +379,29 @@ private Q_SLOTS:
void toLocal8Bit_QString() { toLocal8Bit_impl<QString>(); }
void toLocal8Bit_QStringRef_data() { toLocal8Bit_data(); }
void toLocal8Bit_QStringRef() { toLocal8Bit_impl<QStringRef>(); }
+ void toLocal8Bit_QStringView_data() { toLocal8Bit_data(); }
+ void toLocal8Bit_QStringView() { toLocal8Bit_impl<QStringView>(); }
void toLatin1_QString_data() { toLatin1_data(); }
void toLatin1_QString() { toLatin1_impl<QString>(); }
void toLatin1_QStringRef_data() { toLatin1_data(); }
void toLatin1_QStringRef() { toLatin1_impl<QStringRef>(); }
+ void toLatin1_QStringView_data() { toLatin1_data(); }
+ void toLatin1_QStringView() { toLatin1_impl<QStringView>(); }
void toUtf8_QString_data() { toUtf8_data(); }
void toUtf8_QString() { toUtf8_impl<QString>(); }
void toUtf8_QStringRef_data() { toUtf8_data(); }
void toUtf8_QStringRef() { toUtf8_impl<QStringRef>(); }
+ void toUtf8_QStringView_data() { toUtf8_data(); }
+ void toUtf8_QStringView() { toUtf8_impl<QStringView>(); }
void toUcs4_QString_data() { toUcs4_data(); }
void toUcs4_QString() { toUcs4_impl<QString>(); }
void toUcs4_QStringRef_data() { toUcs4_data(); }
void toUcs4_QStringRef() { toUcs4_impl<QStringRef>(); }
+ void toUcs4_QStringView_data() { toUcs4_data(); }
+ void toUcs4_QStringView() { toUcs4_impl<QStringView>(); }
};
void tst_QStringApiSymmetry::compare_data(bool hasConceptOfNullAndEmpty)
@@ -217,6 +421,9 @@ void tst_QStringApiSymmetry::compare_data(bool hasConceptOfNullAndEmpty)
QTest::newRow("null <> empty") << QStringRef() << QLatin1String()
<< QStringRef(&empty) << QLatin1String("")
<< 0 << 0;
+ QTest::newRow("empty <> null") << QStringRef(&empty) << QLatin1String("")
+ << QStringRef() << QLatin1String()
+ << 0 << 0;
}
#define ROW(lhs, rhs) \
@@ -239,10 +446,20 @@ void tst_QStringApiSymmetry::compare_data(bool hasConceptOfNullAndEmpty)
#undef ROW
}
+template <typename String> String detached(String s)
+{
+ if (!s.isNull()) { // detaching loses nullness, but we need to preserve it
+ auto d = s.data();
+ Q_UNUSED(d);
+ }
+ return s;
+}
+
template <class Str> Str make(const QStringRef &sf, QLatin1String l1, const QByteArray &u8);
template <> QChar make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf.isEmpty() ? QChar() : sf.at(0); }
template <> QStringRef make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf; }
template <> QString make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf.toString(); }
+template <> QStringView make(const QStringRef &sf, QLatin1String, const QByteArray &) { return sf; }
template <> QLatin1String make(const QStringRef &, QLatin1String l1, const QByteArray &) { return l1; }
template <> QByteArray make(const QStringRef &, QLatin1String, const QByteArray &u8) { return u8; }
template <> const char * make(const QStringRef &, QLatin1String, const QByteArray &u8) { return u8.data(); }
@@ -260,6 +477,12 @@ struct has_nothrow_compare {
};
template <typename LHS, typename RHS>
+struct has_qCompareStrings {
+ enum { value = !std::is_same<LHS, QChar>::value && !std::is_same<RHS, QChar>::value &&
+ !is_utf8_encoded<LHS>::value && !is_utf8_encoded<RHS>::value };
+};
+
+template <typename LHS, typename RHS>
void tst_QStringApiSymmetry::compare_impl() const
{
QFETCH(QStringRef, lhsUnicode);
@@ -267,6 +490,8 @@ void tst_QStringApiSymmetry::compare_impl() const
QFETCH(QStringRef, rhsUnicode);
QFETCH(QLatin1String, rhsLatin1);
QFETCH(int, caseSensitiveCompareResult);
+ QFETCH(const int, caseInsensitiveCompareResult);
+ Q_UNUSED(caseInsensitiveCompareResult);
const auto lhsU8 = lhsUnicode.toUtf8();
const auto rhsU8 = rhsUnicode.toUtf8();
@@ -300,6 +525,529 @@ void tst_QStringApiSymmetry::compare_impl() const
#undef CHECK
}
+static QString empty = QLatin1String("");
+// the tests below rely on the fact that these objects' names match their contents:
+static QString a = QStringLiteral("a");
+static QString A = QStringLiteral("A");
+static QString b = QStringLiteral("b");
+static QString B = QStringLiteral("B");
+static QString c = QStringLiteral("c");
+static QString C = QStringLiteral("C");
+static QString ab = QStringLiteral("ab");
+static QString aB = QStringLiteral("aB");
+static QString Ab = QStringLiteral("Ab");
+static QString AB = QStringLiteral("AB");
+static QString bc = QStringLiteral("bc");
+static QString bC = QStringLiteral("bC");
+static QString Bc = QStringLiteral("Bc");
+static QString BC = QStringLiteral("BC");
+static QString abc = QStringLiteral("abc");
+static QString abC = QStringLiteral("abC");
+static QString aBc = QStringLiteral("aBc");
+static QString aBC = QStringLiteral("aBC");
+static QString Abc = QStringLiteral("Abc");
+static QString AbC = QStringLiteral("AbC");
+static QString ABc = QStringLiteral("ABc");
+static QString ABC = QStringLiteral("ABC");
+
+void tst_QStringApiSymmetry::startsWith_data(bool rhsHasVariableLength)
+{
+ QTest::addColumn<QStringRef>("haystackU16");
+ QTest::addColumn<QLatin1String>("haystackL1");
+ QTest::addColumn<QStringRef>("needleU16");
+ QTest::addColumn<QLatin1String>("needleL1");
+ QTest::addColumn<bool>("resultCS");
+ QTest::addColumn<bool>("resultCIS");
+
+ if (rhsHasVariableLength) {
+ QTest::addRow("null ~= ^null") << QStringRef() << QLatin1String()
+ << QStringRef() << QLatin1String() << true << true;
+ QTest::addRow("empty ~= ^null") << QStringRef(&empty) << QLatin1String("")
+ << QStringRef() << QLatin1String() << true << true;
+ QTest::addRow("a ~= ^null") << QStringRef(&a) << QLatin1String("a")
+ << QStringRef() << QLatin1String() << true << true;
+ QTest::addRow("null ~= ^empty") << QStringRef() << QLatin1String()
+ << QStringRef(&empty) << QLatin1String("") << false << false;
+ QTest::addRow("a ~= ^empty") << QStringRef(&a) << QLatin1String("a")
+ << QStringRef(&empty) << QLatin1String("") << true << true;
+ QTest::addRow("empty ~= ^empty") << QStringRef(&empty) << QLatin1String("")
+ << QStringRef(&empty) << QLatin1String("") << true << true;
+ }
+ QTest::addRow("null ~= ^a") << QStringRef() << QLatin1String()
+ << QStringRef(&a) << QLatin1String("a") << false << false;
+ QTest::addRow("empty ~= ^a") << QStringRef(&empty) << QLatin1String("")
+ << QStringRef(&a) << QLatin1String("a") << false << false;
+
+#define ROW(h, n, cs, cis) \
+ QTest::addRow("%s ~= ^%s", #h, #n) << QStringRef(&h) << QLatin1String(#h) \
+ << QStringRef(&n) << QLatin1String(#n) \
+ << bool(cs) << bool(cis)
+ ROW(a, a, 1, 1);
+ ROW(a, A, 0, 1);
+ ROW(a, b, 0, 0);
+
+ if (rhsHasVariableLength)
+ ROW(a, aB, 0, 0);
+
+ ROW(ab, a, 1, 1);
+ if (rhsHasVariableLength) {
+ ROW(ab, ab, 1, 1);
+ ROW(ab, aB, 0, 1);
+ ROW(ab, Ab, 0, 1);
+ }
+ ROW(ab, c, 0, 0);
+
+ if (rhsHasVariableLength)
+ ROW(ab, abc, 0, 0);
+
+ ROW(Abc, c, 0, 0);
+ if (rhsHasVariableLength) {
+ ROW(Abc, ab, 0, 1);
+ ROW(Abc, aB, 0, 1);
+ ROW(Abc, Ab, 1, 1);
+ ROW(Abc, AB, 0, 1);
+ ROW(aBC, ab, 0, 1);
+ ROW(aBC, aB, 1, 1);
+ ROW(aBC, Ab, 0, 1);
+ ROW(aBC, AB, 0, 1);
+ }
+ ROW(ABC, b, 0, 0);
+ ROW(ABC, a, 0, 1);
+#undef ROW
+}
+
+template <typename Haystack, typename Needle>
+void tst_QStringApiSymmetry::startsWith_impl() const
+{
+ QFETCH(const QStringRef, haystackU16);
+ QFETCH(const QLatin1String, haystackL1);
+ QFETCH(const QStringRef, needleU16);
+ QFETCH(const QLatin1String, needleL1);
+ QFETCH(const bool, resultCS);
+ QFETCH(const bool, resultCIS);
+
+ const auto haystackU8 = haystackU16.toUtf8();
+ const auto needleU8 = needleU16.toUtf8();
+
+ const auto haystack = make<Haystack>(haystackU16, haystackL1, haystackU8);
+ const auto needle = make<Needle>(needleU16, needleL1, needleU8);
+
+ QCOMPARE(haystack.startsWith(needle), resultCS);
+ QCOMPARE(haystack.startsWith(needle, Qt::CaseSensitive), resultCS);
+ QCOMPARE(haystack.startsWith(needle, Qt::CaseInsensitive), resultCIS);
+}
+
+void tst_QStringApiSymmetry::endsWith_data(bool rhsHasVariableLength)
+{
+ QTest::addColumn<QStringRef>("haystackU16");
+ QTest::addColumn<QLatin1String>("haystackL1");
+ QTest::addColumn<QStringRef>("needleU16");
+ QTest::addColumn<QLatin1String>("needleL1");
+ QTest::addColumn<bool>("resultCS");
+ QTest::addColumn<bool>("resultCIS");
+
+ if (rhsHasVariableLength) {
+ QTest::addRow("null ~= null$") << QStringRef() << QLatin1String()
+ << QStringRef() << QLatin1String() << true << true;
+ QTest::addRow("empty ~= null$") << QStringRef(&empty) << QLatin1String("")
+ << QStringRef() << QLatin1String() << true << true;
+ QTest::addRow("a ~= null$") << QStringRef(&a) << QLatin1String("a")
+ << QStringRef() << QLatin1String() << true << true;
+ QTest::addRow("null ~= empty$") << QStringRef() << QLatin1String()
+ << QStringRef(&empty) << QLatin1String("") << false << false;
+ QTest::addRow("a ~= empty$") << QStringRef(&a) << QLatin1String("a")
+ << QStringRef(&empty) << QLatin1String("") << true << true;
+ QTest::addRow("empty ~= empty$") << QStringRef(&empty) << QLatin1String("")
+ << QStringRef(&empty) << QLatin1String("") << true << true;
+ }
+ QTest::addRow("null ~= a$") << QStringRef() << QLatin1String()
+ << QStringRef(&a) << QLatin1String("a") << false << false;
+ QTest::addRow("empty ~= a$") << QStringRef(&empty) << QLatin1String("")
+ << QStringRef(&a) << QLatin1String("a") << false << false;
+
+#define ROW(h, n, cs, cis) \
+ QTest::addRow("%s ~= %s$", #h, #n) << QStringRef(&h) << QLatin1String(#h) \
+ << QStringRef(&n) << QLatin1String(#n) \
+ << bool(cs) << bool(cis)
+ ROW(a, a, 1, 1);
+ ROW(a, A, 0, 1);
+ ROW(a, b, 0, 0);
+
+ if (rhsHasVariableLength)
+ ROW(b, ab, 0, 0);
+
+ ROW(ab, b, 1, 1);
+ if (rhsHasVariableLength) {
+ ROW(ab, ab, 1, 1);
+ ROW(ab, aB, 0, 1);
+ ROW(ab, Ab, 0, 1);
+ }
+ ROW(ab, c, 0, 0);
+
+ if (rhsHasVariableLength)
+ ROW(bc, abc, 0, 0);
+
+ ROW(Abc, c, 1, 1);
+ if (rhsHasVariableLength) {
+ ROW(Abc, bc, 1, 1);
+ ROW(Abc, bC, 0, 1);
+ ROW(Abc, Bc, 0, 1);
+ ROW(Abc, BC, 0, 1);
+ ROW(aBC, bc, 0, 1);
+ ROW(aBC, bC, 0, 1);
+ ROW(aBC, Bc, 0, 1);
+ ROW(aBC, BC, 1, 1);
+ }
+ ROW(ABC, b, 0, 0);
+ ROW(ABC, a, 0, 0);
+#undef ROW
+}
+
+template <typename Haystack, typename Needle>
+void tst_QStringApiSymmetry::endsWith_impl() const
+{
+ QFETCH(const QStringRef, haystackU16);
+ QFETCH(const QLatin1String, haystackL1);
+ QFETCH(const QStringRef, needleU16);
+ QFETCH(const QLatin1String, needleL1);
+ QFETCH(const bool, resultCS);
+ QFETCH(const bool, resultCIS);
+
+ const auto haystackU8 = haystackU16.toUtf8();
+ const auto needleU8 = needleU16.toUtf8();
+
+ const auto haystack = make<Haystack>(haystackU16, haystackL1, haystackU8);
+ const auto needle = make<Needle>(needleU16, needleL1, needleU8);
+
+ QCOMPARE(haystack.endsWith(needle), resultCS);
+ QCOMPARE(haystack.endsWith(needle, Qt::CaseSensitive), resultCS);
+ QCOMPARE(haystack.endsWith(needle, Qt::CaseInsensitive), resultCIS);
+}
+
+void tst_QStringApiSymmetry::mid_data()
+{
+ QTest::addColumn<QStringRef>("unicode");
+ QTest::addColumn<QLatin1String>("latin1");
+ QTest::addColumn<int>("pos");
+ QTest::addColumn<int>("n");
+ QTest::addColumn<QStringRef>("result");
+ QTest::addColumn<QStringRef>("result2");
+
+ QTest::addRow("null") << QStringRef() << QLatin1String() << 0 << 0 << QStringRef() << QStringRef();
+ QTest::addRow("empty") << QStringRef(&empty) << QLatin1String("") << 0 << 0 << QStringRef(&empty) << QStringRef(&empty);
+
+ // Some classes' mid() implementations have a wide contract, others a narrow one
+ // so only test valid arguents here:
+#define ROW(base, p, n, r1, r2) \
+ QTest::addRow("%s%d%d", #base, p, n) << QStringRef(&base) << QLatin1String(#base) << p << n << QStringRef(&r1) << QStringRef(&r2)
+
+ ROW(a, 0, 0, a, empty);
+ ROW(a, 0, 1, a, a);
+ ROW(a, 1, 0, empty, empty);
+
+ ROW(ab, 0, 0, ab, empty);
+ ROW(ab, 0, 1, ab, a);
+ ROW(ab, 0, 2, ab, ab);
+ ROW(ab, 1, 0, b, empty);
+ ROW(ab, 1, 1, b, b);
+ ROW(ab, 2, 0, empty, empty);
+
+ ROW(abc, 0, 0, abc, empty);
+ ROW(abc, 0, 1, abc, a);
+ ROW(abc, 0, 2, abc, ab);
+ ROW(abc, 0, 3, abc, abc);
+ ROW(abc, 1, 0, bc, empty);
+ ROW(abc, 1, 1, bc, b);
+ ROW(abc, 1, 2, bc, bc);
+ ROW(abc, 2, 0, c, empty);
+ ROW(abc, 2, 1, c, c);
+ ROW(abc, 3, 0, empty, empty);
+#undef ROW
+}
+
+template <typename String>
+void tst_QStringApiSymmetry::mid_impl()
+{
+ QFETCH(const QStringRef, unicode);
+ QFETCH(const QLatin1String, latin1);
+ QFETCH(const int, pos);
+ QFETCH(const int, n);
+ QFETCH(const QStringRef, result);
+ QFETCH(const QStringRef, result2);
+
+ const auto utf8 = unicode.toUtf8();
+
+ const auto s = make<String>(unicode, latin1, utf8);
+
+ {
+ const auto mid = s.mid(pos);
+ const auto mid2 = s.mid(pos, n);
+
+ QCOMPARE(mid, result);
+ QCOMPARE(mid.isNull(), result.isNull());
+ QCOMPARE(mid.isEmpty(), result.isEmpty());
+
+ QCOMPARE(mid2, result2);
+ QCOMPARE(mid2.isNull(), result2.isNull());
+ QCOMPARE(mid2.isEmpty(), result2.isEmpty());
+ }
+ {
+ const auto mid = detached(s).mid(pos);
+ const auto mid2 = detached(s).mid(pos, n);
+
+ QCOMPARE(mid, result);
+ QCOMPARE(mid.isNull(), result.isNull());
+ QCOMPARE(mid.isEmpty(), result.isEmpty());
+
+ QCOMPARE(mid2, result2);
+ QCOMPARE(mid2.isNull(), result2.isNull());
+ QCOMPARE(mid2.isEmpty(), result2.isEmpty());
+ }
+}
+
+void tst_QStringApiSymmetry::left_data()
+{
+ QTest::addColumn<QStringRef>("unicode");
+ QTest::addColumn<QLatin1String>("latin1");
+ QTest::addColumn<int>("n");
+ QTest::addColumn<QStringRef>("result");
+
+ QTest::addRow("null") << QStringRef() << QLatin1String() << 0 << QStringRef();
+ QTest::addRow("empty") << QStringRef(&empty) << QLatin1String("") << 0 << QStringRef(&empty);
+
+ // Some classes' left() implementations have a wide contract, others a narrow one
+ // so only test valid arguents here:
+#define ROW(base, n, res) \
+ QTest::addRow("%s%d", #base, n) << QStringRef(&base) << QLatin1String(#base) << n << QStringRef(&res);
+
+ ROW(a, 0, empty);
+ ROW(a, 1, a);
+
+ ROW(ab, 0, empty);
+ ROW(ab, 1, a);
+ ROW(ab, 2, ab);
+
+ ROW(abc, 0, empty);
+ ROW(abc, 1, a);
+ ROW(abc, 2, ab);
+ ROW(abc, 3, abc);
+#undef ROW
+}
+
+template <typename String>
+void tst_QStringApiSymmetry::left_impl()
+{
+ QFETCH(const QStringRef, unicode);
+ QFETCH(const QLatin1String, latin1);
+ QFETCH(const int, n);
+ QFETCH(const QStringRef, result);
+
+ const auto utf8 = unicode.toUtf8();
+
+ const auto s = make<String>(unicode, latin1, utf8);
+
+ {
+ const auto left = s.left(n);
+
+ QCOMPARE(left, result);
+ QCOMPARE(left.isNull(), result.isNull());
+ QCOMPARE(left.isEmpty(), result.isEmpty());
+ }
+ {
+ const auto left = detached(s).left(n);
+
+ QCOMPARE(left, result);
+ QCOMPARE(left.isNull(), result.isNull());
+ QCOMPARE(left.isEmpty(), result.isEmpty());
+ }
+ {
+ auto left = s;
+ left.truncate(n);
+
+ QCOMPARE(left, result);
+ QCOMPARE(left.isNull(), result.isNull());
+ QCOMPARE(left.isEmpty(), result.isEmpty());
+ }
+}
+
+void tst_QStringApiSymmetry::right_data()
+{
+ QTest::addColumn<QStringRef>("unicode");
+ QTest::addColumn<QLatin1String>("latin1");
+ QTest::addColumn<int>("n");
+ QTest::addColumn<QStringRef>("result");
+
+ QTest::addRow("null") << QStringRef() << QLatin1String() << 0 << QStringRef();
+ QTest::addRow("empty") << QStringRef(&empty) << QLatin1String("") << 0 << QStringRef(&empty);
+
+ // Some classes' right() implementations have a wide contract, others a narrow one
+ // so only test valid arguents here:
+#define ROW(base, n, res) \
+ QTest::addRow("%s%d", #base, n) << QStringRef(&base) << QLatin1String(#base) << n << QStringRef(&res);
+
+ ROW(a, 0, empty);
+ ROW(a, 1, a);
+
+ ROW(ab, 0, empty);
+ ROW(ab, 1, b);
+ ROW(ab, 2, ab);
+
+ ROW(abc, 0, empty);
+ ROW(abc, 1, c);
+ ROW(abc, 2, bc);
+ ROW(abc, 3, abc);
+#undef ROW
+}
+
+template <typename String>
+void tst_QStringApiSymmetry::right_impl()
+{
+ QFETCH(const QStringRef, unicode);
+ QFETCH(const QLatin1String, latin1);
+ QFETCH(const int, n);
+ QFETCH(const QStringRef, result);
+
+ const auto utf8 = unicode.toUtf8();
+
+ const auto s = make<String>(unicode, latin1, utf8);
+
+ {
+ const auto right = s.right(n);
+
+ QCOMPARE(right, result);
+ QCOMPARE(right.isNull(), result.isNull());
+ QCOMPARE(right.isEmpty(), result.isEmpty());
+ }
+ {
+ const auto right = detached(s).right(n);
+
+ QCOMPARE(right, result);
+ QCOMPARE(right.isNull(), result.isNull());
+ QCOMPARE(right.isEmpty(), result.isEmpty());
+ }
+}
+
+void tst_QStringApiSymmetry::chop_data()
+{
+ QTest::addColumn<QStringRef>("unicode");
+ QTest::addColumn<QLatin1String>("latin1");
+ QTest::addColumn<int>("n");
+ QTest::addColumn<QStringRef>("result");
+
+ QTest::addRow("null") << QStringRef() << QLatin1String() << 0 << QStringRef();
+ QTest::addRow("empty") << QStringRef(&empty) << QLatin1String("") << 0 << QStringRef(&empty);
+
+ // Some classes' truncate() implementations have a wide contract, others a narrow one
+ // so only test valid arguents here:
+#define ROW(base, n, res) \
+ QTest::addRow("%s%d", #base, n) << QStringRef(&base) << QLatin1String(#base) << n << QStringRef(&res);
+
+ ROW(a, 0, a);
+ ROW(a, 1, empty);
+
+ ROW(ab, 0, ab);
+ ROW(ab, 1, a);
+ ROW(ab, 2, empty);
+
+ ROW(abc, 0, abc);
+ ROW(abc, 1, ab);
+ ROW(abc, 2, a);
+ ROW(abc, 3, empty);
+#undef ROW
+}
+
+template <typename String>
+void tst_QStringApiSymmetry::chop_impl()
+{
+ QFETCH(const QStringRef, unicode);
+ QFETCH(const QLatin1String, latin1);
+ QFETCH(const int, n);
+ QFETCH(const QStringRef, result);
+
+ const auto utf8 = unicode.toUtf8();
+
+ const auto s = make<String>(unicode, latin1, utf8);
+
+ {
+ const auto chopped = s.chopped(n);
+
+ QCOMPARE(chopped, result);
+ QCOMPARE(chopped.isNull(), result.isNull());
+ QCOMPARE(chopped.isEmpty(), result.isEmpty());
+ }
+ {
+ const auto chopped = detached(s).chopped(n);
+
+ QCOMPARE(chopped, result);
+ QCOMPARE(chopped.isNull(), result.isNull());
+ QCOMPARE(chopped.isEmpty(), result.isEmpty());
+ }
+ {
+ auto chopped = s;
+ chopped.chop(n);
+
+ QCOMPARE(chopped, result);
+ QCOMPARE(chopped.isNull(), result.isNull());
+ QCOMPARE(chopped.isEmpty(), result.isEmpty());
+ }
+}
+
+void tst_QStringApiSymmetry::trimmed_data()
+{
+ QTest::addColumn<QString>("unicode");
+ QTest::addColumn<QStringRef>("result");
+
+ const auto latin1Whitespace = QLatin1String(" \r\n\t\f\v");
+
+ QTest::addRow("null") << QString() << QStringRef();
+
+ auto add = [latin1Whitespace](const QString &str) {
+ // run through all substrings of latin1Whitespace
+ for (int len = 0; len < latin1Whitespace.size(); ++len) {
+ for (int pos = 0; pos < latin1Whitespace.size() - len; ++pos) {
+ const QString unicode = latin1Whitespace.mid(pos, len) + str + latin1Whitespace.mid(pos, len);
+ const QScopedPointer<const char> escaped(QTest::toString(unicode));
+ QTest::addRow("%s", escaped.data()) << unicode << QStringRef(&str);
+ }
+ }
+ };
+
+ add(empty);
+ add(a);
+ add(ab);
+}
+
+template <typename String>
+void tst_QStringApiSymmetry::trimmed_impl()
+{
+ QFETCH(const QString, unicode);
+ QFETCH(const QStringRef, result);
+
+ const auto utf8 = unicode.toUtf8();
+ const auto l1s = unicode.toLatin1();
+ const auto l1 = l1s.isNull() ? QLatin1String() : QLatin1String(l1s);
+
+ const auto ref = unicode.isNull() ? QStringRef() : QStringRef(&unicode);
+ const auto s = make<String>(ref, l1, utf8);
+
+ QCOMPARE(s.isNull(), unicode.isNull());
+
+ {
+ const auto trimmed = s.trimmed();
+
+ QCOMPARE(trimmed, result);
+ QCOMPARE(trimmed.isNull(), result.isNull());
+ QCOMPARE(trimmed.isEmpty(), result.isEmpty());
+ }
+ {
+ const auto trimmed = detached(s).trimmed();
+
+ QCOMPARE(trimmed, result);
+ QCOMPARE(trimmed.isNull(), result.isNull());
+ QCOMPARE(trimmed.isEmpty(), result.isEmpty());
+ }
+}
+
//
//
// UTF-16-only checks:
@@ -309,6 +1057,7 @@ void tst_QStringApiSymmetry::compare_impl() const
template <class Str> Str make(const QString &s);
template <> QStringRef make(const QString &s) { return QStringRef(&s); }
template <> QString make(const QString &s) { return s; }
+template <> QStringView make(const QString &s) { return s; }
#define REPEAT_16X(X) X X X X X X X X X X X X X X X X
#define LONG_STRING_256 REPEAT_16X("0123456789abcdef")
diff --git a/tests/auto/corelib/tools/qstringiterator/tst_qstringiterator.cpp b/tests/auto/corelib/tools/qstringiterator/tst_qstringiterator.cpp
index 319f772516..7d5504c22c 100644
--- a/tests/auto/corelib/tools/qstringiterator/tst_qstringiterator.cpp
+++ b/tests/auto/corelib/tools/qstringiterator/tst_qstringiterator.cpp
@@ -326,22 +326,22 @@ void tst_QStringIterator::position()
QLatin1Char('p')
// codeunit count: 35
};
+ static const int stringDataSize = sizeof(stringData) / sizeof(stringData[0]);
- const QString string(stringData, sizeof(stringData) / sizeof(stringData[0]));
- QStringIterator i(string);
+ QStringIterator i(QStringView(stringData, stringDataSize));
- QCOMPARE(i.position(), string.constBegin());
+ QCOMPARE(i.position(), stringData);
QVERIFY(i.hasNext());
QVERIFY(!i.hasPrevious());
- i.setPosition(string.constEnd());
- QCOMPARE(i.position(), string.constEnd());
+ i.setPosition(stringData + stringDataSize);
+ QCOMPARE(i.position(), stringData + stringDataSize);
QVERIFY(!i.hasNext());
QVERIFY(i.hasPrevious());
#define QCHAR_UNICODE_VALUE(x) ((uint)(QChar(x).unicode()))
- const QString::const_iterator begin = string.constBegin();
+ const QChar *begin = stringData;
i.setPosition(begin);
QCOMPARE(i.position(), begin);
QCOMPARE(i.peekNext(), QCHAR_UNICODE_VALUE(QLatin1Char('a')));
diff --git a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp
index 2385aa992c..9f054190e5 100644
--- a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp
+++ b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp
@@ -264,6 +264,15 @@ void tst_QStringList::contains()
QVERIFY(list.contains("ARTHUR", Qt::CaseInsensitive));
QVERIFY(list.contains("dent", Qt::CaseInsensitive));
QVERIFY(!list.contains("hans", Qt::CaseInsensitive));
+
+ QVERIFY(list.contains(QLatin1String("arthur")));
+ QVERIFY(!list.contains(QLatin1String("ArthuR")));
+ QVERIFY(!list.contains(QLatin1String("Hans")));
+ QVERIFY(list.contains(QLatin1String("arthur"), Qt::CaseInsensitive));
+ QVERIFY(list.contains(QLatin1String("ArthuR"), Qt::CaseInsensitive));
+ QVERIFY(list.contains(QLatin1String("ARTHUR"), Qt::CaseInsensitive));
+ QVERIFY(list.contains(QLatin1String("dent"), Qt::CaseInsensitive));
+ QVERIFY(!list.contains(QLatin1String("hans"), Qt::CaseInsensitive));
}
void tst_QStringList::removeDuplicates_data()
diff --git a/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp b/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp
index d2374fe0ae..473f563f9b 100644
--- a/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp
+++ b/tests/auto/corelib/tools/qstringref/tst_qstringref.cpp
@@ -1862,7 +1862,9 @@ void tst_QStringRef::double_conversion()
void tst_QStringRef::trimmed()
{
- QString a;
+ QVERIFY(QStringRef().trimmed().isNull());
+ QString a = "";
+ QVERIFY(!QStringRef(&a).trimmed().isNull());
QStringRef b;
a = "Text";
b = a.leftRef(-1);
diff --git a/tests/auto/corelib/tools/qstringview/.gitignore b/tests/auto/corelib/tools/qstringview/.gitignore
new file mode 100644
index 0000000000..5f757d448a
--- /dev/null
+++ b/tests/auto/corelib/tools/qstringview/.gitignore
@@ -0,0 +1 @@
+tst_qstringview
diff --git a/tests/auto/corelib/tools/qstringview/qstringview.pro b/tests/auto/corelib/tools/qstringview/qstringview.pro
new file mode 100644
index 0000000000..e0e9973c91
--- /dev/null
+++ b/tests/auto/corelib/tools/qstringview/qstringview.pro
@@ -0,0 +1,6 @@
+CONFIG += testcase
+TARGET = tst_qstringview
+QT = core testlib
+contains(QT_CONFIG, c++14):CONFIG *= c++14
+contains(QT_CONFIG, c++1z):CONFIG *= c++1z
+SOURCES += tst_qstringview.cpp
diff --git a/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp b/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp
new file mode 100644
index 0000000000..4ae96005d0
--- /dev/null
+++ b/tests/auto/corelib/tools/qstringview/tst_qstringview.cpp
@@ -0,0 +1,619 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QStringView>
+#include <QString>
+#include <QChar>
+#include <QStringRef>
+
+#include <QTest>
+
+#include <string>
+
+template <typename T>
+using CanConvert = std::is_convertible<T, QStringView>;
+
+Q_STATIC_ASSERT(!CanConvert<QLatin1String>::value);
+Q_STATIC_ASSERT(!CanConvert<const char*>::value);
+Q_STATIC_ASSERT(!CanConvert<QByteArray>::value);
+
+// QStringView qchar_does_not_compile() { return QStringView(QChar('a')); }
+// QStringView qlatin1string_does_not_compile() { return QStringView(QLatin1String("a")); }
+// QStringView const_char_star_does_not_compile() { return QStringView("a"); }
+// QStringView qbytearray_does_not_compile() { return QStringView(QByteArray("a")); }
+
+//
+// QChar
+//
+
+Q_STATIC_ASSERT(!CanConvert<QChar>::value);
+
+Q_STATIC_ASSERT(CanConvert<QChar[123]>::value);
+
+Q_STATIC_ASSERT(CanConvert< QString >::value);
+Q_STATIC_ASSERT(CanConvert<const QString >::value);
+Q_STATIC_ASSERT(CanConvert< QString&>::value);
+Q_STATIC_ASSERT(CanConvert<const QString&>::value);
+
+Q_STATIC_ASSERT(CanConvert< QStringRef >::value);
+Q_STATIC_ASSERT(CanConvert<const QStringRef >::value);
+Q_STATIC_ASSERT(CanConvert< QStringRef&>::value);
+Q_STATIC_ASSERT(CanConvert<const QStringRef&>::value);
+
+
+//
+// ushort
+//
+
+Q_STATIC_ASSERT(!CanConvert<ushort>::value);
+
+Q_STATIC_ASSERT(CanConvert<ushort[123]>::value);
+
+Q_STATIC_ASSERT(CanConvert< ushort*>::value);
+Q_STATIC_ASSERT(CanConvert<const ushort*>::value);
+
+
+//
+// char16_t
+//
+
+#if defined(Q_COMPILER_UNICODE_STRINGS)
+
+Q_STATIC_ASSERT(!CanConvert<char16_t>::value);
+
+Q_STATIC_ASSERT(CanConvert< char16_t*>::value);
+Q_STATIC_ASSERT(CanConvert<const char16_t*>::value);
+
+#endif
+
+#if defined(Q_STDLIB_UNICODE_STRINGS)
+
+Q_STATIC_ASSERT(CanConvert< std::u16string >::value);
+Q_STATIC_ASSERT(CanConvert<const std::u16string >::value);
+Q_STATIC_ASSERT(CanConvert< std::u16string&>::value);
+Q_STATIC_ASSERT(CanConvert<const std::u16string&>::value);
+
+#endif
+
+
+//
+// wchar_t
+//
+
+Q_CONSTEXPR bool CanConvertFromWCharT =
+#ifdef Q_OS_WIN
+ true
+#else
+ false
+#endif
+ ;
+
+Q_STATIC_ASSERT(!CanConvert<wchar_t>::value);
+
+Q_STATIC_ASSERT(CanConvert< wchar_t*>::value == CanConvertFromWCharT);
+Q_STATIC_ASSERT(CanConvert<const wchar_t*>::value == CanConvertFromWCharT);
+
+Q_STATIC_ASSERT(CanConvert< std::wstring >::value == CanConvertFromWCharT);
+Q_STATIC_ASSERT(CanConvert<const std::wstring >::value == CanConvertFromWCharT);
+Q_STATIC_ASSERT(CanConvert< std::wstring&>::value == CanConvertFromWCharT);
+Q_STATIC_ASSERT(CanConvert<const std::wstring&>::value == CanConvertFromWCharT);
+
+
+class tst_QStringView : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void constExpr() const;
+ void basics() const;
+ void literals() const;
+ void at() const;
+
+ void fromQString() const;
+ void fromQStringRef() const;
+
+ void fromQCharStar() const
+ {
+ const QChar str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 };
+ fromLiteral(str);
+ }
+
+ void fromUShortStar() const
+ {
+ const ushort str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 };
+ fromLiteral(str);
+ }
+
+ void fromChar16TStar() const
+ {
+#if defined(Q_COMPILER_UNICODE_STRINGS)
+ fromLiteral(u"Hello, World!");
+#else
+ QSKIP("This test requires C++11 char16_t support enabled in the compiler");
+#endif
+ }
+
+ void fromWCharTStar() const
+ {
+#ifdef Q_OS_WIN
+ fromLiteral(L"Hello, World!");
+#else
+ QSKIP("This is a Windows-only test");
+#endif
+ }
+
+ void fromQCharRange() const
+ {
+ const QChar str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' };
+ fromRange(std::begin(str), std::end(str));
+ }
+
+ void fromUShortRange() const
+ {
+ const ushort str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' };
+ fromRange(std::begin(str), std::end(str));
+ }
+
+ void fromChar16TRange() const
+ {
+#if defined(Q_COMPILER_UNICODE_STRINGS)
+ const char16_t str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' };
+ fromRange(std::begin(str), std::end(str));
+#else
+ QSKIP("This test requires C++11 char16_t support enabled in the compiler");
+#endif
+ }
+
+ void fromWCharTRange() const
+ {
+#ifdef Q_OS_WIN
+ const wchar_t str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' };
+ fromRange(std::begin(str), std::end(str));
+#else
+ QSKIP("This is a Windows-only test");
+#endif
+ }
+
+ // std::basic_string
+ void fromStdStringWCharT() const
+ {
+#ifdef Q_OS_WIN
+ fromStdString<wchar_t>();
+#else
+ QSKIP("This is a Windows-only test");
+#endif
+ }
+ void fromStdStringChar16T() const
+ {
+#ifdef Q_STDLIB_UNICODE_STRINGS
+ fromStdString<char16_t>();
+#else
+ QSKIP("This test requires C++11 char16_t support enabled in compiler & stdlib");
+#endif
+ }
+
+private:
+ template <typename String>
+ void conversion_tests(String arg) const;
+ template <typename Char>
+ void fromLiteral(const Char *arg) const;
+ template <typename Char>
+ void fromRange(const Char *first, const Char *last) const;
+ template <typename Char, typename Container>
+ void fromContainer() const;
+ template <typename Char>
+ void fromStdString() const { fromContainer<Char, std::basic_string<Char> >(); }
+};
+
+void tst_QStringView::constExpr() const
+{
+ // compile-time checks
+#ifdef Q_COMPILER_CONSTEXPR
+ {
+ constexpr QStringView sv;
+ Q_STATIC_ASSERT(sv.size() == 0);
+ Q_STATIC_ASSERT(sv.isNull());
+ Q_STATIC_ASSERT(sv.empty());
+ Q_STATIC_ASSERT(sv.isEmpty());
+ Q_STATIC_ASSERT(sv.utf16() == nullptr);
+
+ constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size());
+ Q_STATIC_ASSERT(sv2.isNull());
+ Q_STATIC_ASSERT(sv2.empty());
+ }
+ {
+ constexpr QStringView sv = QStringViewLiteral("");
+ Q_STATIC_ASSERT(sv.size() == 0);
+ Q_STATIC_ASSERT(!sv.isNull());
+ Q_STATIC_ASSERT(sv.empty());
+ Q_STATIC_ASSERT(sv.isEmpty());
+ Q_STATIC_ASSERT(sv.utf16() != nullptr);
+
+ constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size());
+ Q_STATIC_ASSERT(!sv2.isNull());
+ Q_STATIC_ASSERT(sv2.empty());
+ }
+ {
+ constexpr QStringView sv = QStringViewLiteral("Hello");
+ Q_STATIC_ASSERT(sv.size() == 5);
+ Q_STATIC_ASSERT(!sv.empty());
+ Q_STATIC_ASSERT(!sv.isEmpty());
+ Q_STATIC_ASSERT(!sv.isNull());
+ Q_STATIC_ASSERT(*sv.utf16() == 'H');
+ Q_STATIC_ASSERT(sv[0] == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv.front() == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv.first() == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv[4] == QLatin1Char('o'));
+ Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o'));
+ Q_STATIC_ASSERT(sv.back() == QLatin1Char('o'));
+ Q_STATIC_ASSERT(sv.last() == QLatin1Char('o'));
+
+ constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size());
+ Q_STATIC_ASSERT(!sv2.isNull());
+ Q_STATIC_ASSERT(!sv2.empty());
+ Q_STATIC_ASSERT(sv2.size() == 5);
+ }
+#if !defined(Q_OS_WIN) || defined(Q_COMPILER_UNICODE_STRINGS)
+ {
+ Q_STATIC_ASSERT(QStringView(u"Hello").size() == 5);
+ constexpr QStringView sv = u"Hello";
+ Q_STATIC_ASSERT(sv.size() == 5);
+ Q_STATIC_ASSERT(!sv.empty());
+ Q_STATIC_ASSERT(!sv.isEmpty());
+ Q_STATIC_ASSERT(!sv.isNull());
+ Q_STATIC_ASSERT(*sv.utf16() == 'H');
+ Q_STATIC_ASSERT(sv[0] == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv.front() == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv.first() == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv[4] == QLatin1Char('o'));
+ Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o'));
+ Q_STATIC_ASSERT(sv.back() == QLatin1Char('o'));
+ Q_STATIC_ASSERT(sv.last() == QLatin1Char('o'));
+
+ constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size());
+ Q_STATIC_ASSERT(!sv2.isNull());
+ Q_STATIC_ASSERT(!sv2.empty());
+ Q_STATIC_ASSERT(sv2.size() == 5);
+
+ constexpr char16_t *null = nullptr;
+ constexpr QStringView sv3(null);
+ Q_STATIC_ASSERT(sv3.isNull());
+ Q_STATIC_ASSERT(sv3.isEmpty());
+ Q_STATIC_ASSERT(sv3.size() == 0);
+ }
+#else // storage_type is wchar_t
+ {
+ Q_STATIC_ASSERT(QStringView(L"Hello").size() == 5);
+ constexpr QStringView sv = L"Hello";
+ Q_STATIC_ASSERT(sv.size() == 5);
+ Q_STATIC_ASSERT(!sv.empty());
+ Q_STATIC_ASSERT(!sv.isEmpty());
+ Q_STATIC_ASSERT(!sv.isNull());
+ Q_STATIC_ASSERT(*sv.utf16() == 'H');
+ Q_STATIC_ASSERT(sv[0] == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv.at(0) == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv.front() == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv.first() == QLatin1Char('H'));
+ Q_STATIC_ASSERT(sv[4] == QLatin1Char('o'));
+ Q_STATIC_ASSERT(sv.at(4) == QLatin1Char('o'));
+ Q_STATIC_ASSERT(sv.back() == QLatin1Char('o'));
+ Q_STATIC_ASSERT(sv.last() == QLatin1Char('o'));
+
+ constexpr QStringView sv2(sv.utf16(), sv.utf16() + sv.size());
+ Q_STATIC_ASSERT(!sv2.isNull());
+ Q_STATIC_ASSERT(!sv2.empty());
+ Q_STATIC_ASSERT(sv2.size() == 5);
+
+ constexpr wchar_t *null = nullptr;
+ constexpr QStringView sv3(null);
+ Q_STATIC_ASSERT(sv3.isNull());
+ Q_STATIC_ASSERT(sv3.isEmpty());
+ Q_STATIC_ASSERT(sv3.size() == 0);
+ }
+#endif
+#endif
+}
+
+void tst_QStringView::basics() const
+{
+ QStringView sv1;
+
+ // a default-constructed QStringView is null:
+ QVERIFY(sv1.isNull());
+ // which implies it's empty();
+ QVERIFY(sv1.isEmpty());
+
+ QStringView sv2;
+
+ QVERIFY(sv2 == sv1);
+ QVERIFY(!(sv2 != sv1));
+}
+
+void tst_QStringView::literals() const
+{
+#if !defined(Q_OS_WIN) || defined(Q_COMPILER_UNICODE_STRINGS)
+ const char16_t hello[] = u"Hello";
+ const char16_t longhello[] =
+ u"Hello World. This is a much longer message, to exercise qustrlen.";
+ const char16_t withnull[] = u"a\0zzz";
+#else // storage_type is wchar_t
+ const wchar_t hello[] = L"Hello";
+ const wchar_t longhello[] =
+ L"Hello World. This is a much longer message, to exercise qustrlen.";
+ const wchar_t withnull[] = L"a\0zzz";
+#endif
+ Q_STATIC_ASSERT(sizeof(longhello) >= 16);
+
+ QCOMPARE(QStringView(hello).size(), 5);
+ QCOMPARE(QStringView(hello + 0).size(), 5); // forces decay to pointer
+ QStringView sv = hello;
+ QCOMPARE(sv.size(), 5);
+ QVERIFY(!sv.empty());
+ QVERIFY(!sv.isEmpty());
+ QVERIFY(!sv.isNull());
+ QCOMPARE(*sv.utf16(), 'H');
+ QCOMPARE(sv[0], QLatin1Char('H'));
+ QCOMPARE(sv.at(0), QLatin1Char('H'));
+ QCOMPARE(sv.front(), QLatin1Char('H'));
+ QCOMPARE(sv.first(), QLatin1Char('H'));
+ QCOMPARE(sv[4], QLatin1Char('o'));
+ QCOMPARE(sv.at(4), QLatin1Char('o'));
+ QCOMPARE(sv.back(), QLatin1Char('o'));
+ QCOMPARE(sv.last(), QLatin1Char('o'));
+
+ QStringView sv2(sv.utf16(), sv.utf16() + sv.size());
+ QVERIFY(!sv2.isNull());
+ QVERIFY(!sv2.empty());
+ QCOMPARE(sv2.size(), 5);
+
+ QStringView sv3(longhello);
+ QCOMPARE(size_t(sv3.size()), sizeof(longhello)/sizeof(longhello[0]) - 1);
+ QCOMPARE(sv3.last(), QLatin1Char('.'));
+ sv3 = longhello;
+ QCOMPARE(size_t(sv3.size()), sizeof(longhello)/sizeof(longhello[0]) - 1);
+
+ for (int i = 0; i < sv3.size(); ++i) {
+ QStringView sv4(longhello + i);
+ QCOMPARE(size_t(sv4.size()), sizeof(longhello)/sizeof(longhello[0]) - 1 - i);
+ QCOMPARE(sv4.last(), QLatin1Char('.'));
+ sv4 = longhello + i;
+ QCOMPARE(size_t(sv4.size()), sizeof(longhello)/sizeof(longhello[0]) - 1 - i);
+ }
+
+ // these are different results
+ QCOMPARE(size_t(QStringView(withnull).size()), sizeof(withnull)/sizeof(withnull[0]) - 1);
+ QCOMPARE(QStringView(withnull + 0).size(), 1);
+}
+
+void tst_QStringView::at() const
+{
+ QString hello("Hello");
+ QStringView sv(hello);
+ QCOMPARE(sv.at(0), QChar('H')); QCOMPARE(sv[0], QChar('H'));
+ QCOMPARE(sv.at(1), QChar('e')); QCOMPARE(sv[1], QChar('e'));
+ QCOMPARE(sv.at(2), QChar('l')); QCOMPARE(sv[2], QChar('l'));
+ QCOMPARE(sv.at(3), QChar('l')); QCOMPARE(sv[3], QChar('l'));
+ QCOMPARE(sv.at(4), QChar('o')); QCOMPARE(sv[4], QChar('o'));
+}
+
+void tst_QStringView::fromQString() const
+{
+ QString null;
+ QString empty = "";
+
+ QVERIFY( QStringView(null).isNull());
+ QVERIFY( QStringView(null).isEmpty());
+ QVERIFY( QStringView(empty).isEmpty());
+ QVERIFY(!QStringView(empty).isNull());
+
+ conversion_tests(QString("Hello World!"));
+}
+
+void tst_QStringView::fromQStringRef() const
+{
+ QStringRef null;
+ QString emptyS = "";
+ QStringRef empty(&emptyS);
+
+ QVERIFY( QStringView(null).isNull());
+ QVERIFY( QStringView(null).isEmpty());
+ QVERIFY( QStringView(empty).isEmpty());
+ QVERIFY(!QStringView(empty).isNull());
+
+ conversion_tests(QString("Hello World!").midRef(6));
+}
+
+template <typename Char>
+void tst_QStringView::fromLiteral(const Char *arg) const
+{
+ const Char *null = nullptr;
+ const Char empty[] = { 0 };
+
+ QCOMPARE(QStringView(null).size(), qsizetype(0));
+ QCOMPARE(QStringView(null).data(), nullptr);
+ QCOMPARE(QStringView(empty).size(), qsizetype(0));
+ QCOMPARE(static_cast<const void*>(QStringView(empty).data()),
+ static_cast<const void*>(empty));
+
+ QVERIFY( QStringView(null).isNull());
+ QVERIFY( QStringView(null).isEmpty());
+ QVERIFY( QStringView(empty).isEmpty());
+ QVERIFY(!QStringView(empty).isNull());
+
+ conversion_tests(arg);
+}
+
+template <typename Char>
+void tst_QStringView::fromRange(const Char *first, const Char *last) const
+{
+ const Char *null = nullptr;
+ QCOMPARE(QStringView(null, null).size(), 0);
+ QCOMPARE(QStringView(null, null).data(), nullptr);
+ QCOMPARE(QStringView(first, first).size(), 0);
+ QCOMPARE(static_cast<const void*>(QStringView(first, first).data()),
+ static_cast<const void*>(first));
+
+ const auto sv = QStringView(first, last);
+ QCOMPARE(sv.size(), last - first);
+ QCOMPARE(static_cast<const void*>(sv.data()),
+ static_cast<const void*>(first));
+
+ // can't call conversion_tests() here, as it requires a single object
+}
+
+template <typename Char, typename Container>
+void tst_QStringView::fromContainer() const
+{
+ const QString s = "Hello World!";
+
+ Container c;
+ // unspecified whether empty containers make null QStringViews
+ QVERIFY(QStringView(c).isEmpty());
+
+ QCOMPARE(sizeof(Char), sizeof(QChar));
+
+ const auto *data = reinterpret_cast<const Char *>(s.utf16());
+ std::copy(data, data + s.size(), std::back_inserter(c));
+ conversion_tests(std::move(c));
+}
+
+namespace help {
+template <typename T>
+size_t size(const T &t) { return size_t(t.size()); }
+template <typename T>
+size_t size(const T *t)
+{
+ size_t result = 0;
+ if (t) {
+ while (*t++)
+ ++result;
+ }
+ return result;
+}
+size_t size(const QChar *t)
+{
+ size_t result = 0;
+ if (t) {
+ while (!t++->isNull())
+ ++result;
+ }
+ return result;
+}
+
+template <typename T>
+typename T::const_iterator cbegin(const T &t) { return t.cbegin(); }
+template <typename T>
+const T * cbegin(const T *t) { return t; }
+
+template <typename T>
+typename T::const_iterator cend(const T &t) { return t.cend(); }
+template <typename T>
+const T * cend(const T *t) { return t + size(t); }
+
+template <typename T>
+typename T::const_reverse_iterator crbegin(const T &t) { return t.crbegin(); }
+template <typename T>
+std::reverse_iterator<const T*> crbegin(const T *t) { return std::reverse_iterator<const T*>(cend(t)); }
+
+template <typename T>
+typename T::const_reverse_iterator crend(const T &t) { return t.crend(); }
+template <typename T>
+std::reverse_iterator<const T*> crend(const T *t) { return std::reverse_iterator<const T*>(cbegin(t)); }
+
+} // namespace help
+
+template <typename String>
+void tst_QStringView::conversion_tests(String string) const
+{
+ // copy-construct:
+ {
+ QStringView sv = string;
+
+ QCOMPARE(help::size(sv), help::size(string));
+
+ // check iterators:
+
+ QVERIFY(std::equal(help::cbegin(string), help::cend(string),
+ QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.cbegin(), sv.size())));
+ QVERIFY(std::equal(help::cbegin(string), help::cend(string),
+ QT_MAKE_CHECKED_ARRAY_ITERATOR(sv.begin(), sv.size())));
+ QVERIFY(std::equal(help::crbegin(string), help::crend(string),
+ sv.crbegin()));
+ QVERIFY(std::equal(help::crbegin(string), help::crend(string),
+ sv.rbegin()));
+
+ QCOMPARE(sv, string);
+ }
+
+ QStringView sv;
+
+ // copy-assign:
+ {
+ sv = string;
+
+ QCOMPARE(help::size(sv), help::size(string));
+
+ // check relational operators:
+
+ QCOMPARE(sv, string);
+ QCOMPARE(string, sv);
+
+ QVERIFY(!(sv != string));
+ QVERIFY(!(string != sv));
+
+ QVERIFY(!(sv < string));
+ QVERIFY(sv <= string);
+ QVERIFY(!(sv > string));
+ QVERIFY(sv >= string);
+
+ QVERIFY(!(string < sv));
+ QVERIFY(string <= sv);
+ QVERIFY(!(string > sv));
+ QVERIFY(string >= sv);
+ }
+
+ // copy-construct from rvalue (QStringView never assumes ownership):
+ {
+ QStringView sv2 = std::move(string);
+ QCOMPARE(sv2, sv);
+ QCOMPARE(sv2, string);
+ }
+
+ // copy-assign from rvalue (QStringView never assumes ownership):
+ {
+ QStringView sv2;
+ sv2 = std::move(string);
+ QCOMPARE(sv2, sv);
+ QCOMPARE(sv2, string);
+ }
+}
+
+QTEST_APPLESS_MAIN(tst_QStringView)
+#include "tst_qstringview.moc"
diff --git a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp
index e75ed5cc67..9f22c3d51d 100644
--- a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp
+++ b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp
@@ -62,7 +62,9 @@ private slots:
private:
void printTimeZone(const QTimeZone &tz);
#ifdef QT_BUILD_INTERNAL
+ // Generic tests of privates, called by implementation-specific private tests:
void testCetPrivate(const QTimeZonePrivate &tzp);
+ void testEpochTranPrivate(const QTimeZonePrivate &tzp);
#endif // QT_BUILD_INTERNAL
const bool debug;
};
@@ -167,11 +169,11 @@ void tst_QTimeZone::createTest()
QDateTime jun = QDateTime(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC);
QDateTime janPrev = QDateTime(QDate(2011, 1, 1), QTime(0, 0, 0), Qt::UTC);
- QCOMPARE(tz.offsetFromUtc(jan), 46800);
- QCOMPARE(tz.offsetFromUtc(jun), 43200);
+ QCOMPARE(tz.offsetFromUtc(jan), 13 * 3600);
+ QCOMPARE(tz.offsetFromUtc(jun), 12 * 3600);
- QCOMPARE(tz.standardTimeOffset(jan), 43200);
- QCOMPARE(tz.standardTimeOffset(jun), 43200);
+ QCOMPARE(tz.standardTimeOffset(jan), 12 * 3600);
+ QCOMPARE(tz.standardTimeOffset(jun), 12 * 3600);
QCOMPARE(tz.daylightTimeOffset(jan), 3600);
QCOMPARE(tz.daylightTimeOffset(jun), 0);
@@ -183,38 +185,46 @@ void tst_QTimeZone::createTest()
// Only test transitions if host system supports them
if (tz.hasTransitions()) {
QTimeZone::OffsetData tran = tz.nextTransition(jan);
- QCOMPARE(tran.atUtc.toMSecsSinceEpoch(), (qint64)1333202400000);
- QCOMPARE(tran.offsetFromUtc, 43200);
- QCOMPARE(tran.standardTimeOffset, 43200);
+ // 2012-04-01 03:00 NZDT, +13 -> +12
+ QCOMPARE(tran.atUtc,
+ QDateTime(QDate(2012, 4, 1), QTime(3, 0), Qt::OffsetFromUTC, 13 * 3600));
+ QCOMPARE(tran.offsetFromUtc, 12 * 3600);
+ QCOMPARE(tran.standardTimeOffset, 12 * 3600);
QCOMPARE(tran.daylightTimeOffset, 0);
tran = tz.nextTransition(jun);
- QCOMPARE(tran.atUtc.toMSecsSinceEpoch(), (qint64)1348927200000);
- QCOMPARE(tran.offsetFromUtc, 46800);
- QCOMPARE(tran.standardTimeOffset, 43200);
+ // 2012-09-30 02:00 NZST, +12 -> +13
+ QCOMPARE(tran.atUtc,
+ QDateTime(QDate(2012, 9, 30), QTime(2, 0), Qt::OffsetFromUTC, 12 * 3600));
+ QCOMPARE(tran.offsetFromUtc, 13 * 3600);
+ QCOMPARE(tran.standardTimeOffset, 12 * 3600);
QCOMPARE(tran.daylightTimeOffset, 3600);
tran = tz.previousTransition(jan);
- QCOMPARE(tran.atUtc.toMSecsSinceEpoch(), (qint64)1316872800000);
- QCOMPARE(tran.offsetFromUtc, 46800);
- QCOMPARE(tran.standardTimeOffset, 43200);
+ // 2011-09-25 02:00 NZST, +12 -> +13
+ QCOMPARE(tran.atUtc,
+ QDateTime(QDate(2011, 9, 25), QTime(2, 0), Qt::OffsetFromUTC, 12 * 3600));
+ QCOMPARE(tran.offsetFromUtc, 13 * 3600);
+ QCOMPARE(tran.standardTimeOffset, 12 * 3600);
QCOMPARE(tran.daylightTimeOffset, 3600);
tran = tz.previousTransition(jun);
- QCOMPARE(tran.atUtc.toMSecsSinceEpoch(), (qint64)1333202400000);
- QCOMPARE(tran.offsetFromUtc, 43200);
- QCOMPARE(tran.standardTimeOffset, 43200);
+ // 2012-04-01 03:00 NZDT, +13 -> +12 (again)
+ QCOMPARE(tran.atUtc,
+ QDateTime(QDate(2012, 4, 1), QTime(3, 0), Qt::OffsetFromUTC, 13 * 3600));
+ QCOMPARE(tran.offsetFromUtc, 12 * 3600);
+ QCOMPARE(tran.standardTimeOffset, 12 * 3600);
QCOMPARE(tran.daylightTimeOffset, 0);
QTimeZone::OffsetDataList expected;
- tran.atUtc = QDateTime::fromMSecsSinceEpoch(1301752800000, Qt::UTC);
- tran.offsetFromUtc = 46800;
- tran.standardTimeOffset = 43200;
+ tran.atUtc = QDateTime(QDate(2011, 4, 3), QTime(2, 0), Qt::OffsetFromUTC, 13 * 3600);
+ tran.offsetFromUtc = 13 * 3600;
+ tran.standardTimeOffset = 12 * 3600;
tran.daylightTimeOffset = 3600;
expected << tran;
- tran.atUtc = QDateTime::fromMSecsSinceEpoch(1316872800000, Qt::UTC);
- tran.offsetFromUtc = 43200;
- tran.standardTimeOffset = 43200;
+ tran.atUtc = QDateTime(QDate(2011, 9, 25), QTime(2, 0), Qt::OffsetFromUTC, 12 * 3600);
+ tran.offsetFromUtc = 12 * 3600;
+ tran.standardTimeOffset = 12 * 3600;
tran.daylightTimeOffset = 0;
expected << tran;
QTimeZone::OffsetDataList result = tz.transitions(janPrev, jan);
@@ -749,12 +759,13 @@ void tst_QTimeZone::icuTest()
}
testCetPrivate(tzp);
+ testEpochTranPrivate(QIcuTimeZonePrivate("America/Toronto"));
#endif // QT_USE_ICU
}
void tst_QTimeZone::tzTest()
{
-#if defined QT_BUILD_INTERNAL && defined Q_OS_UNIX && !defined Q_OS_MAC
+#if defined QT_BUILD_INTERNAL && defined Q_OS_UNIX && !defined Q_OS_DARWIN
// Known datetimes
qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch();
qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch();
@@ -822,6 +833,7 @@ void tst_QTimeZone::tzTest()
}
testCetPrivate(tzp);
+ testEpochTranPrivate(QTzTimeZonePrivate("America/Toronto"));
// Test first and last transition rule
// Warning: This could vary depending on age of TZ file!
@@ -845,7 +857,8 @@ void tst_QTimeZone::tzTest()
}
dat = tzp.nextTransition(-9999999999999);
- QCOMPARE(dat.atMSecsSinceEpoch, (qint64)-2422054408000);
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch, Qt::OffsetFromUTC, 3600),
+ QDateTime(QDate(1893, 4, 1), QTime(0, 6, 32), Qt::OffsetFromUTC, 3600));
QCOMPARE(dat.standardTimeOffset, 3600);
QCOMPARE(dat.daylightTimeOffset, 0);
@@ -855,37 +868,41 @@ void tst_QTimeZone::tzTest()
// Tets high dates use the POSIX rule
dat = tzp.data(stdHi);
- QCOMPARE(dat.atMSecsSinceEpoch, (qint64)stdHi);
+ QCOMPARE(dat.atMSecsSinceEpoch - stdHi, (qint64)0);
QCOMPARE(dat.offsetFromUtc, 3600);
QCOMPARE(dat.standardTimeOffset, 3600);
QCOMPARE(dat.daylightTimeOffset, 0);
dat = tzp.data(dstHi);
- QCOMPARE(dat.atMSecsSinceEpoch, (qint64)dstHi);
+ QCOMPARE(dat.atMSecsSinceEpoch - dstHi, (qint64)0);
QCOMPARE(dat.offsetFromUtc, 7200);
QCOMPARE(dat.standardTimeOffset, 3600);
QCOMPARE(dat.daylightTimeOffset, 3600);
dat = tzp.previousTransition(stdHi);
- QCOMPARE(dat.atMSecsSinceEpoch, (qint64)4096659600000);
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch, Qt::OffsetFromUTC, 3600),
+ QDateTime(QDate(2099, 10, 26), QTime(2, 0), Qt::OffsetFromUTC, 3600));
QCOMPARE(dat.offsetFromUtc, 3600);
QCOMPARE(dat.standardTimeOffset, 3600);
QCOMPARE(dat.daylightTimeOffset, 0);
dat = tzp.previousTransition(dstHi);
- QCOMPARE(dat.atMSecsSinceEpoch, (qint64)4109965200000);
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch, Qt::OffsetFromUTC, 3600),
+ QDateTime(QDate(2100, 3, 29), QTime(2, 0), Qt::OffsetFromUTC, 3600));
QCOMPARE(dat.offsetFromUtc, 7200);
QCOMPARE(dat.standardTimeOffset, 3600);
QCOMPARE(dat.daylightTimeOffset, 3600);
dat = tzp.nextTransition(stdHi);
- QCOMPARE(dat.atMSecsSinceEpoch, (qint64)4109965200000);
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch, Qt::OffsetFromUTC, 3600),
+ QDateTime(QDate(2100, 3, 29), QTime(2, 0), Qt::OffsetFromUTC, 3600));
QCOMPARE(dat.offsetFromUtc, 7200);
QCOMPARE(dat.standardTimeOffset, 3600);
QCOMPARE(dat.daylightTimeOffset, 3600);
dat = tzp.nextTransition(dstHi);
- QCOMPARE(dat.atMSecsSinceEpoch, (qint64)4128109200000);
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(dat.atMSecsSinceEpoch, Qt::OffsetFromUTC, 3600),
+ QDateTime(QDate(2100, 10, 25), QTime(2, 0), Qt::OffsetFromUTC, 3600));
QCOMPARE(dat.offsetFromUtc, 3600);
QCOMPARE(dat.standardTimeOffset, 3600);
QCOMPARE(dat.daylightTimeOffset, 0);
@@ -917,12 +934,12 @@ void tst_QTimeZone::tzTest()
QDateTime dt(QDate(2016, 3, 28), QTime(0, 0, 0), Qt::UTC);
QCOMPARE(tzBarnaul.data(dt.toMSecsSinceEpoch()).abbreviation, QString("+07"));
}
-#endif // Q_OS_UNIX
+#endif // QT_BUILD_INTERNAL && Q_OS_UNIX && !Q_OS_DARWIN
}
void tst_QTimeZone::macTest()
{
-#if defined(QT_BUILD_INTERNAL) && defined (Q_OS_MAC)
+#if defined(QT_BUILD_INTERNAL) && defined(Q_OS_DARWIN)
// Known datetimes
qint64 std = QDateTime(QDate(2012, 1, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch();
qint64 dst = QDateTime(QDate(2012, 6, 1), QTime(0, 0, 0), Qt::UTC).toMSecsSinceEpoch();
@@ -969,7 +986,8 @@ void tst_QTimeZone::macTest()
}
testCetPrivate(tzp);
-#endif // Q_OS_MAC
+ testEpochTranPrivate(QMacTimeZonePrivate("America/Toronto"));
+#endif // QT_BUILD_INTERNAL && Q_OS_DARWIN
}
void tst_QTimeZone::darwinTypes()
@@ -1034,6 +1052,7 @@ void tst_QTimeZone::winTest()
}
testCetPrivate(tzp);
+ testEpochTranPrivate(QWinTimeZonePrivate("America/Toronto"));
#endif // Q_OS_WIN
}
@@ -1076,50 +1095,103 @@ void tst_QTimeZone::testCetPrivate(const QTimeZonePrivate &tzp)
// Only test transitions if host system supports them
if (tzp.hasTransitions()) {
QTimeZonePrivate::Data tran = tzp.nextTransition(std);
- QCOMPARE(tran.atMSecsSinceEpoch, (qint64)1332637200000);
+ // 2012-03-25 02:00 CET, +1 -> +2
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, Qt::UTC),
+ QDateTime(QDate(2012, 3, 25), QTime(2, 0), Qt::OffsetFromUTC, 3600));
QCOMPARE(tran.offsetFromUtc, 7200);
QCOMPARE(tran.standardTimeOffset, 3600);
QCOMPARE(tran.daylightTimeOffset, 3600);
tran = tzp.nextTransition(dst);
- QCOMPARE(tran.atMSecsSinceEpoch, (qint64)1351386000000);
+ // 2012-10-28 03:00 CEST, +2 -> +1
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, Qt::UTC),
+ QDateTime(QDate(2012, 10, 28), QTime(3, 0), Qt::OffsetFromUTC, 2 * 3600));
QCOMPARE(tran.offsetFromUtc, 3600);
QCOMPARE(tran.standardTimeOffset, 3600);
QCOMPARE(tran.daylightTimeOffset, 0);
tran = tzp.previousTransition(std);
- QCOMPARE(tran.atMSecsSinceEpoch, (qint64)1319936400000);
+ // 2011-10-30 03:00 CEST, +2 -> +1
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, Qt::UTC),
+ QDateTime(QDate(2011, 10, 30), QTime(3, 0), Qt::OffsetFromUTC, 2 * 3600));
QCOMPARE(tran.offsetFromUtc, 3600);
QCOMPARE(tran.standardTimeOffset, 3600);
QCOMPARE(tran.daylightTimeOffset, 0);
tran = tzp.previousTransition(dst);
- QCOMPARE(tran.atMSecsSinceEpoch, (qint64)1332637200000);
+ // 2012-03-25 02:00 CET, +1 -> +2 (again)
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, Qt::UTC),
+ QDateTime(QDate(2012, 3, 25), QTime(2, 0), Qt::OffsetFromUTC, 3600));
QCOMPARE(tran.offsetFromUtc, 7200);
QCOMPARE(tran.standardTimeOffset, 3600);
QCOMPARE(tran.daylightTimeOffset, 3600);
QTimeZonePrivate::DataList expected;
- tran.atMSecsSinceEpoch = (qint64)1301752800000;
+ // 2011-03-27 02:00 CET, +1 -> +2
+ tran.atMSecsSinceEpoch = QDateTime(QDate(2011, 3, 27), QTime(2, 0),
+ Qt::OffsetFromUTC, 3600).toMSecsSinceEpoch();
tran.offsetFromUtc = 7200;
tran.standardTimeOffset = 3600;
tran.daylightTimeOffset = 3600;
expected << tran;
- tran.atMSecsSinceEpoch = (qint64)1316872800000;
+ // 2011-10-30 03:00 CEST, +2 -> +1
+ tran.atMSecsSinceEpoch = QDateTime(QDate(2011, 10, 30), QTime(3, 0),
+ Qt::OffsetFromUTC, 2 * 3600).toMSecsSinceEpoch();
tran.offsetFromUtc = 3600;
tran.standardTimeOffset = 3600;
tran.daylightTimeOffset = 0;
expected << tran;
QTimeZonePrivate::DataList result = tzp.transitions(prev, std);
QCOMPARE(result.count(), expected.count());
- for (int i = 0; i > expected.count(); ++i) {
- QCOMPARE(result.at(i).atMSecsSinceEpoch, expected.at(i).atMSecsSinceEpoch);
+ for (int i = 0; i < expected.count(); ++i) {
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(result.at(i).atMSecsSinceEpoch,
+ Qt::OffsetFromUTC, 3600),
+ QDateTime::fromMSecsSinceEpoch(expected.at(i).atMSecsSinceEpoch,
+ Qt::OffsetFromUTC, 3600));
QCOMPARE(result.at(i).offsetFromUtc, expected.at(i).offsetFromUtc);
QCOMPARE(result.at(i).standardTimeOffset, expected.at(i).standardTimeOffset);
QCOMPARE(result.at(i).daylightTimeOffset, expected.at(i).daylightTimeOffset);
}
}
}
+
+// Needs a zone with DST around the epoch; currently America/Toronto (EST5EDT)
+void tst_QTimeZone::testEpochTranPrivate(const QTimeZonePrivate &tzp)
+{
+ if (!tzp.hasTransitions())
+ return; // test only viable for transitions
+
+ QTimeZonePrivate::Data tran = tzp.nextTransition(0); // i.e. first after epoch
+ // 1970-04-26 02:00 EST, -5 -> -4
+ const QDateTime after = QDateTime(QDate(1970, 4, 26), QTime(2, 0), Qt::OffsetFromUTC, -5 * 3600);
+ const QDateTime found = QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, Qt::UTC);
+#ifdef Q_OS_WIN // MS gets the date wrong: 5th April instead of 26th.
+ QCOMPARE(found.toOffsetFromUtc(-5 * 3600).time(), after.time());
+#else
+ QCOMPARE(found, after);
+#endif
+ QCOMPARE(tran.offsetFromUtc, -4 * 3600);
+ QCOMPARE(tran.standardTimeOffset, -5 * 3600);
+ QCOMPARE(tran.daylightTimeOffset, 3600);
+
+ // Pre-epoch time-zones might not be supported at all:
+ tran = tzp.nextTransition(QDateTime(QDate(1601, 1, 1), QTime(0, 0),
+ Qt::UTC).toMSecsSinceEpoch());
+ if (tran.atMSecsSinceEpoch != QTimeZonePrivate::invalidSeconds()
+ && tran.atMSecsSinceEpoch < 0) {
+ // ... but, if they are, we should be able to search back to them:
+ tran = tzp.previousTransition(0); // i.e. last before epoch
+ // 1969-10-26 02:00 EDT, -4 -> -5
+ QCOMPARE(QDateTime::fromMSecsSinceEpoch(tran.atMSecsSinceEpoch, Qt::UTC),
+ QDateTime(QDate(1969, 10, 26), QTime(2, 0), Qt::OffsetFromUTC, -4 * 3600));
+ QCOMPARE(tran.offsetFromUtc, -5 * 3600);
+ QCOMPARE(tran.standardTimeOffset, -5 * 3600);
+ QCOMPARE(tran.daylightTimeOffset, 0);
+ } else {
+ // Do not use QSKIP(): that would discard the rest of this sub-test's caller.
+ qDebug() << "No support for pre-epoch time-zone transitions";
+ }
+}
#endif // QT_BUILD_INTERNAL
QTEST_APPLESS_MAIN(tst_QTimeZone)
diff --git a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp
index 0806ad1318..3971353cbb 100644
--- a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp
+++ b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp
@@ -30,8 +30,6 @@
#include <qvarlengtharray.h>
#include <qvariant.h>
-const int N = 1;
-
class tst_QVarLengthArray : public QObject
{
Q_OBJECT
diff --git a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp
index 2e34e82388..9812d93a50 100644
--- a/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp
+++ b/tests/auto/corelib/tools/qversionnumber/tst_qversionnumber.cpp
@@ -513,6 +513,14 @@ void tst_QVersionNumber::fromString()
QCOMPARE(QVersionNumber::fromString(constructionString), expectedVersion);
QCOMPARE(QVersionNumber::fromString(constructionString, &index), expectedVersion);
QCOMPARE(index, suffixIndex);
+
+ QCOMPARE(QVersionNumber::fromString(QStringView(constructionString)), expectedVersion);
+ QCOMPARE(QVersionNumber::fromString(QStringView(constructionString), &index), expectedVersion);
+ QCOMPARE(index, suffixIndex);
+
+ QCOMPARE(QVersionNumber::fromString(QLatin1String(constructionString.toLatin1())), expectedVersion);
+ QCOMPARE(QVersionNumber::fromString(QLatin1String(constructionString.toLatin1()), &index), expectedVersion);
+ QCOMPARE(index, suffixIndex);
}
void tst_QVersionNumber::toString_data()
diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro
index e45771a704..f35ed026ac 100644
--- a/tests/auto/corelib/tools/tools.pro
+++ b/tests/auto/corelib/tools/tools.pro
@@ -1,6 +1,7 @@
TEMPLATE=subdirs
SUBDIRS=\
collections \
+ containerapisymmetry \
qalgorithms \
qarraydata \
qarraydata_strictiterators \
@@ -56,6 +57,7 @@ SUBDIRS=\
qstringlist \
qstringmatcher \
qstringref \
+ qstringview \
qtextboundaryfinder \
qtime \
qtimezone \
@@ -65,3 +67,4 @@ SUBDIRS=\
qvector_strictiterators \
qversionnumber
+darwin: SUBDIRS += qmacautoreleasepool