diff options
Diffstat (limited to 'tests/auto/corelib')
51 files changed, 7224 insertions, 1569 deletions
diff --git a/tests/auto/corelib/codecs/utf8/tst_utf8.cpp b/tests/auto/corelib/codecs/utf8/tst_utf8.cpp index c0ed152c32..e513b2cea2 100644 --- a/tests/auto/corelib/codecs/utf8/tst_utf8.cpp +++ b/tests/auto/corelib/codecs/utf8/tst_utf8.cpp @@ -206,88 +206,8 @@ void tst_Utf8::invalidUtf8_data() { QTest::addColumn<QByteArray>("utf8"); - QTest::newRow("1char") << QByteArray("\x80"); - QTest::newRow("2chars-1") << QByteArray("\xC2\xC0"); - QTest::newRow("2chars-2") << QByteArray("\xC3\xDF"); - QTest::newRow("2chars-3") << QByteArray("\xC7\xF0"); - QTest::newRow("3chars-1") << QByteArray("\xE0\xA0\xC0"); - QTest::newRow("3chars-2") << QByteArray("\xE0\xC0\xA0"); - QTest::newRow("4chars-1") << QByteArray("\xF0\x90\x80\xC0"); - QTest::newRow("4chars-2") << QByteArray("\xF0\x90\xC0\x80"); - QTest::newRow("4chars-3") << QByteArray("\xF0\xC0\x80\x80"); - - // Surrogate pairs must now be present either - // U+D800: 1101 10 0000 00 0000 - // encoding: xxxz:1101 xz10:0000 xz00:0000 - QTest::newRow("hi-surrogate") << QByteArray("\xED\xA0\x80"); - // U+DC00: 1101 11 0000 00 0000 - // encoding: xxxz:1101 xz11:0000 xz00:0000 - QTest::newRow("lo-surrogate") << QByteArray("\xED\xB0\x80"); - - // not even in pair: - QTest::newRow("surrogate-pair") << QByteArray("\xED\xA0\x80\xED\xB0\x80"); - - // Characters outside the Unicode range: - // 0x110000: 00 0100 01 0000 00 0000 00 0000 - // encoding: xxxx:z100 xz01:0000 xz00:0000 xz00:0000 - QTest::newRow("non-unicode-1") << QByteArray("\xF4\x90\x80\x80"); - // 0x200000: 00 1000 00 0000 00 0000 00 0000 - // encoding: xxxx:xz00 xz00:1000 xz00:0000 xz00:0000 xz00:0000 - QTest::newRow("non-unicode-2") << QByteArray("\xF8\x88\x80\x80\x80"); - // 0x04000000: 0100 00 0000 00 0000 00 0000 00 0000 - // encoding: xxxx:xxz0 xz00:0100 xz00:0000 xz00:0000 xz00:0001 xz00:0001 - QTest::newRow("non-unicode-3") << QByteArray("\xFC\x84\x80\x80\x80\x80"); - // 0x7fffffff: 1 11 1111 11 1111 11 1111 11 1111 11 1111 - // encoding: xxxx:xxz0 xz00:0100 xz00:0000 xz00:0000 xz00:0001 xz00:0001 - QTest::newRow("non-unicode-4") << QByteArray("\xFD\xBF\xBF\xBF\xBF\xBF"); - - // As seen above, 0xFE and 0xFF never appear: - QTest::newRow("fe") << QByteArray("\xFE"); - QTest::newRow("fe-bis") << QByteArray("\xFE\xBF\xBF\xBF\xBF\xBF\xBF"); - QTest::newRow("ff") << QByteArray("\xFF"); - QTest::newRow("ff-bis") << QByteArray("\xFF\xBF\xBF\xBF\xBF\xBF\xBF\xBF"); - - // some combinations in UTF-8 are invalid even though they have the proper bits set - // these are known as overlong sequences - - // "A": U+0041: 01 00 0001 - // overlong 2: xxz0:0001 xz00:0001 - QTest::newRow("overlong-1-2") << QByteArray("\xC1\x81"); - // overlong 3: xxxz:0000 xz00:0001 xz00:0001 - QTest::newRow("overlong-1-3") << QByteArray("\xE0\x81\x81"); - // overlong 4: xxxx:z000 xz00:0000 xz00:0001 xz00:0001 - QTest::newRow("overlong-1-4") << QByteArray("\xF0\x80\x81\x81"); - // overlong 5: xxxx:xz00 xz00:0000 xz00:0000 xz00:0001 xz00:0001 - QTest::newRow("overlong-1-5") << QByteArray("\xF8\x80\x80\x81\x81"); - // overlong 6: xxxx:xxz0 xz00:0000 xz00:0000 xz00:0000 xz00:0001 xz00:0001 - QTest::newRow("overlong-1-6") << QByteArray("\xFC\x80\x80\x80\x81\x81"); - - // NBSP: U+00A0: 10 00 0000 - // proper encoding: xxz0:0010 xz00:0000 - // overlong 3: xxxz:0000 xz00:0010 xz00:0000 - QTest::newRow("overlong-2-3") << QByteArray("\xC0\x82\x80"); - // overlong 4: xxxx:z000 xz00:0000 xz00:0010 xz00:0000 - QTest::newRow("overlong-2-4") << QByteArray("\xF0\x80\x82\x80"); - // overlong 5: xxxx:xz00 xz00:0000 xz00:0000 xz00:0010 xz00:0000 - QTest::newRow("overlong-2-5") << QByteArray("\xF8\x80\x80\x82\x80"); - // overlong 6: xxxx:xxz0 xz00:0000 xz00:0000 xz00:0000 xz00:0010 xz00:0000 - QTest::newRow("overlong-2-6") << QByteArray("\xFC\x80\x80\x80\x82\x80"); - - // U+0800: 10 0000 00 0000 - // proper encoding: xxxz:0000 xz10:0000 xz00:0000 - // overlong 4: xxxx:z000 xz00:0000 xz10:0000 xz00:0000 - QTest::newRow("overlong-3-4") << QByteArray("\xF0\x80\xA0\x80"); - // overlong 5: xxxx:xz00 xz00:0000 xz00:0000 xz10:0000 xz00:0000 - QTest::newRow("overlong-3-5") << QByteArray("\xF8\x80\x80\xA0\x80"); - // overlong 6: xxxx:xxz0 xz00:0000 xz00:0000 xz00:0000 xz10:0000 xz00:0000 - QTest::newRow("overlong-3-6") << QByteArray("\xFC\x80\x80\x80\xA0\x80"); - - // U+010000: 00 0100 00 0000 00 0000 - // proper encoding: xxxx:z000 xz00:0100 xz00:0000 xz00:0000 - // overlong 5: xxxx:xz00 xz00:0000 xz00:0100 xz00:0000 xz00:0000 - QTest::newRow("overlong-4-5") << QByteArray("\xF8\x80\x84\x80\x80"); - // overlong 6: xxxx:xxz0 xz00:0000 xz00:0000 xz00:0100 xz00:0000 xz00:0000 - QTest::newRow("overlong-4-6") << QByteArray("\xFC\x80\x80\x84\x80\x80"); + extern void loadInvalidUtf8Rows(); + loadInvalidUtf8Rows(); } void tst_Utf8::invalidUtf8() @@ -344,6 +264,9 @@ void tst_Utf8::nonCharacters_data() QTest::newRow("fffe") << QByteArray("\xEF\xBF\xBE") << QString(QChar(0xfffe)); QTest::newRow("ffff") << QByteArray("\xEF\xBF\xBF") << QString(QChar(0xffff)); + + extern void loadNonCharactersRows(); + loadNonCharactersRows(); } void tst_Utf8::nonCharacters() diff --git a/tests/auto/corelib/codecs/utf8/utf8.pro b/tests/auto/corelib/codecs/utf8/utf8.pro index 2d200e82a4..1df6ac7e50 100644 --- a/tests/auto/corelib/codecs/utf8/utf8.pro +++ b/tests/auto/corelib/codecs/utf8/utf8.pro @@ -1,5 +1,5 @@ CONFIG += testcase TARGET = tst_utf8 QT = core testlib -SOURCES += tst_utf8.cpp +SOURCES += tst_utf8.cpp utf8data.cpp CONFIG += parallel_test diff --git a/tests/auto/corelib/codecs/utf8/utf8data.cpp b/tests/auto/corelib/codecs/utf8/utf8data.cpp new file mode 100644 index 0000000000..2c0bae3e5d --- /dev/null +++ b/tests/auto/corelib/codecs/utf8/utf8data.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtTest/QtTest> + +void loadInvalidUtf8Rows() +{ + QTest::newRow("1char") << QByteArray("\x80"); + QTest::newRow("2chars-1") << QByteArray("\xC2\xC0"); + QTest::newRow("2chars-2") << QByteArray("\xC3\xDF"); + QTest::newRow("2chars-3") << QByteArray("\xC7\xF0"); + QTest::newRow("3chars-1") << QByteArray("\xE0\xA0\xC0"); + QTest::newRow("3chars-2") << QByteArray("\xE0\xC0\xA0"); + QTest::newRow("4chars-1") << QByteArray("\xF0\x90\x80\xC0"); + QTest::newRow("4chars-2") << QByteArray("\xF0\x90\xC0\x80"); + QTest::newRow("4chars-3") << QByteArray("\xF0\xC0\x80\x80"); + + // Surrogate pairs must now be present either + // U+D800: 1101 10 0000 00 0000 + // encoding: xxxz:1101 xz10:0000 xz00:0000 + QTest::newRow("hi-surrogate") << QByteArray("\xED\xA0\x80"); + // U+DC00: 1101 11 0000 00 0000 + // encoding: xxxz:1101 xz11:0000 xz00:0000 + QTest::newRow("lo-surrogate") << QByteArray("\xED\xB0\x80"); + + // not even in pair: + QTest::newRow("surrogate-pair") << QByteArray("\xED\xA0\x80\xED\xB0\x80"); + + // Characters outside the Unicode range: + // 0x110000: 00 0100 01 0000 00 0000 00 0000 + // encoding: xxxx:z100 xz01:0000 xz00:0000 xz00:0000 + QTest::newRow("non-unicode-1") << QByteArray("\xF4\x90\x80\x80"); + // 0x200000: 00 1000 00 0000 00 0000 00 0000 + // encoding: xxxx:xz00 xz00:1000 xz00:0000 xz00:0000 xz00:0000 + QTest::newRow("non-unicode-2") << QByteArray("\xF8\x88\x80\x80\x80"); + // 0x04000000: 0100 00 0000 00 0000 00 0000 00 0000 + // encoding: xxxx:xxz0 xz00:0100 xz00:0000 xz00:0000 xz00:0001 xz00:0001 + QTest::newRow("non-unicode-3") << QByteArray("\xFC\x84\x80\x80\x80\x80"); + // 0x7fffffff: 1 11 1111 11 1111 11 1111 11 1111 11 1111 + // encoding: xxxx:xxz0 xz00:0100 xz00:0000 xz00:0000 xz00:0001 xz00:0001 + QTest::newRow("non-unicode-4") << QByteArray("\xFD\xBF\xBF\xBF\xBF\xBF"); + + // As seen above, 0xFE and 0xFF never appear: + QTest::newRow("fe") << QByteArray("\xFE"); + QTest::newRow("fe-bis") << QByteArray("\xFE\xBF\xBF\xBF\xBF\xBF\xBF"); + QTest::newRow("ff") << QByteArray("\xFF"); + QTest::newRow("ff-bis") << QByteArray("\xFF\xBF\xBF\xBF\xBF\xBF\xBF\xBF"); + + // some combinations in UTF-8 are invalid even though they have the proper bits set + // these are known as overlong sequences + + // "A": U+0041: 01 00 0001 + // overlong 2: xxz0:0001 xz00:0001 + QTest::newRow("overlong-1-2") << QByteArray("\xC1\x81"); + // overlong 3: xxxz:0000 xz00:0001 xz00:0001 + QTest::newRow("overlong-1-3") << QByteArray("\xE0\x81\x81"); + // overlong 4: xxxx:z000 xz00:0000 xz00:0001 xz00:0001 + QTest::newRow("overlong-1-4") << QByteArray("\xF0\x80\x81\x81"); + // overlong 5: xxxx:xz00 xz00:0000 xz00:0000 xz00:0001 xz00:0001 + QTest::newRow("overlong-1-5") << QByteArray("\xF8\x80\x80\x81\x81"); + // overlong 6: xxxx:xxz0 xz00:0000 xz00:0000 xz00:0000 xz00:0001 xz00:0001 + QTest::newRow("overlong-1-6") << QByteArray("\xFC\x80\x80\x80\x81\x81"); + + // NBSP: U+00A0: 10 00 0000 + // proper encoding: xxz0:0010 xz00:0000 + // overlong 3: xxxz:0000 xz00:0010 xz00:0000 + QTest::newRow("overlong-2-3") << QByteArray("\xC0\x82\x80"); + // overlong 4: xxxx:z000 xz00:0000 xz00:0010 xz00:0000 + QTest::newRow("overlong-2-4") << QByteArray("\xF0\x80\x82\x80"); + // overlong 5: xxxx:xz00 xz00:0000 xz00:0000 xz00:0010 xz00:0000 + QTest::newRow("overlong-2-5") << QByteArray("\xF8\x80\x80\x82\x80"); + // overlong 6: xxxx:xxz0 xz00:0000 xz00:0000 xz00:0000 xz00:0010 xz00:0000 + QTest::newRow("overlong-2-6") << QByteArray("\xFC\x80\x80\x80\x82\x80"); + + // U+0800: 10 0000 00 0000 + // proper encoding: xxxz:0000 xz10:0000 xz00:0000 + // overlong 4: xxxx:z000 xz00:0000 xz10:0000 xz00:0000 + QTest::newRow("overlong-3-4") << QByteArray("\xF0\x80\xA0\x80"); + // overlong 5: xxxx:xz00 xz00:0000 xz00:0000 xz10:0000 xz00:0000 + QTest::newRow("overlong-3-5") << QByteArray("\xF8\x80\x80\xA0\x80"); + // overlong 6: xxxx:xxz0 xz00:0000 xz00:0000 xz00:0000 xz10:0000 xz00:0000 + QTest::newRow("overlong-3-6") << QByteArray("\xFC\x80\x80\x80\xA0\x80"); + + // U+010000: 00 0100 00 0000 00 0000 + // proper encoding: xxxx:z000 xz00:0100 xz00:0000 xz00:0000 + // overlong 5: xxxx:xz00 xz00:0000 xz00:0100 xz00:0000 xz00:0000 + QTest::newRow("overlong-4-5") << QByteArray("\xF8\x80\x84\x80\x80"); + // overlong 6: xxxx:xxz0 xz00:0000 xz00:0000 xz00:0100 xz00:0000 xz00:0000 + QTest::newRow("overlong-4-6") << QByteArray("\xFC\x80\x80\x84\x80\x80"); + +} + +void loadNonCharactersRows() +{ + // Unicode has a couple of "non-characters" that one can use internally, + // but are not allowed to be used for text interchange. + // + // Those are the last two entries each Unicode Plane (U+FFFE, U+FFFF, + // U+1FFFE, U+1FFFF, etc.) as well as the entries between U+FDD0 and + // U+FDEF (inclusive) + + // U+FDD0 through U+FDEF + for (int i = 0; i < 16; ++i) { + char utf8[] = { char(0357), char(0267), char(0220 + i), 0 }; + QString utf16 = QChar(0xfdd0 + i); + QTest::newRow(qPrintable(QString::number(0xfdd0 + i, 16))) << QByteArray(utf8) << utf16; + } + + // the last two in Planes 1 through 16 + for (uint plane = 1; plane <= 16; ++plane) { + for (uint lower = 0xfffe; lower < 0x10000; ++lower) { + uint ucs4 = (plane << 16) | lower; + char utf8[] = { char(0xf0 | uchar(ucs4 >> 18)), + char(0x80 | (uchar(ucs4 >> 12) & 0x3f)), + char(0x80 | (uchar(ucs4 >> 6) & 0x3f)), + char(0x80 | (uchar(ucs4) & 0x3f)), + 0 }; + ushort utf16[] = { QChar::highSurrogate(ucs4), QChar::lowSurrogate(ucs4), 0 }; + + QTest::newRow(qPrintable(QString::number(ucs4, 16))) << QByteArray(utf8) << QString::fromUtf16(utf16); + } + } + + QTest::newRow("fffe") << QByteArray("\xEF\xBF\xBE") << QString(QChar(0xfffe)); + QTest::newRow("ffff") << QByteArray("\xEF\xBF\xBF") << QString(QChar(0xffff)); +} diff --git a/tests/auto/corelib/global/global.pro b/tests/auto/corelib/global/global.pro index d4293a896c..5489b8330d 100644 --- a/tests/auto/corelib/global/global.pro +++ b/tests/auto/corelib/global/global.pro @@ -6,4 +6,5 @@ SUBDIRS=\ qglobal \ qnumeric \ qrand \ - qlogging + qlogging \ + qtendian diff --git a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp index b3d76bef8a..529bafaa7a 100644 --- a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp +++ b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp @@ -56,6 +56,7 @@ private slots: void qstaticassert(); void qConstructorFunction(); void isEnum(); + void qAlignOf(); }; void tst_QGlobal::qIsNull() @@ -415,5 +416,115 @@ void tst_QGlobal::isEnum() #undef IS_ENUM_FALSE } +struct Empty {}; +template <class T> struct AlignmentInStruct { T dummy; }; + +typedef int (*fun) (); +typedef int (Empty::*memFun) (); + +#define TEST_AlignOf(type, alignment) \ + do { \ + TEST_AlignOf_impl(type, alignment); \ + \ + TEST_AlignOf_impl(type &, alignment); \ + TEST_AlignOf_RValueRef(type &&, alignment); \ + \ + TEST_AlignOf_impl(type [5], alignment); \ + TEST_AlignOf_impl(type (&) [5], alignment); \ + \ + TEST_AlignOf_impl(AlignmentInStruct<type>, alignment); \ + \ + /* Some internal sanity validation, just for fun */ \ + TEST_AlignOf_impl(AlignmentInStruct<type [5]>, alignment); \ + TEST_AlignOf_impl(AlignmentInStruct<type &>, Q_ALIGNOF(void *)); \ + TEST_AlignOf_impl(AlignmentInStruct<type (&) [5]>, \ + Q_ALIGNOF(void *)); \ + TEST_AlignOf_RValueRef(AlignmentInStruct<type &&>, \ + Q_ALIGNOF(void *)); \ + } while (false) \ + /**/ + +#ifdef Q_COMPILER_RVALUE_REFS +#define TEST_AlignOf_RValueRef(type, alignment) \ + TEST_AlignOf_impl(type, alignment) +#else +#define TEST_AlignOf_RValueRef(type, alignment) do {} while (false) +#endif + +#define TEST_AlignOf_impl(type, alignment) \ + do { \ + QCOMPARE(Q_ALIGNOF(type), size_t(alignment)); \ + /* Compare to native operator for compilers that support it, + otherwise... erm... check consistency! :-) */ \ + QCOMPARE(QT_EMULATED_ALIGNOF(type), Q_ALIGNOF(type)); \ + } while (false) + /**/ + +void tst_QGlobal::qAlignOf() +{ + // Built-in types, except 64-bit integers and double + TEST_AlignOf(char, 1); + TEST_AlignOf(signed char, 1); + TEST_AlignOf(unsigned char, 1); + TEST_AlignOf(qint8, 1); + TEST_AlignOf(quint8, 1); + TEST_AlignOf(qint16, 2); + TEST_AlignOf(quint16, 2); + TEST_AlignOf(qint32, 4); + TEST_AlignOf(quint32, 4); + TEST_AlignOf(void *, sizeof(void *)); + + // Depends on platform and compiler, disabling test for now + // TEST_AlignOf(long double, 16); + + // Empty struct + TEST_AlignOf(Empty, 1); + + // Function pointers + TEST_AlignOf(fun, Q_ALIGNOF(void *)); + TEST_AlignOf(memFun, Q_ALIGNOF(void *)); + + + // 64-bit integers and double + TEST_AlignOf_impl(qint64, 8); + TEST_AlignOf_impl(quint64, 8); + TEST_AlignOf_impl(double, 8); + + TEST_AlignOf_impl(qint64 &, 8); + TEST_AlignOf_impl(quint64 &, 8); + TEST_AlignOf_impl(double &, 8); + + TEST_AlignOf_RValueRef(qint64 &&, 8); + TEST_AlignOf_RValueRef(quint64 &&, 8); + TEST_AlignOf_RValueRef(double &&, 8); + + // 32-bit x86 ABI idiosyncrasies +#if defined(Q_PROCESSOR_X86_32) && !defined(Q_OS_WIN) + TEST_AlignOf_impl(AlignmentInStruct<qint64>, 4); +#else + TEST_AlignOf_impl(AlignmentInStruct<qint64>, 8); +#endif + + TEST_AlignOf_impl(AlignmentInStruct<quint64>, Q_ALIGNOF(AlignmentInStruct<qint64>)); + TEST_AlignOf_impl(AlignmentInStruct<double>, Q_ALIGNOF(AlignmentInStruct<qint64>)); + + // 32-bit x86 ABI, Clang disagrees with gcc +#if !defined(Q_PROCESSOR_X86_32) || !defined(Q_CC_CLANG) + TEST_AlignOf_impl(qint64 [5], Q_ALIGNOF(qint64)); +#else + TEST_AlignOf_impl(qint64 [5], Q_ALIGNOF(AlignmentInStruct<qint64>)); +#endif + + TEST_AlignOf_impl(qint64 (&) [5], Q_ALIGNOF(qint64 [5])); + TEST_AlignOf_impl(quint64 [5], Q_ALIGNOF(quint64 [5])); + TEST_AlignOf_impl(quint64 (&) [5], Q_ALIGNOF(quint64 [5])); + TEST_AlignOf_impl(double [5], Q_ALIGNOF(double [5])); + TEST_AlignOf_impl(double (&) [5], Q_ALIGNOF(double [5])); +} + +#undef TEST_AlignOf +#undef TEST_AlignOf_RValueRef +#undef TEST_AlignOf_impl + QTEST_MAIN(tst_QGlobal) #include "tst_qglobal.moc" diff --git a/tests/auto/corelib/global/qtendian/qtendian.pro b/tests/auto/corelib/global/qtendian/qtendian.pro new file mode 100644 index 0000000000..caad0fc764 --- /dev/null +++ b/tests/auto/corelib/global/qtendian/qtendian.pro @@ -0,0 +1,4 @@ +CONFIG += testcase parallel_test +TARGET = tst_qtendian +QT = core 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 new file mode 100644 index 0000000000..002060b0ef --- /dev/null +++ b/tests/auto/corelib/global/qtendian/tst_qtendian.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtCore/qendian.h> + + +class tst_QtEndian: public QObject +{ + Q_OBJECT + +private slots: + void fromBigEndian(); + void fromLittleEndian(); + + void toBigEndian(); + void toLittleEndian(); +}; + +struct TestData +{ + quint64 data64; + quint32 data32; + quint16 data16; + quint8 data8; + + quint8 reserved; +}; + +union RawTestData +{ + uchar rawData[sizeof(TestData)]; + TestData data; +}; + +static const TestData inNativeEndian = { 0x0123456789abcdef, 0x00c0ffee, 0xcafe, 0xcf, '\0' }; +static const RawTestData inBigEndian = { "\x01\x23\x45\x67\x89\xab\xcd\xef" "\x00\xc0\xff\xee" "\xca\xfe" "\xcf" }; +static const RawTestData inLittleEndian = { "\xef\xcd\xab\x89\x67\x45\x23\x01" "\xee\xff\xc0\x00" "\xfe\xca" "\xcf" }; + +#define EXPAND_ENDIAN_TEST(endian) \ + do { \ + /* Unsigned tests */ \ + ENDIAN_TEST(endian, quint, 64); \ + ENDIAN_TEST(endian, quint, 32); \ + ENDIAN_TEST(endian, quint, 16); \ + ENDIAN_TEST(endian, quint, 8); \ + \ + /* Signed tests */ \ + ENDIAN_TEST(endian, qint, 64); \ + ENDIAN_TEST(endian, qint, 32); \ + ENDIAN_TEST(endian, qint, 16); \ + ENDIAN_TEST(endian, qint, 8); \ + } while (false) \ + /**/ + +#define ENDIAN_TEST(endian, type, size) \ + do { \ + QCOMPARE(qFrom ## endian ## Endian( \ + (type ## size)(in ## endian ## Endian.data.data ## size)), \ + (type ## size)(inNativeEndian.data ## size)); \ + QCOMPARE(qFrom ## endian ## Endian<type ## size>( \ + in ## endian ## Endian.rawData + offsetof(TestData, data ## size)), \ + (type ## size)(inNativeEndian.data ## size)); \ + } while (false) \ + /**/ + +void tst_QtEndian::fromBigEndian() +{ + EXPAND_ENDIAN_TEST(Big); +} + +void tst_QtEndian::fromLittleEndian() +{ + EXPAND_ENDIAN_TEST(Little); +} + +#undef ENDIAN_TEST + + +#define ENDIAN_TEST(endian, type, size) \ + do { \ + QCOMPARE(qTo ## endian ## Endian( \ + (type ## size)(inNativeEndian.data ## size)), \ + (type ## size)(in ## endian ## Endian.data.data ## size)); \ + \ + RawTestData test; \ + qTo ## endian ## Endian( \ + (type ## size)(inNativeEndian.data ## size), \ + test.rawData + offsetof(TestData, data ## size)); \ + QCOMPARE(test.data.data ## size, in ## endian ## Endian.data.data ## size ); \ + } while (false) \ + /**/ + +void tst_QtEndian::toBigEndian() +{ + EXPAND_ENDIAN_TEST(Big); +} + +void tst_QtEndian::toLittleEndian() +{ + EXPAND_ENDIAN_TEST(Little); +} + +#undef ENDIAN_TEST + +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 84a885f5b6..7e0cb5e8ca 100644 --- a/tests/auto/corelib/io/io.pro +++ b/tests/auto/corelib/io/io.pro @@ -12,6 +12,7 @@ SUBDIRS=\ qfilesystementry \ qfilesystemwatcher \ qiodevice \ + qipaddress \ qnodebug \ qprocess \ qprocessenvironment \ @@ -31,7 +32,8 @@ SUBDIRS=\ !contains(QT_CONFIG, private_tests): SUBDIRS -= \ qabstractfileengine \ - qfileinfo + qfileinfo \ + qipaddress win32:!contains(QT_CONFIG, private_tests): SUBDIRS -= \ qfilesystementry diff --git a/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp b/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp index c6ccc9308a..0cafc1d5ad 100644 --- a/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp +++ b/tests/auto/corelib/io/qabstractfileengine/tst_qabstractfileengine.cpp @@ -402,7 +402,7 @@ public: if (readSize < 0) return -1; - qMemCopy(data, openFile_->content.constData() + position_, readSize); + memcpy(data, openFile_->content.constData() + position_, readSize); position_ += readSize; return readSize; diff --git a/tests/auto/corelib/io/qipaddress/qipaddress.pro b/tests/auto/corelib/io/qipaddress/qipaddress.pro new file mode 100644 index 0000000000..41fa55aa15 --- /dev/null +++ b/tests/auto/corelib/io/qipaddress/qipaddress.pro @@ -0,0 +1,4 @@ +SOURCES += tst_qipaddress.cpp +TARGET = tst_qipaddress +QT = core core-private testlib +CONFIG += testcase parallel_test diff --git a/tests/auto/corelib/io/qipaddress/tst_qipaddress.cpp b/tests/auto/corelib/io/qipaddress/tst_qipaddress.cpp new file mode 100644 index 0000000000..bad18fabef --- /dev/null +++ b/tests/auto/corelib/io/qipaddress/tst_qipaddress.cpp @@ -0,0 +1,503 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Intel Corporation. +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QString> +#include <QtTest/QtTest> +#include <QtCore/private/qipaddress_p.h> + +#ifdef __GLIBC__ +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +class tst_QIpAddress : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void parseIp4_data(); + void parseIp4(); + void invalidParseIp4_data(); + void invalidParseIp4(); + void ip4ToString_data(); + void ip4ToString(); + + void parseIp6_data(); + void parseIp6(); + void invalidParseIp6_data(); + void invalidParseIp6(); + void ip6ToString_data(); + void ip6ToString(); +}; + +struct Ip6 +{ + QIPAddressUtils::IPv6Address u8; + Ip6() { *this = Ip6(0,0,0,0, 0,0,0,0); } + Ip6(quint16 p1, quint16 p2, quint16 p3, quint16 p4, + quint16 p5, quint16 p6, quint16 p7, quint16 p8) + { + u8[0] = p1 >> 8; + u8[2] = p2 >> 8; + u8[4] = p3 >> 8; + u8[6] = p4 >> 8; + u8[8] = p5 >> 8; + u8[10] = p6 >> 8; + u8[12] = p7 >> 8; + u8[14] = p8 >> 8; + + u8[1] = p1 & 0xff; + u8[3] = p2 & 0xff; + u8[5] = p3 & 0xff; + u8[7] = p4 & 0xff; + u8[9] = p5 & 0xff; + u8[11] = p6 & 0xff; + u8[13] = p7 & 0xff; + u8[15] = p8 & 0xff; + } + + bool operator==(const Ip6 &other) const + { return memcmp(u8, other.u8, sizeof u8) == 0; } +}; +Q_DECLARE_METATYPE(Ip6) + +QT_BEGIN_NAMESPACE +namespace QTest { + template<> + char *toString(const Ip6 &ip6) + { + char buf[sizeof "1111:2222:3333:4444:5555:6666:7777:8888" + 2]; + sprintf(buf, "%x:%x:%x:%x:%x:%x:%x:%x", + ip6.u8[0] << 8 | ip6.u8[1], + ip6.u8[2] << 8 | ip6.u8[3], + ip6.u8[4] << 8 | ip6.u8[5], + ip6.u8[6] << 8 | ip6.u8[7], + ip6.u8[8] << 8 | ip6.u8[9], + ip6.u8[10] << 8 | ip6.u8[11], + ip6.u8[12] << 8 | ip6.u8[13], + ip6.u8[14] << 8 | ip6.u8[15]); + return strdup(buf); + } +} +QT_END_NAMESPACE + +void tst_QIpAddress::parseIp4_data() +{ + QTest::addColumn<QString>("data"); + QTest::addColumn<QIPAddressUtils::IPv4Address>("ip"); + + // valid strings + QTest::newRow("0.0.0.0") << "0.0.0.0" << 0u; + QTest::newRow("10.0.0.1") << "10.0.0.1" << 0x0a000001u; + QTest::newRow("127.0.0.1") << "127.0.0.1" << 0x7f000001u; + QTest::newRow("172.16.0.1") << "172.16.0.1" << 0xac100001u; + QTest::newRow("172.16.16.1") << "172.16.16.1" << 0xac101001u; + QTest::newRow("172.16.16.16") << "172.16.16.16" << 0xac101010u; + QTest::newRow("192.168.0.1") << "192.168.0.1" << 0xc0a80001u; + QTest::newRow("192.168.16.1") << "192.168.16.1" << 0xc0a81001u; + QTest::newRow("192.168.16.16") << "192.168.16.16" << 0xc0a81010u; + QTest::newRow("192.168.192.1") << "192.168.192.1" << 0xc0a8c001u; + QTest::newRow("192.168.192.16") << "192.168.192.16" << 0xc0a8c010u; + QTest::newRow("192.168.192.255") << "192.168.192.255" << 0xc0a8c0ffu; + QTest::newRow("224.0.0.1") << "224.0.0.1" << 0xe0000001u; + QTest::newRow("239.255.255.255") << "239.255.255.255" << 0xefffffffu; + QTest::newRow("255.255.255.255") << "255.255.255.255" << uint(-1); + + // still valid but unusual + QTest::newRow("000.000.000.000") << "000.000.000.000" << 0u; + QTest::newRow("000001.000002.000000003.000000000004") << "000001.000002.000000003.000000000004" << 0x01020304u; + + // octals: + QTest::newRow("012.0250.0377.0377") << "012.0250.0377.0377" << 0x0aa8ffffu; + QTest::newRow("0000000000012.00000000000250.000000000000377.0000000000000000000000000000000000000377") + << "0000000000012.00000000000250.000000000000377.0000000000000000000000000000000000000377" << 0x0aa8ffffu; + + // hex: + QTest::newRow("0xa.0xa.0x7f.0xff") << "0xa.0xa.0x7f.0xff" << 0x0a0a7fffu; + + // dots missing, less than 255: + QTest::newRow("1.2.3") << "1.2.3" << 0x01020003u; + QTest::newRow("1.2") << "1.2" << 0x01000002u; + QTest::newRow("1") << "1" << 1u; + + // dots missing, more than 255, no overwrite + QTest::newRow("1.2.257") << "1.2.257" << 0x01020101u; + QTest::newRow("1.0x010101") << "1.0x010101" << 0x01010101u; + QTest::newRow("2130706433") << "2130706433" << 0x7f000001u; +} + +void tst_QIpAddress::parseIp4() +{ + QFETCH(QString, data); + QFETCH(QIPAddressUtils::IPv4Address, ip); + +#ifdef __GLIBC__ + { + in_addr inet_result; + int inet_ok = inet_aton(data.toLatin1(), &inet_result); + QVERIFY(inet_ok); + QCOMPARE(ntohl(inet_result.s_addr), ip); + } +#endif + + QIPAddressUtils::IPv4Address result; + bool ok = QIPAddressUtils::parseIp4(result, data.constBegin(), data.constEnd()); + QVERIFY(ok); + QCOMPARE(result, ip); +} + +void tst_QIpAddress::invalidParseIp4_data() +{ + QTest::addColumn<QString>("data"); + + // too many dots + QTest::newRow(".") << "."; + QTest::newRow("..") << ".."; + QTest::newRow("...") << "..."; + QTest::newRow("....") << "...."; + QTest::newRow("1.") << "1."; + QTest::newRow("1.2.") << "1.2."; + QTest::newRow("1.2.3.") << "1.2.3."; + QTest::newRow("1.2.3.4.") << "1.2.3.4."; + QTest::newRow("1.2.3..4") << "1.2.3..4"; + + // octet more than 255 + QTest::newRow("2.2.2.257") << "2.2.2.257"; + QTest::newRow("2.2.257.2") << "2.2.257.2"; + QTest::newRow("2.257.2.2") << "2.257.2.2"; + QTest::newRow("257.2.2.2") << "257.2.2.2"; + + // number more than field available + QTest::newRow("2.2.0x01010101") << "2.2.0x01010101"; + QTest::newRow("2.0x01010101") << "2.0x01010101"; + QTest::newRow("4294967296") << "4294967296"; + + // bad octals + QTest::newRow("09") << "09"; + + // bad hex + QTest::newRow("0x1g") << "0x1g"; + + // letters + QTest::newRow("abc") << "abc"; + QTest::newRow("1.2.3a.4") << "1.2.3a.4"; + QTest::newRow("a.2.3.4") << "a.2.3.4"; + QTest::newRow("1.2.3.4a") << "1.2.3.4a"; +} + +void tst_QIpAddress::invalidParseIp4() +{ + QFETCH(QString, data); + +#ifdef __GLIBC__ + { + in_addr inet_result; + int inet_ok = inet_aton(data.toLatin1(), &inet_result); +# ifdef Q_OS_DARWIN + QEXPECT_FAIL("4294967296", "Mac's library does parse this one", Continue); +# endif + QVERIFY(!inet_ok); + } +#endif + + QIPAddressUtils::IPv4Address result; + bool ok = QIPAddressUtils::parseIp4(result, data.constBegin(), data.constEnd()); + QVERIFY(!ok); +} + +void tst_QIpAddress::ip4ToString_data() +{ + QTest::addColumn<QIPAddressUtils::IPv4Address>("ip"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("0.0.0.0") << 0u << "0.0.0.0"; + QTest::newRow("1.2.3.4") << 0x01020304u << "1.2.3.4"; + QTest::newRow("111.222.33.44") << 0x6fde212cu << "111.222.33.44"; + QTest::newRow("255.255.255.255") << 0xffffffffu << "255.255.255.255"; +} + +void tst_QIpAddress::ip4ToString() +{ + QFETCH(QIPAddressUtils::IPv4Address, ip); + QFETCH(QString, expected); + +#ifdef __GLIBC__ + in_addr inet_ip; + inet_ip.s_addr = htonl(ip); + QCOMPARE(QString(inet_ntoa(inet_ip)), expected); +#endif + + QString result; + QIPAddressUtils::toString(result, ip); + QCOMPARE(result, expected); +} + +void tst_QIpAddress::parseIp6_data() +{ + qRegisterMetaType<Ip6>(); + QTest::addColumn<QString>("address"); + QTest::addColumn<Ip6>("expected"); + + // 7 colons, no :: + QTest::newRow("0:0:0:0:0:0:0:0") << "0:0:0:0:0:0:0:0" << Ip6(0,0,0,0,0,0,0,0); + QTest::newRow("0:0:0:0:0:0:0:1") << "0:0:0:0:0:0:0:1" << Ip6(0,0,0,0,0,0,0,1); + QTest::newRow("0:0:0:0:0:0:1:1") << "0:0:0:0:0:0:1:1" << Ip6(0,0,0,0,0,0,1,1); + QTest::newRow("0:0:0:0:0:0:0:103") << "0:0:0:0:0:0:0:103" << Ip6(0,0,0,0,0,0,0,0x103); + QTest::newRow("1:2:3:4:5:6:7:8") << "1:2:3:4:5:6:7:8" << Ip6(1,2,3,4,5,6,7,8); + QTest::newRow("ffee:ddcc:bbaa:9988:7766:5544:3322:1100") + << "ffee:ddcc:bbaa:9988:7766:5544:3322:1100" + << Ip6(0xffee, 0xddcc, 0xbbaa, 0x9988, 0x7766, 0x5544, 0x3322, 0x1100); + + // too many zeroes + QTest::newRow("0:0:0:0:0:0:0:00103") << "0:0:0:0:0:0:0:00103" << Ip6(0,0,0,0,0,0,0,0x103); + + // double-colon + QTest::newRow("::1:2:3:4:5:6:7") << "::1:2:3:4:5:6:7" << Ip6(0,1,2,3,4,5,6,7); + QTest::newRow("1:2:3:4:5:6:7::") << "1:2:3:4:5:6:7::" << Ip6(1,2,3,4,5,6,7,0); + + QTest::newRow("1::2:3:4:5:6:7") << "1::2:3:4:5:6:7" << Ip6(1,0,2,3,4,5,6,7); + QTest::newRow("1:2::3:4:5:6:7") << "1:2::3:4:5:6:7" << Ip6(1,2,0,3,4,5,6,7); + QTest::newRow("1:2:3::4:5:6:7") << "1:2:3::4:5:6:7" << Ip6(1,2,3,0,4,5,6,7); + QTest::newRow("1:2:3:4::5:6:7") << "1:2:3:4::5:6:7" << Ip6(1,2,3,4,0,5,6,7); + QTest::newRow("1:2:3:4:5::6:7") << "1:2:3:4:5::6:7" << Ip6(1,2,3,4,5,0,6,7); + QTest::newRow("1:2:3:4:5:6::7") << "1:2:3:4:5:6::7" << Ip6(1,2,3,4,5,6,0,7); + + QTest::newRow("::1:2:3:4:5:6") << "::1:2:3:4:5:6" << Ip6(0,0,1,2,3,4,5,6); + QTest::newRow("1:2:3:4:5:6::") << "1:2:3:4:5:6::" << Ip6(1,2,3,4,5,6,0,0); + + QTest::newRow("1::2:3:4:5:6") << "1::2:3:4:5:6" << Ip6(1,0,0,2,3,4,5,6); + QTest::newRow("1:2::3:4:5:6") << "1:2::3:4:5:6" << Ip6(1,2,0,0,3,4,5,6); + QTest::newRow("1:2:3::4:5:6") << "1:2:3::4:5:6" << Ip6(1,2,3,0,0,4,5,6); + QTest::newRow("1:2:3:4::5:6") << "1:2:3:4::5:6" << Ip6(1,2,3,4,0,0,5,6); + QTest::newRow("1:2:3:4:5::6") << "1:2:3:4:5::6" << Ip6(1,2,3,4,5,0,0,6); + + QTest::newRow("::1:2:3:4:5") << "::1:2:3:4:5" << Ip6(0,0,0,1,2,3,4,5); + QTest::newRow("1:2:3:4:5::") << "1:2:3:4:5::" << Ip6(1,2,3,4,5,0,0,0); + + QTest::newRow("1::2:3:4:5") << "1::2:3:4:5" << Ip6(1,0,0,0,2,3,4,5); + QTest::newRow("1:2::3:4:5") << "1:2::3:4:5" << Ip6(1,2,0,0,0,3,4,5); + QTest::newRow("1:2:3::4:5") << "1:2:3::4:5" << Ip6(1,2,3,0,0,0,4,5); + QTest::newRow("1:2:3:4::5") << "1:2:3:4::5" << Ip6(1,2,3,4,0,0,0,5); + + QTest::newRow("::1:2:3:4") << "::1:2:3:4" << Ip6(0,0,0,0,1,2,3,4); + QTest::newRow("1:2:3:4::") << "1:2:3:4::" << Ip6(1,2,3,4,0,0,0,0); + + QTest::newRow("1::2:3:4") << "1::2:3:4" << Ip6(1,0,0,0,0,2,3,4); + QTest::newRow("1:2::3:4") << "1:2::3:4" << Ip6(1,2,0,0,0,0,3,4); + QTest::newRow("1:2:3::4") << "1:2:3::4" << Ip6(1,2,3,0,0,0,0,4); + + QTest::newRow("::1:2:3") << "::1:2:3" << Ip6(0,0,0,0,0,1,2,3); + QTest::newRow("1:2:3::") << "1:2:3::" << Ip6(1,2,3,0,0,0,0,0); + + QTest::newRow("1::2:3") << "1::2:3" << Ip6(1,0,0,0,0,0,2,3); + QTest::newRow("1:2::3") << "1:2::3" << Ip6(1,2,0,0,0,0,0,3); + + QTest::newRow("::1:2") << "::1:2" << Ip6(0,0,0,0,0,0,1,2); + QTest::newRow("1:2::") << "1:2::" << Ip6(1,2,0,0,0,0,0,0); + + QTest::newRow("1::2") << "1::2" << Ip6(1,0,0,0,0,0,0,2); + + QTest::newRow("::1") << "::1" << Ip6(0,0,0,0,0,0,0,1); + QTest::newRow("1::") << "1::" << Ip6(1,0,0,0,0,0,0,0); + + QTest::newRow("::") << "::" << Ip6(0,0,0,0,0,0,0,0); + + // embedded IPv4 + QTest::newRow("1:2:3:4:5:6:10.0.16.1") << "1:2:3:4:5:6:10.0.16.1" << Ip6(1,2,3,4,5,6,0xa00,0x1001); + QTest::newRow("1::10.0.16.1") << "1::10.0.16.1" << Ip6(1,0,0,0,0,0,0xa00,0x1001); + QTest::newRow("::10.0.16.1") << "::10.0.16.1" << Ip6(0,0,0,0,0,0,0xa00,0x1001); + QTest::newRow("::0.0.0.0") << "::0.0.0.0" << Ip6(0,0,0,0,0,0,0,0); +} + +void tst_QIpAddress::parseIp6() +{ + QFETCH(QString, address); + QFETCH(Ip6, expected); + +#if defined(__GLIBC__) && defined(AF_INET6) + Ip6 inet_result; + bool inet_ok = inet_pton(AF_INET6, address.toLatin1(), &inet_result.u8); + QVERIFY(inet_ok); + QCOMPARE(inet_result, expected); +#endif + + Ip6 result; + bool ok = QIPAddressUtils::parseIp6(result.u8, address.constBegin(), address.constEnd()); + QVERIFY(ok); + QCOMPARE(result, expected); +} + +void tst_QIpAddress::invalidParseIp6_data() +{ + QTest::addColumn<QString>("address"); + + // too many colons + QTest::newRow("0:0:0:0::0:0:0:0") << "0:0:0:0::0:0:0:0"; + QTest::newRow("0:::") << "0:::"; QTest::newRow(":::0") << ":::0"; + QTest::newRow("16:::::::::::::::::::::::") << "16:::::::::::::::::::::::"; + + // non-hex + QTest::newRow("a:b:c:d:e:f:g:h") << "a:b:c:d:e:f:g:h"; + + // too big number + QTest::newRow("0:0:0:0:0:0:0:10103") << "0:0:0:0:0:0:0:10103"; + + // too short + QTest::newRow("0:0:0:0:0:0:0:") << "0:0:0:0:0:0:0:"; + QTest::newRow("0:0:0:0:0:0:0") << "0:0:0:0:0:0:0"; + QTest::newRow("0:0:0:0:0:0:") << "0:0:0:0:0:0:"; + QTest::newRow("0:0:0:0:0:0") << "0:0:0:0:0:0"; + QTest::newRow("0:0:0:0:0:") << "0:0:0:0:0:"; + QTest::newRow("0:0:0:0:0") << "0:0:0:0:0"; + QTest::newRow("0:0:0:0:") << "0:0:0:0:"; + QTest::newRow("0:0:0:0") << "0:0:0:0"; + QTest::newRow("0:0:0:") << "0:0:0:"; + QTest::newRow("0:0:0") << "0:0:0"; + QTest::newRow("0:0:") << "0:0:"; + QTest::newRow("0:0") << "0:0"; + QTest::newRow("0:") << "0:"; + QTest::newRow("0") << "0"; + QTest::newRow(":0") << ":0"; + QTest::newRow(":0:0") << ":0:0"; + QTest::newRow(":0:0:0") << ":0:0:0"; + QTest::newRow(":0:0:0:0") << ":0:0:0:0"; + QTest::newRow(":0:0:0:0:0") << ":0:0:0:0:0"; + QTest::newRow(":0:0:0:0:0:0") << ":0:0:0:0:0:0"; + QTest::newRow(":0:0:0:0:0:0:0") << ":0:0:0:0:0:0:0"; + + // IPv4 + QTest::newRow("1.2.3.4") << "1.2.3.4"; + + // embedded IPv4 in the wrong position + QTest::newRow("1.2.3.4::") << "1.2.3.4::"; + QTest::newRow("f:1.2.3.4::") << "f:1.2.3.4::"; + QTest::newRow("f:e:d:c:b:1.2.3.4:0") << "f:e:d:c:b:1.2.3.4:0"; + + // bad embedded IPv4 + QTest::newRow("::1.2.3") << "::1.2.3"; + QTest::newRow("::1.2.257") << "::1.2.257"; + QTest::newRow("::1.2") << "::1.2"; + QTest::newRow("::0250.0x10101") << "::0250.0x10101"; + QTest::newRow("::1.2.3.0250") << "::1.2.3.0250"; + QTest::newRow("::1.2.3.0xff") << "::1.2.3.0xff"; + QTest::newRow("::1.2.3.07") << "::1.2.3.07"; + QTest::newRow("::1.2.3.010") << "::1.2.3.010"; + + // separated by something else + QTest::newRow("1.2.3.4.5.6.7.8") << "1.2.3.4.5.6.7.8"; + QTest::newRow("1,2,3,4,5,6,7,8") << "1,2,3,4,5,6,7,8"; + QTest::newRow("1..2") << "1..2"; + QTest::newRow("1:.2") << "1:.2"; + QTest::newRow("1.:2") << "1.:2"; +} + +void tst_QIpAddress::invalidParseIp6() +{ + QFETCH(QString, address); + +#if defined(__GLIBC__) && defined(AF_INET6) + Ip6 inet_result; + bool inet_ok = inet_pton(AF_INET6, address.toLatin1(), &inet_result.u8); + QVERIFY(!inet_ok); +#endif + + Ip6 result; + bool ok = QIPAddressUtils::parseIp6(result.u8, address.constBegin(), address.constEnd()); + QVERIFY(!ok); +} + +void tst_QIpAddress::ip6ToString_data() +{ + qRegisterMetaType<Ip6>(); + QTest::addColumn<Ip6>("ip"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("1:2:3:4:5:6:7:8") << Ip6(1,2,3,4,5,6,7,8) << "1:2:3:4:5:6:7:8"; + QTest::newRow("1:2:3:4:5:6:7:88") << Ip6(1,2,3,4,5,6,7,0x88) << "1:2:3:4:5:6:7:88"; + QTest::newRow("1:2:3:4:5:6:7:888") << Ip6(1,2,3,4,5,6,7,0x888) << "1:2:3:4:5:6:7:888"; + QTest::newRow("1:2:3:4:5:6:7:8888") << Ip6(1,2,3,4,5,6,7,0x8888) << "1:2:3:4:5:6:7:8888"; + QTest::newRow("1:2:3:4:5:6:7:8880") << Ip6(1,2,3,4,5,6,7,0x8880) << "1:2:3:4:5:6:7:8880"; + QTest::newRow("1:2:3:4:5:6:7:8808") << Ip6(1,2,3,4,5,6,7,0x8808) << "1:2:3:4:5:6:7:8808"; + QTest::newRow("1:2:3:4:5:6:7:8088") << Ip6(1,2,3,4,5,6,7,0x8088) << "1:2:3:4:5:6:7:8088"; + + QTest::newRow("1:2:3:4:5:6:7:0") << Ip6(1,2,3,4,5,6,7,0) << "1:2:3:4:5:6:7:0"; + QTest::newRow("0:1:2:3:4:5:6:7") << Ip6(0,1,2,3,4,5,6,7) << "0:1:2:3:4:5:6:7"; + + QTest::newRow("1:2:3:4:5:6::") << Ip6(1,2,3,4,5,6,0,0) << "1:2:3:4:5:6::"; + QTest::newRow("::1:2:3:4:5:6") << Ip6(0,0,1,2,3,4,5,6) << "::1:2:3:4:5:6"; + QTest::newRow("1:0:0:2::3") << Ip6(1,0,0,2,0,0,0,3) << "1:0:0:2::3"; + QTest::newRow("1:::2:0:0:3") << Ip6(1,0,0,0,2,0,0,3) << "1::2:0:0:3"; + QTest::newRow("1::2:0:0:0") << Ip6(1,0,0,0,2,0,0,0) << "1::2:0:0:0"; + QTest::newRow("0:0:0:1::") << Ip6(0,0,0,1,0,0,0,0) << "0:0:0:1::"; + QTest::newRow("::1:0:0:0") << Ip6(0,0,0,0,1,0,0,0) << "::1:0:0:0"; + QTest::newRow("ff02::1") << Ip6(0xff02,0,0,0,0,0,0,1) << "ff02::1"; + QTest::newRow("1::1") << Ip6(1,0,0,0,0,0,0,1) << "1::1"; + QTest::newRow("::1") << Ip6(0,0,0,0,0,0,0,1) << "::1"; + QTest::newRow("1::") << Ip6(1,0,0,0,0,0,0,0) << "1::"; + QTest::newRow("::") << Ip6(0,0,0,0,0,0,0,0) << "::"; + + QTest::newRow("::1.2.3.4") << Ip6(0,0,0,0,0,0,0x102,0x304) << "::1.2.3.4"; + QTest::newRow("::ffff:1.2.3.4") << Ip6(0,0,0,0,0,0xffff,0x102,0x304) << "::ffff:1.2.3.4"; +} + +void tst_QIpAddress::ip6ToString() +{ + QFETCH(Ip6, ip); + QFETCH(QString, expected); + +#if defined(__GLIBC__) && defined(AF_INET6) + { + char buf[INET6_ADDRSTRLEN]; + bool ok = inet_ntop(AF_INET6, ip.u8, buf, sizeof buf) != 0; + QVERIFY(ok); + QCOMPARE(QString(buf), expected); + } +#endif + + QString result; + QIPAddressUtils::toString(result, ip.u8); + QCOMPARE(result, expected); +} + +QTEST_APPLESS_MAIN(tst_QIpAddress) + +#include "tst_qipaddress.moc" diff --git a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp index b15d5fca2c..29f6fe9da4 100644 --- a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp +++ b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp @@ -60,6 +60,7 @@ class tst_qstandardpaths : public QObject private slots: void testDefaultLocations(); void testCustomLocations(); + void enableTestMode(); void testLocateAll(); void testDataLocation(); void testFindExecutable(); @@ -69,6 +70,7 @@ private slots: void testAllWritableLocations(); private: +#ifdef Q_XDG_PLATFORM void setCustomLocations() { m_localConfigDir = m_localConfigTempDir.path(); m_globalConfigDir = m_globalConfigTempDir.path(); @@ -80,13 +82,12 @@ private: qputenv("XDG_DATA_DIRS", QFile::encodeName(m_globalAppDir)); } void setDefaultLocations() { -#ifdef Q_XDG_PLATFORM qputenv("XDG_CONFIG_HOME", QByteArray()); qputenv("XDG_CONFIG_DIRS", QByteArray()); qputenv("XDG_DATA_HOME", QByteArray()); qputenv("XDG_DATA_DIRS", QByteArray()); -#endif } +#endif // Config dirs QString m_localConfigDir; @@ -156,6 +157,58 @@ void tst_qstandardpaths::testCustomLocations() #endif } +void tst_qstandardpaths::enableTestMode() +{ + QStandardPaths::enableTestMode(true); + +#ifdef Q_XDG_PLATFORM + setCustomLocations(); // for the global config dir + const QString qttestDir = QDir::homePath() + QLatin1String("/.qttest"); + + // ConfigLocation + const QString configDir = qttestDir + QLatin1String("/config"); + QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation), configDir); + const QStringList confDirs = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation); + QCOMPARE(confDirs, QStringList() << configDir << m_globalConfigDir); + + // GenericDataLocation + const QString dataDir = qttestDir + QLatin1String("/share"); + QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation), dataDir); + const QStringList gdDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); + QCOMPARE(gdDirs, QStringList() << dataDir << m_globalAppDir); + + // GenericCacheLocation + const QString cacheDir = qttestDir + QLatin1String("/cache"); + QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation), cacheDir); + const QStringList cacheDirs = QStandardPaths::standardLocations(QStandardPaths::GenericCacheLocation); + QCOMPARE(cacheDirs, QStringList() << cacheDir); +#endif + + // On all platforms, we want to ensure that the writableLocation is different in test mode and real mode. + // Check this for locations where test programs typically write. Not desktop, download, music etc... + typedef QHash<QStandardPaths::StandardLocation, QString> LocationHash; + LocationHash testLocations; + testLocations.insert(QStandardPaths::DataLocation, QStandardPaths::writableLocation(QStandardPaths::DataLocation)); + testLocations.insert(QStandardPaths::GenericDataLocation, QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)); + testLocations.insert(QStandardPaths::ConfigLocation, QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)); + testLocations.insert(QStandardPaths::CacheLocation, QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + testLocations.insert(QStandardPaths::GenericCacheLocation, QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)); + // On Windows, what should "Program Files" become, in test mode? + //testLocations.insert(QStandardPaths::ApplicationsLocation, QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)); + + QStandardPaths::enableTestMode(false); + + for (LocationHash::const_iterator it = testLocations.constBegin(); it != testLocations.constEnd(); ++it) + QVERIFY2(QStandardPaths::writableLocation(it.key()) != it.value(), qPrintable(it.value())); + + // Check that this is also true with no env vars set +#ifdef Q_XDG_PLATFORM + setDefaultLocations(); + for (LocationHash::const_iterator it = testLocations.constBegin(); it != testLocations.constEnd(); ++it) + QVERIFY2(QStandardPaths::writableLocation(it.key()) != it.value(), qPrintable(it.value())); +#endif +} + void tst_qstandardpaths::testLocateAll() { #ifdef Q_XDG_PLATFORM diff --git a/tests/auto/corelib/io/qurl/qurl.pro b/tests/auto/corelib/io/qurl/qurl.pro index 84538c0859..b475bdb4d7 100644 --- a/tests/auto/corelib/io/qurl/qurl.pro +++ b/tests/auto/corelib/io/qurl/qurl.pro @@ -1,4 +1,4 @@ CONFIG += testcase parallel_test TARGET = tst_qurl -QT = core-private testlib +QT = core testlib SOURCES = tst_qurl.cpp diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index a74d817b8a..f9fbb8cba8 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2012 Intel Corporation. ** Contact: http://www.qt-project.org/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -39,37 +40,18 @@ ** ****************************************************************************/ +#define QT_DEPRECATED +#define QT_DISABLE_DEPRECATED_BEFORE 0 +#include <qurl.h> #include <QtTest/QtTest> #include <QtCore/QDebug> #include <qcoreapplication.h> #include <qfileinfo.h> -#include <qurl.h> #include <qtextcodec.h> #include <qmap.h> -#include "private/qtldurl_p.h" - -// For testsuites -#define IDNA_ACE_PREFIX "xn--" -#define IDNA_SUCCESS 1 -#define STRINGPREP_NO_UNASSIGNED 1 -#define STRINGPREP_CONTAINS_UNASSIGNED 2 -#define STRINGPREP_CONTAINS_PROHIBITED 3 -#define STRINGPREP_BIDI_BOTH_L_AND_RAL 4 -#define STRINGPREP_BIDI_LEADTRAIL_NOT_RAL 5 - -struct ushortarray { - ushortarray(unsigned short *array = 0) - { - if (array) - memcpy(points, array, sizeof(points)); - } - unsigned short points[100]; -}; - -Q_DECLARE_METATYPE(ushortarray) Q_DECLARE_METATYPE(QUrl::FormattingOptions) class tst_QUrl : public QObject @@ -85,12 +67,12 @@ private slots: void unc(); void assignment(); void comparison(); + void comparison2_data(); + void comparison2(); void copying(); void setUrl(); void i18n_data(); void i18n(); - void punycode_data(); - void punycode(); void resolving_data(); void resolving(); void toString_data(); @@ -136,34 +118,20 @@ private slots: void toPercentEncoding(); void isRelative_data(); void isRelative(); - void setQueryItems(); - void queryItems(); void hasQuery_data(); void hasQuery(); - void hasQueryItem_data(); - void hasQueryItem(); void nameprep(); void isValid(); void schemeValidator_data(); void schemeValidator(); void invalidSchemeValidator(); + void strictParser_data(); + void strictParser(); void tolerantParser(); void correctEncodedMistakes_data(); void correctEncodedMistakes(); void correctDecodedMistakes_data(); void correctDecodedMistakes(); - void idna_testsuite_data(); - void idna_testsuite(); - void nameprep_testsuite_data(); - void nameprep_testsuite(); - void nameprep_highcodes_data(); - void nameprep_highcodes(); - void ace_testsuite_data(); - void ace_testsuite(); - void std3violations_data(); - void std3violations(); - void std3deviations_data(); - void std3deviations(); void tldRestrictions_data(); void tldRestrictions(); void emptyQueryOrFragment(); @@ -180,7 +148,6 @@ private slots: void toEncoded(); void setAuthority_data(); void setAuthority(); - void errorString(); void clear(); void resolvedWithAbsoluteSchemes() const; void resolvedWithAbsoluteSchemes_data() const; @@ -192,8 +159,9 @@ private slots: void toEncodedNotUsingUninitializedPath(); void emptyAuthorityRemovesExistingAuthority(); void acceptEmptyAuthoritySegments(); - void removeAllEncodedQueryItems_data(); - void removeAllEncodedQueryItems(); + void lowercasesScheme(); + void componentEncodings_data(); + void componentEncodings(); }; // Testing get/set functions @@ -262,9 +230,18 @@ void tst_QUrl::hashInPath() QCOMPARE(withHashInPath.path(), QString::fromLatin1("hi#mum.txt")); QCOMPARE(withHashInPath.toEncoded(), QByteArray("hi%23mum.txt")); QCOMPARE(withHashInPath.toString(), QString("hi%23mum.txt")); + QCOMPARE(withHashInPath.toDisplayString(QUrl::PreferLocalFile), QString("hi%23mum.txt")); QUrl fromHashInPath = QUrl::fromEncoded(withHashInPath.toEncoded()); QVERIFY(withHashInPath == fromHashInPath); + + const QUrl localWithHash = QUrl::fromLocalFile("/hi#mum.txt"); + QCOMPARE(localWithHash.path(), QString::fromLatin1("/hi#mum.txt")); + QCOMPARE(localWithHash.toEncoded(), QByteArray("file:///hi%23mum.txt")); + QCOMPARE(localWithHash.toString(), QString("file:///hi%23mum.txt")); + QCOMPARE(localWithHash.path(), QString::fromLatin1("/hi#mum.txt")); + QCOMPARE(localWithHash.toString(QUrl::PreferLocalFile), QString("/hi#mum.txt")); + QCOMPARE(localWithHash.toDisplayString(QUrl::PreferLocalFile), QString("/hi#mum.txt")); } void tst_QUrl::unc() @@ -302,7 +279,8 @@ void tst_QUrl::comparison() // 6.2.2 Syntax-based Normalization QUrl url3 = QUrl::fromEncoded("example://a/b/c/%7Bfoo%7D"); QUrl url4 = QUrl::fromEncoded("eXAMPLE://a/./b/../b/%63/%7bfoo%7d"); - QVERIFY(url3 == url4); + QEXPECT_FAIL("", "Normalization not implemented, will probably not be implemented like this", Continue); + QCOMPARE(url3, url4); // 6.2.2.1 Make sure hexdecimal characters in percent encoding are // treated case-insensitively @@ -311,13 +289,55 @@ void tst_QUrl::comparison() QUrl url6; url6.setEncodedQuery("a=%2A"); QVERIFY(url5 == url6); +} + +void tst_QUrl::comparison2_data() +{ + QTest::addColumn<QUrl>("url1"); + QTest::addColumn<QUrl>("url2"); + QTest::addColumn<int>("ordering"); // like strcmp + + QTest::newRow("null-null") << QUrl() << QUrl() << 0; + + QUrl empty; + empty.setPath("/hello"); // ensure it has detached + empty.setPath(QString()); + QTest::newRow("null-empty") << QUrl() << empty << 0; + + QTest::newRow("scheme-null") << QUrl("x:") << QUrl() << 1; + QTest::newRow("samescheme") << QUrl("x:") << QUrl("x:") << 0; + + // the following three are by choice + // the order could be the opposite and it would still be correct + QTest::newRow("scheme-path") << QUrl("x:") << QUrl("/tmp") << +1; + QTest::newRow("fragment-path") << QUrl("#foo") << QUrl("/tmp") << -1; + QTest::newRow("fragment-scheme") << QUrl("#foo") << QUrl("x:") << -1; + + QTest::newRow("noport-zeroport") << QUrl("http://example.com") << QUrl("http://example.com:0") << -1; +} + +void tst_QUrl::comparison2() +{ + QFETCH(QUrl, url1); + QFETCH(QUrl, url2); + QFETCH(int, ordering); + + QCOMPARE(url1.toString() == url2.toString(), ordering == 0); + QCOMPARE(url1 == url2, ordering == 0); + QCOMPARE(url1 != url2, ordering != 0); + if (ordering == 0) + QCOMPARE(qHash(url1), qHash(url2)); + + QCOMPARE(url1 < url2, ordering < 0); + QCOMPARE(!(url1 < url2), ordering >= 0); + + QCOMPARE(url2 < url1, ordering > 0); + QCOMPARE(!(url2 < url1), ordering <= 0); - // ensure that encoded characters in the query do not match - QUrl url7; - url7.setEncodedQuery("a=%63"); - QUrl url8; - url8.setEncodedQuery("a=c"); - QVERIFY(url7 != url8); + // redundant checks (the above should catch these) + QCOMPARE(url1 < url2 || url2 < url1, ordering != 0); + QVERIFY(!(url1 < url2 && url2 < url1)); + QVERIFY(url1 < url2 || url1 == url2 || url2 < url1); } void tst_QUrl::copying() @@ -352,6 +372,7 @@ void tst_QUrl::setUrl() QCOMPARE(url.port(), -1); QCOMPARE(url.toString(), QString::fromLatin1("file:///")); QCOMPARE(url.toDisplayString(), QString::fromLatin1("file:///")); + QCOMPARE(url.toDisplayString(QUrl::PreferLocalFile), QString::fromLatin1("/")); } { @@ -367,6 +388,7 @@ void tst_QUrl::setUrl() QCOMPARE(url.port(), 80); QCOMPARE(url.toString(), QString::fromLatin1("http://www.foo.bar:80")); QCOMPARE(url.toDisplayString(), QString::fromLatin1("http://www.foo.bar:80")); + QCOMPARE(url.toDisplayString(QUrl::PreferLocalFile), QString::fromLatin1("http://www.foo.bar:80")); QUrl url2("//www1.foo.bar"); QCOMPARE(url.resolved(url2).toString(), QString::fromLatin1("http://www1.foo.bar")); @@ -380,11 +402,11 @@ void tst_QUrl::setUrl() QVERIFY(url.encodedQuery().isEmpty()); QCOMPARE(url.userInfo(), QString::fromLatin1("user:pass")); QVERIFY(url.fragment().isEmpty()); - QCOMPARE(url.host(), QString::fromLatin1("56::56:56:56:127.0.0.1")); - QCOMPARE(url.authority(), QString::fromLatin1("user:pass@[56::56:56:56:127.0.0.1]:99")); + QCOMPARE(url.host(), QString::fromLatin1("56::56:56:56:7f00:1")); + QCOMPARE(url.authority(), QString::fromLatin1("user:pass@[56::56:56:56:7f00:1]:99")); QCOMPARE(url.port(), 99); - QCOMPARE(url.url(), QString::fromLatin1("http://user:pass@[56::56:56:56:127.0.0.1]:99")); - QCOMPARE(url.toDisplayString(), QString::fromLatin1("http://user@[56::56:56:56:127.0.0.1]:99")); + QCOMPARE(url.url(), QString::fromLatin1("http://user:pass@[56::56:56:56:7f00:1]:99")); + QCOMPARE(url.toDisplayString(), QString::fromLatin1("http://user@[56::56:56:56:7f00:1]:99")); } { @@ -451,15 +473,7 @@ void tst_QUrl::setUrl() QUrl url = u1; QVERIFY(url.isValid()); QCOMPARE(url.toString(), QString::fromLatin1("file:///home/dfaure/my#myref")); - QCOMPARE(url.fragment(), QString::fromLatin1("myref")); - } - - { - QString u1 = "file:/home/dfaure/my#myref"; - QUrl url = u1; - QVERIFY(url.isValid()); - - QCOMPARE(url.toString(), QString::fromLatin1("file:///home/dfaure/my#myref")); + QCOMPARE(url.toString(QUrl::PreferLocalFile), QString::fromLatin1("file:///home/dfaure/my#myref")); QCOMPARE(url.fragment(), QString::fromLatin1("myref")); } @@ -550,6 +564,7 @@ void tst_QUrl::setUrl() QUrl url15582("http://alain.knaff.linux.lu/bug-reports/kde/percentage%in%url.html"); QCOMPARE(url15582.toString(), QString::fromLatin1("http://alain.knaff.linux.lu/bug-reports/kde/percentage%25in%25url.html")); QCOMPARE(url15582.toEncoded(), QByteArray("http://alain.knaff.linux.lu/bug-reports/kde/percentage%25in%25url.html")); + QCOMPARE(url15582.toString(QUrl::FullyEncoded), QString("http://alain.knaff.linux.lu/bug-reports/kde/percentage%25in%25url.html")); } { @@ -559,20 +574,24 @@ void tst_QUrl::setUrl() QUrl charles; charles.setPath("/home/charles/foo%20moo"); - QCOMPARE(charles.path(), QString::fromLatin1("/home/charles/foo%20moo")); + QCOMPARE(charles.path(), QString::fromLatin1("/home/charles/foo moo")); + QCOMPARE(charles.path(QUrl::FullyEncoded), QString::fromLatin1("/home/charles/foo%20moo")); QUrl charles2("file:/home/charles/foo%20moo"); QCOMPARE(charles2.path(), QString::fromLatin1("/home/charles/foo moo")); + QCOMPARE(charles2.path(QUrl::FullyEncoded), QString::fromLatin1("/home/charles/foo%20moo")); } { QUrl udir; QCOMPARE(udir.toEncoded(), QByteArray()); + QCOMPARE(udir.toString(QUrl::FullyEncoded), QString()); QVERIFY(!udir.isValid()); udir = QUrl::fromLocalFile("/home/dfaure/file.txt"); QCOMPARE(udir.path(), QString::fromLatin1("/home/dfaure/file.txt")); QCOMPARE(udir.toEncoded(), QByteArray("file:///home/dfaure/file.txt")); + QCOMPARE(udir.toString(QUrl::FullyEncoded), QString("file:///home/dfaure/file.txt")); } { @@ -630,7 +649,7 @@ void tst_QUrl::setUrl() QCOMPARE(url.scheme(), QString("data")); QCOMPARE(url.host(), QString()); QCOMPARE(url.path(), QString("text/javascript,d5 = 'five\\u0027s';")); - QCOMPARE(url.encodedPath().constData(), "text/javascript,d5%20%3D%20'five%5Cu0027s'%3B"); + QCOMPARE(url.encodedPath().constData(), "text/javascript,d5%20=%20'five%5Cu0027s';"); } { //check it calls detach @@ -789,10 +808,21 @@ void tst_QUrl::toString_data() << uint(QUrl::RemovePassword) << QString::fromLatin1("http://ole@www.troll.no:9090/index.html?ole=semann&gud=hei#top"); + // show that QUrl keeps the empty-but-present username if you remove the password + // see data3-bis for another case + QTest::newRow("data2-bis") << QString::fromLatin1("http://:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top") + << uint(QUrl::RemovePassword) + << QString::fromLatin1("http://@www.troll.no:9090/index.html?ole=semann&gud=hei#top"); + QTest::newRow("data3") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top") << uint(QUrl::RemoveUserInfo) << QString::fromLatin1("http://www.troll.no:9090/index.html?ole=semann&gud=hei#top"); + // show that QUrl keeps the empty-but-preset hostname if you remove the userinfo + QTest::newRow("data3-bis") << QString::fromLatin1("http://ole:password@/index.html?ole=semann&gud=hei#top") + << uint(QUrl::RemoveUserInfo) + << QString::fromLatin1("http:///index.html?ole=semann&gud=hei#top"); + QTest::newRow("data4") << QString::fromLatin1("http://ole:password@www.troll.no:9090/index.html?ole=semann&gud=hei#top") << uint(QUrl::RemovePort) << QString::fromLatin1("http://ole:password@www.troll.no/index.html?ole=semann&gud=hei#top"); @@ -907,20 +937,26 @@ void tst_QUrl::toString_constructed_data() QTest::addColumn<QString>("fragment"); QTest::addColumn<QString>("asString"); QTest::addColumn<QByteArray>("asEncoded"); + QTest::addColumn<uint>("options"); QString n(""); QTest::newRow("data1") << n << n << n << QString::fromLatin1("qt.nokia.com") << -1 << QString::fromLatin1("index.html") << QByteArray() << n << QString::fromLatin1("//qt.nokia.com/index.html") - << QByteArray("//qt.nokia.com/index.html"); + << QByteArray("//qt.nokia.com/index.html") << 0u; QTest::newRow("data2") << QString::fromLatin1("file") << n << n << n << -1 << QString::fromLatin1("/root") << QByteArray() - << n << QString::fromLatin1("file:///root") << QByteArray("file:///root"); + << n << QString::fromLatin1("file:///root") << QByteArray("file:///root") << 0u; QTest::newRow("userAndPass") << QString::fromLatin1("http") << QString::fromLatin1("dfaure") << QString::fromLatin1("kde") << "kde.org" << 443 << QString::fromLatin1("/") << QByteArray() << n - << QString::fromLatin1("http://dfaure:kde@kde.org:443/") << QByteArray("http://dfaure:kde@kde.org:443/"); + << QString::fromLatin1("http://dfaure:kde@kde.org:443/") << QByteArray("http://dfaure:kde@kde.org:443/") + << 0u; QTest::newRow("PassWithoutUser") << QString::fromLatin1("http") << n << QString::fromLatin1("kde") << "kde.org" << 443 << QString::fromLatin1("/") << QByteArray() << n - << QString::fromLatin1("http://:kde@kde.org:443/") << QByteArray("http://:kde@kde.org:443/"); + << QString::fromLatin1("http://:kde@kde.org:443/") << QByteArray("http://:kde@kde.org:443/") << 0u; + QTest::newRow("PassWithoutUser-RemovePassword") << QString::fromLatin1("http") << n << QString::fromLatin1("kde") + << "kde.org" << 443 << QString::fromLatin1("/") << QByteArray() << n + << QString::fromLatin1("http://kde.org:443/") << QByteArray("http://kde.org:443/") + << uint(QUrl::RemovePassword); } void tst_QUrl::toString_constructed() @@ -935,6 +971,7 @@ void tst_QUrl::toString_constructed() QFETCH(QString, fragment); QFETCH(QString, asString); QFETCH(QByteArray, asEncoded); + QFETCH(uint, options); QUrl url; if (!scheme.isEmpty()) @@ -955,9 +992,11 @@ void tst_QUrl::toString_constructed() url.setFragment(fragment); QVERIFY(url.isValid()); - QCOMPARE(url.toString(), asString); - QCOMPARE(QString::fromLatin1(url.toEncoded()), QString::fromLatin1(asEncoded)); // readable in case of differences - QCOMPARE(url.toEncoded(), asEncoded); + + QUrl::FormattingOptions formattingOptions(options); + QCOMPARE(url.toString(formattingOptions), asString); + QCOMPARE(QString::fromLatin1(url.toEncoded(formattingOptions)), QString::fromLatin1(asEncoded)); // readable in case of differences + QCOMPARE(url.toEncoded(formattingOptions), asEncoded); } @@ -1001,6 +1040,7 @@ void tst_QUrl::toLocalFile() QUrl url(theUrl); QCOMPARE(url.toLocalFile(), theFile); + QCOMPARE(url.isLocalFile(), !theFile.isEmpty()); } void tst_QUrl::fromLocalFile_data() @@ -1201,7 +1241,7 @@ void tst_QUrl::compat_isValid_01() QFETCH( bool, res ); QUrl url( urlStr ); - QVERIFY( url.isValid() == res ); + QCOMPARE( url.isValid(), res ); } void tst_QUrl::compat_isValid_02_data() @@ -1449,8 +1489,6 @@ void tst_QUrl::symmetry() QCOMPARE(url.path(), QString::fromLatin1("/pub")); // this will be encoded ... QCOMPARE(url.encodedQuery().constData(), QString::fromLatin1("a=b&a=d%C3%B8&a=f").toLatin1().constData()); - // unencoded - QCOMPARE(url.allQueryItemValues("a").join(""), QString::fromUtf8("bdøf")); QCOMPARE(url.fragment(), QString::fromUtf8("vræl")); QUrl onlyHost("//qt.nokia.com"); @@ -1481,40 +1519,58 @@ void tst_QUrl::ipv6_data() { QTest::addColumn<QString>("ipv6Auth"); QTest::addColumn<bool>("isValid"); + QTest::addColumn<QString>("output"); - QTest::newRow("case 1") << QString::fromLatin1("//[56:56:56:56:56:56:56:56]") << true; - QTest::newRow("case 2") << QString::fromLatin1("//[::56:56:56:56:56:56:56]") << true; - QTest::newRow("case 3") << QString::fromLatin1("//[56::56:56:56:56:56:56]") << true; - QTest::newRow("case 4") << QString::fromLatin1("//[56:56::56:56:56:56:56]") << true; - QTest::newRow("case 5") << QString::fromLatin1("//[56:56:56::56:56:56:56]") << true; - QTest::newRow("case 6") << QString::fromLatin1("//[56:56:56:56::56:56:56]") << true; - QTest::newRow("case 7") << QString::fromLatin1("//[56:56:56:56:56::56:56]") << true; - QTest::newRow("case 8") << QString::fromLatin1("//[56:56:56:56:56:56::56]") << true; - QTest::newRow("case 9") << QString::fromLatin1("//[56:56:56:56:56:56:56::]") << true; - QTest::newRow("case 4 with one less") << QString::fromLatin1("//[56::56:56:56:56:56]") << true; - QTest::newRow("case 4 with less and ip4") << QString::fromLatin1("//[56::56:56:56:127.0.0.1]") << true; - QTest::newRow("case 7 with one and ip4") << QString::fromLatin1("//[56::255.0.0.0]") << true; - QTest::newRow("case 2 with ip4") << QString::fromLatin1("//[::56:56:56:56:56:0.0.0.255]") << true; - QTest::newRow("case 2 with half ip4") << QString::fromLatin1("//[::56:56:56:56:56:56:0.255]") << false; - QTest::newRow("case 4 with less and ip4 and port and useinfo") << QString::fromLatin1("//user:pass@[56::56:56:56:127.0.0.1]:99") << true; - QTest::newRow("case :,") << QString::fromLatin1("//[:,]") << false; - QTest::newRow("case ::bla") << QString::fromLatin1("//[::bla]") << false; + QTest::newRow("case 1") << QString::fromLatin1("//[56:56:56:56:56:56:56:56]") << true + << "//[56:56:56:56:56:56:56:56]"; + QTest::newRow("case 2") << QString::fromLatin1("//[::56:56:56:56:56:56:56]") << true + << "//[0:56:56:56:56:56:56:56]"; + QTest::newRow("case 3") << QString::fromLatin1("//[56::56:56:56:56:56:56]") << true + << "//[56:0:56:56:56:56:56:56]"; + QTest::newRow("case 4") << QString::fromLatin1("//[56:56::56:56:56:56:56]") << true + << "//[56:56:0:56:56:56:56:56]"; + QTest::newRow("case 5") << QString::fromLatin1("//[56:56:56::56:56:56:56]") << true + << "//[56:56:56:0:56:56:56:56]"; + QTest::newRow("case 6") << QString::fromLatin1("//[56:56:56:56::56:56:56]") << true + << "//[56:56:56:56:0:56:56:56]"; + QTest::newRow("case 7") << QString::fromLatin1("//[56:56:56:56:56::56:56]") << true + << "//[56:56:56:56:56:0:56:56]"; + QTest::newRow("case 8") << QString::fromLatin1("//[56:56:56:56:56:56::56]") << true + << "//[56:56:56:56:56:56:0:56]"; + QTest::newRow("case 9") << QString::fromLatin1("//[56:56:56:56:56:56:56::]") << true + << "//[56:56:56:56:56:56:56:0]"; + QTest::newRow("case 4 with one less") << QString::fromLatin1("//[56::56:56:56:56:56]") << true + << "//[56::56:56:56:56:56]"; + QTest::newRow("case 4 with less and ip4") << QString::fromLatin1("//[56::56:56:56:127.0.0.1]") << true + << "//[56::56:56:56:7f00:1]"; + QTest::newRow("case 7 with one and ip4") << QString::fromLatin1("//[56::255.0.0.0]") << true + << "//[56::ff00:0]"; + QTest::newRow("case 2 with ip4") << QString::fromLatin1("//[::56:56:56:56:56:0.0.0.255]") << true + << "//[0:56:56:56:56:56:0:ff]"; + QTest::newRow("case 2 with half ip4") << QString::fromLatin1("//[::56:56:56:56:56:56:0.255]") << false << ""; + QTest::newRow("case 4 with less and ip4 and port and useinfo") + << QString::fromLatin1("//user:pass@[56::56:56:56:127.0.0.1]:99") << true + << "//user:pass@[56::56:56:56:7f00:1]:99"; + QTest::newRow("case :,") << QString::fromLatin1("//[:,]") << false << ""; + QTest::newRow("case ::bla") << QString::fromLatin1("//[::bla]") << false << ""; + QTest::newRow("case v4-mapped") << "//[0:0:0:0:0:ffff:7f00:1]" << true << "//[::ffff:127.0.0.1]"; } void tst_QUrl::ipv6() { QFETCH(QString, ipv6Auth); QFETCH(bool, isValid); + QFETCH(QString, output); QUrl url(ipv6Auth); QCOMPARE(url.isValid(), isValid); if (url.isValid()) { - QCOMPARE(url.toString(), ipv6Auth); + QCOMPARE(url.toString(), output); url.setHost(url.host()); - QCOMPARE(url.toString(), ipv6Auth); + QCOMPARE(url.toString(), output); } -}; +} void tst_QUrl::ipv6_2_data() { @@ -1547,26 +1603,6 @@ void tst_QUrl::moreIpv6() QCOMPARE(QString::fromLatin1(waba1.toEncoded()), QString::fromLatin1("http://[::ffff:129.144.52.38]/cgi/test.cgi")); } -void tst_QUrl::punycode_data() -{ - QTest::addColumn<QString>("original"); - QTest::addColumn<QByteArray>("encoded"); - - QTest::newRow("øl") << QString::fromUtf8("øl") << QByteArray("xn--l-4ga"); - QTest::newRow("Bühler") << QString::fromUtf8("Bühler") << QByteArray("xn--Bhler-kva"); - QTest::newRow("räksmörgås") << QString::fromUtf8("räksmörgås") << QByteArray("xn--rksmrgs-5wao1o"); -} - -void tst_QUrl::punycode() -{ - QFETCH(QString, original); - QFETCH(QByteArray, encoded); - - QCOMPARE(QUrl::fromPunycode(encoded), original); - QCOMPARE(QUrl::fromPunycode(QUrl::toPunycode(original)), original); - QCOMPARE(QUrl::toPunycode(original).constData(), encoded.constData()); -} - void tst_QUrl::isRelative_data() { QTest::addColumn<QString>("url"); @@ -1580,7 +1616,7 @@ void tst_QUrl::isRelative_data() QTest::newRow("man: URL, is relative") << "man:mmap" << false; QTest::newRow("javascript: URL, is relative") << "javascript:doSomething()" << false; QTest::newRow("file: URL, is relative") << "file:/blah" << false; - QTest::newRow("/path, is relative") << "/path" << true; + QTest::newRow("/path, is relative") << "/path" << false; QTest::newRow("something, is relative") << "something" << true; // end kde } @@ -1593,93 +1629,6 @@ void tst_QUrl::isRelative() QCOMPARE(QUrl(url).isRelative(), trueFalse); } -void tst_QUrl::setQueryItems() -{ - QUrl url; - - QList<QPair<QString, QString> > query; - query += qMakePair(QString("type"), QString("login")); - query += qMakePair(QString("name"), QString::fromUtf8("åge nissemannsen")); - query += qMakePair(QString("ole&du"), QString::fromUtf8("anne+jørgen=sant")); - query += qMakePair(QString("prosent"), QString("%")); - url.setQueryItems(query); - QVERIFY(!url.isEmpty()); - - QCOMPARE(url.encodedQuery().constData(), - QByteArray("type=login&name=%C3%A5ge%20nissemannsen&ole%26du=" - "anne+j%C3%B8rgen%3Dsant&prosent=%25").constData()); - - url.setQueryDelimiters('>', '/'); - url.setQueryItems(query); - - QCOMPARE(url.encodedQuery(), - QByteArray("type>login/name>%C3%A5ge%20nissemannsen/ole&du>" - "anne+j%C3%B8rgen=sant/prosent>%25")); - - url.setFragment(QString::fromLatin1("top")); - QCOMPARE(url.fragment(), QString::fromLatin1("top")); - - url.setScheme("http"); - url.setHost("qt.nokia.com"); - - QCOMPARE(url.toEncoded().constData(), - "http://qt.nokia.com?type>login/name>%C3%A5ge%20nissemannsen/ole&du>" - "anne+j%C3%B8rgen=sant/prosent>%25#top"); - QCOMPARE(url.toString(), - QString::fromUtf8("http://qt.nokia.com?type>login/name>åge nissemannsen" - "/ole&du>anne+jørgen=sant/prosent>%25#top")); -} - -void tst_QUrl::queryItems() -{ - QUrl url; - QVERIFY(!url.hasQuery()); - - QList<QPair<QString, QString> > newItems; - newItems += qMakePair(QString("2"), QString("b")); - newItems += qMakePair(QString("1"), QString("a")); - newItems += qMakePair(QString("3"), QString("c")); - newItems += qMakePair(QString("4"), QString("a b")); - newItems += qMakePair(QString("5"), QString("&")); - newItems += qMakePair(QString("foo bar"), QString("hello world")); - newItems += qMakePair(QString("foo+bar"), QString("hello+world")); - newItems += qMakePair(QString("tex"), QString("a + b = c")); - url.setQueryItems(newItems); - QVERIFY(url.hasQuery()); - - QList<QPair<QString, QString> > setItems = url.queryItems(); - QVERIFY(newItems == setItems); - - url.addQueryItem("1", "z"); - - QVERIFY(url.hasQueryItem("1")); - QCOMPARE(url.queryItemValue("1").toLatin1().constData(), "a"); - - url.addQueryItem("1", "zz"); - - QStringList expected; - expected += "a"; - expected += "z"; - expected += "zz"; - QCOMPARE(expected, url.allQueryItemValues("1")); - - url.removeQueryItem("1"); - QCOMPARE(url.allQueryItemValues("1").size(), 2); - QCOMPARE(url.queryItemValue("1").toLatin1().constData(), "z"); - - url.removeAllQueryItems("1"); - QVERIFY(!url.hasQueryItem("1")); - - QCOMPARE(url.queryItemValue("4").toLatin1().constData(), "a b"); - QCOMPARE(url.queryItemValue("5").toLatin1().constData(), "&"); - QCOMPARE(url.queryItemValue("tex").toLatin1().constData(), "a + b = c"); - QCOMPARE(url.queryItemValue("foo bar").toLatin1().constData(), "hello world"); - url.setUrl("http://www.google.com/search?q=a+b"); - QCOMPARE(url.queryItemValue("q"), QString("a+b")); - url.setUrl("http://www.google.com/search?q=a=b"); // invalid, but should be tolerated - QCOMPARE(url.queryItemValue("q"), QString("a=b")); -} - void tst_QUrl::hasQuery_data() { QTest::addColumn<QString>("url"); @@ -1709,27 +1658,6 @@ void tst_QUrl::hasQuery() QCOMPARE(qurl.encodedQuery().isNull(), !trueFalse); } -void tst_QUrl::hasQueryItem_data() -{ - QTest::addColumn<QString>("url"); - QTest::addColumn<QString>("item"); - QTest::addColumn<bool>("trueFalse"); - - QTest::newRow("no query items") << "http://www.foo.bar" << "baz" << false; - QTest::newRow("query item: hello") << "http://www.foo.bar?hello=world" << "hello" << true; - QTest::newRow("no query item: world") << "http://www.foo.bar?hello=world" << "world" << false; - QTest::newRow("query item: qt") << "http://www.foo.bar?hello=world&qt=rocks" << "qt" << true; -} - -void tst_QUrl::hasQueryItem() -{ - QFETCH(QString, url); - QFETCH(QString, item); - QFETCH(bool, trueFalse); - - QCOMPARE(QUrl(url).hasQueryItem(item), trueFalse); -} - void tst_QUrl::nameprep() { QUrl url(QString::fromUtf8("http://www.fu""\xc3""\x9f""ball.de/")); @@ -1754,6 +1682,7 @@ void tst_QUrl::isValid() QUrl url = QUrl::fromEncoded("http://strange<username>@ok-hostname/"); QVERIFY(url.isValid()); // < and > are allowed in tolerant mode + QCOMPARE(url.toEncoded(), QByteArray("http://strange%3Cusername%3E@ok-hostname/")); } { QUrl url = QUrl::fromEncoded("http://strange;hostname/here"); @@ -1765,7 +1694,7 @@ void tst_QUrl::isValid() QVERIFY(url.isValid()); url.setAuthority("strange;hostname"); QVERIFY(!url.isValid()); - QVERIFY(url.errorString().contains("invalid hostname")); + QVERIFY(url.errorString().contains("Hostname contains invalid characters")); } { @@ -1778,7 +1707,8 @@ void tst_QUrl::isValid() QVERIFY(url.isValid()); url.setHost("stuff;1"); QVERIFY(!url.isValid()); - QVERIFY(url.errorString().contains("invalid hostname")); + QVERIFY2(url.errorString().contains("Hostname contains invalid characters"), + qPrintable(url.errorString())); } } @@ -1822,6 +1752,8 @@ void tst_QUrl::schemeValidator() QFETCH(QString, toString); QUrl url = QUrl::fromEncoded(encodedUrl); + QEXPECT_FAIL("ftp:/index.html", "high-level URL validation not reimplemented yet", Continue); + QEXPECT_FAIL("mailto://smtp.trolltech.com/ole@bull.name", "high-level URL validation not reimplemented yet", Continue); QCOMPARE(url.isValid(), result); } @@ -1829,27 +1761,26 @@ void tst_QUrl::invalidSchemeValidator() { // test that if scheme does not start with an ALPHA, QUrl::isValid() returns false { - QUrl url("1http://qt.nokia.com", QUrl::StrictMode); - QCOMPARE(url.isValid(), false); + QUrl url("1http://qt.nokia.com"); + QVERIFY(url.scheme().isEmpty()); + QVERIFY(url.path().startsWith("1http")); } { QUrl url("http://qt.nokia.com"); url.setScheme("111http://qt.nokia.com"); QCOMPARE(url.isValid(), false); } - { - QUrl url = QUrl::fromEncoded("1http://qt.nokia.com", QUrl::StrictMode); - QCOMPARE(url.isValid(), false); - } - // non-ALPHA character at other positions in the scheme are ok { QUrl url("ht111tp://qt.nokia.com", QUrl::StrictMode); QVERIFY(url.isValid()); + QCOMPARE(url.scheme(), QString("ht111tp")); } { QUrl url("http://qt.nokia.com"); url.setScheme("ht123tp://qt.nokia.com"); + QVERIFY(!url.isValid()); + url.setScheme("http"); QVERIFY(url.isValid()); } { @@ -1858,6 +1789,55 @@ void tst_QUrl::invalidSchemeValidator() } } +void tst_QUrl::strictParser_data() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("needle"); + + // cannot test bad schemes here, as they are parsed as paths instead + //QTest::newRow("invalid-scheme") << "ht%://example.com" << "Invalid scheme"; + //QTest::newRow("empty-scheme") << ":/" << "Empty scheme"; + + QTest::newRow("invalid-user1") << "http://bad<user_name>@ok-hostname" << "Invalid user name"; + QTest::newRow("invalid-user2") << "http://bad%@ok-hostname" << "Invalid user name"; + + QTest::newRow("invalid-password") << "http://user:pass\x7F@ok-hostname" << "Invalid password"; + + QTest::newRow("invalid-regname") << "http://bad<hostname>" << "Hostname contains invalid characters"; + QTest::newRow("invalid-ipv6") << "http://[:::]" << "Invalid IPv6 address"; + QTest::newRow("invalid-ipvfuture-1") << "http://[v7]" << "Invalid IPvFuture address"; + QTest::newRow("invalid-ipvfuture-2") << "http://[v7.]" << "Invalid IPvFuture address"; + QTest::newRow("invalid-ipvfuture-3") << "http://[v789]" << "Invalid IPvFuture address"; + QTest::newRow("unbalanced-brackets") << "http://[ff02::1" << "Expected ']'"; + + QTest::newRow("empty-port") << "http://example.com:" << "Invalid port"; + QTest::newRow("invalid-port-1") << "http://example.com:-1" << "Invalid port"; + QTest::newRow("invalid-port-2") << "http://example.com:abc" << "Invalid port"; + QTest::newRow("invalid-port-3") << "http://example.com:9a" << "Invalid port"; + QTest::newRow("port-range") << "http://example.com:65536" << "out of range"; + + QTest::newRow("invalid-path") << "foo:/path%\x1F" << "Invalid path"; + // not yet checked: + //QTest::newRow("path-colon-before-slash") << "foo::/" << "':' before any '/'"; + + QTest::newRow("invalid-query") << "foo:?\\#" << "Invalid query"; + + QTest::newRow("invalid-fragment") << "#{}" << "Invalid fragment"; +} + +void tst_QUrl::strictParser() +{ + QFETCH(QString, input); + QFETCH(QString, needle); + + QUrl url(input, QUrl::StrictMode); + QVERIFY(!url.isValid()); + QVERIFY(!url.errorString().isEmpty()); + if (!url.errorString().contains(needle)) + qWarning("Error string changed and does not contain \"%s\" anymore: %s", + qPrintable(needle), qPrintable(url.errorString())); +} + void tst_QUrl::tolerantParser() { { @@ -1865,6 +1845,7 @@ void tst_QUrl::tolerantParser() QVERIFY(url.isValid()); QCOMPARE(url.path(), QString("/path with spaces.html")); QCOMPARE(url.toEncoded(), QByteArray("http://www.example.com/path%20with%20spaces.html")); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("http://www.example.com/path%20with%20spaces.html")); url.setUrl("http://www.example.com/path%20with spaces.html", QUrl::StrictMode); QVERIFY(!url.isValid()); } @@ -1886,58 +1867,90 @@ void tst_QUrl::tolerantParser() QUrl webkit22616 = QUrl::fromEncoded("http://example.com/testya.php?browser-info=s:1400x1050x24:f:9.0%20r152:t:%u0442%u0435%u0441%u0442"); QVERIFY(webkit22616.isValid()); + + // Qt 5 behaviour change: one broken % means all % are considered broken +// QCOMPARE(webkit22616.toEncoded().constData(), +// "http://example.com/testya.php?browser-info=s:1400x1050x24:f:9.0%20r152:t:%25u0442%25u0435%25u0441%25u0442"); QCOMPARE(webkit22616.toEncoded().constData(), - "http://example.com/testya.php?browser-info=s:1400x1050x24:f:9.0%20r152:t:%25u0442%25u0435%25u0441%25u0442"); + "http://example.com/testya.php?browser-info=s:1400x1050x24:f:9.0%2520r152:t:%25u0442%25u0435%25u0441%25u0442"); } { QUrl url; url.setUrl("http://foo.bar/[image][1].jpg"); QVERIFY(url.isValid()); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); QCOMPARE(url.toEncoded(), QByteArray("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); + QCOMPARE(url.toString(), QString("http://foo.bar/[image][1].jpg")); url.setUrl("[].jpg"); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("%5B%5D.jpg")); QCOMPARE(url.toEncoded(), QByteArray("%5B%5D.jpg")); + QCOMPARE(url.toString(), QString("[].jpg")); url.setUrl("/some/[path]/[]"); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("/some/%5Bpath%5D/%5B%5D")); QCOMPARE(url.toEncoded(), QByteArray("/some/%5Bpath%5D/%5B%5D")); + QCOMPARE(url.toString(), QString("/some/[path]/[]")); url.setUrl("//[::56:56:56:56:56:56:56]"); - QCOMPARE(url.toEncoded(), QByteArray("//[::56:56:56:56:56:56:56]")); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]")); + QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]")); + QCOMPARE(url.toString(), QString("//[0:56:56:56:56:56:56:56]")); url.setUrl("//[::56:56:56:56:56:56:56]#[]"); - QCOMPARE(url.toEncoded(), QByteArray("//[::56:56:56:56:56:56:56]#%5B%5D")); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]#%5B%5D")); + QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]#%5B%5D")); + QCOMPARE(url.toString(), QString("//[0:56:56:56:56:56:56:56]#[]")); url.setUrl("//[::56:56:56:56:56:56:56]?[]"); - QCOMPARE(url.toEncoded(), QByteArray("//[::56:56:56:56:56:56:56]?%5B%5D")); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]?[]")); + QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]?[]")); + QCOMPARE(url.toString(), QString("//[0:56:56:56:56:56:56:56]?[]")); + // invoke the tolerant parser's error correction url.setUrl("%hello.com/f%"); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("%25hello.com/f%25")); QCOMPARE(url.toEncoded(), QByteArray("%25hello.com/f%25")); + QCOMPARE(url.toString(), QString("%25hello.com/f%25")); url.setEncodedUrl("http://www.host.com/foo.php?P0=[2006-3-8]"); QVERIFY(url.isValid()); url.setEncodedUrl("http://foo.bar/[image][1].jpg"); QVERIFY(url.isValid()); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); QCOMPARE(url.toEncoded(), QByteArray("http://foo.bar/%5Bimage%5D%5B1%5D.jpg")); + QCOMPARE(url.toString(), QString("http://foo.bar/[image][1].jpg")); url.setEncodedUrl("[].jpg"); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("%5B%5D.jpg")); QCOMPARE(url.toEncoded(), QByteArray("%5B%5D.jpg")); + QCOMPARE(url.toString(), QString("[].jpg")); url.setEncodedUrl("/some/[path]/[]"); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("/some/%5Bpath%5D/%5B%5D")); QCOMPARE(url.toEncoded(), QByteArray("/some/%5Bpath%5D/%5B%5D")); + QCOMPARE(url.toString(), QString("/some/[path]/[]")); url.setEncodedUrl("//[::56:56:56:56:56:56:56]"); - QCOMPARE(url.toEncoded(), QByteArray("//[::56:56:56:56:56:56:56]")); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]")); + QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]")); url.setEncodedUrl("//[::56:56:56:56:56:56:56]#[]"); - QCOMPARE(url.toEncoded(), QByteArray("//[::56:56:56:56:56:56:56]#%5B%5D")); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]#%5B%5D")); + QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]#%5B%5D")); + QCOMPARE(url.toString(), QString("//[0:56:56:56:56:56:56:56]#[]")); url.setEncodedUrl("//[::56:56:56:56:56:56:56]?[]"); - QCOMPARE(url.toEncoded(), QByteArray("//[::56:56:56:56:56:56:56]?%5B%5D")); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("//[0:56:56:56:56:56:56:56]?[]")); + QCOMPARE(url.toEncoded(), QByteArray("//[0:56:56:56:56:56:56:56]?[]")); + QCOMPARE(url.toString(), QString("//[0:56:56:56:56:56:56:56]?[]")); url.setEncodedUrl("data:text/css,div%20{%20border-right:%20solid;%20}"); + QCOMPARE(url.toString(QUrl::FullyEncoded), QString("data:text/css,div%20%7B%20border-right:%20solid;%20%7D")); QCOMPARE(url.toEncoded(), QByteArray("data:text/css,div%20%7B%20border-right:%20solid;%20%7D")); + QCOMPARE(url.toString(), QString("data:text/css,div { border-right: solid; }")); } { @@ -1962,16 +1975,15 @@ void tst_QUrl::correctEncodedMistakes_data() QTest::addColumn<QByteArray>("encodedUrl"); QTest::addColumn<bool>("result"); QTest::addColumn<QString>("toDecoded"); - QTest::addColumn<QByteArray>("toEncoded"); - QTest::newRow("%") << QByteArray("%") << true << QString("%") << QByteArray("%25"); - QTest::newRow("3%") << QByteArray("3%") << true << QString("3%") << QByteArray("3%25"); - QTest::newRow("13%") << QByteArray("13%") << true << QString("13%") << QByteArray("13%25"); - QTest::newRow("13%!") << QByteArray("13%!") << true << QString("13%!") << QByteArray("13%25!"); - QTest::newRow("13%!!") << QByteArray("13%!!") << true << QString("13%!!") << QByteArray("13%25!!"); - QTest::newRow("13%a") << QByteArray("13%a") << true << QString("13%a") << QByteArray("13%25a"); - QTest::newRow("13%az") << QByteArray("13%az") << true << QString("13%az") << QByteArray("13%25az"); - QTest::newRow("13%25") << QByteArray("13%25") << true << QString("13%") << QByteArray("13%25"); + QTest::newRow("%") << QByteArray("%") << true << QString("%25"); + QTest::newRow("3%") << QByteArray("3%") << true << QString("3%25"); + QTest::newRow("13%") << QByteArray("13%") << true << QString("13%25"); + QTest::newRow("13%!") << QByteArray("13%!") << true << QString("13%25!"); + QTest::newRow("13%!!") << QByteArray("13%!!") << true << QString("13%25!!"); + QTest::newRow("13%a") << QByteArray("13%a") << true << QString("13%25a"); + QTest::newRow("13%az") << QByteArray("13%az") << true << QString("13%25az"); + QTest::newRow("13%25") << QByteArray("13%25") << true << QString("13%25"); } void tst_QUrl::correctEncodedMistakes() @@ -1979,14 +1991,11 @@ void tst_QUrl::correctEncodedMistakes() QFETCH(QByteArray, encodedUrl); QFETCH(bool, result); QFETCH(QString, toDecoded); - QFETCH(QByteArray, toEncoded); QUrl url = QUrl::fromEncoded(encodedUrl); QCOMPARE(url.isValid(), result); if (url.isValid()) { - Q_UNUSED(toDecoded); // no full-decoding available at the moment - QCOMPARE(url.toString(), QString::fromLatin1(toEncoded)); - QCOMPARE(url.toEncoded(), toEncoded); + QCOMPARE(url.toString(), toDecoded); } } @@ -1995,16 +2004,14 @@ void tst_QUrl::correctDecodedMistakes_data() QTest::addColumn<QString>("decodedUrl"); QTest::addColumn<bool>("result"); QTest::addColumn<QString>("toDecoded"); - QTest::addColumn<QByteArray>("toEncoded"); - QTest::newRow("%") << QString("%") << true << QString("%") << QByteArray("%25"); - QTest::newRow("3%") << QString("3%") << true << QString("3%") << QByteArray("3%25"); - QTest::newRow("13%") << QString("13%") << true << QString("13%") << QByteArray("13%25"); - QTest::newRow("13%!") << QString("13%!") << true << QString("13%!") << QByteArray("13%25!"); - QTest::newRow("13%!!") << QString("13%!!") << true << QString("13%!!") << QByteArray("13%25!!"); - QTest::newRow("13%a") << QString("13%a") << true << QString("13%a") << QByteArray("13%25a"); - QTest::newRow("13%az") << QString("13%az") << true << QString("13%az") << QByteArray("13%25az"); - QTest::newRow("13%25") << QString("13%25") << true << QString("13%25") << QByteArray("13%25"); + QTest::newRow("%") << QString("%") << true << QString("%25"); + QTest::newRow("3%") << QString("3%") << true << QString("3%25"); + QTest::newRow("13%") << QString("13%") << true << QString("13%25"); + QTest::newRow("13%!") << QString("13%!") << true << QString("13%25!"); + QTest::newRow("13%!!") << QString("13%!!") << true << QString("13%25!!"); + QTest::newRow("13%a") << QString("13%a") << true << QString("13%25a"); + QTest::newRow("13%az") << QString("13%az") << true << QString("13%25az"); } void tst_QUrl::correctDecodedMistakes() @@ -2012,671 +2019,14 @@ void tst_QUrl::correctDecodedMistakes() QFETCH(QString, decodedUrl); QFETCH(bool, result); QFETCH(QString, toDecoded); - QFETCH(QByteArray, toEncoded); QUrl url(decodedUrl); QCOMPARE(url.isValid(), result); if (url.isValid()) { - Q_UNUSED(toDecoded); // no full-decoding available at the moment - QCOMPARE(url.toString(), QString::fromLatin1(toEncoded)); - QCOMPARE(url.toEncoded(), toEncoded); - } -} - -void tst_QUrl::idna_testsuite_data() -{ - QTest::addColumn<int>("numchars"); - QTest::addColumn<ushortarray>("unicode"); - QTest::addColumn<QByteArray>("punycode"); - QTest::addColumn<int>("allowunassigned"); - QTest::addColumn<int>("usestd3asciirules"); - QTest::addColumn<int>("toasciirc"); - QTest::addColumn<int>("tounicoderc"); - - unsigned short d1[] = { 0x0644, 0x064A, 0x0647, 0x0645, 0x0627, 0x0628, 0x062A, 0x0643, - 0x0644, 0x0645, 0x0648, 0x0634, 0x0639, 0x0631, 0x0628, 0x064A, - 0x061F }; - QTest::newRow("Arabic (Egyptian)") << 17 << ushortarray(d1) - << QByteArray(IDNA_ACE_PREFIX "egbpdaj6bu4bxfgehfvwxn") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d2[] = { 0x4ED6, 0x4EEC, 0x4E3A, 0x4EC0, 0x4E48, 0x4E0D, 0x8BF4, 0x4E2D, - 0x6587 }; - QTest::newRow("Chinese (simplified)") << 9 << ushortarray(d2) - << QByteArray(IDNA_ACE_PREFIX "ihqwcrb4cv8a8dqg056pqjye") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d3[] = { 0x4ED6, 0x5011, 0x7232, 0x4EC0, 0x9EBD, 0x4E0D, 0x8AAA, 0x4E2D, - 0x6587 }; - QTest::newRow("Chinese (traditional)") << 9 << ushortarray(d3) - << QByteArray(IDNA_ACE_PREFIX "ihqwctvzc91f659drss3x8bo0yb") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d4[] = { 0x0050, 0x0072, 0x006F, 0x010D, 0x0070, 0x0072, 0x006F, 0x0073, - 0x0074, 0x011B, 0x006E, 0x0065, 0x006D, 0x006C, 0x0075, 0x0076, - 0x00ED, 0x010D, 0x0065, 0x0073, 0x006B, 0x0079 }; - QTest::newRow("Czech") << 22 << ushortarray(d4) - << QByteArray(IDNA_ACE_PREFIX "Proprostnemluvesky-uyb24dma41a") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d5[] = { 0x05DC, 0x05DE, 0x05D4, 0x05D4, 0x05DD, 0x05E4, 0x05E9, 0x05D5, - 0x05D8, 0x05DC, 0x05D0, 0x05DE, 0x05D3, 0x05D1, 0x05E8, 0x05D9, - 0x05DD, 0x05E2, 0x05D1, 0x05E8, 0x05D9, 0x05EA }; - QTest::newRow("Hebrew") << 22 << ushortarray(d5) - << QByteArray(IDNA_ACE_PREFIX "4dbcagdahymbxekheh6e0a7fei0b") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d6[] = { 0x092F, 0x0939, 0x0932, 0x094B, 0x0917, 0x0939, 0x093F, 0x0928, - 0x094D, 0x0926, 0x0940, 0x0915, 0x094D, 0x092F, 0x094B, 0x0902, - 0x0928, 0x0939, 0x0940, 0x0902, 0x092C, 0x094B, 0x0932, 0x0938, - 0x0915, 0x0924, 0x0947, 0x0939, 0x0948, 0x0902 }; - QTest::newRow("Hindi (Devanagari)") << 30 << ushortarray(d6) - << QByteArray(IDNA_ACE_PREFIX "i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd") - << 0 << 0 << IDNA_SUCCESS; - - unsigned short d7[] = { 0x306A, 0x305C, 0x307F, 0x3093, 0x306A, 0x65E5, 0x672C, 0x8A9E, - 0x3092, 0x8A71, 0x3057, 0x3066, 0x304F, 0x308C, 0x306A, 0x3044, - 0x306E, 0x304B }; - QTest::newRow("Japanese (kanji and hiragana)") << 18 << ushortarray(d7) - << QByteArray(IDNA_ACE_PREFIX "n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa") - << 0 << 0 << IDNA_SUCCESS; - - unsigned short d8[] = { 0x043F, 0x043E, 0x0447, 0x0435, 0x043C, 0x0443, 0x0436, 0x0435, - 0x043E, 0x043D, 0x0438, 0x043D, 0x0435, 0x0433, 0x043E, 0x0432, - 0x043E, 0x0440, 0x044F, 0x0442, 0x043F, 0x043E, 0x0440, 0x0443, - 0x0441, 0x0441, 0x043A, 0x0438 }; - QTest::newRow("Russian (Cyrillic)") << 28 << ushortarray(d8) - << QByteArray(IDNA_ACE_PREFIX "b1abfaaepdrnnbgefbadotcwatmq2g4l") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d9[] = { 0x0050, 0x006F, 0x0072, 0x0071, 0x0075, 0x00E9, 0x006E, 0x006F, - 0x0070, 0x0075, 0x0065, 0x0064, 0x0065, 0x006E, 0x0073, 0x0069, - 0x006D, 0x0070, 0x006C, 0x0065, 0x006D, 0x0065, 0x006E, 0x0074, - 0x0065, 0x0068, 0x0061, 0x0062, 0x006C, 0x0061, 0x0072, 0x0065, - 0x006E, 0x0045, 0x0073, 0x0070, 0x0061, 0x00F1, 0x006F, 0x006C }; - QTest::newRow("Spanish") << 40 << ushortarray(d9) - << QByteArray(IDNA_ACE_PREFIX "PorqunopuedensimplementehablarenEspaol-fmd56a") - << 0 << 0 << IDNA_SUCCESS; - - unsigned short d10[] = { 0x0054, 0x1EA1, 0x0069, 0x0073, 0x0061, 0x006F, 0x0068, 0x1ECD, - 0x006B, 0x0068, 0x00F4, 0x006E, 0x0067, 0x0074, 0x0068, 0x1EC3, - 0x0063, 0x0068, 0x1EC9, 0x006E, 0x00F3, 0x0069, 0x0074, 0x0069, - 0x1EBF, 0x006E, 0x0067, 0x0056, 0x0069, 0x1EC7, 0x0074 }; - QTest::newRow("Vietnamese") << 31 << ushortarray(d10) - << QByteArray(IDNA_ACE_PREFIX "TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g") - << 0 << 0 << IDNA_SUCCESS; - - unsigned short d11[] = { 0x0033, 0x5E74, 0x0042, 0x7D44, 0x91D1, 0x516B, 0x5148, 0x751F }; - QTest::newRow("Japanese") << 8 << ushortarray(d11) - << QByteArray(IDNA_ACE_PREFIX "3B-ww4c5e180e575a65lsy2b") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d12[] = { 0x5B89, 0x5BA4, 0x5948, 0x7F8E, 0x6075, 0x002D, 0x0077, 0x0069, - 0x0074, 0x0068, 0x002D, 0x0053, 0x0055, 0x0050, 0x0045, 0x0052, - 0x002D, 0x004D, 0x004F, 0x004E, 0x004B, 0x0045, 0x0059, 0x0053 }; - QTest::newRow("Japanese2") << 24 << ushortarray(d12) - << QByteArray(IDNA_ACE_PREFIX "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n") - << 0 << 0 << IDNA_SUCCESS; - - unsigned short d13[] = { 0x0048, 0x0065, 0x006C, 0x006C, 0x006F, 0x002D, 0x0041, 0x006E, - 0x006F, 0x0074, 0x0068, 0x0065, 0x0072, 0x002D, 0x0057, 0x0061, - 0x0079, 0x002D, 0x305D, 0x308C, 0x305E, 0x308C, 0x306E, 0x5834, - 0x6240 }; - QTest::newRow("Japanese3") << 25 << ushortarray(d13) - << QByteArray(IDNA_ACE_PREFIX "Hello-Another-Way--fc4qua05auwb3674vfr0b") - << 0 << 0 << IDNA_SUCCESS; - - unsigned short d14[] = { 0x3072, 0x3068, 0x3064, 0x5C4B, 0x6839, 0x306E, 0x4E0B, 0x0032 }; - QTest::newRow("Japanese4") << 8 << ushortarray(d14) - << QByteArray(IDNA_ACE_PREFIX "2-u9tlzr9756bt3uc0v") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d15[] = { 0x004D, 0x0061, 0x006A, 0x0069, 0x3067, 0x004B, 0x006F, 0x0069, - 0x3059, 0x308B, 0x0035, 0x79D2, 0x524D }; - QTest::newRow("Japanese5") << 13 << ushortarray(d15) - << QByteArray(IDNA_ACE_PREFIX "MajiKoi5-783gue6qz075azm5e") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d16[] = { 0x30D1, 0x30D5, 0x30A3, 0x30FC, 0x0064, 0x0065, 0x30EB, 0x30F3, 0x30D0 }; - QTest::newRow("Japanese6") << 9 << ushortarray(d16) - << QByteArray(IDNA_ACE_PREFIX "de-jg4avhby1noc0d") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d17[] = { 0x305D, 0x306E, 0x30B9, 0x30D4, 0x30FC, 0x30C9, 0x3067 }; - QTest::newRow("Japanese7") << 7 << ushortarray(d17) - << QByteArray(IDNA_ACE_PREFIX "d9juau41awczczp") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d18[] = { 0x03b5, 0x03bb, 0x03bb, 0x03b7, 0x03bd, 0x03b9, 0x03ba, 0x03ac }; - QTest::newRow("Greek") << 8 << ushortarray(d18) - << QByteArray(IDNA_ACE_PREFIX "hxargifdar") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d19[] = { 0x0062, 0x006f, 0x006e, 0x0121, 0x0075, 0x0073, 0x0061, 0x0127, - 0x0127, 0x0061 }; - QTest::newRow("Maltese (Malti)") << 10 << ushortarray(d19) - << QByteArray(IDNA_ACE_PREFIX "bonusaa-5bb1da") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; - - unsigned short d20[] = {0x043f, 0x043e, 0x0447, 0x0435, 0x043c, 0x0443, 0x0436, 0x0435, - 0x043e, 0x043d, 0x0438, 0x043d, 0x0435, 0x0433, 0x043e, 0x0432, - 0x043e, 0x0440, 0x044f, 0x0442, 0x043f, 0x043e, 0x0440, 0x0443, - 0x0441, 0x0441, 0x043a, 0x0438 }; - QTest::newRow("Russian (Cyrillic)") << 28 << ushortarray(d20) - << QByteArray(IDNA_ACE_PREFIX "b1abfaaepdrnnbgefbadotcwatmq2g4l") - << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; -} - -void tst_QUrl::idna_testsuite() -{ - QFETCH(int, numchars); - QFETCH(ushortarray, unicode); - QFETCH(QByteArray, punycode); - - QString s = QString::fromUtf16(unicode.points, numchars); - QCOMPARE(punycode, QUrl::toPunycode(s)); -} - -void tst_QUrl::nameprep_testsuite_data() -{ - QTest::addColumn<QString>("in"); - QTest::addColumn<QString>("out"); - QTest::addColumn<QString>("profile"); - QTest::addColumn<int>("flags"); - QTest::addColumn<int>("rc"); - - QTest::newRow("Map to nothing") - << QString::fromUtf8("foo\xC2\xAD\xCD\x8F\xE1\xA0\x86\xE1\xA0\x8B" - "bar""\xE2\x80\x8B\xE2\x81\xA0""baz\xEF\xB8\x80\xEF\xB8\x88" - "\xEF\xB8\x8F\xEF\xBB\xBF") - << QString::fromUtf8("foobarbaz") - << QString() << 0 << 0; - - QTest::newRow("Case folding ASCII U+0043 U+0041 U+0046 U+0045") - << QString::fromUtf8("CAFE") - << QString::fromUtf8("cafe") - << QString() << 0 << 0; - - QTest::newRow("Case folding 8bit U+00DF (german sharp s)") - << QString::fromUtf8("\xC3\x9F") - << QString("ss") - << QString() << 0 << 0; - - QTest::newRow("Case folding U+0130 (turkish capital I with dot)") - << QString::fromUtf8("\xC4\xB0") - << QString::fromUtf8("i\xcc\x87") - << QString() << 0 << 0; - - QTest::newRow("Case folding multibyte U+0143 U+037A") - << QString::fromUtf8("\xC5\x83\xCD\xBA") - << QString::fromUtf8("\xC5\x84 \xCE\xB9") - << QString() << 0 << 0; - - QTest::newRow("Case folding U+2121 U+33C6 U+1D7BB") - << QString::fromUtf8("\xE2\x84\xA1\xE3\x8F\x86\xF0\x9D\x9E\xBB") - << QString::fromUtf8("telc\xE2\x88\x95""kg\xCF\x83") - << QString() << 0 << 0; - - QTest::newRow("Normalization of U+006a U+030c U+00A0 U+00AA") - << QString::fromUtf8("\x6A\xCC\x8C\xC2\xA0\xC2\xAA") - << QString::fromUtf8("\xC7\xB0 a") - << QString() << 0 << 0; - - QTest::newRow("Case folding U+1FB7 and normalization") - << QString::fromUtf8("\xE1\xBE\xB7") - << QString::fromUtf8("\xE1\xBE\xB6\xCE\xB9") - << QString() << 0 << 0; - - QTest::newRow("Self-reverting case folding U+01F0 and normalization") -// << QString::fromUtf8("\xC7\xF0") ### typo in the original testsuite - << QString::fromUtf8("\xC7\xB0") - << QString::fromUtf8("\xC7\xB0") - << QString() << 0 << 0; - - QTest::newRow("Self-reverting case folding U+0390 and normalization") - << QString::fromUtf8("\xCE\x90") - << QString::fromUtf8("\xCE\x90") - << QString() << 0 << 0; - - QTest::newRow("Self-reverting case folding U+03B0 and normalization") - << QString::fromUtf8("\xCE\xB0") - << QString::fromUtf8("\xCE\xB0") - << QString() << 0 << 0; - - QTest::newRow("Self-reverting case folding U+1E96 and normalization") - << QString::fromUtf8("\xE1\xBA\x96") - << QString::fromUtf8("\xE1\xBA\x96") - << QString() << 0 << 0; - - QTest::newRow("Self-reverting case folding U+1F56 and normalization") - << QString::fromUtf8("\xE1\xBD\x96") - << QString::fromUtf8("\xE1\xBD\x96") - << QString() << 0 << 0; - - QTest::newRow("ASCII space character U+0020") - << QString::fromUtf8("\x20") - << QString::fromUtf8("\x20") - << QString() << 0 << 0; - - QTest::newRow("Non-ASCII 8bit space character U+00A0") - << QString::fromUtf8("\xC2\xA0") - << QString::fromUtf8("\x20") - << QString() << 0 << 0; - - QTest::newRow("Non-ASCII multibyte space character U+1680") - << QString::fromUtf8("\xE1\x9A\x80") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Non-ASCII multibyte space character U+2000") - << QString::fromUtf8("\xE2\x80\x80") - << QString::fromUtf8("\x20") - << QString() << 0 << 0; - - QTest::newRow("Zero Width Space U+200b") - << QString::fromUtf8("\xE2\x80\x8b") - << QString() - << QString() << 0 << 0; - - QTest::newRow("Non-ASCII multibyte space character U+3000") - << QString::fromUtf8("\xE3\x80\x80") - << QString::fromUtf8("\x20") - << QString() << 0 << 0; - - QTest::newRow("ASCII control characters U+0010 U+007F") - << QString::fromUtf8("\x10\x7F") - << QString::fromUtf8("\x10\x7F") - << QString() << 0 << 0; - - QTest::newRow("Non-ASCII 8bit control character U+0085") - << QString::fromUtf8("\xC2\x85") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Non-ASCII multibyte control character U+180E") - << QString::fromUtf8("\xE1\xA0\x8E") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Zero Width No-Break Space U+FEFF") - << QString::fromUtf8("\xEF\xBB\xBF") - << QString() - << QString() << 0 << 0; - - QTest::newRow("Non-ASCII control character U+1D175") - << QString::fromUtf8("\xF0\x9D\x85\xB5") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Plane 0 private use character U+F123") - << QString::fromUtf8("\xEF\x84\xA3") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Plane 15 private use character U+F1234") - << QString::fromUtf8("\xF3\xB1\x88\xB4") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Plane 16 private use character U+10F234") - << QString::fromUtf8("\xF4\x8F\x88\xB4") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Non-character code point U+8FFFE") - << QString::fromUtf8("\xF2\x8F\xBF\xBE") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Non-character code point U+10FFFF") - << QString::fromUtf8("\xF4\x8F\xBF\xBF") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Surrogate code U+DF42") - << QString::fromUtf8("\xED\xBD\x82") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Non-plain text character U+FFFD") - << QString::fromUtf8("\xEF\xBF\xBD") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Ideographic description character U+2FF5") - << QString::fromUtf8("\xE2\xBF\xB5") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Display property character U+0341") - << QString::fromUtf8("\xCD\x81") - << QString::fromUtf8("\xCC\x81") - << QString() << 0 << 0; - - QTest::newRow("Left-to-right mark U+200E") - << QString::fromUtf8("\xE2\x80\x8E") - << QString::fromUtf8("\xCC\x81") - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Deprecated U+202A") - << QString::fromUtf8("\xE2\x80\xAA") - << QString::fromUtf8("\xCC\x81") - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Language tagging character U+E0001") - << QString::fromUtf8("\xF3\xA0\x80\x81") - << QString::fromUtf8("\xCC\x81") - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Language tagging character U+E0042") - << QString::fromUtf8("\xF3\xA0\x81\x82") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; - - QTest::newRow("Bidi: RandALCat character U+05BE and LCat characters") - << QString::fromUtf8("foo\xD6\xBE""bar") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_BIDI_BOTH_L_AND_RAL; - - QTest::newRow("Bidi: RandALCat character U+FD50 and LCat characters") - << QString::fromUtf8("foo\xEF\xB5\x90""bar") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_BIDI_BOTH_L_AND_RAL; - - QTest::newRow("Bidi: RandALCat character U+FB38 and LCat characters") - << QString::fromUtf8("foo\xEF\xB9\xB6""bar") - << QString::fromUtf8("foo \xd9\x8e""bar") - << QString() << 0 << 0; - - QTest::newRow("Bidi: RandALCat without trailing RandALCat U+0627 U+0031") - << QString::fromUtf8("\xD8\xA7\x31") - << QString() - << QString("Nameprep") << 0 << STRINGPREP_BIDI_LEADTRAIL_NOT_RAL; - - QTest::newRow("Bidi: RandALCat character U+0627 U+0031 U+0628") - << QString::fromUtf8("\xD8\xA7\x31\xD8\xA8") - << QString::fromUtf8("\xD8\xA7\x31\xD8\xA8") - << QString() << 0 << 0; - - QTest::newRow("Unassigned code point U+E0002") - << QString::fromUtf8("\xF3\xA0\x80\x82") - << QString() - << QString("Nameprep") << STRINGPREP_NO_UNASSIGNED << STRINGPREP_CONTAINS_UNASSIGNED; - - QTest::newRow("Larger test (shrinking)") - << QString::fromUtf8("X\xC2\xAD\xC3\x9F\xC4\xB0\xE2\x84\xA1\x6a\xcc\x8c\xc2\xa0\xc2" - "\xaa\xce\xb0\xe2\x80\x80") - << QString::fromUtf8("xssi\xcc\x87""tel\xc7\xb0 a\xce\xb0 ") - << QString("Nameprep") << 0 << 0; - - QTest::newRow("Larger test (expanding)") - << QString::fromUtf8("X\xC3\x9F\xe3\x8c\x96\xC4\xB0\xE2\x84\xA1\xE2\x92\x9F\xE3\x8c\x80") - << QString::fromUtf8("xss\xe3\x82\xad\xe3\x83\xad\xe3\x83\xa1\xe3\x83\xbc\xe3\x83\x88" - "\xe3\x83\xab""i\xcc\x87""tel\x28""d\x29\xe3\x82\xa2\xe3\x83\x91" - "\xe3\x83\xbc\xe3\x83\x88") - << QString() << 0 << 0; -} - -#ifdef QT_BUILD_INTERNAL -QT_BEGIN_NAMESPACE -extern void qt_nameprep(QString *source, int from); -extern bool qt_check_std3rules(const QChar *, int); -QT_END_NAMESPACE -#endif - -void tst_QUrl::nameprep_testsuite() -{ -#ifdef QT_BUILD_INTERNAL - QFETCH(QString, in); - QFETCH(QString, out); - QFETCH(QString, profile); - - QEXPECT_FAIL("Left-to-right mark U+200E", - "Investigate further", Continue); - QEXPECT_FAIL("Deprecated U+202A", - "Investigate further", Continue); - QEXPECT_FAIL("Language tagging character U+E0001", - "Investigate further", Continue); - qt_nameprep(&in, 0); - QCOMPARE(in, out); -#endif -} - -void tst_QUrl::nameprep_highcodes_data() -{ - QTest::addColumn<QString>("in"); - QTest::addColumn<QString>("out"); - QTest::addColumn<QString>("profile"); - QTest::addColumn<int>("flags"); - QTest::addColumn<int>("rc"); - - { - QChar st[] = { '-', 0xd801, 0xdc1d, 'a' }; - QChar se[] = { '-', 0xd801, 0xdc45, 'a' }; - QTest::newRow("highcodes (U+1041D)") - << QString(st, sizeof(st)/sizeof(st[0])) - << QString(se, sizeof(se)/sizeof(se[0])) - << QString() << 0 << 0; - } - { - QChar st[] = { 0x011C, 0xd835, 0xdf6e, 0x0110 }; - QChar se[] = { 0x011D, 0x03C9, 0x0111 }; - QTest::newRow("highcodes (U+1D76E)") - << QString(st, sizeof(st)/sizeof(st[0])) - << QString(se, sizeof(se)/sizeof(se[0])) - << QString() << 0 << 0; - } - { - QChar st[] = { 'D', 0xdb40, 0xdc20, 'o', 0xd834, 0xdd7a, '\'', 0x2060, 'h' }; - QChar se[] = { 'd', 'o', '\'', 'h' }; - QTest::newRow("highcodes (D, U+E0020, o, U+1D17A, ', U+2060, h)") - << QString(st, sizeof(st)/sizeof(st[0])) - << QString(se, sizeof(se)/sizeof(se[0])) - << QString() << 0 << 0; + QCOMPARE(url.toString(), toDecoded); } } -void tst_QUrl::nameprep_highcodes() -{ -#ifdef QT_BUILD_INTERNAL - QFETCH(QString, in); - QFETCH(QString, out); - QFETCH(QString, profile); - - qt_nameprep(&in, 0); - QCOMPARE(in, out); -#endif -} - -void tst_QUrl::ace_testsuite_data() -{ - QTest::addColumn<QString>("in"); - QTest::addColumn<QString>("toace"); - QTest::addColumn<QString>("fromace"); - QTest::addColumn<QString>("unicode"); - - QTest::newRow("ascii-lower") << "fluke" << "fluke" << "fluke" << "fluke"; - QTest::newRow("ascii-mixed") << "FLuke" << "fluke" << "fluke" << "fluke"; - QTest::newRow("ascii-upper") << "FLUKE" << "fluke" << "fluke" << "fluke"; - - QTest::newRow("asciifolded") << QString::fromLatin1("stra\337e") << "strasse" << "." << "strasse"; - QTest::newRow("asciifolded-dotcom") << QString::fromLatin1("stra\337e.example.com") << "strasse.example.com" << "." << "strasse.example.com"; - QTest::newRow("greek-mu") << QString::fromLatin1("\265V") - <<"xn--v-lmb" - << "." - << QString::fromUtf8("\316\274v"); - - QTest::newRow("non-ascii-lower") << QString::fromLatin1("alqualond\353") - << "xn--alqualond-34a" - << "." - << QString::fromLatin1("alqualond\353"); - QTest::newRow("non-ascii-mixed") << QString::fromLatin1("Alqualond\353") - << "xn--alqualond-34a" - << "." - << QString::fromLatin1("alqualond\353"); - QTest::newRow("non-ascii-upper") << QString::fromLatin1("ALQUALOND\313") - << "xn--alqualond-34a" - << "." - << QString::fromLatin1("alqualond\353"); - - QTest::newRow("idn-lower") << "xn--alqualond-34a" << "xn--alqualond-34a" - << QString::fromLatin1("alqualond\353") - << QString::fromLatin1("alqualond\353"); - QTest::newRow("idn-mixed") << "Xn--alqualond-34a" << "xn--alqualond-34a" - << QString::fromLatin1("alqualond\353") - << QString::fromLatin1("alqualond\353"); - QTest::newRow("idn-mixed2") << "XN--alqualond-34a" << "xn--alqualond-34a" - << QString::fromLatin1("alqualond\353") - << QString::fromLatin1("alqualond\353"); - QTest::newRow("idn-mixed3") << "xn--ALQUALOND-34a" << "xn--alqualond-34a" - << QString::fromLatin1("alqualond\353") - << QString::fromLatin1("alqualond\353"); - QTest::newRow("idn-mixed4") << "xn--alqualond-34A" << "xn--alqualond-34a" - << QString::fromLatin1("alqualond\353") - << QString::fromLatin1("alqualond\353"); - QTest::newRow("idn-upper") << "XN--ALQUALOND-34A" << "xn--alqualond-34a" - << QString::fromLatin1("alqualond\353") - << QString::fromLatin1("alqualond\353"); - - QTest::newRow("separator-3002") << QString::fromUtf8("example\343\200\202com") - << "example.com" << "." << "example.com"; - - QString egyptianIDN = - QString::fromUtf8("\331\210\330\262\330\247\330\261\330\251\055\330\247\331\204\330" - "\243\330\252\330\265\330\247\331\204\330\247\330\252.\331\205" - "\330\265\330\261"); - QTest::newRow("egyptian-tld-ace") - << "xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c" - << "xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c" - << "." - << egyptianIDN; - QTest::newRow("egyptian-tld-unicode") - << egyptianIDN - << "xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c" - << "." - << egyptianIDN; - QTest::newRow("egyptian-tld-mix1") - << QString::fromUtf8("\331\210\330\262\330\247\330\261\330\251\055\330\247\331\204\330" - "\243\330\252\330\265\330\247\331\204\330\247\330\252.xn--wgbh1c") - << "xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c" - << "." - << egyptianIDN; - QTest::newRow("egyptian-tld-mix2") - << QString::fromUtf8("xn----rmckbbajlc6dj7bxne2c.\331\205\330\265\330\261") - << "xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c" - << "." - << egyptianIDN; -} - -void tst_QUrl::ace_testsuite() -{ - static const char canonsuffix[] = ".troll.no"; - QFETCH(QString, in); - QFETCH(QString, toace); - QFETCH(QString, fromace); - QFETCH(QString, unicode); - - const char *suffix = canonsuffix; - if (toace.contains('.')) - suffix = 0; - - QString domain = in + suffix; - QCOMPARE(QString::fromLatin1(QUrl::toAce(domain)), toace + suffix); - if (fromace != ".") - QCOMPARE(QUrl::fromAce(domain.toLatin1()), fromace + suffix); - QCOMPARE(QUrl::fromAce(QUrl::toAce(domain)), unicode + suffix); - - domain = in + (suffix ? ".troll.No" : ""); - QCOMPARE(QString::fromLatin1(QUrl::toAce(domain)), toace + suffix); - if (fromace != ".") - QCOMPARE(QUrl::fromAce(domain.toLatin1()), fromace + suffix); - QCOMPARE(QUrl::fromAce(QUrl::toAce(domain)), unicode + suffix); - - domain = in + (suffix ? ".troll.NO" : ""); - QCOMPARE(QString::fromLatin1(QUrl::toAce(domain)), toace + suffix); - if (fromace != ".") - QCOMPARE(QUrl::fromAce(domain.toLatin1()), fromace + suffix); - QCOMPARE(QUrl::fromAce(QUrl::toAce(domain)), unicode + suffix); -} - -void tst_QUrl::std3violations_data() -{ - QTest::addColumn<QString>("source"); - QTest::addColumn<bool>("validUrl"); - - QTest::newRow("too-long") << "this-domain-is-far-too-long-for-its-own-good-and-should-have-been-limited-to-63-chars" << false; - QTest::newRow("dash-begin") << "-x-foo" << false; - QTest::newRow("dash-end") << "x-foo-" << false; - QTest::newRow("dash-begin-end") << "-foo-" << false; - - QTest::newRow("control") << "\033foo" << false; - QTest::newRow("bang") << "foo!" << false; - QTest::newRow("plus") << "foo+bar" << false; - QTest::newRow("dot") << "foo.bar"; - QTest::newRow("startingdot") << ".bar" << false; - QTest::newRow("startingdot2") << ".example.com" << false; - QTest::newRow("slash") << "foo/bar" << true; - QTest::newRow("colon") << "foo:80" << true; - QTest::newRow("question") << "foo?bar" << true; - QTest::newRow("at") << "foo@bar" << true; - QTest::newRow("backslash") << "foo\\bar" << false; - - // these characters are transformed by NFKC to non-LDH characters - QTest::newRow("dot-like") << QString::fromUtf8("foo\342\200\244bar") << false; // U+2024 ONE DOT LEADER - QTest::newRow("slash-like") << QString::fromUtf8("foo\357\274\217bar") << false; // U+FF0F FULLWIDTH SOLIDUS - - // The following should be invalid but isn't - // the DIVISON SLASH doesn't case-fold to a slash - // is this a problem with RFC 3490? - //QTest::newRow("slash-like2") << QString::fromUtf8("foo\342\210\225bar") << false; // U+2215 DIVISION SLASH -} - -void tst_QUrl::std3violations() -{ - QFETCH(QString, source); - -#ifdef QT_BUILD_INTERNAL - { - QString prepped = source; - qt_nameprep(&prepped, 0); - QVERIFY(!qt_check_std3rules(prepped.constData(), prepped.length())); - } -#endif - - if (source.contains('.')) - return; // this test ends here - - QUrl url; - url.setHost(source); - QVERIFY(url.host().isEmpty()); - - QFETCH(bool, validUrl); - if (validUrl) - return; // test ends here for these cases - - url = QUrl("http://" + source + "/some/path"); - QVERIFY(!url.isValid()); -} - -void tst_QUrl::std3deviations_data() -{ - QTest::addColumn<QString>("source"); - - QTest::newRow("ending-dot") << "example.com."; - QTest::newRow("ending-dot3002") << QString("example.com") + QChar(0x3002); - QTest::newRow("underline") << "foo_bar"; //QTBUG-7434 -} - -void tst_QUrl::std3deviations() -{ - QFETCH(QString, source); - QVERIFY(!QUrl::toAce(source).isEmpty()); - - QUrl url; - url.setHost(source); - QVERIFY(!url.host().isEmpty()); -} - void tst_QUrl::tldRestrictions_data() { QTest::addColumn<QString>("tld"); @@ -2784,13 +2134,13 @@ void tst_QUrl::emptyQueryOrFragment() QVERIFY(url.encodedQuery().isNull()); // add encodedQuery - url.setEncodedQuery("abc=def"); + url.setQuery("abc=def"); QVERIFY(url.hasQuery()); - QCOMPARE(QString(url.encodedQuery()), QString(QLatin1String("abc=def"))); + QCOMPARE(url.query(), QString(QLatin1String("abc=def"))); QCOMPARE(url.toString(), QString(QLatin1String("http://www.foo.bar/baz?abc=def"))); // remove encodedQuery - url.setEncodedQuery(0); + url.setQuery(QString()); QVERIFY(!url.hasQuery()); QVERIFY(url.encodedQuery().isNull()); QCOMPARE(url.toString(), QString(QLatin1String("http://www.foo.bar/baz"))); @@ -2862,8 +2212,8 @@ void tst_QUrl::setEncodedFragment() void tst_QUrl::fromEncoded() { QUrl qurl2 = QUrl::fromEncoded("print:/specials/Print%20To%20File%20(PDF%252FAcrobat)", QUrl::TolerantMode); - QCOMPARE(qurl2.path(), QString::fromLatin1("/specials/Print To File (PDF%2FAcrobat)")); - QCOMPARE(QFileInfo(qurl2.path()).fileName(), QString::fromLatin1("Print To File (PDF%2FAcrobat)")); + QCOMPARE(qurl2.path(), QString::fromLatin1("/specials/Print To File (PDF%252FAcrobat)")); + QCOMPARE(QFileInfo(qurl2.path()).fileName(), QString::fromLatin1("Print To File (PDF%252FAcrobat)")); QCOMPARE(qurl2.toEncoded().constData(), "print:/specials/Print%20To%20File%20(PDF%252FAcrobat)"); QUrl qurl = QUrl::fromEncoded("http://\303\244.de"); @@ -2903,10 +2253,10 @@ void tst_QUrl::hosts_data() QTest::newRow("empty3") << QString("http:///file") << QString(""); QTest::newRow("empty4") << QString("http:/file") << QString(""); - // numeric hostnames - QTest::newRow("http://123/") << QString("http://123/") << QString("123"); - QTest::newRow("http://456/") << QString("http://456/") << QString("456"); - QTest::newRow("http://1000/") << QString("http://1000/") << QString("1000"); + // numeric hostnames -> decoded as IPv4 as per inet_aton(3) + QTest::newRow("http://123/") << QString("http://123/") << QString("0.0.0.123"); + QTest::newRow("http://456/") << QString("http://456/") << QString("0.0.1.200"); + QTest::newRow("http://1000/") << QString("http://1000/") << QString("0.0.3.232"); // IP literals QTest::newRow("normal-ip-literal") << QString("http://1.2.3.4") << QString("1.2.3.4"); @@ -2920,12 +2270,18 @@ void tst_QUrl::hosts_data() << QString("2001:200:0:8002:203:47ff:fea5:3085"); QTest::newRow("ipv6-literal-v4compat") << QString("http://[::255.254.253.252]") << QString("::255.254.253.252"); - QTest::newRow("ipv6-literal-v4compat-2") << QString("http://[1000::ffff:127.128.129.1]") - << QString("1000::ffff:127.128.129.1"); - QTest::newRow("long-ipv6-literal-v4compat") << QString("http://[fec0:8000::8002:1000:ffff:200.100.50.250]") - << QString("fec0:8000::8002:1000:ffff:200.100.50.250"); - QTest::newRow("longer-ipv6-literal-v4compat") << QString("http://[fec0:8000:4000:8002:1000:ffff:200.100.50.250]") - << QString("fec0:8000:4000:8002:1000:ffff:200.100.50.250"); + QTest::newRow("ipv6-literal-v4mapped") << QString("http://[::ffff:255.254.253.252]") + << QString("::ffff:255.254.253.252"); + QTest::newRow("ipv6-literal-v4mapped-2") << QString("http://[::ffff:fffe:fdfc]") + << QString("::ffff:255.254.253.252"); + + // no embedded v4 unless the cases above + QTest::newRow("ipv6-literal-v4decoded") << QString("http://[1000::ffff:127.128.129.1]") + << QString("1000::ffff:7f80:8101"); + QTest::newRow("long-ipv6-literal-v4decoded") << QString("http://[fec0:8000::8002:1000:ffff:200.100.50.250]") + << QString("fec0:8000:0:8002:1000:ffff:c864:32fa"); + QTest::newRow("longer-ipv6-literal-v4decoded") << QString("http://[fec0:8000:4000:8002:1000:ffff:200.100.50.250]") + << QString("fec0:8000:4000:8002:1000:ffff:c864:32fa"); // normal hostnames QTest::newRow("normal") << QString("http://intern") << QString("intern"); @@ -2948,16 +2304,19 @@ void tst_QUrl::setPort() { QUrl url; QVERIFY(url.toString().isEmpty()); + url.setHost("a"); url.setPort(80); QCOMPARE(url.port(), 80); - QCOMPARE(url.toString(), QString::fromLatin1("//:80")); + QCOMPARE(url.toString(), QString::fromLatin1("//a:80")); url.setPort(-1); + url.setHost(QString()); QCOMPARE(url.port(), -1); - QVERIFY(url.toString().isEmpty()); + QCOMPARE(url.toString(), QString()); url.setPort(80); QTest::ignoreMessage(QtWarningMsg, "QUrl::setPort: Out of range"); url.setPort(65536); QCOMPARE(url.port(), -1); + QVERIFY(url.errorString().contains("out of range")); } } @@ -3003,19 +2362,6 @@ void tst_QUrl::setAuthority() QCOMPARE(u.toString(), url); } -void tst_QUrl::errorString() -{ - QUrl u = QUrl::fromEncoded("http://strange<username>@bad_hostname/", QUrl::StrictMode); - QVERIFY(!u.isValid()); - QString errorString = "Invalid URL \"http://strange<username>@bad_hostname/\": " - "error at position 14: expected end of URL, but found '<'"; - QCOMPARE(u.errorString(), errorString); - - QUrl v; - errorString = "Invalid URL \"\": "; - QCOMPARE(v.errorString(), errorString); -} - void tst_QUrl::clear() { QUrl url("a"); @@ -3044,7 +2390,7 @@ void tst_QUrl::binaryData_data() QTest::newRow("file-hash") << "http://foo/abc%23_def"; QTest::newRow("file-question") << "http://foo/abc%3F_def"; QTest::newRow("file-nonutf8") << "http://foo/abc%E1_def"; - QTest::newRow("file-slash") << "http://foo/abc%2f_def"; + QTest::newRow("file-slash") << "http://foo/abc%2F_def"; QTest::newRow("ref") << "http://foo/file#a%01%0D%0A%7F"; QTest::newRow("ref-nul") << "http://foo/file#abc%00_def"; @@ -3119,7 +2465,7 @@ void tst_QUrl::fromUserInput_data() portUrl.setPort(80); QTest::newRow("port-0") << "example.org:80" << portUrl; QTest::newRow("port-1") << "http://example.org:80" << portUrl; - portUrl.setPath("path"); + portUrl.setPath("/path"); QTest::newRow("port-2") << "example.org:80/path" << portUrl; QTest::newRow("port-3") << "http://example.org:80/path" << portUrl; @@ -3138,6 +2484,10 @@ void tst_QUrl::fromUserInput_data() // FYI: The scheme in the resulting url user QUrl authUrl("user:pass@domain.com"); QTest::newRow("misc-1") << "user:pass@domain.com" << authUrl; + + // FTP with double slashes in path + QTest::newRow("ftp-double-slash-1") << "ftp.example.com//path" << QUrl("ftp://ftp.example.com/%2Fpath"); + QTest::newRow("ftp-double-slash-1") << "ftp://ftp.example.com//path" << QUrl("ftp://ftp.example.com/%2Fpath"); } void tst_QUrl::fromUserInput() @@ -3275,27 +2625,254 @@ void tst_QUrl::effectiveTLDs() QCOMPARE(domain.topLevelDomain(), TLD); } -void tst_QUrl::removeAllEncodedQueryItems_data() +void tst_QUrl::lowercasesScheme() { - QTest::addColumn<QUrl>("url"); - QTest::addColumn<QByteArray>("key"); - QTest::addColumn<QUrl>("result"); - - QTest::newRow("test1") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b&ccc=c") << QByteArray("bbb") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&ccc=c"); - QTest::newRow("test2") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b&ccc=c") << QByteArray("aaa") << QUrl::fromEncoded("http://qt.nokia.com/foo?bbb=b&ccc=c"); -// QTest::newRow("test3") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b&ccc=c") << QByteArray("ccc") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b"); - QTest::newRow("test4") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b&ccc=c") << QByteArray("b%62b") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b&ccc=c"); - QTest::newRow("test5") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&b%62b=b&ccc=c") << QByteArray("b%62b") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&ccc=c"); - QTest::newRow("test6") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&b%62b=b&ccc=c") << QByteArray("bbb") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&b%62b=b&ccc=c"); + QUrl url; + url.setScheme("HELLO"); + QCOMPARE(url.scheme(), QString("hello")); } -void tst_QUrl::removeAllEncodedQueryItems() +void tst_QUrl::componentEncodings_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<int>("encoding"); + QTest::addColumn<QString>("userName"); + QTest::addColumn<QString>("password"); + QTest::addColumn<QString>("userInfo"); + QTest::addColumn<QString>("host"); + QTest::addColumn<QString>("authority"); + QTest::addColumn<QString>("path"); + QTest::addColumn<QString>("query"); + QTest::addColumn<QString>("fragment"); + QTest::addColumn<QString>("toString"); + + QTest::newRow("empty") << QUrl() << int(QUrl::FullyEncoded) + << QString() << QString() << QString() + << QString() << QString() + << QString() << QString() << QString() << QString(); + + // hostname cannot contain spaces + QTest::newRow("encoded-space") << QUrl("x://user name:pass word@host/path name?query value#fragment value") + << int(QUrl::EncodeSpaces) + << "user%20name" << "pass%20word" << "user%20name:pass%20word" + << "host" << "user%20name:pass%20word@host" + << "/path%20name" << "query%20value" << "fragment%20value" + << "x://user%20name:pass%20word@host/path%20name?query%20value#fragment%20value"; + + QTest::newRow("decoded-space") << QUrl("x://user%20name:pass%20word@host/path%20name?query%20value#fragment%20value") + << int(QUrl::MostDecoded) + << "user name" << "pass word" << "user name:pass word" + << "host" << "user name:pass word@host" + << "/path name" << "query value" << "fragment value" + << "x://user name:pass word@host/path name?query value#fragment value"; + + // binary data is always encoded + // this is also testing non-UTF8 data + QTest::newRow("binary") << QUrl("x://%c0%00:%c1%01@host/%c2%02?%c3%03#%d4%04") + << int(QUrl::MostDecoded) + << "%C0%00" << "%C1%01" << "%C0%00:%C1%01" + << "host" << "%C0%00:%C1%01@host" + << "/%C2%02" << "%C3%03" << "%D4%04" + << "x://%C0%00:%C1%01@host/%C2%02?%C3%03#%D4%04"; + + // unicode tests + // hostnames can participate in this test, but we need a top-level domain that accepts Unicode + QTest::newRow("encoded-unicode") << QUrl(QString::fromUtf8("x://\xc2\x80:\xc3\x90@smørbrød.example.no/\xe0\xa0\x80?\xf0\x90\x80\x80#é")) + << int(QUrl::EncodeUnicode) + << "%C2%80" << "%C3%90" << "%C2%80:%C3%90" + << "xn--smrbrd-cyad.example.no" << "%C2%80:%C3%90@xn--smrbrd-cyad.example.no" + << "/%E0%A0%80" << "%F0%90%80%80" << "%C3%A9" + << "x://%C2%80:%C3%90@xn--smrbrd-cyad.example.no/%E0%A0%80?%F0%90%80%80#%C3%A9"; + QTest::newRow("decoded-unicode") << QUrl("x://%C2%80:%C3%90@XN--SMRBRD-cyad.example.NO/%E0%A0%80?%F0%90%80%80#%C3%A9") + << int(QUrl::MostDecoded) + << QString::fromUtf8("\xc2\x80") << QString::fromUtf8("\xc3\x90") + << QString::fromUtf8("\xc2\x80:\xc3\x90") + << QString::fromUtf8("smørbrød.example.no") + << QString::fromUtf8("\xc2\x80:\xc3\x90@smørbrød.example.no") + << QString::fromUtf8("/\xe0\xa0\x80") + << QString::fromUtf8("\xf0\x90\x80\x80") << QString::fromUtf8("é") + << QString::fromUtf8("x://\xc2\x80:\xc3\x90@smørbrød.example.no/\xe0\xa0\x80?\xf0\x90\x80\x80#é"); + + // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + // these are always decoded + QTest::newRow("decoded-unreserved") << QUrl("x://%61:%71@%41%30%2eexample%2ecom/%7e?%5f#%51") + << int(QUrl::FullyEncoded) + << "a" << "q" << "a:q" + << "a0.example.com" << "a:q@a0.example.com" + << "/~" << "_" << "Q" + << "x://a:q@a0.example.com/~?_#Q"; + + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + // / "*" / "+" / "," / ";" / "=" + // like the unreserved, these are decoded everywhere + // don't test in query because they might remain encoded + QTest::newRow("decoded-subdelims") << QUrl("x://%21%24%26:%27%28%29@host/%2a%2b%2c#%3b%3d") + << int(QUrl::FullyEncoded) + << "!$&" << "'()" << "!$&:'()" + << "host" << "!$&:'()@host" + << "/*+," << "" << ";=" + << "x://!$&:'()@host/*+,#;="; + + // gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + // these are the separators between fields + // they must appear encoded in certain positions, no exceptions + // in other positions, they can appear decoded, so they always do + // 1) test the delimiters that must appear encoded + // (if they were decoded, they'd would change the URL parsing) + QTest::newRow("encoded-gendelims-changing") << QUrl("x://%5b%3a%2f%3f%23%40%5d:%5b%2f%3f%23%40%5d@host/%2f%3f%23?%23") + << int(QUrl::MostDecoded) + << "[:/?#@]" << "[/?#@]" << "[%3A/?#@]:[/?#@]" + << "host" << "%5B%3A/?#%40%5D:%5B/?#%40%5D@host" + << "/%2F?#" << "#" << "" + << "x://%5B%3A%2F%3F%23%40%5D:%5B%2F%3F%23%40%5D@host/%2F%3F%23?%23"; + + // 2) test the delimiters that may appear decoded and would not change the meaning + // and test that %2f is *not* decoded to a slash in the path + // don't test the query because in this mode it doesn't transform anything + QTest::newRow("decoded-gendelims-unchanging") << QUrl("x://:%3a@host/%2f%3a%40#%23%3a%2f%3f%40") + << int(QUrl::FullyEncoded) + << "" << ":" << "::" + << "host" << "::@host" + << "/%2F:@" << "" << "#:/?@" + << "x://::@host/%2F:@##:/?@"; + + // 3) test "[" and "]". Even though they are not ambiguous in the path, query or fragment + // the RFC does not allow them to appear there decoded. QUrl adheres strictly in FullyEncoded mode + QTest::newRow("encoded-square-brackets") << QUrl("x:/[]#[]") + << int(QUrl::FullyEncoded) + << "" << "" << "" + << "" << "" + << "/%5B%5D" << "" << "%5B%5D" + << "x:/%5B%5D#%5B%5D"; + + // 4) like above, but now decode them, which is allowed + QTest::newRow("decoded-square-brackets") << QUrl("x:/%5B%5D#%5B%5D") + << int(QUrl::MostDecoded) + << "" << "" << "" + << "" << "" + << "/[]" << "" << "[]" + << "x:/[]#[]"; + + // test the query + // since QUrl doesn't know what chars the user wants to use for the pair and value delimiters, + // it keeps the delimiters alone except for "#", which must always be encoded. + QTest::newRow("unencoded-delims-query") << QUrl("?!$()*+,;=:/?[]@") + << int(QUrl::FullyEncoded) + << QString() << QString() << QString() + << QString() << QString() + << QString() << "!$()*+,;=:/?[]@" << QString() + << "?!$()*+,;=:/?[]@"; + QTest::newRow("undecoded-delims-query") << QUrl("?%21%24%26%27%28%29%2a%2b%2c%2f%3a%3b%3d%3f%40%5b%5d") + << int(QUrl::MostDecoded) + << QString() << QString() << QString() + << QString() << QString() + << QString() << "%21%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D" << QString() + << "?%21%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D"; + + // reserved characters: '"' / "<" / ">" / "^" / "\" / "{" / "|" "}" + // the RFC does not allow them undecoded anywhere, but we do + QTest::newRow("encoded-reserved") << QUrl("x://\"<>^\\{|}:\"<>^\\{|}@host/\"<>^\\{|}?\"<>^\\{|}#\"<>^\\{|}") + << int(QUrl::FullyEncoded) + << "%22%3C%3E%5E%5C%7B%7C%7D" << "%22%3C%3E%5E%5C%7B%7C%7D" + << "%22%3C%3E%5E%5C%7B%7C%7D:%22%3C%3E%5E%5C%7B%7C%7D" + << "host" << "%22%3C%3E%5E%5C%7B%7C%7D:%22%3C%3E%5E%5C%7B%7C%7D@host" + << "/%22%3C%3E%5E%5C%7B%7C%7D" << "%22%3C%3E%5E%5C%7B%7C%7D" + << "%22%3C%3E%5E%5C%7B%7C%7D" + << "x://%22%3C%3E%5E%5C%7B%7C%7D:%22%3C%3E%5E%5C%7B%7C%7D@host/%22%3C%3E%5E%5C%7B%7C%7D" + "?%22%3C%3E%5E%5C%7B%7C%7D#%22%3C%3E%5E%5C%7B%7C%7D"; + QTest::newRow("decoded-reserved") << QUrl("x://%22%3C%3E%5E%5C%7B%7C%7D:%22%3C%3E%5E%5C%7B%7C%7D@host" + "/%22%3C%3E%5E%5C%7B%7C%7D?%22%3C%3E%5E%5C%7B%7C%7D#%22%3C%3E%5E%5C%7B%7C%7D") + << int(QUrl::DecodeReserved) + << "\"<>^\\{|}" << "\"<>^\\{|}" << "\"<>^\\{|}:\"<>^\\{|}" + << "host" << "\"<>^\\{|}:\"<>^\\{|}@host" + << "/\"<>^\\{|}" << "\"<>^\\{|}" << "\"<>^\\{|}" + << "x://\"<>^\\{|}:\"<>^\\{|}@host/\"<>^\\{|}?\"<>^\\{|}#\"<>^\\{|}"; + + + // Beauty is in the eye of the beholder + // Test PrettyDecoder against our expectations + + // spaces and unicode are considered pretty and are decoded + // this includes hostnames + QTest::newRow("pretty-spaces-unicode") << QUrl("x://%20%c3%a9:%c3%a9%20@XN--SMRBRD-cyad.example.NO/%c3%a9%20?%20%c3%a9#%c3%a9%20") + << int(QUrl::PrettyDecoded) + << QString::fromUtf8(" é") << QString::fromUtf8("é ") + << QString::fromUtf8(" é:é ") + << QString::fromUtf8("smørbrød.example.no") + << QString::fromUtf8(" é:é @smørbrød.example.no") + << QString::fromUtf8("/é ") << QString::fromUtf8(" é") + << QString::fromUtf8("é ") + << QString::fromUtf8("x:// é:é @smørbrød.example.no/é ? é#é "); + + // the pretty form re-encodes the subdelims (except in the query, where they are left alone) + QTest::newRow("pretty-subdelims") << QUrl("x://%21%24%26:%27%28%29@host/%2a%2b%2c?%26=%26&%3d=%3d#%3b%3d") + << int(QUrl::PrettyDecoded) + << "!$&" << "'()" << "!$&:'()" + << "host" << "!$&:'()@host" + << "/*+," << "%26=%26&%3D=%3D" << ";=" + << "x://!$&:'()@host/*+,?%26=%26&%3D=%3D#;="; + + // the pretty form decodes all unambiguous gen-delims + // (except in query, where they are left alone) + QTest::newRow("pretty-gendelims") << QUrl("x://%5b%3a%40%2f%5d:%5b%3a%40%2f%5d@host" + "/%3a%40%5b%3f%23%5d?[?%3f%23]%5b:%3a@%40%5d#%23") + << int(QUrl::PrettyDecoded) + << "[:@/]" << "[:@/]" << "[%3A@/]:[:@/]" + << "host" << "%5B%3A%40/%5D:%5B:%40/%5D@host" + << "/:@[?#]" << "[?%3F#]%5B:%3A@%40%5D" << "#" + << "x://%5B%3A%40%2F%5D:%5B:%40%2F%5D@host/:@[%3F%23]?[?%3F%23]%5B:%3A@%40%5D##"; + + // the pretty form keeps the other characters decoded everywhere + // except when rebuilding the full URL, when we only allow "{}" to remain decoded + QTest::newRow("pretty-reserved") << QUrl("x://\"<>^\\{|}:\"<>^\\{|}@host/\"<>^\\{|}?\"<>^\\{|}#\"<>^\\{|}") + << int(QUrl::PrettyDecoded) + << "\"<>^\\{|}" << "\"<>^\\{|}" << "\"<>^\\{|}:\"<>^\\{|}" + << "host" << "\"<>^\\{|}:\"<>^\\{|}@host" + << "/\"<>^\\{|}" << "\"<>^\\{|}" << "\"<>^\\{|}" + << "x://%22%3C%3E%5E%5C%7B%7C%7D:%22%3C%3E%5E%5C%7B%7C%7D@host/%22%3C%3E%5E%5C{%7C}" + "?%22%3C%3E%5E%5C{%7C}#%22%3C%3E%5E%5C%7B%7C%7D"; +} + +void tst_QUrl::componentEncodings() { QFETCH(QUrl, url); - QFETCH(QByteArray, key); - QFETCH(QUrl, result); - url.removeAllEncodedQueryItems(key); - QCOMPARE(url, result); + QFETCH(int, encoding); + QFETCH(QString, userName); + QFETCH(QString, password); + QFETCH(QString, userInfo); + QFETCH(QString, host); + QFETCH(QString, authority); + QFETCH(QString, path); + QFETCH(QString, query); + QFETCH(QString, fragment); + QFETCH(QString, toString); + + QUrl::ComponentFormattingOptions formatting(encoding); + QCOMPARE(url.userName(formatting), userName); + QCOMPARE(url.password(formatting), password); + QCOMPARE(url.userInfo(formatting), userInfo); + QCOMPARE(url.host(formatting), host); + QCOMPARE(url.authority(formatting), authority); + QCOMPARE(url.path(formatting), path); + QCOMPARE(url.query(formatting), query); + QCOMPARE(url.fragment(formatting), fragment); + QCOMPARE(url.toString(formatting), + (((QString(toString ))))); // the weird () and space is to align the output + + // repeat with the URL we got from toString + QUrl url2(toString); + QCOMPARE(url2.userName(formatting), userName); + QCOMPARE(url2.password(formatting), password); + QCOMPARE(url2.userInfo(formatting), userInfo); + QCOMPARE(url2.host(formatting), host); + QCOMPARE(url2.authority(formatting), authority); + QCOMPARE(url2.path(formatting), path); + QCOMPARE(url2.query(formatting), query); + QCOMPARE(url2.fragment(formatting), fragment); + QCOMPARE(url2.toString(formatting), toString); + + // and use the comparison operator + QCOMPARE(url2, url); } QTEST_MAIN(tst_QUrl) diff --git a/tests/auto/corelib/io/qurlinternal/qurlinternal.pro b/tests/auto/corelib/io/qurlinternal/qurlinternal.pro new file mode 100644 index 0000000000..117ad96446 --- /dev/null +++ b/tests/auto/corelib/io/qurlinternal/qurlinternal.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +TARGET = tst_qurlinternal +SOURCES += tst_qurlinternal.cpp ../../codecs/utf8/utf8data.cpp +QT = core core-private testlib +CONFIG += parallel_test diff --git a/tests/auto/corelib/io/qurlinternal/tst_qurlinternal.cpp b/tests/auto/corelib/io/qurlinternal/tst_qurlinternal.cpp new file mode 100644 index 0000000000..3ac2b46964 --- /dev/null +++ b/tests/auto/corelib/io/qurlinternal/tst_qurlinternal.cpp @@ -0,0 +1,982 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2012 Intel Corporation. +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QUrl> +#include <QtTest/QtTest> + +#include "private/qtldurl_p.h" + +QT_BEGIN_NAMESPACE +Q_CORE_EXPORT extern void qt_nameprep(QString *source, int from); +Q_CORE_EXPORT extern bool qt_check_std3rules(const QChar *, int); +Q_CORE_EXPORT void qt_punycodeEncoder(const QChar *s, int ucLength, QString *output); +Q_CORE_EXPORT QString qt_punycodeDecoder(const QString &pc); +Q_CORE_EXPORT int qt_urlRecode(QString &appendTo, const QChar *input, const QChar *end, + QUrl::ComponentFormattingOptions encoding, const ushort *tableModifications = 0); +QT_END_NAMESPACE + +// For testsuites +#define IDNA_ACE_PREFIX "xn--" +#define IDNA_SUCCESS 1 +#define STRINGPREP_NO_UNASSIGNED 1 +#define STRINGPREP_CONTAINS_UNASSIGNED 2 +#define STRINGPREP_CONTAINS_PROHIBITED 3 +#define STRINGPREP_BIDI_BOTH_L_AND_RAL 4 +#define STRINGPREP_BIDI_LEADTRAIL_NOT_RAL 5 + +struct ushortarray { + ushortarray(unsigned short *array = 0) + { + if (array) + memcpy(points, array, sizeof(points)); + } + + unsigned short points[100]; +}; + +Q_DECLARE_METATYPE(ushortarray) +Q_DECLARE_METATYPE(QUrl::FormattingOptions) +Q_DECLARE_METATYPE(QUrl::ComponentFormattingOptions) + +class tst_QUrlInternal : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + // IDNA internals + void idna_testsuite_data(); + void idna_testsuite(); + void nameprep_testsuite_data(); + void nameprep_testsuite(); + void nameprep_highcodes_data(); + void nameprep_highcodes(); + void ace_testsuite_data(); + void ace_testsuite(); + void std3violations_data(); + void std3violations(); + void std3deviations_data(); + void std3deviations(); + + // percent-encoding internals + void correctEncodedMistakes_data(); + void correctEncodedMistakes(); + void encodingRecode_data(); + void encodingRecode(); + void encodingRecodeInvalidUtf8_data(); + void encodingRecodeInvalidUtf8(); +}; +#include "tst_qurlinternal.moc" + +void tst_QUrlInternal::idna_testsuite_data() +{ + QTest::addColumn<int>("numchars"); + QTest::addColumn<ushortarray>("unicode"); + QTest::addColumn<QByteArray>("punycode"); + QTest::addColumn<int>("allowunassigned"); + QTest::addColumn<int>("usestd3asciirules"); + QTest::addColumn<int>("toasciirc"); + QTest::addColumn<int>("tounicoderc"); + + unsigned short d1[] = { 0x0644, 0x064A, 0x0647, 0x0645, 0x0627, 0x0628, 0x062A, 0x0643, + 0x0644, 0x0645, 0x0648, 0x0634, 0x0639, 0x0631, 0x0628, 0x064A, + 0x061F }; + QTest::newRow("Arabic (Egyptian)") << 17 << ushortarray(d1) + << QByteArray(IDNA_ACE_PREFIX "egbpdaj6bu4bxfgehfvwxn") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d2[] = { 0x4ED6, 0x4EEC, 0x4E3A, 0x4EC0, 0x4E48, 0x4E0D, 0x8BF4, 0x4E2D, + 0x6587 }; + QTest::newRow("Chinese (simplified)") << 9 << ushortarray(d2) + << QByteArray(IDNA_ACE_PREFIX "ihqwcrb4cv8a8dqg056pqjye") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d3[] = { 0x4ED6, 0x5011, 0x7232, 0x4EC0, 0x9EBD, 0x4E0D, 0x8AAA, 0x4E2D, + 0x6587 }; + QTest::newRow("Chinese (traditional)") << 9 << ushortarray(d3) + << QByteArray(IDNA_ACE_PREFIX "ihqwctvzc91f659drss3x8bo0yb") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d4[] = { 0x0050, 0x0072, 0x006F, 0x010D, 0x0070, 0x0072, 0x006F, 0x0073, + 0x0074, 0x011B, 0x006E, 0x0065, 0x006D, 0x006C, 0x0075, 0x0076, + 0x00ED, 0x010D, 0x0065, 0x0073, 0x006B, 0x0079 }; + QTest::newRow("Czech") << 22 << ushortarray(d4) + << QByteArray(IDNA_ACE_PREFIX "Proprostnemluvesky-uyb24dma41a") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d5[] = { 0x05DC, 0x05DE, 0x05D4, 0x05D4, 0x05DD, 0x05E4, 0x05E9, 0x05D5, + 0x05D8, 0x05DC, 0x05D0, 0x05DE, 0x05D3, 0x05D1, 0x05E8, 0x05D9, + 0x05DD, 0x05E2, 0x05D1, 0x05E8, 0x05D9, 0x05EA }; + QTest::newRow("Hebrew") << 22 << ushortarray(d5) + << QByteArray(IDNA_ACE_PREFIX "4dbcagdahymbxekheh6e0a7fei0b") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d6[] = { 0x092F, 0x0939, 0x0932, 0x094B, 0x0917, 0x0939, 0x093F, 0x0928, + 0x094D, 0x0926, 0x0940, 0x0915, 0x094D, 0x092F, 0x094B, 0x0902, + 0x0928, 0x0939, 0x0940, 0x0902, 0x092C, 0x094B, 0x0932, 0x0938, + 0x0915, 0x0924, 0x0947, 0x0939, 0x0948, 0x0902 }; + QTest::newRow("Hindi (Devanagari)") << 30 << ushortarray(d6) + << QByteArray(IDNA_ACE_PREFIX "i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd") + << 0 << 0 << IDNA_SUCCESS; + + unsigned short d7[] = { 0x306A, 0x305C, 0x307F, 0x3093, 0x306A, 0x65E5, 0x672C, 0x8A9E, + 0x3092, 0x8A71, 0x3057, 0x3066, 0x304F, 0x308C, 0x306A, 0x3044, + 0x306E, 0x304B }; + QTest::newRow("Japanese (kanji and hiragana)") << 18 << ushortarray(d7) + << QByteArray(IDNA_ACE_PREFIX "n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa") + << 0 << 0 << IDNA_SUCCESS; + + unsigned short d8[] = { 0x043F, 0x043E, 0x0447, 0x0435, 0x043C, 0x0443, 0x0436, 0x0435, + 0x043E, 0x043D, 0x0438, 0x043D, 0x0435, 0x0433, 0x043E, 0x0432, + 0x043E, 0x0440, 0x044F, 0x0442, 0x043F, 0x043E, 0x0440, 0x0443, + 0x0441, 0x0441, 0x043A, 0x0438 }; + QTest::newRow("Russian (Cyrillic)") << 28 << ushortarray(d8) + << QByteArray(IDNA_ACE_PREFIX "b1abfaaepdrnnbgefbadotcwatmq2g4l") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d9[] = { 0x0050, 0x006F, 0x0072, 0x0071, 0x0075, 0x00E9, 0x006E, 0x006F, + 0x0070, 0x0075, 0x0065, 0x0064, 0x0065, 0x006E, 0x0073, 0x0069, + 0x006D, 0x0070, 0x006C, 0x0065, 0x006D, 0x0065, 0x006E, 0x0074, + 0x0065, 0x0068, 0x0061, 0x0062, 0x006C, 0x0061, 0x0072, 0x0065, + 0x006E, 0x0045, 0x0073, 0x0070, 0x0061, 0x00F1, 0x006F, 0x006C }; + QTest::newRow("Spanish") << 40 << ushortarray(d9) + << QByteArray(IDNA_ACE_PREFIX "PorqunopuedensimplementehablarenEspaol-fmd56a") + << 0 << 0 << IDNA_SUCCESS; + + unsigned short d10[] = { 0x0054, 0x1EA1, 0x0069, 0x0073, 0x0061, 0x006F, 0x0068, 0x1ECD, + 0x006B, 0x0068, 0x00F4, 0x006E, 0x0067, 0x0074, 0x0068, 0x1EC3, + 0x0063, 0x0068, 0x1EC9, 0x006E, 0x00F3, 0x0069, 0x0074, 0x0069, + 0x1EBF, 0x006E, 0x0067, 0x0056, 0x0069, 0x1EC7, 0x0074 }; + QTest::newRow("Vietnamese") << 31 << ushortarray(d10) + << QByteArray(IDNA_ACE_PREFIX "TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g") + << 0 << 0 << IDNA_SUCCESS; + + unsigned short d11[] = { 0x0033, 0x5E74, 0x0042, 0x7D44, 0x91D1, 0x516B, 0x5148, 0x751F }; + QTest::newRow("Japanese") << 8 << ushortarray(d11) + << QByteArray(IDNA_ACE_PREFIX "3B-ww4c5e180e575a65lsy2b") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + // this test does NOT include nameprepping, so the capitals will remain + unsigned short d12[] = { 0x5B89, 0x5BA4, 0x5948, 0x7F8E, 0x6075, 0x002D, 0x0077, 0x0069, + 0x0074, 0x0068, 0x002D, 0x0053, 0x0055, 0x0050, 0x0045, 0x0052, + 0x002D, 0x004D, 0x004F, 0x004E, 0x004B, 0x0045, 0x0059, 0x0053 }; + QTest::newRow("Japanese2") << 24 << ushortarray(d12) + << QByteArray(IDNA_ACE_PREFIX "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n") + << 0 << 0 << IDNA_SUCCESS; + + unsigned short d13[] = { 0x0048, 0x0065, 0x006C, 0x006C, 0x006F, 0x002D, 0x0041, 0x006E, + 0x006F, 0x0074, 0x0068, 0x0065, 0x0072, 0x002D, 0x0057, 0x0061, + 0x0079, 0x002D, 0x305D, 0x308C, 0x305E, 0x308C, 0x306E, 0x5834, + 0x6240 }; + QTest::newRow("Japanese3") << 25 << ushortarray(d13) + << QByteArray(IDNA_ACE_PREFIX "Hello-Another-Way--fc4qua05auwb3674vfr0b") + << 0 << 0 << IDNA_SUCCESS; + + unsigned short d14[] = { 0x3072, 0x3068, 0x3064, 0x5C4B, 0x6839, 0x306E, 0x4E0B, 0x0032 }; + QTest::newRow("Japanese4") << 8 << ushortarray(d14) + << QByteArray(IDNA_ACE_PREFIX "2-u9tlzr9756bt3uc0v") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d15[] = { 0x004D, 0x0061, 0x006A, 0x0069, 0x3067, 0x004B, 0x006F, 0x0069, + 0x3059, 0x308B, 0x0035, 0x79D2, 0x524D }; + QTest::newRow("Japanese5") << 13 << ushortarray(d15) + << QByteArray(IDNA_ACE_PREFIX "MajiKoi5-783gue6qz075azm5e") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d16[] = { 0x30D1, 0x30D5, 0x30A3, 0x30FC, 0x0064, 0x0065, 0x30EB, 0x30F3, 0x30D0 }; + QTest::newRow("Japanese6") << 9 << ushortarray(d16) + << QByteArray(IDNA_ACE_PREFIX "de-jg4avhby1noc0d") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d17[] = { 0x305D, 0x306E, 0x30B9, 0x30D4, 0x30FC, 0x30C9, 0x3067 }; + QTest::newRow("Japanese7") << 7 << ushortarray(d17) + << QByteArray(IDNA_ACE_PREFIX "d9juau41awczczp") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d18[] = { 0x03b5, 0x03bb, 0x03bb, 0x03b7, 0x03bd, 0x03b9, 0x03ba, 0x03ac }; + QTest::newRow("Greek") << 8 << ushortarray(d18) + << QByteArray(IDNA_ACE_PREFIX "hxargifdar") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d19[] = { 0x0062, 0x006f, 0x006e, 0x0121, 0x0075, 0x0073, 0x0061, 0x0127, + 0x0127, 0x0061 }; + QTest::newRow("Maltese (Malti)") << 10 << ushortarray(d19) + << QByteArray(IDNA_ACE_PREFIX "bonusaa-5bb1da") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; + + unsigned short d20[] = {0x043f, 0x043e, 0x0447, 0x0435, 0x043c, 0x0443, 0x0436, 0x0435, + 0x043e, 0x043d, 0x0438, 0x043d, 0x0435, 0x0433, 0x043e, 0x0432, + 0x043e, 0x0440, 0x044f, 0x0442, 0x043f, 0x043e, 0x0440, 0x0443, + 0x0441, 0x0441, 0x043a, 0x0438 }; + QTest::newRow("Russian (Cyrillic)") << 28 << ushortarray(d20) + << QByteArray(IDNA_ACE_PREFIX "b1abfaaepdrnnbgefbadotcwatmq2g4l") + << 0 << 0 << IDNA_SUCCESS << IDNA_SUCCESS; +} + +void tst_QUrlInternal::idna_testsuite() +{ +#ifdef QT_BUILD_INTERNAL + QFETCH(int, numchars); + QFETCH(ushortarray, unicode); + QFETCH(QByteArray, punycode); + + QString result; + qt_punycodeEncoder((QChar*)unicode.points, numchars, &result); + QCOMPARE(result.toLatin1(), punycode); + QCOMPARE(qt_punycodeDecoder(result), QString::fromUtf16(unicode.points, numchars)); +#endif +} + +void tst_QUrlInternal::nameprep_testsuite_data() +{ + QTest::addColumn<QString>("in"); + QTest::addColumn<QString>("out"); + QTest::addColumn<QString>("profile"); + QTest::addColumn<int>("flags"); + QTest::addColumn<int>("rc"); + + QTest::newRow("Map to nothing") + << QString::fromUtf8("foo\xC2\xAD\xCD\x8F\xE1\xA0\x86\xE1\xA0\x8B" + "bar""\xE2\x80\x8B\xE2\x81\xA0""baz\xEF\xB8\x80\xEF\xB8\x88" + "\xEF\xB8\x8F\xEF\xBB\xBF") + << QString::fromUtf8("foobarbaz") + << QString() << 0 << 0; + + QTest::newRow("Case folding ASCII U+0043 U+0041 U+0046 U+0045") + << QString::fromUtf8("CAFE") + << QString::fromUtf8("cafe") + << QString() << 0 << 0; + + QTest::newRow("Case folding 8bit U+00DF (german sharp s)") + << QString::fromUtf8("\xC3\x9F") + << QString("ss") + << QString() << 0 << 0; + + QTest::newRow("Case folding U+0130 (turkish capital I with dot)") + << QString::fromUtf8("\xC4\xB0") + << QString::fromUtf8("i\xcc\x87") + << QString() << 0 << 0; + + QTest::newRow("Case folding multibyte U+0143 U+037A") + << QString::fromUtf8("\xC5\x83\xCD\xBA") + << QString::fromUtf8("\xC5\x84 \xCE\xB9") + << QString() << 0 << 0; + + QTest::newRow("Case folding U+2121 U+33C6 U+1D7BB") + << QString::fromUtf8("\xE2\x84\xA1\xE3\x8F\x86\xF0\x9D\x9E\xBB") + << QString::fromUtf8("telc\xE2\x88\x95""kg\xCF\x83") + << QString() << 0 << 0; + + QTest::newRow("Normalization of U+006a U+030c U+00A0 U+00AA") + << QString::fromUtf8("\x6A\xCC\x8C\xC2\xA0\xC2\xAA") + << QString::fromUtf8("\xC7\xB0 a") + << QString() << 0 << 0; + + QTest::newRow("Case folding U+1FB7 and normalization") + << QString::fromUtf8("\xE1\xBE\xB7") + << QString::fromUtf8("\xE1\xBE\xB6\xCE\xB9") + << QString() << 0 << 0; + + QTest::newRow("Self-reverting case folding U+01F0 and normalization") +// << QString::fromUtf8("\xC7\xF0") ### typo in the original testsuite + << QString::fromUtf8("\xC7\xB0") + << QString::fromUtf8("\xC7\xB0") + << QString() << 0 << 0; + + QTest::newRow("Self-reverting case folding U+0390 and normalization") + << QString::fromUtf8("\xCE\x90") + << QString::fromUtf8("\xCE\x90") + << QString() << 0 << 0; + + QTest::newRow("Self-reverting case folding U+03B0 and normalization") + << QString::fromUtf8("\xCE\xB0") + << QString::fromUtf8("\xCE\xB0") + << QString() << 0 << 0; + + QTest::newRow("Self-reverting case folding U+1E96 and normalization") + << QString::fromUtf8("\xE1\xBA\x96") + << QString::fromUtf8("\xE1\xBA\x96") + << QString() << 0 << 0; + + QTest::newRow("Self-reverting case folding U+1F56 and normalization") + << QString::fromUtf8("\xE1\xBD\x96") + << QString::fromUtf8("\xE1\xBD\x96") + << QString() << 0 << 0; + + QTest::newRow("ASCII space character U+0020") + << QString::fromUtf8("\x20") + << QString::fromUtf8("\x20") + << QString() << 0 << 0; + + QTest::newRow("Non-ASCII 8bit space character U+00A0") + << QString::fromUtf8("\xC2\xA0") + << QString::fromUtf8("\x20") + << QString() << 0 << 0; + + QTest::newRow("Non-ASCII multibyte space character U+1680") + << QString::fromUtf8("\xE1\x9A\x80") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Non-ASCII multibyte space character U+2000") + << QString::fromUtf8("\xE2\x80\x80") + << QString::fromUtf8("\x20") + << QString() << 0 << 0; + + QTest::newRow("Zero Width Space U+200b") + << QString::fromUtf8("\xE2\x80\x8b") + << QString() + << QString() << 0 << 0; + + QTest::newRow("Non-ASCII multibyte space character U+3000") + << QString::fromUtf8("\xE3\x80\x80") + << QString::fromUtf8("\x20") + << QString() << 0 << 0; + + QTest::newRow("ASCII control characters U+0010 U+007F") + << QString::fromUtf8("\x10\x7F") + << QString::fromUtf8("\x10\x7F") + << QString() << 0 << 0; + + QTest::newRow("Non-ASCII 8bit control character U+0085") + << QString::fromUtf8("\xC2\x85") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Non-ASCII multibyte control character U+180E") + << QString::fromUtf8("\xE1\xA0\x8E") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Zero Width No-Break Space U+FEFF") + << QString::fromUtf8("\xEF\xBB\xBF") + << QString() + << QString() << 0 << 0; + + QTest::newRow("Non-ASCII control character U+1D175") + << QString::fromUtf8("\xF0\x9D\x85\xB5") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Plane 0 private use character U+F123") + << QString::fromUtf8("\xEF\x84\xA3") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Plane 15 private use character U+F1234") + << QString::fromUtf8("\xF3\xB1\x88\xB4") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Plane 16 private use character U+10F234") + << QString::fromUtf8("\xF4\x8F\x88\xB4") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Non-character code point U+8FFFE") + << QString::fromUtf8("\xF2\x8F\xBF\xBE") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Non-character code point U+10FFFF") + << QString::fromUtf8("\xF4\x8F\xBF\xBF") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Surrogate code U+DF42") + << QString::fromUtf8("\xED\xBD\x82") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Non-plain text character U+FFFD") + << QString::fromUtf8("\xEF\xBF\xBD") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Ideographic description character U+2FF5") + << QString::fromUtf8("\xE2\xBF\xB5") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Display property character U+0341") + << QString::fromUtf8("\xCD\x81") + << QString::fromUtf8("\xCC\x81") + << QString() << 0 << 0; + + QTest::newRow("Left-to-right mark U+200E") + << QString::fromUtf8("\xE2\x80\x8E") + << QString::fromUtf8("\xCC\x81") + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Deprecated U+202A") + << QString::fromUtf8("\xE2\x80\xAA") + << QString::fromUtf8("\xCC\x81") + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Language tagging character U+E0001") + << QString::fromUtf8("\xF3\xA0\x80\x81") + << QString::fromUtf8("\xCC\x81") + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Language tagging character U+E0042") + << QString::fromUtf8("\xF3\xA0\x81\x82") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_CONTAINS_PROHIBITED; + + QTest::newRow("Bidi: RandALCat character U+05BE and LCat characters") + << QString::fromUtf8("foo\xD6\xBE""bar") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_BIDI_BOTH_L_AND_RAL; + + QTest::newRow("Bidi: RandALCat character U+FD50 and LCat characters") + << QString::fromUtf8("foo\xEF\xB5\x90""bar") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_BIDI_BOTH_L_AND_RAL; + + QTest::newRow("Bidi: RandALCat character U+FB38 and LCat characters") + << QString::fromUtf8("foo\xEF\xB9\xB6""bar") + << QString::fromUtf8("foo \xd9\x8e""bar") + << QString() << 0 << 0; + + QTest::newRow("Bidi: RandALCat without trailing RandALCat U+0627 U+0031") + << QString::fromUtf8("\xD8\xA7\x31") + << QString() + << QString("Nameprep") << 0 << STRINGPREP_BIDI_LEADTRAIL_NOT_RAL; + + QTest::newRow("Bidi: RandALCat character U+0627 U+0031 U+0628") + << QString::fromUtf8("\xD8\xA7\x31\xD8\xA8") + << QString::fromUtf8("\xD8\xA7\x31\xD8\xA8") + << QString() << 0 << 0; + + QTest::newRow("Unassigned code point U+E0002") + << QString::fromUtf8("\xF3\xA0\x80\x82") + << QString() + << QString("Nameprep") << STRINGPREP_NO_UNASSIGNED << STRINGPREP_CONTAINS_UNASSIGNED; + + QTest::newRow("Larger test (shrinking)") + << QString::fromUtf8("X\xC2\xAD\xC3\x9F\xC4\xB0\xE2\x84\xA1\x6a\xcc\x8c\xc2\xa0\xc2" + "\xaa\xce\xb0\xe2\x80\x80") + << QString::fromUtf8("xssi\xcc\x87""tel\xc7\xb0 a\xce\xb0 ") + << QString("Nameprep") << 0 << 0; + + QTest::newRow("Larger test (expanding)") + << QString::fromUtf8("X\xC3\x9F\xe3\x8c\x96\xC4\xB0\xE2\x84\xA1\xE2\x92\x9F\xE3\x8c\x80") + << QString::fromUtf8("xss\xe3\x82\xad\xe3\x83\xad\xe3\x83\xa1\xe3\x83\xbc\xe3\x83\x88" + "\xe3\x83\xab""i\xcc\x87""tel\x28""d\x29\xe3\x82\xa2\xe3\x83\x91" + "\xe3\x83\xbc\xe3\x83\x88") + << QString() << 0 << 0; +} + +void tst_QUrlInternal::nameprep_testsuite() +{ +#ifdef QT_BUILD_INTERNAL + QFETCH(QString, in); + QFETCH(QString, out); + QFETCH(QString, profile); + + QEXPECT_FAIL("Left-to-right mark U+200E", + "Investigate further", Continue); + QEXPECT_FAIL("Deprecated U+202A", + "Investigate further", Continue); + QEXPECT_FAIL("Language tagging character U+E0001", + "Investigate further", Continue); + qt_nameprep(&in, 0); + QCOMPARE(in, out); +#endif +} + +void tst_QUrlInternal::nameprep_highcodes_data() +{ + QTest::addColumn<QString>("in"); + QTest::addColumn<QString>("out"); + QTest::addColumn<QString>("profile"); + QTest::addColumn<int>("flags"); + QTest::addColumn<int>("rc"); + + { + QChar st[] = { '-', 0xd801, 0xdc1d, 'a' }; + QChar se[] = { '-', 0xd801, 0xdc45, 'a' }; + QTest::newRow("highcodes (U+1041D)") + << QString(st, sizeof(st)/sizeof(st[0])) + << QString(se, sizeof(se)/sizeof(se[0])) + << QString() << 0 << 0; + } + { + QChar st[] = { 0x011C, 0xd835, 0xdf6e, 0x0110 }; + QChar se[] = { 0x011D, 0x03C9, 0x0111 }; + QTest::newRow("highcodes (U+1D76E)") + << QString(st, sizeof(st)/sizeof(st[0])) + << QString(se, sizeof(se)/sizeof(se[0])) + << QString() << 0 << 0; + } + { + QChar st[] = { 'D', 0xdb40, 0xdc20, 'o', 0xd834, 0xdd7a, '\'', 0x2060, 'h' }; + QChar se[] = { 'd', 'o', '\'', 'h' }; + QTest::newRow("highcodes (D, U+E0020, o, U+1D17A, ', U+2060, h)") + << QString(st, sizeof(st)/sizeof(st[0])) + << QString(se, sizeof(se)/sizeof(se[0])) + << QString() << 0 << 0; + } +} + +void tst_QUrlInternal::nameprep_highcodes() +{ +#ifdef QT_BUILD_INTERNAL + QFETCH(QString, in); + QFETCH(QString, out); + QFETCH(QString, profile); + + qt_nameprep(&in, 0); + QCOMPARE(in, out); +#endif +} + +void tst_QUrlInternal::ace_testsuite_data() +{ + QTest::addColumn<QString>("in"); + QTest::addColumn<QString>("toace"); + QTest::addColumn<QString>("fromace"); + QTest::addColumn<QString>("unicode"); + + QTest::newRow("ascii-lower") << "fluke" << "fluke" << "fluke" << "fluke"; + QTest::newRow("ascii-mixed") << "FLuke" << "fluke" << "fluke" << "fluke"; + QTest::newRow("ascii-upper") << "FLUKE" << "fluke" << "fluke" << "fluke"; + + QTest::newRow("asciifolded") << QString::fromLatin1("stra\337e") << "strasse" << "." << "strasse"; + QTest::newRow("asciifolded-dotcom") << QString::fromLatin1("stra\337e.example.com") << "strasse.example.com" << "." << "strasse.example.com"; + QTest::newRow("greek-mu") << QString::fromLatin1("\265V") + <<"xn--v-lmb" + << "." + << QString::fromUtf8("\316\274v"); + + QTest::newRow("non-ascii-lower") << QString::fromLatin1("alqualond\353") + << "xn--alqualond-34a" + << "." + << QString::fromLatin1("alqualond\353"); + QTest::newRow("non-ascii-mixed") << QString::fromLatin1("Alqualond\353") + << "xn--alqualond-34a" + << "." + << QString::fromLatin1("alqualond\353"); + QTest::newRow("non-ascii-upper") << QString::fromLatin1("ALQUALOND\313") + << "xn--alqualond-34a" + << "." + << QString::fromLatin1("alqualond\353"); + + QTest::newRow("idn-lower") << "xn--alqualond-34a" << "xn--alqualond-34a" + << QString::fromLatin1("alqualond\353") + << QString::fromLatin1("alqualond\353"); + QTest::newRow("idn-mixed") << "Xn--alqualond-34a" << "xn--alqualond-34a" + << QString::fromLatin1("alqualond\353") + << QString::fromLatin1("alqualond\353"); + QTest::newRow("idn-mixed2") << "XN--alqualond-34a" << "xn--alqualond-34a" + << QString::fromLatin1("alqualond\353") + << QString::fromLatin1("alqualond\353"); + QTest::newRow("idn-mixed3") << "xn--ALQUALOND-34a" << "xn--alqualond-34a" + << QString::fromLatin1("alqualond\353") + << QString::fromLatin1("alqualond\353"); + QTest::newRow("idn-mixed4") << "xn--alqualond-34A" << "xn--alqualond-34a" + << QString::fromLatin1("alqualond\353") + << QString::fromLatin1("alqualond\353"); + QTest::newRow("idn-upper") << "XN--ALQUALOND-34A" << "xn--alqualond-34a" + << QString::fromLatin1("alqualond\353") + << QString::fromLatin1("alqualond\353"); + + QTest::newRow("separator-3002") << QString::fromUtf8("example\343\200\202com") + << "example.com" << "." << "example.com"; + + QString egyptianIDN = + QString::fromUtf8("\331\210\330\262\330\247\330\261\330\251\055\330\247\331\204\330" + "\243\330\252\330\265\330\247\331\204\330\247\330\252.\331\205" + "\330\265\330\261"); + QTest::newRow("egyptian-tld-ace") + << "xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c" + << "xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c" + << "." + << egyptianIDN; + QTest::newRow("egyptian-tld-unicode") + << egyptianIDN + << "xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c" + << "." + << egyptianIDN; + QTest::newRow("egyptian-tld-mix1") + << QString::fromUtf8("\331\210\330\262\330\247\330\261\330\251\055\330\247\331\204\330" + "\243\330\252\330\265\330\247\331\204\330\247\330\252.xn--wgbh1c") + << "xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c" + << "." + << egyptianIDN; + QTest::newRow("egyptian-tld-mix2") + << QString::fromUtf8("xn----rmckbbajlc6dj7bxne2c.\331\205\330\265\330\261") + << "xn----rmckbbajlc6dj7bxne2c.xn--wgbh1c" + << "." + << egyptianIDN; +} + +void tst_QUrlInternal::ace_testsuite() +{ + static const char canonsuffix[] = ".troll.no"; + QFETCH(QString, in); + QFETCH(QString, toace); + QFETCH(QString, fromace); + QFETCH(QString, unicode); + + const char *suffix = canonsuffix; + if (toace.contains('.')) + suffix = 0; + + QString domain = in + suffix; + QCOMPARE(QString::fromLatin1(QUrl::toAce(domain)), toace + suffix); + if (fromace != ".") + QCOMPARE(QUrl::fromAce(domain.toLatin1()), fromace + suffix); + QCOMPARE(QUrl::fromAce(QUrl::toAce(domain)), unicode + suffix); + + domain = in + (suffix ? ".troll.No" : ""); + QCOMPARE(QString::fromLatin1(QUrl::toAce(domain)), toace + suffix); + if (fromace != ".") + QCOMPARE(QUrl::fromAce(domain.toLatin1()), fromace + suffix); + QCOMPARE(QUrl::fromAce(QUrl::toAce(domain)), unicode + suffix); + + domain = in + (suffix ? ".troll.NO" : ""); + QCOMPARE(QString::fromLatin1(QUrl::toAce(domain)), toace + suffix); + if (fromace != ".") + QCOMPARE(QUrl::fromAce(domain.toLatin1()), fromace + suffix); + QCOMPARE(QUrl::fromAce(QUrl::toAce(domain)), unicode + suffix); +} + +void tst_QUrlInternal::std3violations_data() +{ + QTest::addColumn<QString>("source"); + QTest::addColumn<bool>("validUrl"); + + QTest::newRow("too-long") << "this-domain-is-far-too-long-for-its-own-good-and-should-have-been-limited-to-63-chars" << false; + QTest::newRow("dash-begin") << "-x-foo" << false; + QTest::newRow("dash-end") << "x-foo-" << false; + QTest::newRow("dash-begin-end") << "-foo-" << false; + + QTest::newRow("control") << "\033foo" << false; + QTest::newRow("bang") << "foo!" << false; + QTest::newRow("plus") << "foo+bar" << false; + QTest::newRow("dot") << "foo.bar"; + QTest::newRow("startingdot") << ".bar" << false; + QTest::newRow("startingdot2") << ".example.com" << false; + QTest::newRow("slash") << "foo/bar" << true; + QTest::newRow("colon") << "foo:80" << true; + QTest::newRow("question") << "foo?bar" << true; + QTest::newRow("at") << "foo@bar" << true; + QTest::newRow("backslash") << "foo\\bar" << false; + + // these characters are transformed by NFKC to non-LDH characters + QTest::newRow("dot-like") << QString::fromUtf8("foo\342\200\244bar") << false; // U+2024 ONE DOT LEADER + QTest::newRow("slash-like") << QString::fromUtf8("foo\357\274\217bar") << false; // U+FF0F FULLWIDTH SOLIDUS + + // The following should be invalid but isn't + // the DIVISON SLASH doesn't case-fold to a slash + // is this a problem with RFC 3490? + //QTest::newRow("slash-like2") << QString::fromUtf8("foo\342\210\225bar") << false; // U+2215 DIVISION SLASH +} + +void tst_QUrlInternal::std3violations() +{ + QFETCH(QString, source); + +#ifdef QT_BUILD_INTERNAL + { + QString prepped = source; + qt_nameprep(&prepped, 0); + QVERIFY(!qt_check_std3rules(prepped.constData(), prepped.length())); + } +#endif + + if (source.contains('.')) + return; // this test ends here + + QUrl url; + url.setHost(source); + QVERIFY(url.host().isEmpty()); + + QFETCH(bool, validUrl); + if (validUrl) + return; // test ends here for these cases + + url = QUrl("http://" + source + "/some/path"); + QVERIFY(!url.isValid()); +} + +void tst_QUrlInternal::std3deviations_data() +{ + QTest::addColumn<QString>("source"); + + QTest::newRow("ending-dot") << "example.com."; + QTest::newRow("ending-dot3002") << QString("example.com") + QChar(0x3002); + QTest::newRow("underline") << "foo_bar"; //QTBUG-7434 +} + +void tst_QUrlInternal::std3deviations() +{ + QFETCH(QString, source); + QVERIFY(!QUrl::toAce(source).isEmpty()); + + QUrl url; + url.setHost(source); + QVERIFY(!url.host().isEmpty()); +} + +void tst_QUrlInternal::correctEncodedMistakes_data() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("empty") << "" << ""; + + // these contain one invalid percent + QTest::newRow("%") << QString("%") << QString("%25"); + QTest::newRow("3%") << QString("3%") << QString("3%25"); + QTest::newRow("13%") << QString("13%") << QString("13%25"); + QTest::newRow("13%!") << QString("13%!") << QString("13%25!"); + QTest::newRow("13%!!") << QString("13%!!") << QString("13%25!!"); + QTest::newRow("13%a") << QString("13%a") << QString("13%25a"); + QTest::newRow("13%az") << QString("13%az") << QString("13%25az"); + + // two invalid percents + QTest::newRow("13%%") << "13%%" << "13%25%25"; + QTest::newRow("13%a%a") << "13%a%a" << "13%25a%25a"; + QTest::newRow("13%az%az") << "13%az%az" << "13%25az%25az"; + + // these are correct (idempotent) + QTest::newRow("13%25") << QString("13%25") << QString("13%25"); + QTest::newRow("13%25%25") << QString("13%25%25") << QString("13%25%25"); + + // these contain one invalid and one valid + // the code assumes they are all invalid + QTest::newRow("13%13..%") << "13%13..%" << "13%2513..%25"; + QTest::newRow("13%..%13") << "13%..%13" << "13%25..%2513"; + + // three percents, one invalid + QTest::newRow("%01%02%3") << "%01%02%3" << "%2501%2502%253"; + + // now mix bad percents with Unicode decoding + QTest::newRow("%C2%") << "%C2%" << "%25C2%25"; + QTest::newRow("%C2%A") << "%C2%A" << "%25C2%25A"; + QTest::newRow("%C2%Az") << "%C2%Az" << "%25C2%25Az"; + QTest::newRow("%E2%A0%") << "%E2%A0%" << "%25E2%25A0%25"; + QTest::newRow("%E2%A0%A") << "%E2%A0%A" << "%25E2%25A0%25A"; + QTest::newRow("%E2%A0%Az") << "%E2%A0%Az" << "%25E2%25A0%25Az"; + QTest::newRow("%F2%A0%A0%") << "%F2%A0%A0%" << "%25F2%25A0%25A0%25"; + QTest::newRow("%F2%A0%A0%A") << "%F2%A0%A0%A" << "%25F2%25A0%25A0%25A"; + QTest::newRow("%F2%A0%A0%Az") << "%F2%A0%A0%Az" << "%25F2%25A0%25A0%25Az"; +} + +void tst_QUrlInternal::correctEncodedMistakes() +{ + QFETCH(QString, input); + QFETCH(QString, expected); + + // prepend some data to be sure that it remains there + QString output = QTest::currentDataTag(); + expected.prepend(output); + + if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), 0)) + output += input; + QCOMPARE(output, expected); +} + +static void addUtf8Data(const char *name, const char *data) +{ + QString encoded = QByteArray(data).toPercentEncoding(); + QString decoded = QString::fromUtf8(data); + + QTest::newRow(QByteArray("decode-") + name) << encoded << QUrl::ComponentFormattingOptions(QUrl::MostDecoded) << decoded; + QTest::newRow(QByteArray("encode-") + name) << decoded << QUrl::ComponentFormattingOptions(QUrl::FullyEncoded) << encoded; +} + +void tst_QUrlInternal::encodingRecode_data() +{ + typedef QUrl::ComponentFormattingOptions F; + QTest::addColumn<QString>("input"); + QTest::addColumn<F>("encodingMode"); + QTest::addColumn<QString>("expected"); + + // -- idempotent tests -- + for (int i = 0; i < 0x10; ++i) { + QByteArray code = QByteArray::number(i, 16); + F mode = QUrl::ComponentFormattingOption(i << 12); + + QTest::newRow("null-0x" + code) << QString() << mode << QString(); + QTest::newRow("empty-0x" + code) << "" << mode << ""; + + // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + // Unreserved characters are never encoded + QTest::newRow("alpha-0x" + code) << "abcABCZZzz" << mode << "abcABCZZzz"; + QTest::newRow("digits-0x" + code) << "01234567890" << mode << "01234567890"; + QTest::newRow("otherunreserved-0x" + code) << "-._~" << mode << "-._~"; + + // Control characters are always encoded + // Use uppercase because the output is also uppercased + QTest::newRow("control-nul-0x" + code) << "%00" << mode << "%00"; + QTest::newRow("control-0x" + code) << "%0D%0A%1F%1A%7F" << mode << "%0D%0A%1F%1A%7F"; + + // The percent is always encoded + QTest::newRow("percent-0x" + code) << "25%2525" << mode << "25%2525"; + + // mixed control and unreserved + QTest::newRow("control-unreserved-0x" + code) << "Foo%00Bar%0D%0Abksp%7F" << mode << "Foo%00Bar%0D%0Abksp%7F"; + } + + // gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + // / "*" / "+" / "," / ";" / "=" + // in the default operation, delimiters don't get encoded or decoded + static const char delimiters[] = ":/?#[]@" "!$&'()*+,;="; + for (const char *c = delimiters; *c; ++c) { + QByteArray code = QByteArray::number(*c, 16); + QString encoded = QString("abc%") + code.toUpper() + "def" ; + QString decoded = QString("abc") + *c + "def" ; + QTest::newRow("delimiter-encoded-" + code) << encoded << F(QUrl::FullyEncoded) << encoded; + QTest::newRow("delimiter-decoded-" + code) << decoded << F(QUrl::FullyEncoded) << decoded; + } + + // encode control characters + QTest::newRow("encode-control") << "\1abc\2\033esc" << F(QUrl::MostDecoded) << "%01abc%02%1Besc"; + QTest::newRow("encode-nul") << QString::fromLatin1("abc\0def", 7) << F(QUrl::MostDecoded) << "abc%00def"; + + // space + QTest::newRow("space-leave-decoded") << "Hello World " << F(QUrl::MostDecoded) << "Hello World "; + QTest::newRow("space-leave-encoded") << "Hello%20World%20" << F(QUrl::FullyEncoded) << "Hello%20World%20"; + QTest::newRow("space-encode") << "Hello World " << F(QUrl::FullyEncoded) << "Hello%20World%20"; + QTest::newRow("space-decode") << "Hello%20World%20" << F(QUrl::MostDecoded) << "Hello World "; + + // decode unreserved + QTest::newRow("unreserved-decode") << "%66%6f%6f%42a%72" << F(QUrl::FullyEncoded) << "fooBar"; + + // mix encoding with decoding + QTest::newRow("encode-control-decode-space") << "\1\2%200" << F(QUrl::MostDecoded) << "%01%02 0"; + QTest::newRow("decode-space-encode-control") << "%20\1\2" << F(QUrl::MostDecoded) << " %01%02"; + + // decode and encode valid UTF-8 data + // invalid is tested in encodingRecodeInvalidUtf8 + addUtf8Data("utf8-2char-1", "\xC2\x80"); // U+0080 + addUtf8Data("utf8-2char-2", "\xDF\xBF"); // U+07FF + addUtf8Data("utf8-3char-1", "\xE0\xA0\x80"); // U+0800 + addUtf8Data("utf8-3char-2", "\xED\x9F\xBF"); // U+D7FF + addUtf8Data("utf8-3char-3", "\xEE\x80\x80"); // U+E000 + addUtf8Data("utf8-3char-4", "\xEF\xBF\xBD"); // U+FFFD + addUtf8Data("utf8-4char-1", "\xF0\x90\x80\x80"); // U+10000 + addUtf8Data("utf8-4char-2", "\xF4\x8F\xBF\xBD"); // U+10FFFD + + // longer UTF-8 sequences, mixed with unreserved + addUtf8Data("utf8-string-1", "R\xc3\xa9sum\xc3\xa9"); + addUtf8Data("utf8-string-2", "\xDF\xBF\xE0\xA0\x80""A"); + addUtf8Data("utf8-string-3", "\xE0\xA0\x80\xDF\xBF..."); + + // special cases: stuff we can encode, but not decode + QTest::newRow("unicode-noncharacter") << QString(QChar(0xffff)) << F(QUrl::FullyEncoded) << "%EF%BF%BF"; + QTest::newRow("unicode-lo-surrogate") << QString(QChar(0xD800)) << F(QUrl::FullyEncoded) << "%ED%A0%80"; + QTest::newRow("unicode-hi-surrogate") << QString(QChar(0xDC00)) << F(QUrl::FullyEncoded) << "%ED%B0%80"; + + // a couple of Unicode strings with leading spaces + QTest::newRow("space-unicode") << QString::fromUtf8(" \xc2\xa0") << F(QUrl::FullyEncoded) << "%20%C2%A0"; + QTest::newRow("space-space-unicode") << QString::fromUtf8(" \xc2\xa0") << F(QUrl::FullyEncoded) << "%20%20%C2%A0"; + QTest::newRow("space-space-space-unicode") << QString::fromUtf8(" \xc2\xa0") << F(QUrl::FullyEncoded) << "%20%20%20%C2%A0"; + + // hex case testing + QTest::newRow("FF") << "%FF" << F(QUrl::FullyEncoded) << "%FF"; + QTest::newRow("Ff") << "%Ff" << F(QUrl::FullyEncoded) << "%FF"; + QTest::newRow("fF") << "%fF" << F(QUrl::FullyEncoded) << "%FF"; + QTest::newRow("ff") << "%ff" << F(QUrl::FullyEncoded) << "%FF"; + + // decode UTF-8 mixed with non-UTF-8 and unreserved + QTest::newRow("utf8-mix-1") << "%80%C2%80" << F(QUrl::MostDecoded) << QString::fromUtf8("%80\xC2\x80"); + QTest::newRow("utf8-mix-2") << "%C2%C2%80" << F(QUrl::MostDecoded) << QString::fromUtf8("%C2\xC2\x80"); + QTest::newRow("utf8-mix-3") << "%E0%C2%80" << F(QUrl::MostDecoded) << QString::fromUtf8("%E0\xC2\x80"); + QTest::newRow("utf8-mix-3") << "A%C2%80" << F(QUrl::MostDecoded) << QString::fromUtf8("A\xC2\x80"); + QTest::newRow("utf8-mix-3") << "%C2%80A" << F(QUrl::MostDecoded) << QString::fromUtf8("\xC2\x80""A"); +} + +void tst_QUrlInternal::encodingRecode() +{ + QFETCH(QString, input); + QFETCH(QString, expected); + QFETCH(QUrl::ComponentFormattingOptions, encodingMode); + + // prepend some data to be sure that it remains there + QString output = QTest::currentDataTag(); + expected.prepend(output); + + if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), encodingMode)) + output += input; + QCOMPARE(output, expected); +} + +void tst_QUrlInternal::encodingRecodeInvalidUtf8_data() +{ + QTest::addColumn<QByteArray>("utf8"); + QTest::addColumn<QString>("utf16"); + + extern void loadInvalidUtf8Rows(); + loadInvalidUtf8Rows(); + + extern void loadNonCharactersRows(); + loadNonCharactersRows(); + + QTest::newRow("utf8-mix-4") << QByteArray("\xE0.A2\x80"); + QTest::newRow("utf8-mix-5") << QByteArray("\xE0\xA2.80"); + QTest::newRow("utf8-mix-6") << QByteArray("\xE0\xA2\x33"); +} + +void tst_QUrlInternal::encodingRecodeInvalidUtf8() +{ + QFETCH(QByteArray, utf8); + QString input = utf8.toPercentEncoding(); + + // prepend some data to be sure that it remains there + QString output = QTest::currentDataTag(); + + if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), QUrl::MostDecoded)) + output += input; + QCOMPARE(output, QTest::currentDataTag() + input); + + // this is just control + output = QTest::currentDataTag(); + if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), QUrl::FullyEncoded)) + output += input; + QCOMPARE(output, QTest::currentDataTag() + input); +} + +QTEST_APPLESS_MAIN(tst_QUrlInternal) diff --git a/tests/auto/corelib/io/qurlquery/qurlquery.pro b/tests/auto/corelib/io/qurlquery/qurlquery.pro new file mode 100644 index 0000000000..d344e48337 --- /dev/null +++ b/tests/auto/corelib/io/qurlquery/qurlquery.pro @@ -0,0 +1,5 @@ +QT = core core-private testlib +TARGET = tst_qurlquery +CONFIG += parallel_test testcase +SOURCES += tst_qurlquery.cpp +DEFINES += SRCDIR=\\\"$$PWD/\\\" diff --git a/tests/auto/corelib/io/qurlquery/tst_qurlquery.cpp b/tests/auto/corelib/io/qurlquery/tst_qurlquery.cpp new file mode 100644 index 0000000000..7148a71153 --- /dev/null +++ b/tests/auto/corelib/io/qurlquery/tst_qurlquery.cpp @@ -0,0 +1,818 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Intel Corporation. +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QUrlQuery> +#include <QtTest/QtTest> + +typedef QList<QPair<QString, QString> > QueryItems; +Q_DECLARE_METATYPE(QueryItems) +Q_DECLARE_METATYPE(QUrl::ComponentFormattingOptions) + +class tst_QUrlQuery : public QObject +{ + Q_OBJECT + +public: + tst_QUrlQuery() + { + qRegisterMetaType<QueryItems>(); + } + +private Q_SLOTS: + void constructing(); + void addRemove(); + void multiAddRemove(); + void multiplyAddSamePair(); + void setQueryItems_data(); + void setQueryItems(); + void basicParsing_data(); + void basicParsing(); + void reconstructQuery_data(); + void reconstructQuery(); + void encodedSetQueryItems_data(); + void encodedSetQueryItems(); + void encodedParsing_data(); + void encodedParsing(); + void differentDelimiters(); + + // old tests from tst_qurl.cpp + // add new tests above + void old_queryItems(); + void old_hasQueryItem_data(); + void old_hasQueryItem(); +}; + +static QString prettyElement(const QString &key, const QString &value) +{ + QString result; + if (key.isNull()) + result += "null -> "; + else + result += '"' % key % "\" -> "; + if (value.isNull()) + result += "null"; + else + result += '"' % value % '"'; + return result; +} + +static QString prettyPair(QList<QPair<QString, QString> >::const_iterator it) +{ + return prettyElement(it->first, it->second); +} + +template <typename T> +static QByteArray prettyList(const T &items) +{ + QString result = "("; + bool first = true; + typename T::const_iterator it = items.constBegin(); + for ( ; it != items.constEnd(); ++it) { + if (!first) + result += ", "; + first = false; + result += prettyPair(it); + } + result += ")"; + return result.toLocal8Bit(); +} + +static bool compare(const QList<QPair<QString, QString> > &actual, const QueryItems &expected, + const char *actualStr, const char *expectedStr, const char *file, int line) +{ + return QTest::compare_helper(actual == expected, "Compared values are not the same", + qstrdup(prettyList(actual)), qstrdup(prettyList(expected).data()), + actualStr, expectedStr, file, line); +} + +#define COMPARE_ITEMS(actual, expected) \ + do { \ + if (!compare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \ + return; \ + } while (0) + +inline QueryItems operator+(QueryItems items, const QPair<QString, QString> &pair) +{ + // items is already a copy + items.append(pair); + return items; +} + +inline QueryItems operator+(const QPair<QString, QString> &pair, QueryItems items) +{ + // items is already a copy + items.prepend(pair); + return items; +} + +inline QPair<QString, QString> qItem(const QString &first, const QString &second) +{ + return qMakePair(first, second); +} + +inline QPair<QString, QString> qItem(const char *first, const QString &second) +{ + return qMakePair(QString::fromUtf8(first), second); +} + +inline QPair<QString, QString> qItem(const char *first, const char *second) +{ + return qMakePair(QString::fromUtf8(first), QString::fromUtf8(second)); +} + +inline QPair<QString, QString> qItem(const QString &first, const char *second) +{ + return qMakePair(first, QString::fromUtf8(second)); +} + +static QUrlQuery emptyQuery() +{ + return QUrlQuery(); +} + +void tst_QUrlQuery::constructing() +{ + QUrlQuery empty; + QVERIFY(empty.isEmpty()); + QCOMPARE(empty.queryPairDelimiter(), QUrlQuery::defaultQueryPairDelimiter()); + QCOMPARE(empty.queryValueDelimiter(), QUrlQuery::defaultQueryValueDelimiter()); + // undefined whether it is detached, but don't crash + QVERIFY(empty.isDetached() || !empty.isDetached()); + + empty.clear(); + QVERIFY(empty.isEmpty()); + + { + QUrlQuery copy(empty); + QVERIFY(copy.isEmpty()); + QVERIFY(!copy.isDetached()); + QVERIFY(copy == empty); + QVERIFY(!(copy != empty)); + + copy = empty; + QVERIFY(copy == empty); + + copy = QUrlQuery(); + QVERIFY(copy == empty); + } + { + QUrlQuery copy(emptyQuery()); + QVERIFY(copy == empty); + } + + QVERIFY(!empty.hasQueryItem("a")); + QVERIFY(empty.queryItemValue("a").isEmpty()); + QVERIFY(empty.allQueryItemValues("a").isEmpty()); + + QVERIFY(!empty.hasQueryItem("")); + QVERIFY(empty.queryItemValue("").isEmpty()); + QVERIFY(empty.allQueryItemValues("").isEmpty()); + + QVERIFY(!empty.hasQueryItem(QString())); + QVERIFY(empty.queryItemValue(QString()).isEmpty()); + QVERIFY(empty.allQueryItemValues(QString()).isEmpty()); + + QVERIFY(empty.queryItems().isEmpty()); + + QUrlQuery other; + other.addQueryItem("a", "b"); + QVERIFY(!other.isEmpty()); + QVERIFY(other.isDetached()); + QVERIFY(other != empty); + QVERIFY(!(other == empty)); + + QUrlQuery copy(other); + QVERIFY(copy == other); + + copy.clear(); + QVERIFY(copy.isEmpty()); + QVERIFY(copy != other); + + copy = other; + QVERIFY(!copy.isEmpty()); + QVERIFY(copy == other); + + copy = QUrlQuery(); + QVERIFY(copy.isEmpty()); + + empty.setQueryDelimiters('(', ')'); + QCOMPARE(empty.queryValueDelimiter(), QChar(QLatin1Char('('))); + QCOMPARE(empty.queryPairDelimiter(), QChar(QLatin1Char(')'))); + + QList<QPair<QString, QString> > query; + query += qMakePair(QString("type"), QString("login")); + query += qMakePair(QString("name"), QString::fromUtf8("åge nissemannsen")); + query += qMakePair(QString("ole&du"), QString::fromUtf8("anne+jørgen=sant")); + query += qMakePair(QString("prosent"), QString("%")); + copy.setQueryItems(query); + QVERIFY(!copy.isEmpty()); +} + +void tst_QUrlQuery::addRemove() +{ + QUrlQuery query; + + { + // one item + query.addQueryItem("a", "b"); + QVERIFY(!query.isEmpty()); + QVERIFY(query.hasQueryItem("a")); + QCOMPARE(query.queryItemValue("a"), QString("b")); + QCOMPARE(query.allQueryItemValues("a"), QStringList() << "b"); + + QList<QPair<QString, QString> > allItems = query.queryItems(); + QCOMPARE(allItems.count(), 1); + QCOMPARE(allItems.at(0).first, QString("a")); + QCOMPARE(allItems.at(0).second, QString("b")); + } + + QUrlQuery original = query; + + { + // two items + query.addQueryItem("c", "d"); + QVERIFY(query.hasQueryItem("a")); + QCOMPARE(query.queryItemValue("a"), QString("b")); + QCOMPARE(query.allQueryItemValues("a"), QStringList() << "b"); + QVERIFY(query.hasQueryItem("c")); + QCOMPARE(query.queryItemValue("c"), QString("d")); + QCOMPARE(query.allQueryItemValues("c"), QStringList() << "d"); + + QList<QPair<QString, QString> > allItems = query.queryItems(); + QCOMPARE(allItems.count(), 2); + QVERIFY(allItems.contains(qItem("a", "b"))); + QVERIFY(allItems.contains(qItem("c", "d"))); + + QVERIFY(query != original); + QVERIFY(!(query == original)); + } + + { + // remove an item that isn't there + QUrlQuery copy = query; + query.removeQueryItem("e"); + QCOMPARE(query, copy); + } + + { + // remove an item + query.removeQueryItem("c"); + QVERIFY(query.hasQueryItem("a")); + QCOMPARE(query.queryItemValue("a"), QString("b")); + QCOMPARE(query.allQueryItemValues("a"), QStringList() << "b"); + + QList<QPair<QString, QString> > allItems = query.queryItems(); + QCOMPARE(allItems.count(), 1); + QCOMPARE(allItems.at(0).first, QString("a")); + QCOMPARE(allItems.at(0).second, QString("b")); + + QVERIFY(query == original); + QVERIFY(!(query != original)); + } + + { + // add an item with en empty value + QString emptyButNotNull(0, Qt::Uninitialized); + QVERIFY(emptyButNotNull.isEmpty()); + QVERIFY(!emptyButNotNull.isNull()); + + query.addQueryItem("e", ""); + QVERIFY(query.hasQueryItem("a")); + QCOMPARE(query.queryItemValue("a"), QString("b")); + QCOMPARE(query.allQueryItemValues("a"), QStringList() << "b"); + QVERIFY(query.hasQueryItem("e")); + QCOMPARE(query.queryItemValue("e"), emptyButNotNull); + QCOMPARE(query.allQueryItemValues("e"), QStringList() << emptyButNotNull); + + QList<QPair<QString, QString> > allItems = query.queryItems(); + QCOMPARE(allItems.count(), 2); + QVERIFY(allItems.contains(qItem("a", "b"))); + QVERIFY(allItems.contains(qItem("e", emptyButNotNull))); + + QVERIFY(query != original); + QVERIFY(!(query == original)); + } + + { + // remove the items + query.removeQueryItem("a"); + query.removeQueryItem("e"); + QVERIFY(query.isEmpty()); + } +} + +void tst_QUrlQuery::multiAddRemove() +{ + QUrlQuery query; + + { + // one item, two values + query.addQueryItem("a", "b"); + query.addQueryItem("a", "c"); + QVERIFY(!query.isEmpty()); + QVERIFY(query.hasQueryItem("a")); + + // returns the first one + QVERIFY(query.queryItemValue("a") == "b"); + + // order is the order we set them in + QVERIFY(query.allQueryItemValues("a") == QStringList() << "b" << "c"); + } + + { + // add another item, two values + query.addQueryItem("A", "B"); + query.addQueryItem("A", "C"); + QVERIFY(query.hasQueryItem("A")); + QVERIFY(query.hasQueryItem("a")); + + QVERIFY(query.queryItemValue("a") == "b"); + QVERIFY(query.allQueryItemValues("a") == QStringList() << "b" << "c"); + QVERIFY(query.queryItemValue("A") == "B"); + QVERIFY(query.allQueryItemValues("A") == QStringList() << "B" << "C"); + } + + { + // remove one of the original items + query.removeQueryItem("a"); + QVERIFY(query.hasQueryItem("a")); + + // it must have removed the first one + QVERIFY(query.queryItemValue("a") == "c"); + } + + { + // remove the items we added later + query.removeAllQueryItems("A"); + QVERIFY(!query.isEmpty()); + QVERIFY(!query.hasQueryItem("A")); + } + + { + // add one element to the current, then remove them + query.addQueryItem("a", "d"); + query.removeAllQueryItems("a"); + QVERIFY(!query.hasQueryItem("a")); + QVERIFY(query.isEmpty()); + } +} + +void tst_QUrlQuery::multiplyAddSamePair() +{ + QUrlQuery query; + query.addQueryItem("a", "a"); + query.addQueryItem("a", "a"); + QCOMPARE(query.allQueryItemValues("a"), QStringList() << "a" << "a"); + + query.addQueryItem("a", "a"); + QCOMPARE(query.allQueryItemValues("a"), QStringList() << "a" << "a" << "a"); + + query.removeQueryItem("a"); + QCOMPARE(query.allQueryItemValues("a"), QStringList() << "a" << "a"); +} + +void tst_QUrlQuery::setQueryItems_data() +{ + QTest::addColumn<QueryItems>("items"); + QString emptyButNotNull(0, Qt::Uninitialized); + + QTest::newRow("empty") << QueryItems(); + QTest::newRow("1-novalue") << (QueryItems() << qItem("a", QString())); + QTest::newRow("1-emptyvalue") << (QueryItems() << qItem("a", emptyButNotNull)); + + QueryItems list; + list << qItem("a", "b"); + QTest::newRow("1-value") << list; + QTest::newRow("1-multi") << (list + qItem("a", "c")); + QTest::newRow("1-duplicated") << (list + qItem("a", "b")); + + list << qItem("c", "d"); + QTest::newRow("2") << list; + + list << qItem("c", "e"); + QTest::newRow("2-multi") << list; +} + +void tst_QUrlQuery::setQueryItems() +{ + QFETCH(QueryItems, items); + QUrlQuery query; + + QueryItems::const_iterator it = items.constBegin(); + for ( ; it != items.constEnd(); ++it) + query.addQueryItem(it->first, it->second); + COMPARE_ITEMS(query.queryItems(), items); + + query.clear(); + + query.setQueryItems(items); + COMPARE_ITEMS(query.queryItems(), items); +} + +void tst_QUrlQuery::basicParsing_data() +{ + QTest::addColumn<QString>("queryString"); + QTest::addColumn<QueryItems>("items"); + QString emptyButNotNull(0, Qt::Uninitialized); + + QTest::newRow("null") << QString() << QueryItems(); + QTest::newRow("empty") << "" << QueryItems(); + + QTest::newRow("1-novalue") << "a" << (QueryItems() << qItem("a", QString())); + QTest::newRow("1-emptyvalue") << "a=" << (QueryItems() << qItem("a", emptyButNotNull)); + QTest::newRow("1-value") << "a=b" << (QueryItems() << qItem("a", "b")); + + // some longer keys + QTest::newRow("1-longkey-novalue") << "thisisalongkey" << (QueryItems() << qItem("thisisalongkey", QString())); + QTest::newRow("1-longkey-emptyvalue") << "thisisalongkey=" << (QueryItems() << qItem("thisisalongkey", emptyButNotNull)); + QTest::newRow("1-longkey-value") << "thisisalongkey=b" << (QueryItems() << qItem("thisisalongkey", "b")); + + // longer values + QTest::newRow("1-longvalue-value") << "a=thisisalongreasonablyvalue" + << (QueryItems() << qItem("a", "thisisalongreasonablyvalue")); + QTest::newRow("1-longboth-value") << "thisisalongkey=thisisalongreasonablyvalue" + << (QueryItems() << qItem("thisisalongkey", "thisisalongreasonablyvalue")); + + // two or more entries + QueryItems baselist; + baselist << qItem("a", "b") << qItem("c", "d"); + QTest::newRow("2-ab-cd") << "a=b&c=d" << baselist; + QTest::newRow("2-cd-ab") << "c=d&a=b" << (QueryItems() << qItem("c", "d") << qItem("a", "b")); + + // the same entry multiply defined + QTest::newRow("2-a-a") << "a&a" << (QueryItems() << qItem("a", QString()) << qItem("a", QString())); + QTest::newRow("2-ab-a") << "a=b&a" << (QueryItems() << qItem("a", "b") << qItem("a", QString())); + QTest::newRow("2-ab-ab") << "a=b&a=b" << (QueryItems() << qItem("a", "b") << qItem("a", "b")); + QTest::newRow("2-ab-ac") << "a=b&a=c" << (QueryItems() << qItem("a", "b") << qItem("a", "c")); + + QPair<QString, QString> novalue = qItem("somekey", QString()); + QueryItems list2 = baselist + novalue; + QTest::newRow("3-novalue-ab-cd") << "somekey&a=b&c=d" << (novalue + baselist); + QTest::newRow("3-ab-novalue-cd") << "a=b&somekey&c=d" << (QueryItems() << qItem("a", "b") << novalue << qItem("c", "d")); + QTest::newRow("3-ab-cd-novalue") << "a=b&c=d&somekey" << list2; + + list2 << qItem("otherkeynovalue", QString()); + QTest::newRow("4-ab-cd-novalue-novalue") << "a=b&c=d&somekey&otherkeynovalue" << list2; + + QPair<QString, QString> emptyvalue = qItem("somekey", emptyButNotNull); + list2 = baselist + emptyvalue; + QTest::newRow("3-emptyvalue-ab-cd") << "somekey=&a=b&c=d" << (emptyvalue + baselist); + QTest::newRow("3-ab-emptyvalue-cd") << "a=b&somekey=&c=d" << (QueryItems() << qItem("a", "b") << emptyvalue << qItem("c", "d")); + QTest::newRow("3-ab-cd-emptyvalue") << "a=b&c=d&somekey=" << list2; +} + +void tst_QUrlQuery::basicParsing() +{ + QFETCH(QString, queryString); + QFETCH(QueryItems, items); + + QUrlQuery query(queryString); + QCOMPARE(query.isEmpty(), items.isEmpty()); + COMPARE_ITEMS(query.queryItems(), items); +} + +void tst_QUrlQuery::reconstructQuery_data() +{ + QTest::addColumn<QString>("queryString"); + QTest::addColumn<QueryItems>("items"); + QString emptyButNotNull(0, Qt::Uninitialized); + + QTest::newRow("null") << QString() << QueryItems(); + QTest::newRow("empty") << "" << QueryItems(); + + QTest::newRow("1-novalue") << "a" << (QueryItems() << qItem("a", QString())); + QTest::newRow("1-emptyvalue") << "a=" << (QueryItems() << qItem("a", emptyButNotNull)); + QTest::newRow("1-value") << "a=b" << (QueryItems() << qItem("a", "b")); + + // some longer keys + QTest::newRow("1-longkey-novalue") << "thisisalongkey" << (QueryItems() << qItem("thisisalongkey", QString())); + QTest::newRow("1-longkey-emptyvalue") << "thisisalongkey=" << (QueryItems() << qItem("thisisalongkey", emptyButNotNull)); + QTest::newRow("1-longkey-value") << "thisisalongkey=b" << (QueryItems() << qItem("thisisalongkey", "b")); + + // longer values + QTest::newRow("1-longvalue-value") << "a=thisisalongreasonablyvalue" + << (QueryItems() << qItem("a", "thisisalongreasonablyvalue")); + QTest::newRow("1-longboth-value") << "thisisalongkey=thisisalongreasonablyvalue" + << (QueryItems() << qItem("thisisalongkey", "thisisalongreasonablyvalue")); + + // two or more entries + QueryItems baselist; + baselist << qItem("a", "b") << qItem("c", "d"); + QTest::newRow("2-ab-cd") << "a=b&c=d" << baselist; + + // the same entry multiply defined + QTest::newRow("2-a-a") << "a&a" << (QueryItems() << qItem("a", QString()) << qItem("a", QString())); + QTest::newRow("2-ab-ab") << "a=b&a=b" << (QueryItems() << qItem("a", "b") << qItem("a", "b")); + QTest::newRow("2-ab-ac") << "a=b&a=c" << (QueryItems() << qItem("a", "b") << qItem("a", "c")); + QTest::newRow("2-ac-ab") << "a=c&a=b" << (QueryItems() << qItem("a", "c") << qItem("a", "b")); + QTest::newRow("2-ab-cd") << "a=b&c=d" << (QueryItems() << qItem("a", "b") << qItem("c", "d")); + QTest::newRow("2-cd-ab") << "c=d&a=b" << (QueryItems() << qItem("c", "d") << qItem("a", "b")); + + QueryItems list2 = baselist + qItem("somekey", QString()); + QTest::newRow("3-ab-cd-novalue") << "a=b&c=d&somekey" << list2; + + list2 << qItem("otherkeynovalue", QString()); + QTest::newRow("4-ab-cd-novalue-novalue") << "a=b&c=d&somekey&otherkeynovalue" << list2; + + list2 = baselist + qItem("somekey", emptyButNotNull); + QTest::newRow("3-ab-cd-emptyvalue") << "a=b&c=d&somekey=" << list2; +} + +void tst_QUrlQuery::reconstructQuery() +{ + QFETCH(QString, queryString); + QFETCH(QueryItems, items); + + QUrlQuery query; + + // add the items + for (QueryItems::ConstIterator it = items.constBegin(); it != items.constEnd(); ++it) { + query.addQueryItem(it->first, it->second); + } + QCOMPARE(query.query(), queryString); +} + +void tst_QUrlQuery::encodedSetQueryItems_data() +{ + QTest::addColumn<QString>("queryString"); + QTest::addColumn<QString>("key"); + QTest::addColumn<QString>("value"); + QTest::addColumn<QUrl::ComponentFormattingOptions>("encoding"); + QTest::addColumn<QString>("expectedQuery"); + QTest::addColumn<QString>("expectedKey"); + QTest::addColumn<QString>("expectedValue"); + typedef QUrl::ComponentFormattingOptions F; + + QTest::newRow("nul") << "f%00=bar%00" << "f%00" << "bar%00" << F(QUrl::PrettyDecoded) + << "f%00=bar%00" << "f%00" << "bar%00"; + QTest::newRow("non-decodable-1") << "foo%01%7f=b%1ar" << "foo%01%7f" << "b%1ar" << F(QUrl::PrettyDecoded) + << "foo%01%7F=b%1Ar" << "foo%01%7F" << "b%1Ar"; + QTest::newRow("non-decodable-2") << "foo\x01\x7f=b\x1ar" << "foo\x01\x7f" << "b\x1Ar" << F(QUrl::PrettyDecoded) + << "foo%01%7F=b%1Ar" << "foo%01%7F" << "b%1Ar"; + + QTest::newRow("space") << "%20=%20" << "%20" << "%20" << F(QUrl::PrettyDecoded) + << " = " << " " << " "; + QTest::newRow("encode-space") << " = " << " " << " " << F(QUrl::FullyEncoded) + << "%20=%20" << "%20" << "%20"; + + // tri-state + QTest::newRow("decode-non-delimiters") << "%3C%5C%3E=%7B%7C%7D%5E%60" << "%3C%5C%3E" << "%7B%7C%7D%5E%60" << F(QUrl::DecodeReserved) + << "<\\>={|}^`" << "<\\>" << "{|}^`"; + QTest::newRow("encode-non-delimiters") << "<\\>={|}^`" << "<\\>" << "{|}^`" << F(QUrl::EncodeReserved) + << "%3C%5C%3E=%7B%7C%7D%5E%60" << "%3C%5C%3E" << "%7B%7C%7D%5E%60"; + QTest::newRow("pretty-non-delimiters") << "<\\>={|}^`" << "<\\>" << "{|}^`" << F(QUrl::PrettyDecoded) + << "%3C%5C%3E=%7B%7C%7D%5E%60" << "<\\>" << "{|}^`"; + + QTest::newRow("equals") << "%3D=%3D" << "%3D" << "%3D" << F(QUrl::PrettyDecoded) + << "%3D=%3D" << "=" << "="; + QTest::newRow("equals-2") << "%3D==" << "=" << "=" << F(QUrl::PrettyDecoded) + << "%3D=%3D" << "=" << "="; + QTest::newRow("ampersand") << "%26=%26" << "%26" << "%26" << F(QUrl::PrettyDecoded) + << "%26=%26" << "&" << "&"; + QTest::newRow("hash") << "#=#" << "%23" << "%23" << F(QUrl::PrettyDecoded) + << "#=#" << "#" << "#"; + QTest::newRow("decode-hash") << "%23=%23" << "%23" << "%23" << F(QUrl::PrettyDecoded) + << "#=#" << "#" << "#"; + + QTest::newRow("percent") << "%25=%25" << "%25" << "%25" << F(QUrl::PrettyDecoded) + << "%25=%25" << "%25" << "%25"; + QTest::newRow("bad-percent-1") << "%=%" << "%" << "%" << F(QUrl::PrettyDecoded) + << "%25=%25" << "%25" << "%25"; + QTest::newRow("bad-percent-2") << "%2=%2" << "%2" << "%2" << F(QUrl::PrettyDecoded) + << "%252=%252" << "%252" << "%252"; + + QTest::newRow("plus") << "+=+" << "+" << "+" << F(QUrl::PrettyDecoded) + << "+=+" << "+" << "+"; + QTest::newRow("2b") << "%2b=%2b" << "%2b" << "%2b" << F(QUrl::PrettyDecoded) + << "%2B=%2B" << "%2B" << "%2B"; + // plus signs must not be touched + QTest::newRow("encode-plus") << "+=+" << "+" << "+" << F(QUrl::FullyEncoded) + << "+=+" << "+" << "+"; + QTest::newRow("decode-2b") << "%2b=%2b" << "%2b" << "%2b" << F(QUrl::MostDecoded) + << "%2B=%2B" << "%2B" << "%2B"; + + + QTest::newRow("unicode") << "q=R%C3%a9sum%c3%A9" << "q" << "R%C3%a9sum%c3%A9" << F(QUrl::PrettyDecoded) + << QString::fromUtf8("q=R\xc3\xa9sum\xc3\xa9") << "q" << QString::fromUtf8("R\xc3\xa9sum\xc3\xa9"); + QTest::newRow("encode-unicode") << QString::fromUtf8("q=R\xc3\xa9sum\xc3\xa9") << "q" << QString::fromUtf8("R\xc3\xa9sum\xc3\xa9") + << F(QUrl::FullyEncoded) + << "q=R%C3%A9sum%C3%A9" << "q" << "R%C3%A9sum%C3%A9"; +} + +void tst_QUrlQuery::encodedSetQueryItems() +{ + QFETCH(QString, key); + QFETCH(QString, value); + QFETCH(QString, expectedQuery); + QFETCH(QString, expectedKey); + QFETCH(QString, expectedValue); + QFETCH(QUrl::ComponentFormattingOptions, encoding); + QUrlQuery query; + + query.addQueryItem(key, value); + COMPARE_ITEMS(query.queryItems(encoding), QueryItems() << qItem(expectedKey, expectedValue)); + QCOMPARE(query.query(encoding), expectedQuery); +} + +void tst_QUrlQuery::encodedParsing_data() +{ + encodedSetQueryItems_data(); +} + +void tst_QUrlQuery::encodedParsing() +{ + QFETCH(QString, queryString); + QFETCH(QString, expectedQuery); + QFETCH(QString, expectedKey); + QFETCH(QString, expectedValue); + QFETCH(QUrl::ComponentFormattingOptions, encoding); + + QUrlQuery query(queryString); + COMPARE_ITEMS(query.queryItems(encoding), QueryItems() << qItem(expectedKey, expectedValue)); + QCOMPARE(query.query(encoding), expectedQuery); +} + +void tst_QUrlQuery::differentDelimiters() +{ + QUrlQuery query; + query.setQueryDelimiters('(', ')'); + + { + // parse: + query.setQuery("foo(bar)hello(world)"); + + QueryItems expected; + expected << qItem("foo", "bar") << qItem("hello", "world"); + COMPARE_ITEMS(query.queryItems(), expected); + COMPARE_ITEMS(query.queryItems(QUrl::FullyEncoded), expected); + COMPARE_ITEMS(query.queryItems(QUrl::MostDecoded), expected); + } + + { + // reconstruct: + // note the final ')' is missing because there are no further items + QCOMPARE(query.query(), QString("foo(bar)hello(world")); + } + + { + // set items containing the new delimiters and the old ones + query.clear(); + query.addQueryItem("z(=)", "y(&)"); + QCOMPARE(query.query(), QString("z%28=%29(y%28&%29")); + + QUrlQuery copy = query; + QCOMPARE(query.query(), QString("z%28=%29(y%28&%29")); + + copy.setQueryDelimiters(QUrlQuery::defaultQueryValueDelimiter(), + QUrlQuery::defaultQueryPairDelimiter()); + QCOMPARE(copy.query(), QString("z(%3D)=y(%26)")); + } +} + +void tst_QUrlQuery::old_queryItems() +{ + // test imported from old tst_qurl.cpp + QUrlQuery url; + + QList<QPair<QString, QString> > newItems; + newItems += qMakePair(QString("1"), QString("a")); + newItems += qMakePair(QString("2"), QString("b")); + newItems += qMakePair(QString("3"), QString("c")); + newItems += qMakePair(QString("4"), QString("a b")); + newItems += qMakePair(QString("5"), QString("&")); + newItems += qMakePair(QString("foo bar"), QString("hello world")); + newItems += qMakePair(QString("foo+bar"), QString("hello+world")); + newItems += qMakePair(QString("tex"), QString("a + b = c")); + url.setQueryItems(newItems); + QVERIFY(!url.isEmpty()); + + QList<QPair<QString, QString> > setItems = url.queryItems(); + QVERIFY(newItems == setItems); + + url.addQueryItem("1", "z"); + +#if 0 + // undefined behaviour in the new QUrlQuery + + QVERIFY(url.hasQueryItem("1")); + QCOMPARE(url.queryItemValue("1").toLatin1().constData(), "a"); + + url.addQueryItem("1", "zz"); + + QStringList expected; + expected += "a"; + expected += "z"; + expected += "zz"; + QCOMPARE(url.allQueryItemValues("1"), expected); + + url.removeQueryItem("1"); + QCOMPARE(url.allQueryItemValues("1").size(), 2); + QCOMPARE(url.queryItemValue("1").toLatin1().constData(), "z"); +#endif + + url.removeAllQueryItems("1"); + QVERIFY(!url.hasQueryItem("1")); + + QCOMPARE(url.queryItemValue("4").toLatin1().constData(), "a b"); + QCOMPARE(url.queryItemValue("5").toLatin1().constData(), "&"); + QCOMPARE(url.queryItemValue("tex").toLatin1().constData(), "a + b = c"); + QCOMPARE(url.queryItemValue("foo bar").toLatin1().constData(), "hello world"); + + //url.setUrl("http://www.google.com/search?q=a+b"); + url.setQuery("q=a+b"); + QCOMPARE(url.queryItemValue("q"), QString("a+b")); + + //url.setUrl("http://www.google.com/search?q=a=b"); // invalid, but should be tolerated + url.setQuery("q=a=b"); + QCOMPARE(url.queryItemValue("q"), QString("a=b")); +} + +void tst_QUrlQuery::old_hasQueryItem_data() +{ + QTest::addColumn<QString>("url"); + QTest::addColumn<QString>("item"); + QTest::addColumn<bool>("trueFalse"); + + // the old tests started with "http://www.foo.bar" + QTest::newRow("no query items") << "" << "baz" << false; + QTest::newRow("query item: hello") << "hello=world" << "hello" << true; + QTest::newRow("no query item: world") << "hello=world" << "world" << false; + QTest::newRow("query item: qt") << "hello=world&qt=rocks" << "qt" << true; +} + +void tst_QUrlQuery::old_hasQueryItem() +{ + QFETCH(QString, url); + QFETCH(QString, item); + QFETCH(bool, trueFalse); + + QCOMPARE(QUrlQuery(url).hasQueryItem(item), trueFalse); +} + +#if 0 +// this test doesn't make sense anymore +void tst_QUrl::removeAllEncodedQueryItems_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QByteArray>("key"); + QTest::addColumn<QUrl>("result"); + + QTest::newRow("test1") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b&ccc=c") << QByteArray("bbb") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&ccc=c"); + QTest::newRow("test2") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b&ccc=c") << QByteArray("aaa") << QUrl::fromEncoded("http://qt.nokia.com/foo?bbb=b&ccc=c"); +// QTest::newRow("test3") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b&ccc=c") << QByteArray("ccc") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b"); + QTest::newRow("test4") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b&ccc=c") << QByteArray("b%62b") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&bbb=b&ccc=c"); + QTest::newRow("test5") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&b%62b=b&ccc=c") << QByteArray("b%62b") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&ccc=c"); + QTest::newRow("test6") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&b%62b=b&ccc=c") << QByteArray("bbb") << QUrl::fromEncoded("http://qt.nokia.com/foo?aaa=a&b%62b=b&ccc=c"); +} + +void tst_QUrl::removeAllEncodedQueryItems() +{ + QFETCH(QUrl, url); + QFETCH(QByteArray, key); + QFETCH(QUrl, result); + url.removeAllEncodedQueryItems(key); + QCOMPARE(url, result); +} +#endif + +QTEST_APPLESS_MAIN(tst_QUrlQuery) + +#include "tst_qurlquery.moc" diff --git a/tests/auto/corelib/itemmodels/qabstractitemmodel/qabstractitemmodel.pro b/tests/auto/corelib/itemmodels/qabstractitemmodel/qabstractitemmodel.pro index 9e59251379..8bfe6628da 100644 --- a/tests/auto/corelib/itemmodels/qabstractitemmodel/qabstractitemmodel.pro +++ b/tests/auto/corelib/itemmodels/qabstractitemmodel/qabstractitemmodel.pro @@ -6,3 +6,4 @@ mtdir = ../../../other/modeltest INCLUDEPATH += $$PWD/$${mtdir} SOURCES = tst_qabstractitemmodel.cpp $${mtdir}/dynamictreemodel.cpp $${mtdir}/modeltest.cpp HEADERS = $${mtdir}/dynamictreemodel.h $${mtdir}/modeltest.h +CONFIG += insignificant_test # QTBUG-25325 diff --git a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp index 8d451dbff9..28babdbacb 100644 --- a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp +++ b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp @@ -2135,8 +2135,8 @@ void tst_QAbstractItemModel::testChildrenLayoutsChanged() QVERIFY(p1FirstPersistent.row() == 1); QVERIFY(p1LastPersistent.row() == 0); - QVERIFY(p2FirstPersistent.row() == 9); - QVERIFY(p2LastPersistent.row() == 8); + QCOMPARE(p2FirstPersistent.row(), 9); + QCOMPARE(p2LastPersistent.row(), 8); } } diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp index f4aefb2726..e8f6c29a94 100644 --- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp +++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp @@ -617,6 +617,7 @@ public: bool unregisterTimer(int ) { return false; } bool unregisterTimers(QObject *) { return false; } QList<TimerInfo> registeredTimers(QObject *) const { return QList<TimerInfo>(); } + int remainingTime(int) { return 0; } void wakeUp() {} void interrupt() {} void flush() {} diff --git a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp index 1651d00738..55997a3ca0 100644 --- a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp +++ b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp @@ -114,6 +114,7 @@ public slots: void voidSlotNoParameterNames(bool, int); signals: void voidSignal(); + void voidSignalVoid(void); void voidSignalInt(int voidSignalIntArg); void voidSignalQReal(qreal voidSignalQRealArg); void voidSignalQString(const QString &voidSignalQStringArg); @@ -196,7 +197,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSignal") << QByteArray("voidSignal()") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>()) << (QList<QByteArray>()) << (QList<QByteArray>()) @@ -205,7 +206,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidInvokable") << QByteArray("voidInvokable()") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>()) << (QList<QByteArray>()) << (QList<QByteArray>()) @@ -214,7 +215,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSlot") << QByteArray("voidSlot()") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>()) << (QList<QByteArray>()) << (QList<QByteArray>()) @@ -223,16 +224,25 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject()") << QByteArray("MethodTestObject()") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>()) << (QList<QByteArray>()) << (QList<QByteArray>()) << QMetaMethod::Public << QMetaMethod::Constructor; + QTest::newRow("voidSignalVoid") + << QByteArray("voidSignalVoid()") + << int(QMetaType::Void) << QByteArray("void") + << QList<int>() + << QList<QByteArray>() + << QList<QByteArray>() + << QMetaMethod::Protected + << QMetaMethod::Signal; + QTest::newRow("voidSignalInt") << QByteArray("voidSignalInt(int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("int")) << (QList<QByteArray>() << QByteArray("voidSignalIntArg")) @@ -241,7 +251,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidInvokableInt") << QByteArray("voidInvokableInt(int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("int")) << (QList<QByteArray>() << QByteArray("voidInvokableIntArg")) @@ -250,7 +260,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSlotInt") << QByteArray("voidSlotInt(int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("int")) << (QList<QByteArray>() << QByteArray("voidSlotIntArg")) @@ -259,7 +269,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(int)") << QByteArray("MethodTestObject(int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("int")) << (QList<QByteArray>() << QByteArray("constructorIntArg")) @@ -268,7 +278,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSignalQReal") << QByteArray("voidSignalQReal(qreal)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << qMetaTypeId<qreal>()) << (QList<QByteArray>() << QByteArray("qreal")) << (QList<QByteArray>() << QByteArray("voidSignalQRealArg")) @@ -277,7 +287,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidInvokableQReal") << QByteArray("voidInvokableQReal(qreal)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << qMetaTypeId<qreal>()) << (QList<QByteArray>() << QByteArray("qreal")) << (QList<QByteArray>() << QByteArray("voidInvokableQRealArg")) @@ -286,7 +296,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSlotQReal") << QByteArray("voidSlotQReal(qreal)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << qMetaTypeId<qreal>()) << (QList<QByteArray>() << QByteArray("qreal")) << (QList<QByteArray>() << QByteArray("voidSlotQRealArg")) @@ -295,7 +305,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(qreal)") << QByteArray("MethodTestObject(qreal)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << qMetaTypeId<qreal>()) << (QList<QByteArray>() << QByteArray("qreal")) << (QList<QByteArray>() << QByteArray("constructorQRealArg")) @@ -304,7 +314,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSignalQString") << QByteArray("voidSignalQString(QString)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << int(QMetaType::QString)) << (QList<QByteArray>() << QByteArray("QString")) << (QList<QByteArray>() << QByteArray("voidSignalQStringArg")) @@ -313,7 +323,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidInvokableQString") << QByteArray("voidInvokableQString(QString)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << int(QMetaType::QString)) << (QList<QByteArray>() << QByteArray("QString")) << (QList<QByteArray>() << QByteArray("voidInvokableQStringArg")) @@ -322,7 +332,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSlotQString") << QByteArray("voidSlotQString(QString)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << int(QMetaType::QString)) << (QList<QByteArray>() << QByteArray("QString")) << (QList<QByteArray>() << QByteArray("voidSlotQStringArg")) @@ -331,7 +341,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(QString)") << QByteArray("MethodTestObject(QString)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << int(QMetaType::QString)) << (QList<QByteArray>() << QByteArray("QString")) << (QList<QByteArray>() << QByteArray("constructorQStringArg")) @@ -340,7 +350,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSignalCustomType") << QByteArray("voidSignalCustomType(CustomType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << qMetaTypeId<CustomType>()) << (QList<QByteArray>() << QByteArray("CustomType")) << (QList<QByteArray>() << QByteArray("voidSignalCustomTypeArg")) @@ -349,7 +359,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidInvokableCustomType") << QByteArray("voidInvokableCustomType(CustomType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << qMetaTypeId<CustomType>()) << (QList<QByteArray>() << QByteArray("CustomType")) << (QList<QByteArray>() << QByteArray("voidInvokableCustomTypeArg")) @@ -358,7 +368,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSlotCustomType") << QByteArray("voidSlotCustomType(CustomType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << qMetaTypeId<CustomType>()) << (QList<QByteArray>() << QByteArray("CustomType")) << (QList<QByteArray>() << QByteArray("voidSlotCustomTypeArg")) @@ -367,7 +377,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(CustomType)") << QByteArray("MethodTestObject(CustomType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << qMetaTypeId<CustomType>()) << (QList<QByteArray>() << QByteArray("CustomType")) << (QList<QByteArray>() << QByteArray("constructorCustomTypeArg")) @@ -376,7 +386,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSignalCustomUnregisteredType") << QByteArray("voidSignalCustomUnregisteredType(CustomUnregisteredType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << 0) << (QList<QByteArray>() << QByteArray("CustomUnregisteredType")) << (QList<QByteArray>() << QByteArray("voidSignalCustomUnregisteredTypeArg")) @@ -385,7 +395,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidInvokableCustomUnregisteredType") << QByteArray("voidInvokableCustomUnregisteredType(CustomUnregisteredType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << 0) << (QList<QByteArray>() << QByteArray("CustomUnregisteredType")) << (QList<QByteArray>() << QByteArray("voidInvokableCustomUnregisteredTypeArg")) @@ -394,7 +404,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSlotCustomUnregisteredType") << QByteArray("voidSlotCustomUnregisteredType(CustomUnregisteredType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << 0) << (QList<QByteArray>() << QByteArray("CustomUnregisteredType")) << (QList<QByteArray>() << QByteArray("voidSlotCustomUnregisteredTypeArg")) @@ -403,7 +413,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(CustomUnregisteredType)") << QByteArray("MethodTestObject(CustomUnregisteredType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << 0) << (QList<QByteArray>() << QByteArray("CustomUnregisteredType")) << (QList<QByteArray>() << QByteArray("constructorCustomUnregisteredTypeArg")) @@ -536,7 +546,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)") << QByteArray("MethodTestObject(bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << parameterTypes << parameterTypeNames << parameterNames << QMetaMethod::Public << QMetaMethod::Constructor; @@ -544,7 +554,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSignalNoParameterNames") << QByteArray("voidSignalNoParameterNames(bool,int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << int(QMetaType::Bool) << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("bool") << QByteArray("int")) << (QList<QByteArray>() << QByteArray("") << QByteArray("")) @@ -553,7 +563,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidInvokableNoParameterNames") << QByteArray("voidInvokableNoParameterNames(bool,int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << int(QMetaType::Bool) << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("bool") << QByteArray("int")) << (QList<QByteArray>() << QByteArray("") << QByteArray("")) @@ -562,7 +572,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("voidSlotNoParameterNames") << QByteArray("voidSlotNoParameterNames(bool,int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::Void) << QByteArray("void") << (QList<int>() << int(QMetaType::Bool) << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("bool") << QByteArray("int")) << (QList<QByteArray>() << QByteArray("") << QByteArray("")) @@ -571,7 +581,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(bool,int)") << QByteArray("MethodTestObject(bool,int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << int(QMetaType::Bool) << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("bool") << QByteArray("int")) << (QList<QByteArray>() << QByteArray("") << QByteArray("")) @@ -603,15 +613,51 @@ void tst_QMetaMethod::method() QCOMPARE(method.methodType(), methodType); QCOMPARE(method.access(), access); - QCOMPARE(method.signature(), signature.constData()); + QVERIFY(!method.methodSignature().isEmpty()); + if (method.methodSignature() != signature) { + // QMetaMethod should always produce a semantically equivalent signature + int signatureIndex = (methodType == QMetaMethod::Constructor) + ? mo->indexOfConstructor(method.methodSignature()) + : mo->indexOfMethod(method.methodSignature()); + QCOMPARE(signatureIndex, index); + } - QCOMPARE(method.tag(), ""); + QByteArray computedName = signature.left(signature.indexOf('(')); + QCOMPARE(method.name(), computedName); - QCOMPARE(method.typeName(), returnTypeName.constData()); - QCOMPARE(QMetaType::type(method.typeName()), returnType); + QCOMPARE(method.tag(), ""); + QCOMPARE(method.returnType(), returnType); + QVERIFY(method.typeName() != 0); + if (QByteArray(method.typeName()) != returnTypeName) { + // QMetaMethod should always produce a semantically equivalent typename + QCOMPARE(QMetaType::type(method.typeName()), QMetaType::type(returnTypeName)); + } - QCOMPARE(method.parameterTypes(), parameterTypeNames); + if (method.parameterTypes() != parameterTypeNames) { + // QMetaMethod should always produce semantically equivalent typenames + QList<QByteArray> actualTypeNames = method.parameterTypes(); + QCOMPARE(actualTypeNames.size(), parameterTypeNames.size()); + for (int i = 0; i < parameterTypeNames.size(); ++i) { + QCOMPARE(QMetaType::type(actualTypeNames.at(i)), + QMetaType::type(parameterTypeNames.at(i))); + } + } QCOMPARE(method.parameterNames(), parameterNames); + + QCOMPARE(method.parameterCount(), parameterTypes.size()); + for (int i = 0; i < parameterTypes.size(); ++i) + QCOMPARE(method.parameterType(i), parameterTypes.at(i)); + + { + QVector<int> actualParameterTypes(parameterTypes.size()); + method.getParameterTypes(actualParameterTypes.data()); + for (int i = 0; i < parameterTypes.size(); ++i) + QCOMPARE(actualParameterTypes.at(i), parameterTypes.at(i)); + } + + // Bogus indexes + QCOMPARE(method.parameterType(-1), 0); + QCOMPARE(method.parameterType(parameterTypes.size()), 0); } void tst_QMetaMethod::invalidMethod() diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 09fd0a7adb..5cf28b5141 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -816,9 +816,22 @@ void tst_QMetaObject::normalizedSignature_data() QTest::newRow("function") << "void foo()" << "void foo()"; QTest::newRow("spaces") << " void foo( ) " << "void foo()"; + QTest::newRow("void") << "void foo(void)" << "void foo()"; + QTest::newRow("void spaces") << "void foo( void )" << "void foo()"; + QTest::newRow("void*") << "void foo(void*)" << "void foo(void*)"; + QTest::newRow("void* spaces") << "void foo( void * )" << "void foo(void*)"; + QTest::newRow("function ptr") << "void foo(void(*)(void))" << "void foo(void(*)())"; + QTest::newRow("function ptr spaces") << "void foo( void ( * ) ( void ))" << "void foo(void(*)())"; + QTest::newRow("function ptr void*") << "void foo(void(*)(void*))" << "void foo(void(*)(void*))"; + QTest::newRow("function ptr void* spaces") << "void foo( void ( * ) ( void * ))" << "void foo(void(*)(void*))"; QTest::newRow("template args") << " void foo( QMap<a, a>, QList<b>) " << "void foo(QMap<a,a>,QList<b>)"; + QTest::newRow("void template args") << " void foo( Foo<void>, Bar<void> ) " + << "void foo(Foo<void>,Bar<void>)"; + QTest::newRow("void* template args") << " void foo( Foo<void*>, Bar<void *> ) " + << "void foo(Foo<void*>,Bar<void*>)"; QTest::newRow("rettype") << "QList<int, int> foo()" << "QList<int,int>foo()"; + QTest::newRow("rettype void template") << "Foo<void> foo()" << "Foo<void>foo()"; QTest::newRow("const rettype") << "const QString *foo()" << "const QString*foo()"; QTest::newRow("const ref") << "const QString &foo()" << "const QString&foo()"; QTest::newRow("reference") << "QString &foo()" << "QString&foo()"; @@ -877,6 +890,7 @@ void tst_QMetaObject::normalizedType_data() QTest::newRow("struct") << "const struct foo*" << "const foo*"; QTest::newRow("struct2") << "struct foo const*" << "const foo*"; QTest::newRow("enum") << "enum foo" << "foo"; + QTest::newRow("void") << "void" << "void"; } void tst_QMetaObject::normalizedType() @@ -978,25 +992,25 @@ void tst_QMetaObject::propertyNotify() QVERIFY(prop.isValid()); QVERIFY(prop.hasNotifySignal()); QMetaMethod signal = prop.notifySignal(); - QCOMPARE(signal.signature(), "value6Changed()"); + QCOMPARE(signal.methodSignature(), QByteArray("value6Changed()")); prop = mo->property(mo->indexOfProperty("value7")); QVERIFY(prop.isValid()); QVERIFY(prop.hasNotifySignal()); signal = prop.notifySignal(); - QCOMPARE(signal.signature(), "value7Changed(QString)"); + QCOMPARE(signal.methodSignature(), QByteArray("value7Changed(QString)")); prop = mo->property(mo->indexOfProperty("value8")); QVERIFY(prop.isValid()); QVERIFY(!prop.hasNotifySignal()); signal = prop.notifySignal(); - QCOMPARE(signal.signature(), (const char *)0); + QCOMPARE(signal.methodSignature(), QByteArray()); prop = mo->property(mo->indexOfProperty("value")); QVERIFY(prop.isValid()); QVERIFY(!prop.hasNotifySignal()); signal = prop.notifySignal(); - QCOMPARE(signal.signature(), (const char *)0); + QCOMPARE(signal.methodSignature(), QByteArray()); } void tst_QMetaObject::propertyConstant() @@ -1114,7 +1128,7 @@ void tst_QMetaObject::indexOfMethod() QFETCH(bool, isSignal); int idx = object->metaObject()->indexOfMethod(name); QVERIFY(idx >= 0); - QCOMPARE(object->metaObject()->method(idx).signature(), name.constData()); + QCOMPARE(object->metaObject()->method(idx).methodSignature(), name); QCOMPARE(object->metaObject()->indexOfSlot(name), isSignal ? -1 : idx); QCOMPARE(object->metaObject()->indexOfSignal(name), !isSignal ? -1 : idx); } diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 97b14a374e..4b0a64ab54 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -217,6 +217,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(nullMethod.signature(), QByteArray()); QVERIFY(nullMethod.methodType() == QMetaMethod::Method); QVERIFY(nullMethod.returnType().isEmpty()); + QVERIFY(nullMethod.parameterTypes().isEmpty()); QVERIFY(nullMethod.parameterNames().isEmpty()); QVERIFY(nullMethod.tag().isEmpty()); QVERIFY(nullMethod.access() == QMetaMethod::Public); @@ -228,7 +229,8 @@ void tst_QMetaObjectBuilder::method() QMetaMethodBuilder method1 = builder.addMethod("foo(const QString&, int)"); QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Method); - QVERIFY(method1.returnType().isEmpty()); + QCOMPARE(method1.returnType(), QByteArray("void")); + QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QVERIFY(method1.parameterNames().isEmpty()); QVERIFY(method1.tag().isEmpty()); QVERIFY(method1.access() == QMetaMethod::Public); @@ -242,6 +244,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("int")); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Public); @@ -267,6 +270,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Method); QCOMPARE(method1.returnType(), QByteArray("int")); + QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QCOMPARE(method1.parameterNames(), QList<QByteArray>() << "a" << "b"); QCOMPARE(method1.tag(), QByteArray("tag")); QVERIFY(method1.access() == QMetaMethod::Private); @@ -276,6 +280,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("int")); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Public); @@ -296,6 +301,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Method); QCOMPARE(method1.returnType(), QByteArray("int")); + QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QCOMPARE(method1.parameterNames(), QList<QByteArray>() << "a" << "b"); QCOMPARE(method1.tag(), QByteArray("tag")); QVERIFY(method1.access() == QMetaMethod::Private); @@ -305,6 +311,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("QString")); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QCOMPARE(method2.parameterNames(), QList<QByteArray>() << "c"); QCOMPARE(method2.tag(), QByteArray("Q_FOO")); QVERIFY(method2.access() == QMetaMethod::Protected); @@ -320,6 +327,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("QString")); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QCOMPARE(method2.parameterNames(), QList<QByteArray>() << "c"); QCOMPARE(method2.tag(), QByteArray("Q_FOO")); QVERIFY(method2.access() == QMetaMethod::Protected); @@ -346,7 +354,8 @@ void tst_QMetaObjectBuilder::slot() QMetaMethodBuilder method1 = builder.addSlot("foo(const QString&, int)"); QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Slot); - QVERIFY(method1.returnType().isEmpty()); + QCOMPARE(method1.returnType(), QByteArray("void")); + QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QVERIFY(method1.parameterNames().isEmpty()); QVERIFY(method1.tag().isEmpty()); QVERIFY(method1.access() == QMetaMethod::Public); @@ -358,7 +367,8 @@ void tst_QMetaObjectBuilder::slot() QMetaMethodBuilder method2 = builder.addSlot("bar(QString)"); QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Slot); - QVERIFY(method2.returnType().isEmpty()); + QCOMPARE(method2.returnType(), QByteArray("void")); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Public); @@ -383,7 +393,8 @@ void tst_QMetaObjectBuilder::signal() QMetaMethodBuilder method1 = builder.addSignal("foo(const QString&, int)"); QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Signal); - QVERIFY(method1.returnType().isEmpty()); + QCOMPARE(method1.returnType(), QByteArray("void")); + QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QVERIFY(method1.parameterNames().isEmpty()); QVERIFY(method1.tag().isEmpty()); QVERIFY(method1.access() == QMetaMethod::Protected); @@ -395,7 +406,8 @@ void tst_QMetaObjectBuilder::signal() QMetaMethodBuilder method2 = builder.addSignal("bar(QString)"); QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Signal); - QVERIFY(method2.returnType().isEmpty()); + QCOMPARE(method2.returnType(), QByteArray("void")); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Protected); @@ -421,6 +433,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); QVERIFY(ctor1.returnType().isEmpty()); + QCOMPARE(ctor1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QVERIFY(ctor1.parameterNames().isEmpty()); QVERIFY(ctor1.tag().isEmpty()); QVERIFY(ctor1.access() == QMetaMethod::Public); @@ -433,6 +446,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QVERIFY(ctor2.returnType().isEmpty()); + QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(ctor2.parameterNames().isEmpty()); QVERIFY(ctor2.tag().isEmpty()); QVERIFY(ctor2.access() == QMetaMethod::Public); @@ -458,6 +472,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor1.returnType(), QByteArray("int")); + QCOMPARE(ctor1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QCOMPARE(ctor1.parameterNames(), QList<QByteArray>() << "a" << "b"); QCOMPARE(ctor1.tag(), QByteArray("tag")); QVERIFY(ctor1.access() == QMetaMethod::Private); @@ -466,6 +481,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QVERIFY(ctor2.returnType().isEmpty()); + QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(ctor2.parameterNames().isEmpty()); QVERIFY(ctor2.tag().isEmpty()); QVERIFY(ctor2.access() == QMetaMethod::Public); @@ -484,6 +500,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor1.returnType(), QByteArray("int")); + QCOMPARE(ctor1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QCOMPARE(ctor1.parameterNames(), QList<QByteArray>() << "a" << "b"); QCOMPARE(ctor1.tag(), QByteArray("tag")); QVERIFY(ctor1.access() == QMetaMethod::Private); @@ -492,6 +509,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor2.returnType(), QByteArray("QString")); + QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString"); QCOMPARE(ctor2.parameterNames(), QList<QByteArray>() << "c"); QCOMPARE(ctor2.tag(), QByteArray("Q_FOO")); QVERIFY(ctor2.access() == QMetaMethod::Protected); @@ -506,6 +524,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor2.returnType(), QByteArray("QString")); + QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString"); QCOMPARE(ctor2.parameterNames(), QList<QByteArray>() << "c"); QCOMPARE(ctor2.tag(), QByteArray("Q_FOO")); QVERIFY(ctor2.access() == QMetaMethod::Protected); @@ -525,6 +544,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(prototypeConstructor.signature(), QByteArray("SomethingOfEverything()")); QVERIFY(prototypeConstructor.methodType() == QMetaMethod::Constructor); QCOMPARE(prototypeConstructor.returnType(), QByteArray()); + QVERIFY(prototypeConstructor.parameterTypes().isEmpty()); QVERIFY(prototypeConstructor.access() == QMetaMethod::Public); QCOMPARE(prototypeConstructor.index(), 1); @@ -754,7 +774,7 @@ void tst_QMetaObjectBuilder::variantProperty() QCOMPARE(QMetaType::Type(prop.userType()), QMetaType::QVariant); QCOMPARE(QByteArray(prop.typeName()), QByteArray("QVariant")); - qFree(meta); + free(meta); } void tst_QMetaObjectBuilder::notifySignal() @@ -1161,12 +1181,15 @@ bool tst_QMetaObjectBuilder::checkForSideEffects static bool sameMethod(const QMetaMethod& method1, const QMetaMethod& method2) { - if (QByteArray(method1.signature()) != QByteArray(method2.signature())) + if (method1.methodSignature() != method2.methodSignature()) return false; if (QByteArray(method1.typeName()) != QByteArray(method2.typeName())) return false; + if (method1.parameterTypes() != method2.parameterTypes()) + return false; + if (method1.parameterNames() != method2.parameterNames()) return false; @@ -1391,7 +1414,7 @@ TestObject::TestObject(QObject *parent) TestObject::~TestObject() { - qFree(m_metaObject); + free(m_metaObject); } QMetaObject *TestObject::buildMetaObject() @@ -1466,7 +1489,7 @@ void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break; default: { QMetaMethod ctor = _o->metaObject()->constructor(_id); - qFatal("You forgot to add a case for CreateInstance %s", ctor.signature()); + qFatal("You forgot to add a case for CreateInstance %s", ctor.methodSignature().constData()); } } } else if (_c == QMetaObject::InvokeMetaMethod) { @@ -1478,7 +1501,7 @@ void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, case 2: *reinterpret_cast<QVariantList(*)>(_a[0]) = _t->listInvokableQRealQString(*reinterpret_cast<qreal(*)>(_a[1]), *reinterpret_cast<QString(*)>(_a[2])); break; default: { QMetaMethod method = _o->metaObject()->method(_o->metaObject()->methodOffset() + _id); - qFatal("You forgot to add a case for InvokeMetaMethod %s", method.signature()); + qFatal("You forgot to add a case for InvokeMetaMethod %s", method.methodSignature().constData()); } } } else if (_c == QMetaObject::IndexOfMethod) { diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 09bd9fe445..4eb26928ac 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -97,16 +97,36 @@ private slots: void isEnum(); void registerStreamBuiltin(); void automaticTemplateRegistration(); + void saveAndLoadBuiltin_data(); + void saveAndLoadBuiltin(); + void saveAndLoadCustom(); }; struct Foo { int i; }; + +class CustomQObject : public QObject +{ + Q_OBJECT +public: + CustomQObject(QObject *parent = 0) + : QObject(parent) + { + } +}; + +class CustomNonQObject {}; + void tst_QMetaType::defined() { QCOMPARE(int(QMetaTypeId2<QString>::Defined), 1); QCOMPARE(int(QMetaTypeId2<Foo>::Defined), 0); QCOMPARE(int(QMetaTypeId2<void*>::Defined), 1); QCOMPARE(int(QMetaTypeId2<int*>::Defined), 0); + QVERIFY(QMetaTypeId2<CustomQObject*>::Defined); + QVERIFY(!QMetaTypeId2<CustomQObject>::Defined); + QVERIFY(!QMetaTypeId2<CustomNonQObject>::Defined); + QVERIFY(!QMetaTypeId2<CustomNonQObject*>::Defined); } struct Bar @@ -312,6 +332,7 @@ void tst_QMetaType::typeName_data() QTest::addColumn<QString>("aTypeName"); QT_FOR_EACH_STATIC_TYPE(TYPENAME_DATA) + QTest::newRow("QMetaType::UnknownType") << QMetaType::UnknownType << static_cast<const char*>(0); QTest::newRow("Whity<double>") << static_cast<QMetaType::Type>(::qMetaTypeId<Whity<double> >()) << QString::fromLatin1("Whity<double>"); QTest::newRow("Whity<int>") << static_cast<QMetaType::Type>(::qMetaTypeId<Whity<int> >()) << QString::fromLatin1("Whity<int>"); @@ -510,6 +531,46 @@ template<> struct TestValueFactory<QMetaType::QRegExp> { #endif } }; +template<> struct TestValueFactory<QMetaType::QRegularExpression> { + static QRegularExpression *create() + { +#ifndef QT_NO_REGEXP + return new QRegularExpression("abc.*def"); +#else + return 0; +#endif + } +}; +template<> struct TestValueFactory<QMetaType::QJsonValue> { + static QJsonValue *create() { return new QJsonValue(123.); } +}; +template<> struct TestValueFactory<QMetaType::QJsonObject> { + static QJsonObject *create() { + QJsonObject *o = new QJsonObject(); + o->insert("a", 123.); + o->insert("b", true); + o->insert("c", QJsonValue::Null); + o->insert("d", QLatin1String("ciao")); + return o; + } +}; +template<> struct TestValueFactory<QMetaType::QJsonArray> { + static QJsonArray *create() { + QJsonArray *a = new QJsonArray(); + a->append(123.); + a->append(true); + a->append(QJsonValue::Null); + a->append(QLatin1String("ciao")); + return a; + } +}; +template<> struct TestValueFactory<QMetaType::QJsonDocument> { + static QJsonDocument *create() { + return new QJsonDocument( + QJsonDocument::fromJson("{ 'foo': 123, 'bar': [true, null, 'ciao'] }") + ); + } +}; template<> struct TestValueFactory<QMetaType::QVariant> { static QVariant *create() { return new QVariant(QStringList(QStringList() << "Q" << "t")); } }; @@ -630,6 +691,8 @@ void tst_QMetaType::sizeOf_data() { QTest::addColumn<QMetaType::Type>("type"); QTest::addColumn<size_t>("size"); + + QTest::newRow("QMetaType::UnknownType") << QMetaType::UnknownType << size_t(0); #define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \ QTest::newRow(#RealType) << QMetaType::MetaTypeName << size_t(QTypeInfo<RealType>::sizeOf); FOR_EACH_CORE_METATYPE(ADD_METATYPE_TEST_ROW) @@ -805,41 +868,15 @@ void tst_QMetaType::construct_data() create_data(); } -#ifndef Q_ALIGNOF -template<uint N> -struct RoundToNextHighestPowerOfTwo -{ -private: - enum { V1 = N-1 }; - enum { V2 = V1 | (V1 >> 1) }; - enum { V3 = V2 | (V2 >> 2) }; - enum { V4 = V3 | (V3 >> 4) }; - enum { V5 = V4 | (V4 >> 8) }; - enum { V6 = V5 | (V5 >> 16) }; -public: - enum { Value = V6 + 1 }; -}; -#endif - -template<class T> -struct TypeAlignment -{ -#ifdef Q_ALIGNOF - enum { Value = Q_ALIGNOF(T) }; -#else - enum { Value = RoundToNextHighestPowerOfTwo<sizeof(T)>::Value }; -#endif -}; - template<int ID> static void testConstructHelper() { typedef typename MetaEnumToType<ID>::Type Type; QMetaType info(ID); int size = info.sizeOf(); - void *storage1 = qMallocAligned(size, TypeAlignment<Type>::Value); + void *storage1 = qMallocAligned(size, Q_ALIGNOF(Type)); void *actual1 = QMetaType::construct(ID, storage1, /*copy=*/0); - void *storage2 = qMallocAligned(size, TypeAlignment<Type>::Value); + void *storage2 = qMallocAligned(size, Q_ALIGNOF(Type)); void *actual2 = info.construct(storage2, /*copy=*/0); QCOMPARE(actual1, storage1); QCOMPARE(actual2, storage2); @@ -908,9 +945,9 @@ static void testConstructCopyHelper() QMetaType info(ID); int size = QMetaType::sizeOf(ID); QCOMPARE(info.sizeOf(), size); - void *storage1 = qMallocAligned(size, TypeAlignment<Type>::Value); + void *storage1 = qMallocAligned(size, Q_ALIGNOF(Type)); void *actual1 = QMetaType::construct(ID, storage1, expected); - void *storage2 = qMallocAligned(size, TypeAlignment<Type>::Value); + void *storage2 = qMallocAligned(size, Q_ALIGNOF(Type)); void *actual2 = info.construct(storage2, expected); QCOMPARE(actual1, storage1); QCOMPARE(actual2, storage2); @@ -1052,6 +1089,7 @@ void tst_QMetaType::isRegistered_data() QTest::newRow("-1") << -1 << false; QTest::newRow("-42") << -42 << false; QTest::newRow("IsRegisteredDummyType + 1") << (dummyTypeId + 1) << false; + QTest::newRow("QMetaType::UnknownType") << int(QMetaType::UnknownType) << false; } void tst_QMetaType::isRegistered() @@ -1291,6 +1329,133 @@ void tst_QMetaType::automaticTemplateRegistration() } } +template <typename T> +struct StreamingTraits +{ + enum { isStreamable = 1 }; // Streamable by default +}; + +// Non-streamable types + +#define DECLARE_NONSTREAMABLE(Type) \ + template<> struct StreamingTraits<Type> { enum { isStreamable = 0 }; }; + +DECLARE_NONSTREAMABLE(void) +DECLARE_NONSTREAMABLE(void*) +DECLARE_NONSTREAMABLE(QModelIndex) +DECLARE_NONSTREAMABLE(QJsonValue) +DECLARE_NONSTREAMABLE(QJsonObject) +DECLARE_NONSTREAMABLE(QJsonArray) +DECLARE_NONSTREAMABLE(QJsonDocument) +DECLARE_NONSTREAMABLE(QObject*) +DECLARE_NONSTREAMABLE(QWidget*) + +#define DECLARE_GUI_CLASS_NONSTREAMABLE(MetaTypeName, MetaTypeId, RealType) \ + DECLARE_NONSTREAMABLE(RealType) +QT_FOR_EACH_STATIC_GUI_CLASS(DECLARE_GUI_CLASS_NONSTREAMABLE) +#undef DECLARE_GUI_CLASS_NONSTREAMABLE + +#define DECLARE_WIDGETS_CLASS_NONSTREAMABLE(MetaTypeName, MetaTypeId, RealType) \ + DECLARE_NONSTREAMABLE(RealType) +QT_FOR_EACH_STATIC_WIDGETS_CLASS(DECLARE_WIDGETS_CLASS_NONSTREAMABLE) +#undef DECLARE_WIDGETS_CLASS_NONSTREAMABLE + +#undef DECLARE_NONSTREAMABLE + +void tst_QMetaType::saveAndLoadBuiltin_data() +{ + QTest::addColumn<int>("type"); + QTest::addColumn<bool>("isStreamable"); + +#define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \ + QTest::newRow(#RealType) << MetaTypeId << bool(StreamingTraits<RealType>::isStreamable); + QT_FOR_EACH_STATIC_TYPE(ADD_METATYPE_TEST_ROW) +#undef ADD_METATYPE_TEST_ROW +} + +void tst_QMetaType::saveAndLoadBuiltin() +{ + QFETCH(int, type); + QFETCH(bool, isStreamable); + + void *value = QMetaType::create(type); + + QByteArray ba; + QDataStream stream(&ba, QIODevice::ReadWrite); + QCOMPARE(QMetaType::save(stream, type, value), isStreamable); + QCOMPARE(stream.status(), QDataStream::Ok); + + if (isStreamable) { + QVERIFY(QMetaType::load(stream, type, value)); // Hmmm, shouldn't it return false? + QCOMPARE(stream.status(), QDataStream::ReadPastEnd); + } + + stream.device()->seek(0); + stream.resetStatus(); + QCOMPARE(QMetaType::load(stream, type, value), isStreamable); + QCOMPARE(stream.status(), QDataStream::Ok); + + if (isStreamable) { + QVERIFY(QMetaType::load(stream, type, value)); // Hmmm, shouldn't it return false? + QCOMPARE(stream.status(), QDataStream::ReadPastEnd); + } + + QMetaType::destroy(type, value); +} + +struct CustomStreamableType +{ + int a; +}; +Q_DECLARE_METATYPE(CustomStreamableType) + +QDataStream &operator<<(QDataStream &out, const CustomStreamableType &t) +{ + out << t.a; return out; +} + +QDataStream &operator>>(QDataStream &in, CustomStreamableType &t) +{ + int a; + in >> a; + if (in.status() == QDataStream::Ok) + t.a = a; + return in; +} + +void tst_QMetaType::saveAndLoadCustom() +{ + CustomStreamableType t; + t.a = 123; + + int id = ::qMetaTypeId<CustomStreamableType>(); + QByteArray ba; + QDataStream stream(&ba, QIODevice::ReadWrite); + QVERIFY(!QMetaType::save(stream, id, &t)); + QCOMPARE(stream.status(), QDataStream::Ok); + QVERIFY(!QMetaType::load(stream, id, &t)); + QCOMPARE(stream.status(), QDataStream::Ok); + + qRegisterMetaTypeStreamOperators<CustomStreamableType>("CustomStreamableType"); + QVERIFY(QMetaType::save(stream, id, &t)); + QCOMPARE(stream.status(), QDataStream::Ok); + + CustomStreamableType t2; + t2.a = -1; + QVERIFY(QMetaType::load(stream, id, &t2)); // Hmmm, shouldn't it return false? + QCOMPARE(stream.status(), QDataStream::ReadPastEnd); + QCOMPARE(t2.a, -1); + + stream.device()->seek(0); + stream.resetStatus(); + QVERIFY(QMetaType::load(stream, id, &t2)); + QCOMPARE(stream.status(), QDataStream::Ok); + QCOMPARE(t2.a, t.a); + + QVERIFY(QMetaType::load(stream, id, &t2)); // Hmmm, shouldn't it return false? + QCOMPARE(stream.status(), QDataStream::ReadPastEnd); +} + // Compile-time test, it should be possible to register function pointer types class Undefined; diff --git a/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp b/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp deleted file mode 100644 index 2d180b88ea..0000000000 --- a/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** Meta object code from reading C++ file 'oldnormalizeobject.h' -** -** Created: Wed Nov 18 11:43:05 2009 -** by: The Qt Meta Object Compiler version 62 (Qt 4.6.0) -** -*****************************************************************************/ - -// Yhis file was generated from moc version 4.6 to test binary compatibility -// It should *not* be generated by the current moc - -#include "oldnormalizeobject.h" - -QT_BEGIN_MOC_NAMESPACE -static const uint qt_meta_data_OldNormalizeObject[] = { - - // content: - 4, // revision - 0, // classname - 0, 0, // classinfo - 6, 14, // methods - 0, 0, // properties - 0, 0, // enums/sets - 0, 0, // constructors - 0, // flags - 3, // signalCount - - // signals: signature, parameters, type, tag, flags - 24, 20, 19, 19, 0x05, - 57, 20, 19, 19, 0x05, - 100, 20, 19, 19, 0x05, - - // slots: signature, parameters, type, tag, flags - 149, 20, 19, 19, 0x0a, - 180, 20, 19, 19, 0x0a, - 221, 20, 19, 19, 0x0a, - - 0 // eod -}; - -static const char qt_meta_stringdata_OldNormalizeObject[] = { - "OldNormalizeObject\0\0ref\0" - "typeRefSignal(Template<Class&>&)\0" - "constTypeRefSignal(Template<const Class&>)\0" - "typeConstRefSignal(Template<const Class&>const&)\0" - "typeRefSlot(Template<Class&>&)\0" - "constTypeRefSlot(Template<const Class&>)\0" - "typeConstRefSlot(Template<const Class&>const&)\0" -}; - -const QMetaObject OldNormalizeObject::staticMetaObject = { - { &QObject::staticMetaObject, qt_meta_stringdata_OldNormalizeObject, - qt_meta_data_OldNormalizeObject, 0 } -}; - -#ifdef Q_NO_DATA_RELOCATION -const QMetaObject &OldNormalizeObject::getStaticMetaObject() { return staticMetaObject; } -#endif //Q_NO_DATA_RELOCATION - -const QMetaObject *OldNormalizeObject::metaObject() const -{ - return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; -} - -void *OldNormalizeObject::qt_metacast(const char *_clname) -{ - if (!_clname) return 0; - if (!strcmp(_clname, qt_meta_stringdata_OldNormalizeObject)) - return static_cast<void*>(const_cast< OldNormalizeObject*>(this)); - return QObject::qt_metacast(_clname); -} - -int OldNormalizeObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a) -{ - _id = QObject::qt_metacall(_c, _id, _a); - if (_id < 0) - return _id; - if (_c == QMetaObject::InvokeMetaMethod) { - switch (_id) { - case 0: typeRefSignal((*reinterpret_cast< Template<Class&>(*)>(_a[1]))); break; - case 1: constTypeRefSignal((*reinterpret_cast< const Template<const Class&>(*)>(_a[1]))); break; - case 2: typeConstRefSignal((*reinterpret_cast< Template<const Class&>const(*)>(_a[1]))); break; - case 3: typeRefSlot((*reinterpret_cast< Template<Class&>(*)>(_a[1]))); break; - case 4: constTypeRefSlot((*reinterpret_cast< const Template<const Class&>(*)>(_a[1]))); break; - case 5: typeConstRefSlot((*reinterpret_cast< Template<const Class&>const(*)>(_a[1]))); break; - default: ; - } - _id -= 6; - } - return _id; -} - -// SIGNAL 0 -void OldNormalizeObject::typeRefSignal(Template<Class&> & _t1) -{ - void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; - QMetaObject::activate(this, &staticMetaObject, 0, _a); -} - -// SIGNAL 1 -void OldNormalizeObject::constTypeRefSignal(const Template<const Class&> & _t1) -{ - void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; - QMetaObject::activate(this, &staticMetaObject, 1, _a); -} - -// SIGNAL 2 -void OldNormalizeObject::typeConstRefSignal(Template<Class const&> const & _t1) -{ - void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; - QMetaObject::activate(this, &staticMetaObject, 2, _a); -} -QT_END_MOC_NAMESPACE diff --git a/tests/auto/corelib/kernel/qobject/oldnormalizeobject.h b/tests/auto/corelib/kernel/qobject/oldnormalizeobject.h deleted file mode 100644 index f73027707a..0000000000 --- a/tests/auto/corelib/kernel/qobject/oldnormalizeobject.h +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the QtTest module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef OLDNORMALIZEOBJECT_H -#define OLDNORMALIZEOBJECT_H - -#include <QObject> - -struct Struct; -class Class; -template <typename T> class Template; - -// An object with old moc output that incorrectly normalizes 'T<C> const &' in the function -// signatures -class OldNormalizeObject : public QObject -{ - /* tmake ignore Q_OBJECT */ - Q_OBJECT - -signals: - void typeRefSignal(Template<Class &> &ref); - void constTypeRefSignal(const Template<const Class &> &ref); - void typeConstRefSignal(Template<Class const &> const &ref); - -public slots: - void typeRefSlot(Template<Class &> &) {} - void constTypeRefSlot(const Template<const Class &> &) {} - void typeConstRefSlot(Template<Class const &> const &) {} -}; - -#endif // OLDNORMALIZEOBJECT_H diff --git a/tests/auto/corelib/kernel/qobject/test/test.pro b/tests/auto/corelib/kernel/qobject/test/test.pro index c08f910f8f..824223db15 100644 --- a/tests/auto/corelib/kernel/qobject/test/test.pro +++ b/tests/auto/corelib/kernel/qobject/test/test.pro @@ -3,9 +3,5 @@ TARGET = ../tst_qobject QT = core-private network testlib SOURCES = ../tst_qobject.cpp -# this is here for a reason, moc_oldnormalizedobject.cpp is not auto-generated, it was generated by -# moc from Qt 4.6, and should *not* be generated by the current moc -SOURCES += ../moc_oldnormalizeobject.cpp - load(testcase) # for target.path and installTestHelperApp() installTestHelperApp("../signalbug/signalbug",signalbug,signalbug) diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index f7f13e65ea..f429af7ce2 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -45,6 +45,7 @@ #include <qpointer.h> #include <qtimer.h> #include <qregexp.h> +#include <qregularexpression.h> #include <qmetaobject.h> #include <qvariant.h> #include <QTcpServer> @@ -164,6 +165,7 @@ signals: void signal3(); void signal4(); QT_MOC_COMPAT void signal5(); + void signal6(void); public slots: void aPublicSlot() { aPublicSlotCalled++; } @@ -656,6 +658,26 @@ void tst_QObject::findChildren() l = qFindChildren<QObject*>(&o, QRegExp("harry")); QCOMPARE(l.size(), 0); + l = o.findChildren<QObject*>(QRegularExpression("o.*")); + QCOMPARE(l.size(), 5); + QVERIFY(l.contains(&o1)); + QVERIFY(l.contains(&o2)); + QVERIFY(l.contains(&o11)); + QVERIFY(l.contains(&o12)); + QVERIFY(l.contains(&o111)); + l = o.findChildren<QObject*>(QRegularExpression("t.*")); + QCOMPARE(l.size(), 2); + QVERIFY(l.contains(&t1)); + QVERIFY(l.contains(&t121)); + tl = o.findChildren<QTimer*>(QRegularExpression(".*")); + QCOMPARE(tl.size(), 3); + QVERIFY(tl.contains(&t1)); + QVERIFY(tl.contains(&t121)); + tl = o.findChildren<QTimer*>(QRegularExpression("o.*")); + QCOMPARE(tl.size(), 0); + l = o.findChildren<QObject*>(QRegularExpression("harry")); + QCOMPARE(l.size(), 0); + // empty and null string check op = qFindChild<QObject*>(&o); QCOMPARE(op, &o1); @@ -803,6 +825,8 @@ void tst_QObject::connectDisconnectNotify_data() QTest::newRow("combo2") << SIGNAL( signal2(void) ) << SLOT( slot2( ) ); QTest::newRow("combo3") << SIGNAL( signal3( ) ) << SLOT( slot3(void) ); QTest::newRow("combo4") << SIGNAL( signal4( void ) )<< SLOT( slot4( void ) ); + QTest::newRow("combo5") << SIGNAL( signal6( void ) ) << SLOT( slot4() ); + QTest::newRow("combo6") << SIGNAL( signal6() ) << SLOT( slot4() ); } void tst_QObject::connectDisconnectNotify() @@ -1793,56 +1817,56 @@ void tst_QObject::metamethod() QMetaMethod m; m = mobj->method(mobj->indexOfMethod("invoke1()")); - QVERIFY(QByteArray(m.signature()) == "invoke1()"); + QVERIFY(m.methodSignature() == "invoke1()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Public); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("sinvoke1()")); - QVERIFY(QByteArray(m.signature()) == "sinvoke1()"); + QVERIFY(m.methodSignature() == "sinvoke1()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Public); QVERIFY((m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("invoke2()")); - QVERIFY(QByteArray(m.signature()) == "invoke2()"); + QVERIFY(m.methodSignature() == "invoke2()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Protected); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY((m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("sinvoke2()")); - QVERIFY(QByteArray(m.signature()) == "sinvoke2()"); + QVERIFY(m.methodSignature() == "sinvoke2()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Protected); QVERIFY((m.attributes() & QMetaMethod::Scriptable)); QVERIFY((m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("invoke3()")); - QVERIFY(QByteArray(m.signature()) == "invoke3()"); + QVERIFY(m.methodSignature() == "invoke3()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Private); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("sinvoke3()")); - QVERIFY(QByteArray(m.signature()) == "sinvoke3()"); + QVERIFY(m.methodSignature() == "sinvoke3()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Private); QVERIFY((m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("signal5()")); - QVERIFY(QByteArray(m.signature()) == "signal5()"); + QVERIFY(m.methodSignature() == "signal5()"); QVERIFY(m.methodType() == QMetaMethod::Signal); QVERIFY(m.access() == QMetaMethod::Protected); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY((m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("aPublicSlot()")); - QVERIFY(QByteArray(m.signature()) == "aPublicSlot()"); + QVERIFY(m.methodSignature() == "aPublicSlot()"); QVERIFY(m.methodType() == QMetaMethod::Slot); QVERIFY(m.access() == QMetaMethod::Public); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); @@ -2291,8 +2315,6 @@ public slots: void constTemplateSlot3(const Template< const int >) {} }; -#include "oldnormalizeobject.h" - void tst_QObject::normalize() { NormalizeObject object; @@ -2603,82 +2625,6 @@ void tst_QObject::normalize() SIGNAL(typeConstRefSignal(Template<Class const &> const &)), SLOT(typeConstRefSlot(Template<Class const &> const &)))); - // same test again, this time with an object compiled with old moc output... we know that - // it is not possible to connect everything, whic is the purpose for this test - OldNormalizeObject oldobject; - - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(constTypeRefSignal(const Template<const Class &> &)), - SLOT(constTypeRefSlot(const Template<const Class &> &)))); - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(constTypeRefSignal(const Template<const Class &> &)), - SLOT(constTypeRefSlot(const Template<Class const &> &)))); - // this fails in older versions, but passes now due to proper normalizing - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(constTypeRefSignal(const Template<const Class &> &)), - SLOT(constTypeRefSlot(Template<Class const &> const &)))); - // this fails in older versions, but passes now due to proper normalizing - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(constTypeRefSignal(Template<const Class &> const &)), - SLOT(constTypeRefSlot(Template<Class const &> const &)))); - // this fails in older versions, but passes now due to proper normalizing - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(constTypeRefSignal(Template<Class const &> const &)), - SLOT(constTypeRefSlot(Template<Class const &> const &)))); - - // these fail in older Qt versions, but pass now due to proper normalizing - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(constTypeRefSignal(const Template<const Class &> &)), - SLOT(typeConstRefSlot(const Template<const Class &> &)))); - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(constTypeRefSignal(const Template<const Class &> &)), - SLOT(typeConstRefSlot(const Template<Class const &> &)))); - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(constTypeRefSignal(const Template<const Class &> &)), - SLOT(typeConstRefSlot(Template<Class const &> const &)))); - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(constTypeRefSignal(Template<const Class &> const &)), - SLOT(typeConstRefSlot(Template<Class const &> const &)))); - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(constTypeRefSignal(Template<Class const &> const &)), - SLOT(typeConstRefSlot(Template<Class const &> const &)))); - - // these also fail in older Qt versions, but pass now due to proper normalizing - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(typeConstRefSignal(const Template<const Class &> &)), - SLOT(constTypeRefSlot(const Template<const Class &> &)))); - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(typeConstRefSignal(const Template<const Class &> &)), - SLOT(constTypeRefSlot(const Template<Class const &> &)))); - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(typeConstRefSignal(const Template<const Class &> &)), - SLOT(constTypeRefSlot(Template<Class const &> const &)))); - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(typeConstRefSignal(Template<const Class &> const &)), - SLOT(constTypeRefSlot(Template<Class const &> const &)))); - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(typeConstRefSignal(Template<Class const &> const &)), - SLOT(constTypeRefSlot(Template<Class const &> const &)))); - - // this fails in older versions, but passes now due to proper normalizing - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(typeConstRefSignal(const Template<const Class &> &)), - SLOT(typeConstRefSlot(const Template<const Class &> &)))); - // this fails in older versions, but passes now due to proper normalizing - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(typeConstRefSignal(const Template<const Class &> &)), - SLOT(typeConstRefSlot(const Template<Class const &> &)))); - // this fails in older versions, but passes now due to proper normalizing - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(typeConstRefSignal(const Template<const Class &> &)), - SLOT(typeConstRefSlot(Template<Class const &> const &)))); - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(typeConstRefSignal(Template<const Class &> const &)), - SLOT(typeConstRefSlot(Template<Class const &> const &)))); - QVERIFY(oldobject.connect(&oldobject, - SIGNAL(typeConstRefSignal(Template<Class const &> const &)), - SLOT(typeConstRefSlot(Template<Class const &> const &)))); - QVERIFY(object.connect(&object, SIGNAL(typePointerConstRefSignal(Class*const&)), SLOT(typePointerConstRefSlot(Class*const&)))); diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp index e8dade9387..e4ecfb6f2d 100644 --- a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp +++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp @@ -55,6 +55,7 @@ private slots: void zeroTimer(); void singleShotTimeout(); void timeout(); + void remainingTime(); void livelock_data(); void livelock(); void timerInfiniteRecursion_data(); @@ -143,6 +144,22 @@ void tst_QTimer::timeout() QVERIFY(helper.count > oldCount); } +void tst_QTimer::remainingTime() +{ + TimerHelper helper; + QTimer timer; + + connect(&timer, SIGNAL(timeout()), &helper, SLOT(timeout())); + timer.start(200); + + QCOMPARE(helper.count, 0); + + QTest::qWait(50); + QCOMPARE(helper.count, 0); + + int remainingTime = timer.remainingTime(); + QVERIFY2(qAbs(remainingTime - 150) < 50, qPrintable(QString::number(remainingTime))); +} void tst_QTimer::livelock_data() { diff --git a/tests/auto/corelib/kernel/qtranslator/qtranslator.pro b/tests/auto/corelib/kernel/qtranslator/qtranslator.pro index c644f83a22..41c3dea924 100644 --- a/tests/auto/corelib/kernel/qtranslator/qtranslator.pro +++ b/tests/auto/corelib/kernel/qtranslator/qtranslator.pro @@ -1,6 +1,6 @@ -CONFIG += testcase +CONFIG += testcase parallel_test TARGET = tst_qtranslator -QT += widgets testlib +QT = core testlib SOURCES = tst_qtranslator.cpp RESOURCES += qtranslator.qrc diff --git a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp index 033d10001f..4689fc432a 100644 --- a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp +++ b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp @@ -40,18 +40,17 @@ ****************************************************************************/ #include <QtTest/QtTest> -#include <QWidget> #include <qtranslator.h> #include <qfile.h> -class tst_QTranslator : public QWidget +class tst_QTranslator : public QObject { Q_OBJECT public: tst_QTranslator(); protected: - bool event(QEvent *event); + bool eventFilter(QObject *obj, QEvent *event); private slots: void initTestCase(); @@ -71,8 +70,7 @@ private: tst_QTranslator::tst_QTranslator() : languageChangeEventCounter(0) { - show(); - hide(); + qApp->installEventFilter(this); } void tst_QTranslator::initTestCase() @@ -83,11 +81,11 @@ void tst_QTranslator::initTestCase() QVERIFY2(QDir::setCurrent(testdata_dir), qPrintable("Could not chdir to " + testdata_dir)); } -bool tst_QTranslator::event(QEvent *event) +bool tst_QTranslator::eventFilter(QObject *, QEvent *event) { if (event->type() == QEvent::LanguageChange) ++languageChangeEventCounter; - return QWidget::event(event); + return false; } void tst_QTranslator::load() diff --git a/tests/auto/corelib/kernel/qvariant/stream/qt5.0/qregularexpression.bin b/tests/auto/corelib/kernel/qvariant/stream/qt5.0/qregularexpression.bin Binary files differnew file mode 100644 index 0000000000..eaa50f7310 --- /dev/null +++ b/tests/auto/corelib/kernel/qvariant/stream/qt5.0/qregularexpression.bin diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 655c714322..1e382dde3a 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -58,6 +58,7 @@ #include <qmatrix4x4.h> #include <qpen.h> #include <qpolygon.h> +#include <qpalette.h> #include <qtransform.h> #include <qvector2d.h> #include <qvector3d.h> @@ -192,6 +193,7 @@ private slots: void toLocale(); void toRegExp(); + void toRegularExpression(); void matrix(); @@ -274,6 +276,8 @@ private slots: void forwardDeclare(); void debugStream_data(); void debugStream(); + void debugStreamType_data(); + void debugStreamType(); void loadQt4Stream_data(); void loadQt4Stream(); @@ -283,6 +287,9 @@ private slots: void loadQt5Stream(); void saveQt5Stream_data(); void saveQt5Stream(); + + void guiVariantAtExit(); + void widgetsVariantAtExit(); private: void dataStream_data(QDataStream::Version version); void loadQVariantFromDataStream(QDataStream::Version version); @@ -1319,6 +1326,21 @@ void tst_QVariant::toRegExp() rx = variant.toRegExp(); } +void tst_QVariant::toRegularExpression() +{ + QVariant variant; + QRegularExpression re = variant.toRegularExpression(); + QCOMPARE(re, QRegularExpression()); + + variant = QRegularExpression("abc.*def"); + re = variant.toRegularExpression(); + QCOMPARE(re, QRegularExpression("abc.*def")); + + variant = QVariant::fromValue(QRegularExpression("[ab]\\w+")); + re = variant.value<QRegularExpression>(); + QCOMPARE(re, QRegularExpression("[ab]\\w+")); +} + void tst_QVariant::matrix() { QVariant variant; @@ -1519,6 +1541,8 @@ void tst_QVariant::writeToReadFromDataStream_data() QTest::newRow( "qchar_null" ) << QVariant(QChar(0)) << true; QTest::newRow( "regexp" ) << QVariant(QRegExp("foo", Qt::CaseInsensitive)) << false; QTest::newRow( "regexp_empty" ) << QVariant(QRegExp()) << false; + QTest::newRow( "regularexpression" ) << QVariant(QRegularExpression("abc.*def")) << false; + QTest::newRow( "regularexpression_empty" ) << QVariant(QRegularExpression()) << false; // types known to QMetaType, but not part of QVariant::Type QTest::newRow("QMetaType::Long invalid") << QVariant(QMetaType::Long, (void *) 0) << false; @@ -1625,8 +1649,8 @@ void tst_QVariant::writeToReadFromOldDataStream() void tst_QVariant::checkDataStream() { - QTest::ignoreMessage(QtWarningMsg, "Trying to construct an instance of an invalid type, type id: 46"); - const QByteArray settingsHex("0000002effffffffff"); + QTest::ignoreMessage(QtWarningMsg, "Trying to construct an instance of an invalid type, type id: 49"); + const QByteArray settingsHex("00000031ffffffffff"); const QByteArray settings = QByteArray::fromHex(settingsHex); QDataStream in(settings); QVariant v; @@ -1944,6 +1968,7 @@ void tst_QVariant::typeName_data() QTest::newRow("48") << int(QVariant::Vector3D) << QByteArray("QVector3D"); QTest::newRow("49") << int(QVariant::Vector4D) << QByteArray("QVector4D"); QTest::newRow("50") << int(QVariant::Quaternion) << QByteArray("QQuaternion"); + QTest::newRow("51") << int(QVariant::RegularExpression) << QByteArray("QRegularExpression"); } void tst_QVariant::typeName() @@ -2547,9 +2572,24 @@ public: }; Q_DECLARE_METATYPE(CustomQObjectDerived*) +class CustomQObjectDerivedNoMetaType : public CustomQObject { + Q_OBJECT +public: + CustomQObjectDerivedNoMetaType(QObject *parent = 0) : CustomQObject(parent) {} +}; + void tst_QVariant::qvariant_cast_QObject_derived() { { + CustomQObjectDerivedNoMetaType *object = new CustomQObjectDerivedNoMetaType(this); + QVariant data = QVariant::fromValue(object); + QVERIFY(data.userType() == qMetaTypeId<CustomQObjectDerivedNoMetaType*>()); + QCOMPARE(data.value<QObject *>(), object); + QCOMPARE(data.value<CustomQObjectDerivedNoMetaType *>(), object); + QCOMPARE(data.value<CustomQObject *>(), object); + QVERIFY(data.value<CustomQWidget*>() == 0); + } + { CustomQObjectDerived *object = new CustomQObjectDerived(this); QVariant data = QVariant::fromValue(object); @@ -2787,7 +2827,7 @@ Q_DECLARE_METATYPE( MyClass ) void tst_QVariant::loadUnknownUserType() { qRegisterMetaType<MyClass>("MyClass"); - char data[] = {0, 0, 1, 0, 0, 0, 0, 0, 8, 77, 121, 67, 108, 97, 115, 115, 0}; + char data[] = {0, 0, QMetaType::User >> 8 , char(QMetaType::User), 0, 0, 0, 0, 8, 'M', 'y', 'C', 'l', 'a', 's', 's', 0}; QByteArray ba(data, sizeof(data)); QDataStream ds(&ba, QIODevice::ReadOnly); @@ -3548,6 +3588,10 @@ void tst_QVariant::loadQVariantFromDataStream(QDataStream::Version version) stream >> typeName >> loadedVariant; const int id = QMetaType::type(typeName.toLatin1()); + if (id == QMetaType::Void) { + // Void type is not supported by QVariant + return; + } QVariant constructedVariant(static_cast<QVariant::Type>(id)); QCOMPARE(constructedVariant.userType(), id); @@ -3567,6 +3611,10 @@ void tst_QVariant::saveQVariantFromDataStream(QDataStream::Version version) dataFileStream >> typeName; QByteArray data = file.readAll(); const int id = QMetaType::type(typeName.toLatin1()); + if (id == QMetaType::Void) { + // Void type is not supported by QVariant + return; + } QBuffer buffer; buffer.open(QIODevice::ReadWrite); @@ -3588,8 +3636,8 @@ void tst_QVariant::saveQVariantFromDataStream(QDataStream::Version version) class MessageHandler { public: - MessageHandler(const int typeId) - : oldMsgHandler(qInstallMsgHandler(handler)) + MessageHandler(const int typeId, QtMsgHandler msgHandler = handler) + : oldMsgHandler(qInstallMsgHandler(msgHandler)) { currentId = typeId; } @@ -3603,13 +3651,24 @@ public: { return ok; } -private: +protected: static void handler(QtMsgType, const char *txt) { QString msg = QString::fromLatin1(txt); // Format itself is not important, but basic data as a type name should be included in the output - ok = msg.startsWith("QVariant(") + QMetaType::typeName(currentId); - QVERIFY2(ok, (QString::fromLatin1("Message is not valid: '") + msg + '\'').toLatin1().constData()); + ok = msg.startsWith("QVariant("); + QVERIFY2(ok, (QString::fromLatin1("Message is not started correctly: '") + msg + '\'').toLatin1().constData()); + ok &= (currentId == QMetaType::UnknownType + ? msg.contains("Invalid") + : msg.contains(QMetaType::typeName(currentId))); + QVERIFY2(ok, (QString::fromLatin1("Message doesn't contain type name: '") + msg + '\'').toLatin1().constData()); + if (currentId == QMetaType::Char || currentId == QMetaType::QChar) { + // Chars insert '\0' into the qdebug stream, it is not possible to find a real string length + return; + } + ok &= msg.endsWith(") "); + QVERIFY2(ok, (QString::fromLatin1("Message is not correctly finished: '") + msg + '\'').toLatin1().constData()); + } QtMsgHandler oldMsgHandler; @@ -3627,11 +3686,14 @@ void tst_QVariant::debugStream_data() const char *tagName = QMetaType::typeName(id); if (!tagName) continue; - QTest::newRow(tagName) << QVariant(static_cast<QVariant::Type>(id)) << id; + if (id != QMetaType::Void) { + QTest::newRow(tagName) << QVariant(static_cast<QVariant::Type>(id)) << id; + } } QTest::newRow("QBitArray(111)") << QVariant(QBitArray(3, true)) << qMetaTypeId<QBitArray>(); QTest::newRow("CustomStreamableClass") << QVariant(qMetaTypeId<CustomStreamableClass>(), 0) << qMetaTypeId<CustomStreamableClass>(); QTest::newRow("MyClass") << QVariant(qMetaTypeId<MyClass>(), 0) << qMetaTypeId<MyClass>(); + QTest::newRow("InvalidVariant") << QVariant() << int(QMetaType::UnknownType); } void tst_QVariant::debugStream() @@ -3644,5 +3706,62 @@ void tst_QVariant::debugStream() QVERIFY(msgHandler.testPassed()); } +struct MessageHandlerType : public MessageHandler +{ + MessageHandlerType(const int typeId) + : MessageHandler(typeId, handler) + {} + static void handler(QtMsgType, const char *txt) + { + QString msg = QString::fromLatin1(txt); + // Format itself is not important, but basic data as a type name should be included in the output + ok = msg.startsWith("QVariant::"); + QVERIFY2(ok, (QString::fromLatin1("Message is not started correctly: '") + msg + '\'').toLatin1().constData()); + ok &= (currentId == QMetaType::UnknownType + ? msg.contains("Invalid") + : msg.contains(QMetaType::typeName(currentId))); + QVERIFY2(ok, (QString::fromLatin1("Message doesn't contain type name: '") + msg + '\'').toLatin1().constData()); + } +}; + +void tst_QVariant::debugStreamType_data() +{ + debugStream_data(); +} + +void tst_QVariant::debugStreamType() +{ + QFETCH(QVariant, variant); + QFETCH(int, typeId); + + MessageHandlerType msgHandler(typeId); + qDebug() << QVariant::Type(typeId); + QVERIFY(msgHandler.testPassed()); +} + +void tst_QVariant::guiVariantAtExit() +{ + // crash test, it should not crash at QGuiApplication exit + static QVariant cursor = QCursor(); + static QVariant point = QPoint(); + static QVariant image = QImage(); + static QVariant pallete = QPalette(); + Q_UNUSED(cursor); + Q_UNUSED(point); + Q_UNUSED(image); + Q_UNUSED(pallete); + QVERIFY(true); +} + +void tst_QVariant::widgetsVariantAtExit() +{ + // crash test, it should not crash at QGuiApplication exit + static QVariant icon= QIcon(); + static QVariant sizePolicy = QSizePolicy(); + Q_UNUSED(icon); + Q_UNUSED(sizePolicy); + QVERIFY(true); +} + QTEST_MAIN(tst_QVariant) #include "tst_qvariant.moc" diff --git a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp index a6d38ca078..c2dc8a4cc6 100644 --- a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp +++ b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp @@ -52,6 +52,7 @@ class tst_QAtomicInt : public QObject private slots: void warningFree(); + void alignment(); // QAtomicInt members void constructor_data(); @@ -92,33 +93,101 @@ private: static void warningFreeHelper(); }; -void tst_QAtomicInt::warningFreeHelper() +template <int I> +static inline void assemblyMarker(void *ptr = 0) { - qFatal("This code is bogus, and shouldn't be run. We're looking for compiler warnings only."); + puts((char *)ptr + I); +} - QBasicAtomicInt i = Q_BASIC_ATOMIC_INITIALIZER(0); +QT_BEGIN_NAMESPACE +template <typename T> class QBasicAtomicInteger; // even if it this class isn't supported +QT_END_NAMESPACE - int expectedValue = 0; - int newValue = 0; - int valueToAdd = 0; +template <typename T, typename Atomic> +static void warningFreeHelperTemplate() +{ + T expectedValue = 0; + T newValue = 0; + T valueToAdd = 0; + + // the marker calls are here only to provide a divider for + // those reading the assembly output + assemblyMarker<0>(); + Atomic i = Q_BASIC_ATOMIC_INITIALIZER(0); + printf("%d\n", int(i.loadAcquire())); + assemblyMarker<1>(&i); + + // the loads sometimes generate no assembly output + i.load(); + assemblyMarker<11>(&i); + i.loadAcquire(); + assemblyMarker<12>(&i); + + i.store(newValue); + assemblyMarker<21>(&i); + i.storeRelease(newValue); + assemblyMarker<22>(&i); i.ref(); + assemblyMarker<31>(&i); i.deref(); + assemblyMarker<32>(&i); i.testAndSetRelaxed(expectedValue, newValue); + assemblyMarker<41>(&i); i.testAndSetAcquire(expectedValue, newValue); + assemblyMarker<42>(&i); i.testAndSetRelease(expectedValue, newValue); + assemblyMarker<43>(&i); i.testAndSetOrdered(expectedValue, newValue); + assemblyMarker<44>(&i); i.fetchAndStoreRelaxed(newValue); + assemblyMarker<51>(&i); i.fetchAndStoreAcquire(newValue); + assemblyMarker<52>(&i); i.fetchAndStoreRelease(newValue); + assemblyMarker<53>(&i); i.fetchAndStoreOrdered(newValue); + assemblyMarker<54>(&i); i.fetchAndAddRelaxed(valueToAdd); + assemblyMarker<61>(&i); i.fetchAndAddAcquire(valueToAdd); + assemblyMarker<62>(&i); i.fetchAndAddRelease(valueToAdd); + assemblyMarker<63>(&i); i.fetchAndAddOrdered(valueToAdd); + assemblyMarker<64>(&i); +} + +void tst_QAtomicInt::warningFreeHelper() +{ + qFatal("This code is bogus, and shouldn't be run. We're looking for compiler warnings only."); + warningFreeHelperTemplate<int, QBasicAtomicInt>(); + +#ifdef Q_ATOMIC_INT32_IS_SUPPORTED + warningFreeHelperTemplate<int, QBasicAtomicInteger<int> >(); + warningFreeHelperTemplate<unsigned int, QBasicAtomicInteger<unsigned int> >(); +#endif + +#ifdef Q_ATOMIC_INT16_IS_SUPPORTED + warningFreeHelperTemplate<qint16, QBasicAtomicInteger<qint16> >(); + warningFreeHelperTemplate<quint16, QBasicAtomicInteger<quint16> >(); +#endif + +#ifdef Q_ATOMIC_INT8_IS_SUPPORTED + warningFreeHelperTemplate<char, QBasicAtomicInteger<char> >(); + warningFreeHelperTemplate<signed char, QBasicAtomicInteger<signed char> >(); + warningFreeHelperTemplate<unsigned char, QBasicAtomicInteger<unsigned char> >(); +#endif + +#ifdef Q_ATOMIC_INT64_IS_SUPPORTED +#if !defined(__i386__) || (defined(Q_CC_GNU) && defined(__OPTIMIZE__)) + warningFreeHelperTemplate<qlonglong, QBasicAtomicInteger<qlonglong> >(); + warningFreeHelperTemplate<qulonglong, QBasicAtomicInteger<qulonglong> >(); +#endif +#endif } void tst_QAtomicInt::warningFree() @@ -130,6 +199,35 @@ void tst_QAtomicInt::warningFree() (void)foo; } +template <typename T> struct TypeInStruct { T type; }; + +void tst_QAtomicInt::alignment() +{ +#ifdef Q_ALIGNOF + // this will cause a build error if the alignment isn't the same + char dummy1[Q_ALIGNOF(QBasicAtomicInt) == Q_ALIGNOF(TypeInStruct<int>) ? 1 : -1]; + char dummy2[Q_ALIGNOF(QAtomicInt) == Q_ALIGNOF(TypeInStruct<int>) ? 1 : -1]; + (void)dummy1; (void)dummy2; + +#ifdef Q_ATOMIC_INT32_IS_SUPPORTED + QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<int>), Q_ALIGNOF(TypeInStruct<int>)); +#endif + +#ifdef Q_ATOMIC_INT16_IS_SUPPORTED + QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<short>), Q_ALIGNOF(TypeInStruct<short>)); +#endif + +#ifdef Q_ATOMIC_INT8_IS_SUPPORTED + QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<char>), Q_ALIGNOF(TypeInStruct<char>)); +#endif + +#ifdef Q_ATOMIC_INT64_IS_SUPPORTED + QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<qlonglong>), Q_ALIGNOF(TypeInStruct<qlonglong>)); +#endif + +#endif +} + void tst_QAtomicInt::constructor_data() { QTest::addColumn<int>("value"); diff --git a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp index a8f7e037d0..ee6460d35c 100644 --- a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp +++ b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp @@ -49,6 +49,7 @@ class tst_QAtomicPointer : public QObject Q_OBJECT private slots: void warningFree(); + void alignment(); void constructor(); void copy_constructor(); @@ -114,6 +115,15 @@ void tst_QAtomicPointer::warningFree() (void)foo; } +void tst_QAtomicPointer::alignment() +{ +#ifdef Q_ALIGNOF + // this will cause a build error if the alignment isn't the same + char dummy[Q_ALIGNOF(QBasicAtomicPointer<void>) == Q_ALIGNOF(void*) ? 1 : -1]; + (void)dummy; +#endif +} + void tst_QAtomicPointer::constructor() { void *one = this; diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp index 8eccd17376..41cfc52354 100644 --- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp +++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp @@ -1234,6 +1234,7 @@ public: bool unregisterTimer(int ) { return false; } bool unregisterTimers(QObject *) { return false; } QList<TimerInfo> registeredTimers(QObject *) const { return QList<TimerInfo>(); } + int remainingTime(int) { return 0; } void wakeUp() {} void interrupt() {} void flush() {} diff --git a/tests/auto/corelib/tools/qarraydata/qarraydata.pro b/tests/auto/corelib/tools/qarraydata/qarraydata.pro new file mode 100644 index 0000000000..8e368117fa --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/qarraydata.pro @@ -0,0 +1,5 @@ +TARGET = tst_qarraydata +SOURCES += tst_qarraydata.cpp +HEADERS += simplevector.h +QT = core testlib +CONFIG += testcase parallel_test diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h new file mode 100644 index 0000000000..7e679704c8 --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QARRAY_TEST_SIMPLE_VECTOR_H +#define QARRAY_TEST_SIMPLE_VECTOR_H + +#include <QtCore/qarraydata.h> +#include <QtCore/qarraydatapointer.h> + +#include <algorithm> + +template <class T> +struct SimpleVector +{ +private: + typedef QTypedArrayData<T> Data; + +public: + typedef T value_type; + typedef typename Data::iterator iterator; + typedef typename Data::const_iterator const_iterator; + + SimpleVector() + { + } + + explicit SimpleVector(size_t n) + : d(Data::allocate(n)) + { + if (n) + d->appendInitialize(n); + } + + SimpleVector(size_t n, const T &t) + : d(Data::allocate(n)) + { + if (n) + d->copyAppend(n, t); + } + + SimpleVector(const T *begin, const T *end) + : d(Data::allocate(end - begin)) + { + if (end - begin) + d->copyAppend(begin, end); + } + + SimpleVector(QArrayDataPointerRef<T> ptr) + : d(ptr) + { + } + + explicit SimpleVector(Data *ptr) + : d(ptr) + { + } + + bool empty() const { return d->size == 0; } + bool isNull() const { return d.isNull(); } + bool isEmpty() const { return this->empty(); } + + bool isStatic() const { return d->ref.isStatic(); } + bool isShared() const { return d->ref.isShared(); } + bool isSharedWith(const SimpleVector &other) const { return d == other.d; } + bool isSharable() const { return d->ref.isSharable(); } + + void setSharable(bool sharable) { d.setSharable(sharable); } + + size_t size() const { return d->size; } + size_t capacity() const { return d->alloc; } + + iterator begin() { detach(); return d->begin(); } + iterator end() { detach(); return d->end(); } + + const_iterator begin() const { return d->begin(); } + const_iterator end() const { return d->end(); } + + const_iterator constBegin() const { return begin(); } + const_iterator constEnd() const { return end(); } + + T &operator[](size_t i) { Q_ASSERT(i < size_t(d->size)); detach(); return begin()[i]; } + T &at(size_t i) { Q_ASSERT(i < size_t(d->size)); detach(); return begin()[i]; } + + const T &operator[](size_t i) const { Q_ASSERT(i < size_t(d->size)); return begin()[i]; } + const T &at(size_t i) const { Q_ASSERT(i < size_t(d->size)); return begin()[i]; } + + T &front() + { + Q_ASSERT(!isEmpty()); + detach(); + return *begin(); + } + + T &back() + { + Q_ASSERT(!isEmpty()); + detach(); + return *(end() - 1); + } + + const T &front() const + { + Q_ASSERT(!isEmpty()); + return *begin(); + } + + const T &back() const + { + Q_ASSERT(!isEmpty()); + return *(end() - 1); + } + + void reserve(size_t n) + { + if (n == 0) + return; + + if (n <= capacity()) { + if (d->capacityReserved) + return; + if (!d->ref.isShared()) { + d->capacityReserved = 1; + return; + } + } + + SimpleVector detached(Data::allocate(qMax(n, size()), + d->detachFlags() | Data::CapacityReserved)); + if (size()) + detached.d->copyAppend(constBegin(), constEnd()); + detached.swap(*this); + } + + void resize(size_t newSize) + { + if (size() == newSize) + return; + + if (d.needsDetach() || newSize > capacity()) { + SimpleVector detached(Data::allocate( + d->detachCapacity(newSize), d->detachFlags())); + if (newSize) { + if (newSize < size()) { + const T *const begin = constBegin(); + detached.d->copyAppend(begin, begin + newSize); + } else { + if (size()) { + const T *const begin = constBegin(); + detached.d->copyAppend(begin, begin + size()); + } + detached.d->appendInitialize(newSize); + } + } + detached.swap(*this); + return; + } + + if (newSize > size()) + d->appendInitialize(newSize); + else + d->truncate(newSize); + } + + void prepend(const_iterator first, const_iterator last) + { + if (!d->size) { + append(first, last); + return; + } + + if (first == last) + return; + + T *const begin = d->begin(); + if (d.needsDetach() + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + d->detachCapacity(size() + (last - first)), + d->detachFlags() | Data::Grow)); + + detached.d->copyAppend(first, last); + detached.d->copyAppend(begin, begin + d->size); + detached.swap(*this); + + return; + } + + d->insert(begin, first, last); + } + + void append(const_iterator first, const_iterator last) + { + if (first == last) + return; + + if (d.needsDetach() + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + d->detachCapacity(size() + (last - first)), + d->detachFlags() | Data::Grow)); + + if (d->size) { + const T *const begin = constBegin(); + detached.d->copyAppend(begin, begin + d->size); + } + detached.d->copyAppend(first, last); + detached.swap(*this); + + return; + } + + d->copyAppend(first, last); + } + + void insert(int position, const_iterator first, const_iterator last) + { + if (position < 0) + position += d->size + 1; + + if (position <= 0) { + prepend(first, last); + return; + } + + if (size_t(position) >= size()) { + append(first, last); + return; + } + + if (first == last) + return; + + T *const begin = d->begin(); + T *const where = begin + position; + const T *const end = begin + d->size; + if (d.needsDetach() + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + d->detachCapacity(size() + (last - first)), + d->detachFlags() | Data::Grow)); + + if (position) + detached.d->copyAppend(begin, where); + detached.d->copyAppend(first, last); + detached.d->copyAppend(where, end); + detached.swap(*this); + + return; + } + + if ((first >= where && first < end) + || (last > where && last <= end)) { + // Copy overlapping data first and only then shuffle it into place + T *start = d->begin() + position; + T *middle = d->end(); + + d->copyAppend(first, last); + std::rotate(start, middle, d->end()); + + return; + } + + d->insert(where, first, last); + } + + void swap(SimpleVector &other) + { + qSwap(d, other.d); + } + + void clear() + { + d.clear(); + } + + void detach() + { + d.detach(); + } + + static SimpleVector fromRawData(const T *data, size_t size, + QArrayData::AllocationOptions options = Data::Default) + { + return SimpleVector(Data::fromRawData(data, size, options)); + } + +private: + QArrayDataPointer<T> d; +}; + +template <class T> +bool operator==(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + if (lhs.isSharedWith(rhs)) + return true; + if (lhs.size() != rhs.size()) + return false; + return std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template <class T> +bool operator!=(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return !(lhs == rhs); +} + +template <class T> +bool operator<(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} + +template <class T> +bool operator>(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return rhs < lhs; +} + +template <class T> +bool operator<=(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return !(rhs < lhs); +} + +template <class T> +bool operator>=(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return !(lhs < rhs); +} + +namespace std { + template <class T> + void swap(SimpleVector<T> &v1, SimpleVector<T> &v2) + { + v1.swap(v2); + } +} + +#endif // include guard diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp new file mode 100644 index 0000000000..53217b2222 --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -0,0 +1,1605 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtCore/QString> +#include <QtCore/qarraydata.h> + +#include "simplevector.h" + +struct SharedNullVerifier +{ + SharedNullVerifier() + { + Q_ASSERT(QArrayData::shared_null[0].ref.isStatic()); + Q_ASSERT(QArrayData::shared_null[0].ref.isShared()); + Q_ASSERT(QArrayData::shared_null[0].ref.isSharable()); + } +}; + +// This is meant to verify/ensure that shared_null is not being dynamically +// initialized and stays away from the order-of-static-initialization fiasco. +// +// Of course, if this was to fail, qmake and the build should have crashed and +// burned before we ever got to this point :-) +SharedNullVerifier globalInit; + +class tst_QArrayData : public QObject +{ + Q_OBJECT + +private slots: + void referenceCounting(); + void sharedNullEmpty(); + void staticData(); + void simpleVector(); + void simpleVectorReserve_data(); + void simpleVectorReserve(); + void allocate_data(); + void allocate(); + void alignment_data(); + void alignment(); + void typedData(); + void gccBug43247(); + void arrayOps(); + void arrayOps2(); + void setSharable_data(); + void setSharable(); + void fromRawData(); + void literals(); + void variadicLiterals(); + void rValueReferences(); + void grow(); +}; + +template <class T> const T &const_(const T &t) { return t; } + +void tst_QArrayData::referenceCounting() +{ + { + // Reference counting initialized to 1 (owned) + QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(1) }, 0, 0, 0, 0 }; + + QCOMPARE(array.ref.atomic.load(), 1); + + QVERIFY(!array.ref.isStatic()); + QVERIFY(array.ref.isSharable()); + + QVERIFY(array.ref.ref()); + QCOMPARE(array.ref.atomic.load(), 2); + + QVERIFY(array.ref.deref()); + QCOMPARE(array.ref.atomic.load(), 1); + + QVERIFY(array.ref.ref()); + QCOMPARE(array.ref.atomic.load(), 2); + + QVERIFY(array.ref.deref()); + QCOMPARE(array.ref.atomic.load(), 1); + + QVERIFY(!array.ref.deref()); + QCOMPARE(array.ref.atomic.load(), 0); + + // Now would be a good time to free/release allocated data + } + + { + // Reference counting initialized to 0 (non-sharable) + QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 }; + + QCOMPARE(array.ref.atomic.load(), 0); + + QVERIFY(!array.ref.isStatic()); + QVERIFY(!array.ref.isSharable()); + + QVERIFY(!array.ref.ref()); + // Reference counting fails, data should be copied + QCOMPARE(array.ref.atomic.load(), 0); + + QVERIFY(!array.ref.deref()); + QCOMPARE(array.ref.atomic.load(), 0); + + // Free/release data + } + + { + // Reference counting initialized to -1 (static read-only data) + QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; + + QCOMPARE(array.ref.atomic.load(), -1); + + QVERIFY(array.ref.isStatic()); + QVERIFY(array.ref.isSharable()); + + QVERIFY(array.ref.ref()); + QCOMPARE(array.ref.atomic.load(), -1); + + QVERIFY(array.ref.deref()); + QCOMPARE(array.ref.atomic.load(), -1); + } +} + +void tst_QArrayData::sharedNullEmpty() +{ + QArrayData *null = const_cast<QArrayData *>(QArrayData::shared_null); + QArrayData *empty = QArrayData::allocate(1, Q_ALIGNOF(QArrayData), 0); + + QVERIFY(null->ref.isStatic()); + QVERIFY(null->ref.isSharable()); + QVERIFY(null->ref.isShared()); + + QVERIFY(empty->ref.isStatic()); + QVERIFY(empty->ref.isSharable()); + QVERIFY(empty->ref.isShared()); + + QCOMPARE(null->ref.atomic.load(), -1); + QCOMPARE(empty->ref.atomic.load(), -1); + + QVERIFY(null->ref.ref()); + QVERIFY(empty->ref.ref()); + + QCOMPARE(null->ref.atomic.load(), -1); + QCOMPARE(empty->ref.atomic.load(), -1); + + QVERIFY(null->ref.deref()); + QVERIFY(empty->ref.deref()); + + QCOMPARE(null->ref.atomic.load(), -1); + QCOMPARE(empty->ref.atomic.load(), -1); + + QVERIFY(null != empty); + + QCOMPARE(null->size, 0); + QCOMPARE(null->alloc, 0u); + QCOMPARE(null->capacityReserved, 0u); + + QCOMPARE(empty->size, 0); + QCOMPARE(empty->alloc, 0u); + QCOMPARE(empty->capacityReserved, 0u); +} + +void tst_QArrayData::staticData() +{ + QStaticArrayData<char, 10> charArray = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(char, 10), + { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' } + }; + QStaticArrayData<int, 10> intArray = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } + }; + QStaticArrayData<double, 10> doubleArray = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(double, 10), + { 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f } + }; + + QCOMPARE(charArray.header.size, 10); + QCOMPARE(intArray.header.size, 10); + QCOMPARE(doubleArray.header.size, 10); + + QCOMPARE(charArray.header.data(), reinterpret_cast<void *>(&charArray.data)); + QCOMPARE(intArray.header.data(), reinterpret_cast<void *>(&intArray.data)); + QCOMPARE(doubleArray.header.data(), reinterpret_cast<void *>(&doubleArray.data)); +} + +void tst_QArrayData::simpleVector() +{ + QArrayData data0 = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; + QStaticArrayData<int, 7> data1 = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 7), + { 0, 1, 2, 3, 4, 5, 6 } + }; + + int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + SimpleVector<int> v1; + SimpleVector<int> v2(v1); + SimpleVector<int> v3(static_cast<QTypedArrayData<int> *>(&data0)); + SimpleVector<int> v4(static_cast<QTypedArrayData<int> *>(&data1.header)); + SimpleVector<int> v5(static_cast<QTypedArrayData<int> *>(&data0)); + SimpleVector<int> v6(static_cast<QTypedArrayData<int> *>(&data1.header)); + SimpleVector<int> v7(10, 5); + SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array)); + + v3 = v1; + v1.swap(v3); + v4.clear(); + + QVERIFY(v1.isNull()); + QVERIFY(v2.isNull()); + QVERIFY(v3.isNull()); + QVERIFY(v4.isNull()); + QVERIFY(!v5.isNull()); + QVERIFY(!v6.isNull()); + QVERIFY(!v7.isNull()); + QVERIFY(!v8.isNull()); + + QVERIFY(v1.isEmpty()); + QVERIFY(v2.isEmpty()); + QVERIFY(v3.isEmpty()); + QVERIFY(v4.isEmpty()); + QVERIFY(v5.isEmpty()); + QVERIFY(!v6.isEmpty()); + QVERIFY(!v7.isEmpty()); + QVERIFY(!v8.isEmpty()); + + QCOMPARE(v1.size(), size_t(0)); + QCOMPARE(v2.size(), size_t(0)); + QCOMPARE(v3.size(), size_t(0)); + QCOMPARE(v4.size(), size_t(0)); + QCOMPARE(v5.size(), size_t(0)); + QCOMPARE(v6.size(), size_t(7)); + QCOMPARE(v7.size(), size_t(10)); + QCOMPARE(v8.size(), size_t(10)); + + QCOMPARE(v1.capacity(), size_t(0)); + QCOMPARE(v2.capacity(), size_t(0)); + QCOMPARE(v3.capacity(), size_t(0)); + QCOMPARE(v4.capacity(), size_t(0)); + QCOMPARE(v5.capacity(), size_t(0)); + // v6.capacity() is unspecified, for now + QVERIFY(v7.capacity() >= size_t(10)); + QVERIFY(v8.capacity() >= size_t(10)); + + QVERIFY(v1.isStatic()); + QVERIFY(v2.isStatic()); + QVERIFY(v3.isStatic()); + QVERIFY(v4.isStatic()); + QVERIFY(v5.isStatic()); + QVERIFY(v6.isStatic()); + QVERIFY(!v7.isStatic()); + QVERIFY(!v8.isStatic()); + + QVERIFY(v1.isShared()); + QVERIFY(v2.isShared()); + QVERIFY(v3.isShared()); + QVERIFY(v4.isShared()); + QVERIFY(v5.isShared()); + QVERIFY(v6.isShared()); + QVERIFY(!v7.isShared()); + QVERIFY((SimpleVector<int>(v7), v7.isShared())); + QVERIFY(!v7.isShared()); + QVERIFY(!v8.isShared()); + + QVERIFY(v1.isSharable()); + QVERIFY(v2.isSharable()); + QVERIFY(v3.isSharable()); + QVERIFY(v4.isSharable()); + QVERIFY(v5.isSharable()); + QVERIFY(v6.isSharable()); + QVERIFY(v7.isSharable()); + QVERIFY(v8.isSharable()); + + QVERIFY(v1.isSharedWith(v2)); + QVERIFY(v1.isSharedWith(v3)); + QVERIFY(v1.isSharedWith(v4)); + QVERIFY(!v1.isSharedWith(v5)); + QVERIFY(!v1.isSharedWith(v6)); + + QCOMPARE((void *)v1.constBegin(), (void *)v1.constEnd()); + QCOMPARE((void *)v4.constBegin(), (void *)v4.constEnd()); + QCOMPARE((void *)(v6.constBegin() + v6.size()), (void *)v6.constEnd()); + QCOMPARE((void *)(v7.constBegin() + v7.size()), (void *)v7.constEnd()); + QCOMPARE((void *)(v8.constBegin() + v8.size()), (void *)v8.constEnd()); + + QVERIFY(v1 == v2); + QVERIFY(v1 == v3); + QVERIFY(v1 == v4); + QVERIFY(v1 == v5); + QVERIFY(!(v1 == v6)); + + QVERIFY(v1 != v6); + QVERIFY(v4 != v6); + QVERIFY(v5 != v6); + QVERIFY(!(v1 != v5)); + + QVERIFY(v1 < v6); + QVERIFY(!(v6 < v1)); + QVERIFY(v6 > v1); + QVERIFY(!(v1 > v6)); + QVERIFY(v1 <= v6); + QVERIFY(!(v6 <= v1)); + QVERIFY(v6 >= v1); + QVERIFY(!(v1 >= v6)); + + { + SimpleVector<int> temp(v6); + + QCOMPARE(const_(v6).front(), 0); + QCOMPARE(const_(v6).back(), 6); + + QVERIFY(temp.isShared()); + QVERIFY(temp.isSharedWith(v6)); + + QCOMPARE(temp.front(), 0); + QCOMPARE(temp.back(), 6); + + // Detached + QVERIFY(!temp.isShared()); + const int *const tempBegin = temp.begin(); + + for (size_t i = 0; i < v6.size(); ++i) { + QCOMPARE(const_(v6)[i], int(i)); + QCOMPARE(const_(v6).at(i), int(i)); + QCOMPARE(&const_(v6)[i], &const_(v6).at(i)); + + QCOMPARE(const_(v8)[i], const_(v6)[i]); + + QCOMPARE(temp[i], int(i)); + QCOMPARE(temp.at(i), int(i)); + QCOMPARE(&temp[i], &temp.at(i)); + } + + // A single detach should do + QCOMPARE(temp.begin(), tempBegin); + } + + { + int count = 0; + Q_FOREACH (int value, v7) { + QCOMPARE(value, 5); + ++count; + } + + QCOMPARE(count, 10); + } + + { + int count = 0; + Q_FOREACH (int value, v8) { + QCOMPARE(value, count); + ++count; + } + + QCOMPARE(count, 10); + } + + v5 = v6; + QVERIFY(v5.isSharedWith(v6)); + QVERIFY(!v1.isSharedWith(v5)); + + v1.swap(v6); + QVERIFY(v6.isNull()); + QVERIFY(v1.isSharedWith(v5)); + + { + using std::swap; + swap(v1, v6); + QVERIFY(v5.isSharedWith(v6)); + QVERIFY(!v1.isSharedWith(v5)); + } + + v1.prepend(array, array + sizeof(array)/sizeof(array[0])); + QCOMPARE(v1.size(), size_t(10)); + QVERIFY(v1 == v8); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.prepend(array, array + sizeof(array)/sizeof(array[0])); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(20)); + QCOMPARE(v6.size(), size_t(10)); + + for (int i = 0; i < 20; ++i) + QCOMPARE(v1[i], v6[i % 10]); + + v1.clear(); + + v1.append(array, array + sizeof(array)/sizeof(array[0])); + QCOMPARE(v1.size(), size_t(10)); + QVERIFY(v1 == v8); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.append(array, array + sizeof(array)/sizeof(array[0])); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(20)); + QCOMPARE(v6.size(), size_t(10)); + + for (int i = 0; i < 20; ++i) + QCOMPARE(v1[i], v6[i % 10]); + + v1.insert(0, v6.constBegin(), v6.constEnd()); + QCOMPARE(v1.size(), size_t(30)); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.insert(10, v6.constBegin(), v6.constEnd()); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(60)); + QCOMPARE(v6.size(), size_t(30)); + + for (int i = 0; i < 30; ++i) + QCOMPARE(v6[i], v8[i % 10]); + + v1.insert(v1.size(), v6.constBegin(), v6.constEnd()); + QCOMPARE(v1.size(), size_t(90)); + + v1.insert(-1, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(100)); + + v1.insert(-11, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(110)); + + v1.insert(-200, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(120)); + + for (int i = 0; i < 120; ++i) + QCOMPARE(v1[i], v8[i % 10]); + + { + v7.setSharable(true); + QVERIFY(v7.isSharable()); + + SimpleVector<int> copy1(v7); + QVERIFY(copy1.isSharedWith(v7)); + + v7.setSharable(false); + QVERIFY(!v7.isSharable()); + + QVERIFY(!copy1.isSharedWith(v7)); + QCOMPARE(v7.size(), copy1.size()); + for (size_t i = 0; i < copy1.size(); ++i) + QCOMPARE(v7[i], copy1[i]); + + SimpleVector<int> clone(v7); + QVERIFY(!clone.isSharedWith(v7)); + QCOMPARE(clone.size(), copy1.size()); + for (size_t i = 0; i < copy1.size(); ++i) + QCOMPARE(clone[i], copy1[i]); + + v7.setSharable(true); + QVERIFY(v7.isSharable()); + + SimpleVector<int> copy2(v7); + QVERIFY(copy2.isSharedWith(v7)); + } + + { + SimpleVector<int> null; + SimpleVector<int> empty(0, 5); + + QVERIFY(null.isSharable()); + QVERIFY(empty.isSharable()); + + null.setSharable(true); + empty.setSharable(true); + + QVERIFY(null.isSharable()); + QVERIFY(empty.isSharable()); + + QVERIFY(null.isEmpty()); + QVERIFY(empty.isEmpty()); + + null.setSharable(false); + empty.setSharable(false); + + QVERIFY(!null.isSharable()); + QVERIFY(!empty.isSharable()); + + QVERIFY(null.isEmpty()); + QVERIFY(empty.isEmpty()); + + null.setSharable(true); + empty.setSharable(true); + + QVERIFY(null.isSharable()); + QVERIFY(empty.isSharable()); + + QVERIFY(null.isEmpty()); + QVERIFY(empty.isEmpty()); + } +} + +Q_DECLARE_METATYPE(SimpleVector<int>) + +void tst_QArrayData::simpleVectorReserve_data() +{ + QTest::addColumn<SimpleVector<int> >("vector"); + QTest::addColumn<size_t>("capacity"); + QTest::addColumn<size_t>("size"); + + QTest::newRow("null") << SimpleVector<int>() << size_t(0) << size_t(0); + QTest::newRow("empty") << SimpleVector<int>(0, 42) << size_t(0) << size_t(0); + QTest::newRow("non-empty") << SimpleVector<int>(5, 42) << size_t(5) << size_t(5); + + static const QStaticArrayData<int, 15> array = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 15), + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } }; + QArrayDataPointerRef<int> p = { + static_cast<QTypedArrayData<int> *>( + const_cast<QArrayData *>(&array.header)) }; + + QTest::newRow("static") << SimpleVector<int>(p) << size_t(0) << size_t(15); + QTest::newRow("raw-data") << SimpleVector<int>::fromRawData(array.data, 15) << size_t(0) << size_t(15); +} + +void tst_QArrayData::simpleVectorReserve() +{ + QFETCH(SimpleVector<int>, vector); + QFETCH(size_t, capacity); + QFETCH(size_t, size); + + QVERIFY(!capacity || capacity >= size); + + QCOMPARE(vector.capacity(), capacity); + QCOMPARE(vector.size(), size); + + const SimpleVector<int> copy(vector); + + vector.reserve(0); + QCOMPARE(vector.capacity(), capacity); + QCOMPARE(vector.size(), size); + + vector.reserve(10); + + // zero-capacity (immutable) resets with detach + if (!capacity) + capacity = size; + + QCOMPARE(vector.capacity(), qMax(size_t(10), capacity)); + QCOMPARE(vector.size(), size); + + vector.reserve(20); + QCOMPARE(vector.capacity(), size_t(20)); + QCOMPARE(vector.size(), size); + + vector.reserve(30); + QCOMPARE(vector.capacity(), size_t(30)); + QCOMPARE(vector.size(), size); + + QVERIFY(vector == copy); +} + +struct Deallocator +{ + Deallocator(size_t objectSize, size_t alignment) + : objectSize(objectSize) + , alignment(alignment) + { + } + + ~Deallocator() + { + Q_FOREACH (QArrayData *data, headers) + QArrayData::deallocate(data, objectSize, alignment); + } + + size_t objectSize; + size_t alignment; + QVector<QArrayData *> headers; +}; + +Q_DECLARE_METATYPE(const QArrayData *) +Q_DECLARE_METATYPE(QArrayData::AllocationOptions) + +void tst_QArrayData::allocate_data() +{ + QTest::addColumn<size_t>("objectSize"); + QTest::addColumn<size_t>("alignment"); + QTest::addColumn<QArrayData::AllocationOptions>("allocateOptions"); + QTest::addColumn<bool>("isCapacityReserved"); + QTest::addColumn<bool>("isSharable"); + QTest::addColumn<const QArrayData *>("commonEmpty"); + + struct { + char const *typeName; + size_t objectSize; + size_t alignment; + } types[] = { + { "char", sizeof(char), Q_ALIGNOF(char) }, + { "short", sizeof(short), Q_ALIGNOF(short) }, + { "void *", sizeof(void *), Q_ALIGNOF(void *) } + }; + + QArrayData *shared_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0); + QArrayData *unsharable_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, QArrayData::Unsharable); + + QVERIFY(shared_empty); + QVERIFY(unsharable_empty); + + struct { + char const *description; + QArrayData::AllocationOptions allocateOptions; + bool isCapacityReserved; + bool isSharable; + const QArrayData *commonEmpty; + } options[] = { + { "Default", QArrayData::Default, false, true, shared_empty }, + { "Reserved", QArrayData::CapacityReserved, true, true, shared_empty }, + { "Reserved | Unsharable", + QArrayData::CapacityReserved | QArrayData::Unsharable, true, false, + unsharable_empty }, + { "Unsharable", QArrayData::Unsharable, false, false, unsharable_empty }, + { "Grow", QArrayData::Grow, false, true, shared_empty } + }; + + for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i) + for (size_t j = 0; j < sizeof(options)/sizeof(options[0]); ++j) + QTest::newRow(qPrintable( + QLatin1String(types[i].typeName) + + QLatin1String(": ") + + QLatin1String(options[j].description))) + << types[i].objectSize << types[i].alignment + << options[j].allocateOptions << options[j].isCapacityReserved + << options[j].isSharable << options[j].commonEmpty; +} + +void tst_QArrayData::allocate() +{ + QFETCH(size_t, objectSize); + QFETCH(size_t, alignment); + QFETCH(QArrayData::AllocationOptions, allocateOptions); + QFETCH(bool, isCapacityReserved); + QFETCH(bool, isSharable); + QFETCH(const QArrayData *, commonEmpty); + + // Minimum alignment that can be requested is that of QArrayData. + // Typically, this alignment is sizeof(void *) and ensured by malloc. + size_t minAlignment = qMax(alignment, Q_ALIGNOF(QArrayData)); + + // Shared Empty + QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0, + QArrayData::AllocationOptions(allocateOptions)), commonEmpty); + + Deallocator keeper(objectSize, minAlignment); + keeper.headers.reserve(1024); + + for (int capacity = 1; capacity <= 1024; capacity <<= 1) { + QArrayData *data = QArrayData::allocate(objectSize, minAlignment, + capacity, QArrayData::AllocationOptions(allocateOptions)); + keeper.headers.append(data); + + QCOMPARE(data->size, 0); + if (allocateOptions & QArrayData::Grow) + QVERIFY(data->alloc > uint(capacity)); + else + QCOMPARE(data->alloc, uint(capacity)); + QCOMPARE(data->capacityReserved, uint(isCapacityReserved)); + QCOMPARE(data->ref.isSharable(), isSharable); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(data->data(), 'A', objectSize * capacity); + } +} + +class Unaligned +{ + char dummy[8]; +}; + +void tst_QArrayData::alignment_data() +{ + QTest::addColumn<size_t>("alignment"); + + for (int i = 1; i < 10; ++i) { + size_t alignment = 1u << i; + QTest::newRow(qPrintable(QString::number(alignment))) << alignment; + } +} + +void tst_QArrayData::alignment() +{ + QFETCH(size_t, alignment); + + // Minimum alignment that can be requested is that of QArrayData. + // Typically, this alignment is sizeof(void *) and ensured by malloc. + size_t minAlignment = qMax(alignment, Q_ALIGNOF(QArrayData)); + + Deallocator keeper(sizeof(Unaligned), minAlignment); + keeper.headers.reserve(100); + + for (int i = 0; i < 100; ++i) { + QArrayData *data = QArrayData::allocate(sizeof(Unaligned), + minAlignment, 8, QArrayData::Default); + keeper.headers.append(data); + + QVERIFY(data); + QCOMPARE(data->size, 0); + QVERIFY(data->alloc >= uint(8)); + + // These conditions should hold as long as header and array are + // allocated together + QVERIFY(data->offset >= qptrdiff(sizeof(QArrayData))); + QVERIFY(data->offset <= qptrdiff(sizeof(QArrayData) + + minAlignment - Q_ALIGNOF(QArrayData))); + + // Data is aligned + QCOMPARE(quintptr(data->data()) % alignment, quintptr(0u)); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(data->data(), 'A', sizeof(Unaligned) * 8); + } +} + +void tst_QArrayData::typedData() +{ + QStaticArrayData<int, 10> data = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } + }; + QCOMPARE(data.header.size, 10); + + { + QTypedArrayData<int> *array = + static_cast<QTypedArrayData<int> *>(&data.header); + QCOMPARE(array->data(), data.data); + + int j = 0; + for (QTypedArrayData<int>::iterator iter = array->begin(); + iter != array->end(); ++iter, ++j) + QCOMPARE(iter, data.data + j); + QCOMPARE(j, 10); + } + + { + const QTypedArrayData<int> *array = + static_cast<const QTypedArrayData<int> *>(&data.header); + + QCOMPARE(array->data(), data.data); + + int j = 0; + for (QTypedArrayData<int>::const_iterator iter = array->begin(); + iter != array->end(); ++iter, ++j) + QCOMPARE(iter, data.data + j); + QCOMPARE(j, 10); + } + + { + QTypedArrayData<int> *null = QTypedArrayData<int>::sharedNull(); + QTypedArrayData<int> *empty = QTypedArrayData<int>::allocate(0); + + QVERIFY(null != empty); + + QCOMPARE(null->size, 0); + QCOMPARE(empty->size, 0); + + QCOMPARE(null->begin(), null->end()); + QCOMPARE(empty->begin(), empty->end()); + } + + + { + Deallocator keeper(sizeof(char), + Q_ALIGNOF(QTypedArrayData<char>::AlignmentDummy)); + QArrayData *array = QTypedArrayData<char>::allocate(10); + keeper.headers.append(array); + + QVERIFY(array); + QCOMPARE(array->size, 0); + QCOMPARE(array->alloc, 10u); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(array->data(), 0, 10 * sizeof(char)); + + keeper.headers.clear(); + QTypedArrayData<short>::deallocate(array); + + QVERIFY(true); + } + + { + Deallocator keeper(sizeof(short), + Q_ALIGNOF(QTypedArrayData<short>::AlignmentDummy)); + QArrayData *array = QTypedArrayData<short>::allocate(10); + keeper.headers.append(array); + + QVERIFY(array); + QCOMPARE(array->size, 0); + QCOMPARE(array->alloc, 10u); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(array->data(), 0, 10 * sizeof(short)); + + keeper.headers.clear(); + QTypedArrayData<short>::deallocate(array); + + QVERIFY(true); + } + + { + Deallocator keeper(sizeof(double), + Q_ALIGNOF(QTypedArrayData<double>::AlignmentDummy)); + QArrayData *array = QTypedArrayData<double>::allocate(10); + keeper.headers.append(array); + + QVERIFY(array); + QCOMPARE(array->size, 0); + QCOMPARE(array->alloc, 10u); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(array->data(), 0, 10 * sizeof(double)); + + keeper.headers.clear(); + QTypedArrayData<double>::deallocate(array); + + QVERIFY(true); + } +} + +void tst_QArrayData::gccBug43247() +{ + // This test tries to verify QArrayData is not affected by GCC optimizer + // bug #43247. + // Reported on GCC 4.4.3, Linux, affects QVector + + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (3)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (4)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (5)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (6)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (7)"); + + SimpleVector<int> array(10, 0); + // QVector<int> vector(10, 0); + + for (int i = 0; i < 10; ++i) { + if (i >= 3 && i < 8) + qDebug("GCC Optimization bug #43247 not triggered (%i)", i); + + // When access to data is implemented through an array of size 1, this + // line lets the compiler assume i == 0, and the conditional above is + // skipped. + QVERIFY(array.at(i) == 0); + // QVERIFY(vector.at(i) == 0); + } +} + +struct CountedObject +{ + CountedObject() + : id(liveCount++) + , flags(DefaultConstructed) + { + } + + CountedObject(const CountedObject &other) + : id(other.id) + , flags(other.flags == DefaultConstructed + ? ObjectFlags(CopyConstructed | DefaultConstructed) + : CopyConstructed) + { + ++liveCount; + } + + ~CountedObject() + { + --liveCount; + } + + CountedObject &operator=(const CountedObject &other) + { + flags = ObjectFlags(other.flags | CopyAssigned); + id = other.id; + return *this; + } + + struct LeakChecker + { + LeakChecker() + : previousLiveCount(liveCount) + { + } + + ~LeakChecker() + { + QCOMPARE(liveCount, previousLiveCount); + } + + private: + const size_t previousLiveCount; + }; + + enum ObjectFlags { + DefaultConstructed = 1, + CopyConstructed = 2, + CopyAssigned = 4 + }; + + size_t id; // not unique + ObjectFlags flags; + + static size_t liveCount; +}; + +size_t CountedObject::liveCount = 0; + +void tst_QArrayData::arrayOps() +{ + CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker) + + const int intArray[5] = { 80, 101, 100, 114, 111 }; + const QString stringArray[5] = { + QLatin1String("just"), + QLatin1String("for"), + QLatin1String("testing"), + QLatin1String("a"), + QLatin1String("vector") + }; + const CountedObject objArray[5]; + + QVERIFY(!QTypeInfo<int>::isComplex && !QTypeInfo<int>::isStatic); + QVERIFY(QTypeInfo<QString>::isComplex && !QTypeInfo<QString>::isStatic); + QVERIFY(QTypeInfo<CountedObject>::isComplex && QTypeInfo<CountedObject>::isStatic); + + QCOMPARE(CountedObject::liveCount, size_t(5)); + for (size_t i = 0; i < 5; ++i) + QCOMPARE(objArray[i].id, i); + + //////////////////////////////////////////////////////////////////////////// + // copyAppend (I) + SimpleVector<int> vi(intArray, intArray + 5); + SimpleVector<QString> vs(stringArray, stringArray + 5); + SimpleVector<CountedObject> vo(objArray, objArray + 5); + + QCOMPARE(CountedObject::liveCount, size_t(10)); + for (int i = 0; i < 5; ++i) { + QCOMPARE(vi[i], intArray[i]); + QVERIFY(vs[i].isSharedWith(stringArray[i])); + + QCOMPARE(vo[i].id, objArray[i].id); + QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed + | CountedObject::DefaultConstructed); + } + + //////////////////////////////////////////////////////////////////////////// + // destroyAll + vi.clear(); + vs.clear(); + vo.clear(); + + QCOMPARE(CountedObject::liveCount, size_t(5)); + + //////////////////////////////////////////////////////////////////////////// + // copyAppend (II) + int referenceInt = 7; + QString referenceString = QLatin1String("reference"); + CountedObject referenceObject; + + vi = SimpleVector<int>(5, referenceInt); + vs = SimpleVector<QString>(5, referenceString); + vo = SimpleVector<CountedObject>(5, referenceObject); + + QCOMPARE(vi.size(), size_t(5)); + QCOMPARE(vs.size(), size_t(5)); + QCOMPARE(vo.size(), size_t(5)); + + QCOMPARE(CountedObject::liveCount, size_t(11)); + for (int i = 0; i < 5; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + + QCOMPARE(vo[i].id, referenceObject.id); + QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed + | CountedObject::DefaultConstructed); + } + + //////////////////////////////////////////////////////////////////////////// + // insert + vi.reserve(30); + vs.reserve(30); + vo.reserve(30); + + QCOMPARE(vi.size(), size_t(5)); + QCOMPARE(vs.size(), size_t(5)); + QCOMPARE(vo.size(), size_t(5)); + + QVERIFY(vi.capacity() >= 30); + QVERIFY(vs.capacity() >= 30); + QVERIFY(vo.capacity() >= 30); + + // Displace as many elements as array is extended by + vi.insert(0, intArray, intArray + 5); + vs.insert(0, stringArray, stringArray + 5); + vo.insert(0, objArray, objArray + 5); + + QCOMPARE(vi.size(), size_t(10)); + QCOMPARE(vs.size(), size_t(10)); + QCOMPARE(vo.size(), size_t(10)); + + // Displace more elements than array is extended by + vi.insert(0, intArray, intArray + 5); + vs.insert(0, stringArray, stringArray + 5); + vo.insert(0, objArray, objArray + 5); + + QCOMPARE(vi.size(), size_t(15)); + QCOMPARE(vs.size(), size_t(15)); + QCOMPARE(vo.size(), size_t(15)); + + // Displace less elements than array is extended by + vi.insert(5, vi.constBegin(), vi.constEnd()); + vs.insert(5, vs.constBegin(), vs.constEnd()); + vo.insert(5, vo.constBegin(), vo.constEnd()); + + QCOMPARE(vi.size(), size_t(30)); + QCOMPARE(vs.size(), size_t(30)); + QCOMPARE(vo.size(), size_t(30)); + + QCOMPARE(CountedObject::liveCount, size_t(36)); + for (int i = 0; i < 5; ++i) { + QCOMPARE(vi[i], intArray[i % 5]); + QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); + + QCOMPARE(vo[i].id, objArray[i % 5].id); + QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed + | CountedObject::CopyAssigned); + } + + for (int i = 5; i < 15; ++i) { + QCOMPARE(vi[i], intArray[i % 5]); + QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); + + QCOMPARE(vo[i].id, objArray[i % 5].id); + QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed + | CountedObject::CopyAssigned); + } + + for (int i = 15; i < 20; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + + QCOMPARE(vo[i].id, referenceObject.id); + QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed + | CountedObject::CopyAssigned); + } + + for (int i = 20; i < 25; ++i) { + QCOMPARE(vi[i], intArray[i % 5]); + QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); + + QCOMPARE(vo[i].id, objArray[i % 5].id); + + // Originally inserted as (DefaultConstructed | CopyAssigned), later + // get shuffled into place by std::rotate (SimpleVector::insert, + // overlapping mode). + // Depending on implementation of rotate, final assignment can be: + // - straight from source: DefaultConstructed | CopyAssigned + // - through a temporary: CopyConstructed | CopyAssigned + QCOMPARE(vo[i].flags & CountedObject::CopyAssigned, + int(CountedObject::CopyAssigned)); + } + + for (int i = 25; i < 30; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + + QCOMPARE(vo[i].id, referenceObject.id); + QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed + | CountedObject::CopyAssigned); + } +} + +void tst_QArrayData::arrayOps2() +{ + CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker) + + //////////////////////////////////////////////////////////////////////////// + // appendInitialize + SimpleVector<int> vi(5); + SimpleVector<QString> vs(5); + SimpleVector<CountedObject> vo(5); + + QCOMPARE(vi.size(), size_t(5)); + QCOMPARE(vs.size(), size_t(5)); + QCOMPARE(vo.size(), size_t(5)); + + QCOMPARE(CountedObject::liveCount, size_t(5)); + for (size_t i = 0; i < 5; ++i) { + QCOMPARE(vi[i], 0); + QVERIFY(vs[i].isNull()); + + QCOMPARE(vo[i].id, i); + QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed)); + } + + //////////////////////////////////////////////////////////////////////////// + // appendInitialize, again + + // These will detach + vi.resize(10); + vs.resize(10); + vo.resize(10); + + QCOMPARE(vi.size(), size_t(10)); + QCOMPARE(vs.size(), size_t(10)); + QCOMPARE(vo.size(), size_t(10)); + + QCOMPARE(CountedObject::liveCount, size_t(10)); + for (size_t i = 0; i < 5; ++i) { + QCOMPARE(vi[i], 0); + QVERIFY(vs[i].isNull()); + + QCOMPARE(vo[i].id, i); + QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed + | CountedObject::CopyConstructed); + } + + for (size_t i = 5; i < 10; ++i) { + QCOMPARE(vi[i], 0); + QVERIFY(vs[i].isNull()); + + QCOMPARE(vo[i].id, i + 5); + QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed)); + } + + //////////////////////////////////////////////////////////////////////////// + // truncate + QVERIFY(!vi.isShared()); + QVERIFY(!vs.isShared()); + QVERIFY(!vo.isShared()); + + // These shouldn't detach + vi.resize(7); + vs.resize(7); + vo.resize(7); + + QCOMPARE(vi.size(), size_t(7)); + QCOMPARE(vs.size(), size_t(7)); + QCOMPARE(vo.size(), size_t(7)); + + QCOMPARE(CountedObject::liveCount, size_t(7)); + for (size_t i = 0; i < 5; ++i) { + QCOMPARE(vi[i], 0); + QVERIFY(vs[i].isNull()); + + QCOMPARE(vo[i].id, i); + QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed + | CountedObject::CopyConstructed); + } + + for (size_t i = 5; i < 7; ++i) { + QCOMPARE(vi[i], 0); + QVERIFY(vs[i].isNull()); + + QCOMPARE(vo[i].id, i + 5); + QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed)); + } +} + +Q_DECLARE_METATYPE(QArrayDataPointer<int>) + +static inline bool arrayIsFilledWith(const QArrayDataPointer<int> &array, + int fillValue, size_t size) +{ + const int *iter = array->begin(); + const int *const end = array->end(); + + for (size_t i = 0; i < size; ++i, ++iter) + if (*iter != fillValue) + return false; + + if (iter != end) + return false; + + return true; +} + +void tst_QArrayData::setSharable_data() +{ + QTest::addColumn<QArrayDataPointer<int> >("array"); + QTest::addColumn<size_t>("size"); + QTest::addColumn<size_t>("capacity"); + QTest::addColumn<bool>("isCapacityReserved"); + QTest::addColumn<int>("fillValue"); + + QArrayDataPointer<int> null; + QArrayDataPointer<int> empty; empty.clear(); + + static QStaticArrayData<int, 10> staticArrayData = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 } + }; + + QArrayDataPointer<int> emptyReserved(QTypedArrayData<int>::allocate(5, + QArrayData::CapacityReserved)); + QArrayDataPointer<int> nonEmpty(QTypedArrayData<int>::allocate(5, + QArrayData::Default)); + QArrayDataPointer<int> nonEmptyExtraCapacity( + QTypedArrayData<int>::allocate(10, QArrayData::Default)); + QArrayDataPointer<int> nonEmptyReserved(QTypedArrayData<int>::allocate(15, + QArrayData::CapacityReserved)); + QArrayDataPointer<int> staticArray( + static_cast<QTypedArrayData<int> *>(&staticArrayData.header)); + QArrayDataPointer<int> rawData( + QTypedArrayData<int>::fromRawData(staticArrayData.data, 10)); + + nonEmpty->copyAppend(5, 1); + nonEmptyExtraCapacity->copyAppend(5, 1); + nonEmptyReserved->copyAppend(7, 2); + + QTest::newRow("shared-null") << null << size_t(0) << size_t(0) << false << 0; + QTest::newRow("shared-empty") << empty << size_t(0) << size_t(0) << false << 0; + // unsharable-empty implicitly tested in shared-empty + QTest::newRow("empty-reserved") << emptyReserved << size_t(0) << size_t(5) << true << 0; + QTest::newRow("non-empty") << nonEmpty << size_t(5) << size_t(5) << false << 1; + QTest::newRow("non-empty-extra-capacity") << nonEmptyExtraCapacity << size_t(5) << size_t(10) << false << 1; + QTest::newRow("non-empty-reserved") << nonEmptyReserved << size_t(7) << size_t(15) << true << 2; + QTest::newRow("static-array") << staticArray << size_t(10) << size_t(0) << false << 3; + QTest::newRow("raw-data") << rawData << size_t(10) << size_t(0) << false << 3; +} + +void tst_QArrayData::setSharable() +{ + QFETCH(QArrayDataPointer<int>, array); + QFETCH(size_t, size); + QFETCH(size_t, capacity); + QFETCH(bool, isCapacityReserved); + QFETCH(int, fillValue); + + QVERIFY(array->ref.isShared()); // QTest has a copy + QVERIFY(array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + // shared-null becomes shared-empty, may otherwise detach + array.setSharable(true); + + QVERIFY(array->ref.isSharable()); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(array->ref.isShared()); + QVERIFY(array->ref.isSharable()); + QCOMPARE(copy.data(), array.data()); + } + + // Unshare, must detach + array.setSharable(false); + + // Immutability (alloc == 0) is lost on detach, as is additional capacity + // if capacityReserved flag is not set. + if ((capacity == 0 && size != 0) + || (!isCapacityReserved && capacity > size)) + capacity = size; + + QVERIFY(!array->ref.isShared()); + QVERIFY(!array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(!array->ref.isShared()); + QVERIFY(!array->ref.isSharable()); + + // Null/empty is always shared + QCOMPARE(copy->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(copy->ref.isSharable()); + + QCOMPARE(size_t(copy->size), size); + QCOMPARE(size_t(copy->alloc), capacity); + QCOMPARE(bool(copy->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(copy, fillValue, size)); + } + + // Make sharable, again + array.setSharable(true); + + QCOMPARE(array->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(array->ref.isShared()); + QCOMPARE(copy.data(), array.data()); + } + + QCOMPARE(array->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(array->ref.isSharable()); +} + +void tst_QArrayData::fromRawData() +{ + static const int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + { + // Default: Immutable, sharable + SimpleVector<int> raw = SimpleVector<int>::fromRawData(array, + sizeof(array)/sizeof(array[0]), QArrayData::Default); + + QCOMPARE(raw.size(), size_t(11)); + QCOMPARE(raw.constBegin(), array); + QCOMPARE((void *)raw.constEnd(), (void *)(array + sizeof(array)/sizeof(array[0]))); + + QVERIFY(!raw.isShared()); + QVERIFY(SimpleVector<int>(raw).isSharedWith(raw)); + QVERIFY(!raw.isShared()); + + // Detach + QCOMPARE(raw.back(), 11); + QVERIFY(raw.constBegin() != array); + } + + { + // Immutable, unsharable + SimpleVector<int> raw = SimpleVector<int>::fromRawData(array, + sizeof(array)/sizeof(array[0]), QArrayData::Unsharable); + + QCOMPARE(raw.size(), size_t(11)); + QCOMPARE(raw.constBegin(), array); + QCOMPARE((void *)raw.constEnd(), (void *)(array + sizeof(array)/sizeof(array[0]))); + + SimpleVector<int> copy(raw); + QVERIFY(!copy.isSharedWith(raw)); + QVERIFY(!raw.isShared()); + + QCOMPARE(copy.size(), size_t(11)); + + for (size_t i = 0; i < 11; ++i) + QCOMPARE(const_(copy)[i], const_(raw)[i]); + + QCOMPARE(raw.size(), size_t(11)); + QCOMPARE(raw.constBegin(), array); + QCOMPARE((void *)raw.constEnd(), (void *)(array + sizeof(array)/sizeof(array[0]))); + + // Detach + QCOMPARE(raw.back(), 11); + QVERIFY(raw.constBegin() != array); + } +} + +void tst_QArrayData::literals() +{ + { + QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ"); + QCOMPARE(d->size, 10 + 1); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], char('A' + i)); + } + + { + // wchar_t is not necessarily 2-bytes + QArrayDataPointer<wchar_t> d = Q_ARRAY_LITERAL(wchar_t, L"ABCDEFGHIJ"); + QCOMPARE(d->size, 10 + 1); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], wchar_t('A' + i)); + } + + { + SimpleVector<char> v = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ"); + + QVERIFY(!v.isNull()); + QVERIFY(!v.isEmpty()); + QCOMPARE(v.size(), size_t(11)); + // v.capacity() is unspecified, for now + +#if defined(Q_COMPILER_VARIADIC_MACROS) \ + && (defined(Q_COMPILER_LAMBDA) || defined(Q_CC_GNU)) + QVERIFY(v.isStatic()); +#endif + + QVERIFY(v.isSharable()); + QCOMPARE((void *)(v.constBegin() + v.size()), (void *)v.constEnd()); + + for (int i = 0; i < 10; ++i) + QCOMPARE(const_(v)[i], char('A' + i)); + QCOMPARE(const_(v)[10], char('\0')); + } +} + +void tst_QArrayData::variadicLiterals() +{ +#if defined(Q_COMPILER_VARIADIC_MACROS) \ + && (defined(Q_COMPILER_LAMBDA) || defined(Q_CC_GNU)) + { + QArrayDataPointer<int> d = + Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + QCOMPARE(d->size, 10); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], i); + } + + { + QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char, + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'); + QCOMPARE(d->size, 10); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], char('A' + i)); + } + + { + QArrayDataPointer<const char *> d = Q_ARRAY_LITERAL(const char *, + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J"); + QCOMPARE(d->size, 10); + for (int i = 0; i < 10; ++i) { + QCOMPARE(d->data()[i][0], char('A' + i)); + QCOMPARE(d->data()[i][1], '\0'); + } + } + + { + SimpleVector<int> v = Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6); + + QVERIFY(!v.isNull()); + QVERIFY(!v.isEmpty()); + QCOMPARE(v.size(), size_t(7)); + // v.capacity() is unspecified, for now + + QVERIFY(v.isStatic()); + + QVERIFY(v.isSharable()); + QCOMPARE((void *)(v.constBegin() + v.size()), (void *)v.constEnd()); + + for (int i = 0; i < 7; ++i) + QCOMPARE(const_(v)[i], i); + } +#else + QSKIP("Variadic Q_ARRAY_LITERAL not available in current configuration."); +#endif // defined(Q_COMPILER_VARIADIC_MACROS) +} + +#ifdef Q_COMPILER_RVALUE_REFS +// std::remove_reference is in C++11, but requires library support +template <class T> struct RemoveReference { typedef T Type; }; +template <class T> struct RemoveReference<T &> { typedef T Type; }; + +// single-argument std::move is in C++11, but requires library support +template <class T> +typename RemoveReference<T>::Type &&cxx11Move(T &&t) +{ + return static_cast<typename RemoveReference<T>::Type &&>(t); +} + +struct CompilerHasCxx11ImplicitMoves +{ + static bool value() + { + DetectImplicitMove d(cxx11Move(DetectImplicitMove())); + return d.constructor == DetectConstructor::MoveConstructor; + } + + struct DetectConstructor + { + Q_DECL_CONSTEXPR DetectConstructor() + : constructor(DefaultConstructor) + { + } + + Q_DECL_CONSTEXPR DetectConstructor(const DetectConstructor &) + : constructor(CopyConstructor) + { + } + + Q_DECL_CONSTEXPR DetectConstructor(DetectConstructor &&) + : constructor(MoveConstructor) + { + } + + enum Constructor { + DefaultConstructor, + CopyConstructor, + MoveConstructor + }; + + Constructor constructor; + }; + + struct DetectImplicitMove + : DetectConstructor + { + }; +}; +#endif + +void tst_QArrayData::rValueReferences() +{ +#ifdef Q_COMPILER_RVALUE_REFS + if (!CompilerHasCxx11ImplicitMoves::value()) + QSKIP("Implicit move ctor not supported in current configuration"); + + SimpleVector<int> v1(1, 42); + SimpleVector<int> v2; + + const SimpleVector<int>::const_iterator begin = v1.constBegin(); + + QVERIFY(!v1.isNull()); + QVERIFY(v2.isNull()); + + // move-assign + v2 = cxx11Move(v1); + + QVERIFY(v1.isNull()); + QVERIFY(!v2.isNull()); + QCOMPARE(v2.constBegin(), begin); + + SimpleVector<int> v3(cxx11Move(v2)); + + QVERIFY(v1.isNull()); + QVERIFY(v2.isNull()); + QVERIFY(!v3.isNull()); + QCOMPARE(v3.constBegin(), begin); + + QCOMPARE(v3.size(), size_t(1)); + QCOMPARE(v3.front(), 42); +#else + QSKIP("RValue references are not supported in current configuration"); +#endif +} + +void tst_QArrayData::grow() +{ + SimpleVector<int> vector; + + QCOMPARE(vector.size(), size_t(0)); + + size_t previousCapacity = vector.capacity(); + size_t allocations = 0; + for (size_t i = 1; i <= (1 << 20); ++i) { + int source[1] = { int(i) }; + vector.append(source, source + 1); + QCOMPARE(vector.size(), i); + if (vector.capacity() != previousCapacity) { + previousCapacity = vector.capacity(); + ++allocations; + } + } + QCOMPARE(vector.size(), size_t(1 << 20)); + + // QArrayData::Grow prevents excessive allocations on a growing container + QVERIFY(allocations > 20 / 2); + QVERIFY(allocations < 20 * 2); + + for (size_t i = 0; i < (1 << 20); ++i) + QCOMPARE(const_(vector).at(i), int(i + 1)); +} + +QTEST_APPLESS_MAIN(tst_QArrayData) +#include "tst_qarraydata.moc" diff --git a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp index 5e53683abd..267aa71085 100644 --- a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp +++ b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp @@ -43,6 +43,7 @@ #include <qbytearray.h> #include <qfile.h> +#include <qhash.h> #include <limits.h> #include <private/qtools_p.h> #if defined(Q_OS_WINCE) @@ -91,8 +92,14 @@ private slots: void chop_data(); void chop(); void prepend(); + void prependExtended_data(); + void prependExtended(); void append(); + void appendExtended_data(); + void appendExtended(); void insert(); + void insertExtended_data(); + void insertExtended(); void remove_data(); void remove(); void replace_data(); @@ -117,6 +124,12 @@ private slots: void toFromHex_data(); void toFromHex(); void toFromPercentEncoding(); + void fromPercentEncoding_data(); + void fromPercentEncoding(); + void toPercentEncoding_data(); + void toPercentEncoding(); + void toPercentEncoding2_data(); + void toPercentEncoding2(); void compare_data(); void compare(); @@ -130,11 +143,103 @@ private slots: void byteRefDetaching() const; void reserve(); + void reserveExtended_data(); + void reserveExtended(); void movablity_data(); void movablity(); void literals(); }; +static const struct StaticByteArrays { + struct Standard { + QByteArrayData data; + const char string[8]; + } standard; + struct NotNullTerminated { + QByteArrayData data; + const char string[8]; + } notNullTerminated; + struct Shifted { + QByteArrayData data; + const char dummy; // added to change offset of string + const char string[8]; + } shifted; + struct ShiftedNotNullTerminated { + QByteArrayData data; + const char dummy; // added to change offset of string + const char string[8]; + } shiftedNotNullTerminated; + +} statics = {{{ Q_REFCOUNT_INITIALIZE_STATIC, /* length = */ 4, 0, 0, sizeof(QByteArrayData) }, "data"} + ,{{ Q_REFCOUNT_INITIALIZE_STATIC, /* length = */ 4, 0, 0, sizeof(QByteArrayData) }, "dataBAD"} + ,{{ Q_REFCOUNT_INITIALIZE_STATIC, /* length = */ 4, 0, 0, sizeof(QByteArrayData) + sizeof(char) }, 0, "data"} + ,{{ Q_REFCOUNT_INITIALIZE_STATIC, /* length = */ 4, 0, 0, sizeof(QByteArrayData) + sizeof(char) }, 0, "dataBAD"} + }; + +static const QByteArrayDataPtr staticStandard = { const_cast<QByteArrayData *>(&statics.standard.data) }; +static const QByteArrayDataPtr staticNotNullTerminated = { const_cast<QByteArrayData *>(&statics.notNullTerminated.data) }; +static const QByteArrayDataPtr staticShifted = { const_cast<QByteArrayData *>(&statics.shifted.data) }; +static const QByteArrayDataPtr staticShiftedNotNullTerminated = { const_cast<QByteArrayData *>(&statics.shiftedNotNullTerminated.data) }; + +template <class T> const T &verifyZeroTermination(const T &t) { return t; } + +QByteArray verifyZeroTermination(const QByteArray &ba) +{ + // This test does some evil stuff, it's all supposed to work. + + QByteArray::DataPtr baDataPtr = const_cast<QByteArray &>(ba).data_ptr(); + + // Skip if isStatic() or fromRawData(), as those offer no guarantees + if (baDataPtr->ref.isStatic() + || baDataPtr->offset != QByteArray().data_ptr()->offset) + return ba; + + int baSize = ba.size(); + char baTerminator = ba.constData()[baSize]; + if ('\0' != baTerminator) + return QString::fromAscii( + "*** Result ('%1') not null-terminated: 0x%2 ***").arg(QString::fromAscii(ba)) + .arg(baTerminator, 2, 16, QChar('0')).toAscii(); + + // Skip mutating checks on shared strings + if (baDataPtr->ref.isShared()) + return ba; + + const char *baData = ba.constData(); + const QByteArray baCopy(baData, baSize); // Deep copy + + const_cast<char *>(baData)[baSize] = 'x'; + if ('x' != ba.constData()[baSize]) { + return QString::fromAscii("*** Failed to replace null-terminator in " + "result ('%1') ***").arg(QString::fromAscii(ba)).toAscii(); + } + if (ba != baCopy) { + return QString::fromAscii( "*** Result ('%1') differs from its copy " + "after null-terminator was replaced ***").arg(QString::fromAscii(ba)).toAscii(); + } + const_cast<char *>(baData)[baSize] = '\0'; // Restore sanity + + return ba; +} + +// Overriding QTest's QCOMPARE, to check QByteArray for null termination +#undef QCOMPARE +#define QCOMPARE(actual, expected) \ + do { \ + if (!QTest::qCompare(verifyZeroTermination(actual), expected, \ + #actual, #expected, __FILE__, __LINE__)) \ + return; \ + } while (0) \ + /**/ +#undef QTEST +#define QTEST(actual, testElement) \ + do { \ + if (!QTest::qTest(verifyZeroTermination(actual), testElement, \ + #actual, #testElement, __FILE__, __LINE__)) \ + return; \ + } while (0) \ + /**/ + tst_QByteArray::tst_QByteArray() { qRegisterMetaType<qulonglong>("qulonglong"); @@ -517,7 +622,7 @@ void tst_QByteArray::fromBase64() void tst_QByteArray::qvsnprintf() { char buf[20]; - qMemSet(buf, 42, sizeof(buf)); + memset(buf, 42, sizeof(buf)); QCOMPARE(::qsnprintf(buf, 10, "%s", "bubu"), 4); QCOMPARE(static_cast<const char *>(buf), "bubu"); @@ -526,12 +631,12 @@ void tst_QByteArray::qvsnprintf() QCOMPARE(buf[5], char(42)); #endif - qMemSet(buf, 42, sizeof(buf)); + memset(buf, 42, sizeof(buf)); QCOMPARE(::qsnprintf(buf, 5, "%s", "bubu"), 4); QCOMPARE(static_cast<const char *>(buf), "bubu"); QCOMPARE(buf[5], char(42)); - qMemSet(buf, 42, sizeof(buf)); + memset(buf, 42, sizeof(buf)); #ifdef Q_OS_WIN // VS 2005 uses the Qt implementation of vsnprintf. # if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_OS_WINCE) @@ -556,7 +661,7 @@ void tst_QByteArray::qvsnprintf() QCOMPARE(buf[4], char(42)); #ifndef Q_OS_WIN - qMemSet(buf, 42, sizeof(buf)); + memset(buf, 42, sizeof(buf)); QCOMPARE(::qsnprintf(buf, 10, ""), 0); #endif } @@ -701,6 +806,35 @@ void tst_QByteArray::prepend() QCOMPARE(ba.prepend("\0 ", 2), QByteArray::fromRawData("\0 321foo", 8)); } +void tst_QByteArray::prependExtended_data() +{ + QTest::addColumn<QByteArray>("array"); + QTest::newRow("literal") << QByteArray(QByteArrayLiteral("data")); + QTest::newRow("standard") << QByteArray(staticStandard); + QTest::newRow("shifted") << QByteArray(staticShifted); + QTest::newRow("notNullTerminated") << QByteArray(staticNotNullTerminated); + QTest::newRow("shiftedNotNullTerminated") << QByteArray(staticShiftedNotNullTerminated); + QTest::newRow("non static data") << QByteArray("data"); + QTest::newRow("from raw data") << QByteArray::fromRawData("data", 4); + QTest::newRow("from raw data not terminated") << QByteArray::fromRawData("dataBAD", 4); +} + +void tst_QByteArray::prependExtended() +{ + QFETCH(QByteArray, array); + + QCOMPARE(QByteArray().prepend(array), QByteArray("data")); + QCOMPARE(QByteArray("").prepend(array), QByteArray("data")); + + QCOMPARE(array.prepend((char*)0), QByteArray("data")); + QCOMPARE(array.prepend(QByteArray()), QByteArray("data")); + QCOMPARE(array.prepend("1"), QByteArray("1data")); + QCOMPARE(array.prepend(QByteArray("2")), QByteArray("21data")); + QCOMPARE(array.prepend('3'), QByteArray("321data")); + QCOMPARE(array.prepend("\0 ", 2), QByteArray::fromRawData("\0 321data", 9)); + QCOMPARE(array.size(), 9); +} + void tst_QByteArray::append() { QByteArray ba("foo"); @@ -714,6 +848,28 @@ void tst_QByteArray::append() QCOMPARE(ba.size(), 7); } +void tst_QByteArray::appendExtended_data() +{ + prependExtended_data(); +} + +void tst_QByteArray::appendExtended() +{ + QFETCH(QByteArray, array); + + QCOMPARE(QByteArray().append(array), QByteArray("data")); + QCOMPARE(QByteArray("").append(array), QByteArray("data")); + + QCOMPARE(array.append((char*)0), QByteArray("data")); + QCOMPARE(array.append(QByteArray()), QByteArray("data")); + QCOMPARE(array.append("1"), QByteArray("data1")); + QCOMPARE(array.append(QByteArray("2")), QByteArray("data12")); + QCOMPARE(array.append('3'), QByteArray("data123")); + QCOMPARE(array.append("\0"), QByteArray("data123")); + QCOMPARE(array.append("\0", 1), QByteArray::fromRawData("data123\0", 8)); + QCOMPARE(array.size(), 8); +} + void tst_QByteArray::insert() { QByteArray ba("Meal"); @@ -736,6 +892,18 @@ void tst_QByteArray::insert() QCOMPARE(ba.size(), 5); } +void tst_QByteArray::insertExtended_data() +{ + prependExtended_data(); +} + +void tst_QByteArray::insertExtended() +{ + QFETCH(QByteArray, array); + QCOMPARE(array.insert(1, "i"), QByteArray("diata")); + QCOMPARE(array.size(), 5); +} + void tst_QByteArray::remove_data() { QTest::addColumn<QByteArray>("src"); @@ -1078,18 +1246,25 @@ void tst_QByteArray::toULongLong() // global function defined in qbytearray.cpp void tst_QByteArray::qAllocMore() { - static const int t[] = { - INT_MIN, INT_MIN + 1, -1234567, -66000, -1025, - -3, -1, 0, +1, +3, +1025, +66000, +1234567, INT_MAX - 1, INT_MAX, - INT_MAX/3 - }; - static const int N = sizeof(t)/sizeof(t[0]); - - // make sure qAllocMore() doesn't loop infinitely on any input - for (int i = 0; i < N; ++i) { - for (int j = 0; j < N; ++j) { - ::qAllocMore(t[i], t[j]); - } + using QT_PREPEND_NAMESPACE(qAllocMore); + + // Not very important, but please behave :-) + QVERIFY(qAllocMore(0, 0) >= 0); + + for (int i = 1; i < 1 << 8; i <<= 1) + QVERIFY(qAllocMore(i, 0) >= i); + + for (int i = 1 << 8; i < 1 << 30; i <<= 1) { + const int alloc = qAllocMore(i, 0); + + QVERIFY(alloc >= i); + QCOMPARE(qAllocMore(i - 8, 8), alloc - 8); + QCOMPARE(qAllocMore(i - 16, 16), alloc - 16); + QCOMPARE(qAllocMore(i - 24, 24), alloc - 24); + QCOMPARE(qAllocMore(i - 32, 32), alloc - 32); + + QVERIFY(qAllocMore(i - 1, 0) >= i - 1); + QVERIFY(qAllocMore(i + 1, 0) >= i + 1); } } @@ -1111,7 +1286,7 @@ void tst_QByteArray::appendAfterFromRawData() arr += QByteArray::fromRawData(data, sizeof(data)); data[0] = 'Y'; } - QVERIFY(arr.at(0) == 'X'); + QCOMPARE(arr.at(0), 'X'); } void tst_QByteArray::toFromHex_data() @@ -1231,6 +1406,91 @@ void tst_QByteArray::toFromPercentEncoding() QVERIFY(QByteArray::fromPercentEncoding(QByteArray()).isNull()); } +void tst_QByteArray::fromPercentEncoding_data() +{ + QTest::addColumn<QByteArray>("encodedString"); + QTest::addColumn<QByteArray>("decodedString"); + + QTest::newRow("NormalString") << QByteArray("filename") << QByteArray("filename"); + QTest::newRow("NormalStringEncoded") << QByteArray("file%20name") << QByteArray("file name"); + QTest::newRow("JustEncoded") << QByteArray("%20") << QByteArray(" "); + QTest::newRow("HTTPUrl") << QByteArray("http://qt.nokia.com") << QByteArray("http://qt.nokia.com"); + QTest::newRow("HTTPUrlEncoded") << QByteArray("http://qt%20nokia%20com") << QByteArray("http://qt nokia com"); + QTest::newRow("EmptyString") << QByteArray("") << QByteArray(""); + QTest::newRow("Task27166") << QByteArray("Fran%C3%A7aise") << QByteArray("Française"); +} + +void tst_QByteArray::fromPercentEncoding() +{ + QFETCH(QByteArray, encodedString); + QFETCH(QByteArray, decodedString); + + QCOMPARE(QByteArray::fromPercentEncoding(encodedString), decodedString); +} + +void tst_QByteArray::toPercentEncoding_data() +{ + QTest::addColumn<QByteArray>("decodedString"); + QTest::addColumn<QByteArray>("encodedString"); + + QTest::newRow("NormalString") << QByteArray("filename") << QByteArray("filename"); + QTest::newRow("NormalStringEncoded") << QByteArray("file name") << QByteArray("file%20name"); + QTest::newRow("JustEncoded") << QByteArray(" ") << QByteArray("%20"); + QTest::newRow("HTTPUrl") << QByteArray("http://qt.nokia.com") << QByteArray("http%3A//qt.nokia.com"); + QTest::newRow("HTTPUrlEncoded") << QByteArray("http://qt nokia com") << QByteArray("http%3A//qt%20nokia%20com"); + QTest::newRow("EmptyString") << QByteArray("") << QByteArray(""); + QTest::newRow("Task27166") << QByteArray("Française") << QByteArray("Fran%C3%A7aise"); +} + +void tst_QByteArray::toPercentEncoding() +{ + QFETCH(QByteArray, decodedString); + QFETCH(QByteArray, encodedString); + + QCOMPARE(decodedString.toPercentEncoding("/.").constData(), encodedString.constData()); +} + +void tst_QByteArray::toPercentEncoding2_data() +{ + QTest::addColumn<QByteArray>("original"); + QTest::addColumn<QByteArray>("encoded"); + QTest::addColumn<QByteArray>("excludeInEncoding"); + QTest::addColumn<QByteArray>("includeInEncoding"); + + QTest::newRow("test_01") << QByteArray("abcdevghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678-._~") + << QByteArray("abcdevghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678-._~") + << QByteArray("") + << QByteArray(""); + QTest::newRow("test_02") << QByteArray("{\t\n\r^\"abc}") + << QByteArray("%7B%09%0A%0D%5E%22abc%7D") + << QByteArray("") + << QByteArray(""); + QTest::newRow("test_03") << QByteArray("://?#[]@!$&'()*+,;=") + << QByteArray("%3A%2F%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D") + << QByteArray("") + << QByteArray(""); + QTest::newRow("test_04") << QByteArray("://?#[]@!$&'()*+,;=") + << QByteArray("%3A%2F%2F%3F%23%5B%5D%40!$&'()*+,;=") + << QByteArray("!$&'()*+,;=") + << QByteArray(""); + QTest::newRow("test_05") << QByteArray("abcd") + << QByteArray("a%62%63d") + << QByteArray("") + << QByteArray("bc"); +} + +void tst_QByteArray::toPercentEncoding2() +{ + QFETCH(QByteArray, original); + QFETCH(QByteArray, encoded); + QFETCH(QByteArray, excludeInEncoding); + QFETCH(QByteArray, includeInEncoding); + + QByteArray encodedData = original.toPercentEncoding(excludeInEncoding, includeInEncoding); + QCOMPARE(encodedData.constData(), encoded.constData()); + QCOMPARE(original, QByteArray::fromPercentEncoding(encodedData)); +} + void tst_QByteArray::compare_data() { QTest::addColumn<QByteArray>("str1"); @@ -1298,6 +1558,9 @@ void tst_QByteArray::compare() QCOMPARE(str2 <= str1, isGreater || isEqual); QCOMPARE(str2 >= str1, isLess || isEqual); QCOMPARE(str2 != str1, !isEqual); + + if (isEqual) + QVERIFY(qHash(str1) == qHash(str2)); } void tst_QByteArray::compareCharStar_data() @@ -1449,6 +1712,23 @@ void tst_QByteArray::repeated_data() const << QByteArray(("abc")) << QByteArray(("abcabcabcabc")) << 4; + + QTest::newRow("static not null terminated") + << QByteArray(staticNotNullTerminated) + << QByteArray("datadatadatadata") + << 4; + QTest::newRow("static standard") + << QByteArray(staticStandard) + << QByteArray("datadatadatadata") + << 4; + QTest::newRow("static shifted not null terminated") + << QByteArray(staticShiftedNotNullTerminated) + << QByteArray("datadatadatadata") + << 4; + QTest::newRow("static shifted") + << QByteArray(staticShifted) + << QByteArray("datadatadatadata") + << 4; } void tst_QByteArray::byteRefDetaching() const @@ -1501,6 +1781,22 @@ void tst_QByteArray::reserve() nil2.reserve(0); } +void tst_QByteArray::reserveExtended_data() +{ + prependExtended_data(); +} + +void tst_QByteArray::reserveExtended() +{ + QFETCH(QByteArray, array); + array.reserve(1024); + QVERIFY(array.capacity() == 1024); + QCOMPARE(array, QByteArray("data")); + array.squeeze(); + QCOMPARE(array, QByteArray("data")); + QCOMPARE(array.capacity(), array.size()); +} + void tst_QByteArray::movablity_data() { QTest::addColumn<QByteArray>("array"); @@ -1511,6 +1807,8 @@ void tst_QByteArray::movablity_data() QTest::newRow("empty") << QByteArray(""); QTest::newRow("null") << QByteArray(); QTest::newRow("sss") << QByteArray(3, 's'); + + prependExtended_data(); } void tst_QByteArray::movablity() @@ -1587,8 +1885,8 @@ void tst_QByteArray::literals() QVERIFY(str.length() == 4); QVERIFY(str == "abcd"); - QVERIFY(str.data_ptr()->ref == -1); - QVERIFY(str.data_ptr()->offset == 0); + QVERIFY(str.data_ptr()->ref.isStatic()); + QVERIFY(str.data_ptr()->offset == sizeof(QByteArrayData)); const char *s = str.constData(); QByteArray str2 = str; diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index 3b5d15dbfc..5bd13b23a3 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -73,6 +73,7 @@ private slots: void noNeedlessRehashes(); void const_shared_null(); + void twoArguments_qHash(); }; struct Foo { @@ -525,14 +526,14 @@ void tst_QHash::key() hash2.insert(3, "two"); QCOMPARE(hash2.key("one"), 1); QCOMPARE(hash2.key("one", def), 1); - QCOMPARE(hash2.key("two"), 2); - QCOMPARE(hash2.key("two", def), 2); + QVERIFY(hash2.key("two") == 2 || hash2.key("two") == 3); + QVERIFY(hash2.key("two", def) == 2 || hash2.key("two", def) == 3); QCOMPARE(hash2.key("three"), 0); QCOMPARE(hash2.key("three", def), def); hash2.insert(-1, "two"); - QCOMPARE(hash2.key("two"), -1); - QCOMPARE(hash2.key("two", def), -1); + QVERIFY(hash2.key("two") == 2 || hash2.key("two") == 3 || hash2.key("two") == -1); + QVERIFY(hash2.key("two", def) == 2 || hash2.key("two", def) == 3 || hash2.key("two", def) == -1); hash2.insert(0, "zero"); QCOMPARE(hash2.key("zero"), 0); @@ -1203,5 +1204,101 @@ void tst_QHash::const_shared_null() QVERIFY(!hash2.isDetached()); } +// This gets set to != 0 in wrong qHash overloads +static int wrongqHashOverload = 0; + +struct OneArgumentQHashStruct1 {}; +bool operator==(const OneArgumentQHashStruct1 &, const OneArgumentQHashStruct1 &) { return false; } +uint qHash(OneArgumentQHashStruct1) { return 0; } + +struct OneArgumentQHashStruct2 {}; +bool operator==(const OneArgumentQHashStruct2 &, const OneArgumentQHashStruct2 &) { return false; } +uint qHash(const OneArgumentQHashStruct2 &) { return 0; } + +struct OneArgumentQHashStruct3 {}; +bool operator==(const OneArgumentQHashStruct3 &, const OneArgumentQHashStruct3 &) { return false; } +uint qHash(OneArgumentQHashStruct3) { return 0; } +uint qHash(OneArgumentQHashStruct3 &, uint) { wrongqHashOverload = 1; return 0; } + +struct OneArgumentQHashStruct4 {}; +bool operator==(const OneArgumentQHashStruct4 &, const OneArgumentQHashStruct4 &) { return false; } +uint qHash(const OneArgumentQHashStruct4 &) { return 0; } +uint qHash(OneArgumentQHashStruct4 &, uint) { wrongqHashOverload = 1; return 0; } + + +struct TwoArgumentsQHashStruct1 {}; +bool operator==(const TwoArgumentsQHashStruct1 &, const TwoArgumentsQHashStruct1 &) { return false; } +uint qHash(const TwoArgumentsQHashStruct1 &) { wrongqHashOverload = 1; return 0; } +uint qHash(const TwoArgumentsQHashStruct1 &, uint) { return 0; } + +struct TwoArgumentsQHashStruct2 {}; +bool operator==(const TwoArgumentsQHashStruct2 &, const TwoArgumentsQHashStruct2 &) { return false; } +uint qHash(TwoArgumentsQHashStruct2) { wrongqHashOverload = 1; return 0; } +uint qHash(const TwoArgumentsQHashStruct2 &, uint) { return 0; } + +struct TwoArgumentsQHashStruct3 {}; +bool operator==(const TwoArgumentsQHashStruct3 &, const TwoArgumentsQHashStruct3 &) { return false; } +uint qHash(const TwoArgumentsQHashStruct3 &) { wrongqHashOverload = 1; return 0; } +uint qHash(TwoArgumentsQHashStruct3, uint) { return 0; } + +struct TwoArgumentsQHashStruct4 {}; +bool operator==(const TwoArgumentsQHashStruct4 &, const TwoArgumentsQHashStruct4 &) { return false; } +uint qHash(TwoArgumentsQHashStruct4) { wrongqHashOverload = 1; return 0; } +uint qHash(TwoArgumentsQHashStruct4, uint) { return 0; } + +/*! + \internal + + Check that QHash picks up the right overload. + The best one, for a type T, is the two-args version of qHash: + either uint qHash(T, uint) or uint qHash(const T &, uint). + + If neither of these exists, then one between + uint qHash(T) or uint qHash(const T &) must exist + (and it gets selected instead). +*/ +void tst_QHash::twoArguments_qHash() +{ + QHash<OneArgumentQHashStruct1, int> oneArgHash1; + OneArgumentQHashStruct1 oneArgObject1; + oneArgHash1[oneArgObject1] = 1; + QCOMPARE(wrongqHashOverload, 0); + + QHash<OneArgumentQHashStruct2, int> oneArgHash2; + OneArgumentQHashStruct2 oneArgObject2; + oneArgHash2[oneArgObject2] = 1; + QCOMPARE(wrongqHashOverload, 0); + + QHash<OneArgumentQHashStruct3, int> oneArgHash3; + OneArgumentQHashStruct3 oneArgObject3; + oneArgHash3[oneArgObject3] = 1; + QCOMPARE(wrongqHashOverload, 0); + + QHash<OneArgumentQHashStruct4, int> oneArgHash4; + OneArgumentQHashStruct4 oneArgObject4; + oneArgHash4[oneArgObject4] = 1; + QCOMPARE(wrongqHashOverload, 0); + + QHash<TwoArgumentsQHashStruct1, int> twoArgsHash1; + TwoArgumentsQHashStruct1 twoArgsObject1; + twoArgsHash1[twoArgsObject1] = 1; + QCOMPARE(wrongqHashOverload, 0); + + QHash<TwoArgumentsQHashStruct2, int> twoArgsHash2; + TwoArgumentsQHashStruct2 twoArgsObject2; + twoArgsHash2[twoArgsObject2] = 1; + QCOMPARE(wrongqHashOverload, 0); + + QHash<TwoArgumentsQHashStruct3, int> twoArgsHash3; + TwoArgumentsQHashStruct3 twoArgsObject3; + twoArgsHash3[twoArgsObject3] = 1; + QCOMPARE(wrongqHashOverload, 0); + + QHash<TwoArgumentsQHashStruct4, int> twoArgsHash4; + TwoArgumentsQHashStruct4 twoArgsObject4; + twoArgsHash4[twoArgsObject4] = 1; + QCOMPARE(wrongqHashOverload, 0); +} + QTEST_APPLESS_MAIN(tst_QHash) #include "tst_qhash.moc" diff --git a/tests/auto/corelib/tools/qlist/tst_qlist.cpp b/tests/auto/corelib/tools/qlist/tst_qlist.cpp index 934130d3ee..c883c1c5f6 100644 --- a/tests/auto/corelib/tools/qlist/tst_qlist.cpp +++ b/tests/auto/corelib/tools/qlist/tst_qlist.cpp @@ -48,6 +48,9 @@ class tst_QList : public QObject Q_OBJECT private slots: + void init(); + void cleanup(); + void length() const; void lengthSignature() const; void append() const; @@ -84,8 +87,100 @@ private slots: void initializeList() const; void const_shared_null() const; + void setSharable1_data() const; + void setSharable1() const; + void setSharable2_data() const; + void setSharable2() const; + +private: + int dummyForGuard; +}; + +struct Complex +{ + Complex(int val) + : value(val) + , checkSum(this) + { + ++liveCount; + } + + Complex(Complex const &other) + : value(other.value) + , checkSum(this) + { + ++liveCount; + } + + Complex &operator=(Complex const &other) + { + check(); other.check(); + + value = other.value; + return *this; + } + + ~Complex() + { + --liveCount; + check(); + } + + operator int() const { return value; } + + bool operator==(Complex const &other) const + { + check(); other.check(); + return value == other.value; + } + + bool check() const + { + if (this != checkSum) { + ++errorCount; + return false; + } + return true; + } + + struct Guard + { + Guard() : initialLiveCount(liveCount) {} + ~Guard() { if (liveCount != initialLiveCount) ++errorCount; } + + private: + Q_DISABLE_COPY(Guard); + int initialLiveCount; + }; + + static void resetErrors() { errorCount = 0; } + static int errors() { return errorCount; } + +private: + static int errorCount; + static int liveCount; + + int value; + void *checkSum; }; +int Complex::errorCount = 0; +int Complex::liveCount = 0; + +void tst_QList::init() +{ + Complex::resetErrors(); + new (&dummyForGuard) Complex::Guard(); +} + +void tst_QList::cleanup() +{ + QCOMPARE(Complex::errors(), 0); + + reinterpret_cast<Complex::Guard *>(&dummyForGuard)->~Guard(); + QCOMPARE(Complex::errors(), 0); +} + void tst_QList::length() const { /* Empty list. */ @@ -690,5 +785,82 @@ void tst_QList::const_shared_null() const QVERIFY(!list2.isDetached()); } +Q_DECLARE_METATYPE(QList<int>); +Q_DECLARE_METATYPE(QList<Complex>); + +template <class T> +void generateSetSharableData() +{ + QTest::addColumn<QList<T> >("list"); + QTest::addColumn<int>("size"); + + QTest::newRow("null") << QList<T>() << 0; + QTest::newRow("non-empty") << (QList<T>() << T(0) << T(1) << T(2) << T(3) << T(4)) << 5; +} + +template <class T> +void runSetSharableTest() +{ + QFETCH(QList<T>, list); + QFETCH(int, size); + + QVERIFY(!list.isDetached()); // Shared with QTest + + list.setSharable(true); + + QCOMPARE(list.size(), size); + + { + QList<T> copy(list); + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(list)); + } + + list.setSharable(false); + QVERIFY(list.isDetached() || list.isSharedWith(QList<T>())); + + { + QList<T> copy(list); + + QVERIFY(copy.isDetached() || copy.isSharedWith(QList<T>())); + QCOMPARE(copy.size(), size); + QCOMPARE(copy, list); + } + + list.setSharable(true); + + { + QList<T> copy(list); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(list)); + } + + for (int i = 0; i < list.size(); ++i) + QCOMPARE(int(list[i]), i); + + QCOMPARE(list.size(), size); +} + +void tst_QList::setSharable1_data() const +{ + generateSetSharableData<int>(); +} + +void tst_QList::setSharable2_data() const +{ + generateSetSharableData<Complex>(); +} + +void tst_QList::setSharable1() const +{ + runSetSharableTest<int>(); +} + +void tst_QList::setSharable2() const +{ + runSetSharableTest<Complex>(); +} + QTEST_APPLESS_MAIN(tst_QList) #include "tst_qlist.moc" diff --git a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp index f9de2f0c05..7e12e42107 100644 --- a/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp +++ b/tests/auto/corelib/tools/qlocale/tst_qlocale.cpp @@ -1751,7 +1751,7 @@ void tst_QLocale::dayName_data() QTest::newRow("C narrow") << QString("C") << QString("7") << 7 << QLocale::NarrowFormat; QTest::newRow("ru_RU long") << QString("ru_RU") << QString::fromUtf8("\320\262\320\276\321\201\320\272\321\200\320\265\321\201\320\265\320\275\321\214\320\265") << 7 << QLocale::LongFormat; - QTest::newRow("ru_RU short") << QString("ru_RU") << QString::fromUtf8("\320\222\321\201") << 7 << QLocale::ShortFormat; + QTest::newRow("ru_RU short") << QString("ru_RU") << QString::fromUtf8("\320\262\321\201") << 7 << QLocale::ShortFormat; QTest::newRow("ru_RU narrow") << QString("ru_RU") << QString::fromUtf8("\320\222") << 7 << QLocale::NarrowFormat; } diff --git a/tests/auto/corelib/tools/qmap/tst_qmap.cpp b/tests/auto/corelib/tools/qmap/tst_qmap.cpp index 7d0ef7d7e4..a28ea94aa5 100644 --- a/tests/auto/corelib/tools/qmap/tst_qmap.cpp +++ b/tests/auto/corelib/tools/qmap/tst_qmap.cpp @@ -41,10 +41,10 @@ #define QT_STRICT_ITERATORS +#include <qmap.h> #include <QtTest/QtTest> #include <QDebug> -#include <qmap.h> class tst_QMap : public QObject { @@ -74,6 +74,11 @@ private slots: void qmultimap_specific(); void const_shared_null(); + + void equal_range(); + void setSharable(); + + void insert(); }; typedef QMap<QString, QString> StringMap; @@ -105,6 +110,11 @@ int MyClass::count = 0; typedef QMap<QString, MyClass> MyMap; +QDebug operator << (QDebug d, const MyClass &c) { + d << c.str; + return d; +} + void tst_QMap::init() { MyClass::count = 0; @@ -152,6 +162,7 @@ void tst_QMap::count() map.insert( "Paul", MyClass("Tvete 6") ); QCOMPARE( map.count(), 9 ); + QCOMPARE( map.count("Paul"), 1 ); #ifndef Q_CC_SUN QCOMPARE( MyClass::count, 9 ); #endif @@ -519,6 +530,7 @@ void tst_QMap::find() for(i = 3; i < 10; ++i) { compareString = testString.arg(i); map1.insertMulti(4, compareString); + QCOMPARE(map1.count(4), i - 2); } QMap<int, QString>::const_iterator it=map1.constFind(4); @@ -588,6 +600,33 @@ void tst_QMap::lowerUpperBound() QCOMPARE(map1.lowerBound(2).key(), 5); // returns iterator to (5, "five") QCOMPARE(map1.lowerBound(10).key(), 10); // returns iterator to (10, "ten") QVERIFY(map1.lowerBound(999) == map1.end()); // returns end() + + map1.insert(3, "three"); + map1.insert(7, "seven"); + map1.insertMulti(7, "seven_2"); + + QCOMPARE(map1.upperBound(0).key(), 1); + QCOMPARE(map1.upperBound(1).key(), 3); + QCOMPARE(map1.upperBound(2).key(), 3); + QCOMPARE(map1.upperBound(3).key(), 5); + QCOMPARE(map1.upperBound(7).key(), 10); + QVERIFY(map1.upperBound(10) == map1.end()); + QVERIFY(map1.upperBound(999) == map1.end()); + + QCOMPARE(map1.lowerBound(0).key(), 1); + QCOMPARE(map1.lowerBound(1).key(), 1); + QCOMPARE(map1.lowerBound(2).key(), 3); + QCOMPARE(map1.lowerBound(3).key(), 3); + QCOMPARE(map1.lowerBound(4).key(), 5); + QCOMPARE(map1.lowerBound(5).key(), 5); + QCOMPARE(map1.lowerBound(6).key(), 7); + QCOMPARE(map1.lowerBound(7).key(), 7); + QCOMPARE(map1.lowerBound(6).value(), QString("seven_2")); + QCOMPARE(map1.lowerBound(7).value(), QString("seven_2")); + QCOMPARE((++map1.lowerBound(6)).value(), QString("seven")); + QCOMPARE((++map1.lowerBound(7)).value(), QString("seven")); + QCOMPARE(map1.lowerBound(10).key(), 10); + QVERIFY(map1.lowerBound(999) == map1.end()); } void tst_QMap::mergeCompare() @@ -655,8 +694,9 @@ void tst_QMap::iterators() stlIt--; QVERIFY(stlIt.value() == "Teststring 3"); - for(stlIt = map.begin(), i = 1; stlIt != map.end(), i < 100; ++stlIt, ++i) + for(stlIt = map.begin(), i = 1; stlIt != map.end(); ++stlIt, ++i) QVERIFY(stlIt.value() == testString.arg(i)); + QCOMPARE(i, 100); //STL-Style const-iterators @@ -675,8 +715,9 @@ void tst_QMap::iterators() cstlIt--; QVERIFY(cstlIt.value() == "Teststring 3"); - for(cstlIt = map.constBegin(), i = 1; cstlIt != map.constEnd(), i < 100; ++cstlIt, ++i) + for(cstlIt = map.constBegin(), i = 1; cstlIt != map.constEnd(); ++cstlIt, ++i) QVERIFY(cstlIt.value() == testString.arg(i)); + QCOMPARE(i, 100); //Java-Style iterators @@ -846,10 +887,129 @@ void tst_QMap::const_shared_null() QMap<int, QString> map2; map2.setSharable(true); QVERIFY(!map2.isDetached()); +} + +void tst_QMap::equal_range() +{ + QMap<int, QString> map; + + QPair<QMap<int, QString>::iterator, QMap<int, QString>::iterator> result = map.equal_range(0); + QCOMPARE(result.first, map.end()); + QCOMPARE(result.second, map.end()); + + map.insert(1, "one"); + + result = map.equal_range(0); + QCOMPARE(result.first, map.find(1)); + QCOMPARE(result.second, map.find(1)); - QMap<int, QString> map3; - map3.setInsertInOrder(true); - map3.setInsertInOrder(false); + result = map.equal_range(1); + QCOMPARE(result.first, map.find(1)); + QCOMPARE(result.second, map.end()); + + result = map.equal_range(2); + QCOMPARE(result.first, map.end()); + QCOMPARE(result.second, map.end()); + + for (int i = -10; i < 10; i += 2) + map.insert(i, QString("%1").arg(i)); + + result = map.equal_range(0); + QCOMPARE(result.first, map.find(0)); + QCOMPARE(result.second, map.find(1)); + + result = map.equal_range(1); + QCOMPARE(result.first, map.find(1)); + QCOMPARE(result.second, map.find(2)); + + result = map.equal_range(2); + QCOMPARE(result.first, map.find(2)); + QCOMPARE(result.second, map.find(4)); + + map.insertMulti(1, "another one"); + result = map.equal_range(1); + QCOMPARE(result.first, map.find(1)); + QCOMPARE(result.second, map.find(2)); + + QCOMPARE(map.count(1), 2); +} + +template <class T> +const T &const_(const T &t) +{ + return t; +} + +void tst_QMap::setSharable() +{ + QMap<int, QString> map; + + map.insert(1, "um"); + map.insert(2, "dois"); + map.insert(4, "quatro"); + map.insert(5, "cinco"); + + map.setSharable(true); + QCOMPARE(map.size(), 4); + QCOMPARE(const_(map)[4], QString("quatro")); + + { + QMap<int, QString> copy(map); + + QVERIFY(!map.isDetached()); + QVERIFY(copy.isSharedWith(map)); + } + + map.setSharable(false); + QVERIFY(map.isDetached()); + QCOMPARE(map.size(), 4); + QCOMPARE(const_(map)[4], QString("quatro")); + + { + QMap<int, QString> copy(map); + + QVERIFY(map.isDetached()); + QVERIFY(copy.isDetached()); + + QCOMPARE(copy.size(), 4); + QCOMPARE(const_(copy)[4], QString("quatro")); + + QCOMPARE(map, copy); + } + + map.setSharable(true); + QCOMPARE(map.size(), 4); + QCOMPARE(const_(map)[4], QString("quatro")); + + { + QMap<int, QString> copy(map); + + QVERIFY(!map.isDetached()); + QVERIFY(copy.isSharedWith(map)); + } +} + +void tst_QMap::insert() +{ + QMap<QString, float> map; + map.insert("cs/key1", 1); + map.insert("cs/key2", 2); + map.insert("cs/key1", 3); + QCOMPARE(map.count(), 2); + + QMap<int, int> intMap; + for (int i = 0; i < 1000; ++i) { + intMap.insert(i, i); + } + + QCOMPARE(intMap.size(), 1000); + + for (int i = 0; i < 1000; ++i) { + QCOMPARE(intMap.value(i), i); + intMap.insert(i, -1); + QCOMPARE(intMap.size(), 1000); + QCOMPARE(intMap.value(i), -1); + } } QTEST_APPLESS_MAIN(tst_QMap) diff --git a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp index 5b697a3509..bb9be1d65f 100644 --- a/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp +++ b/tests/auto/corelib/tools/qsharedpointer/tst_qsharedpointer.cpp @@ -114,15 +114,19 @@ public: } }; -template <typename Base> -class RefCountHack: public Base +template<typename T> static inline +QtSharedPointer::ExternalRefCountData *refCountData(const QtSharedPointer::ExternalRefCount<T> &b) { -public: - using Base::d; -}; -template<typename Base> static inline -QtSharedPointer::ExternalRefCountData *refCountData(const Base &b) -{ return static_cast<const RefCountHack<Base> *>(&b)->d; } + // access d-pointer: + struct Dummy { + void* value; + QtSharedPointer::ExternalRefCountData* data; + }; + // sanity checks: + Q_STATIC_ASSERT(sizeof(QtSharedPointer::ExternalRefCount<T>) == sizeof(Dummy)); + Q_ASSERT(static_cast<const Dummy*>(static_cast<const void*>(&b))->value == b.data()); + return static_cast<const Dummy*>(static_cast<const void*>(&b))->data; +} class Data { @@ -1619,7 +1623,8 @@ void hashAndMapTest() QVERIFY(it != c.end()); QCOMPARE(it.key(), k1); ++it; - QVERIFY(it == c.end()); + if (Ordered) + QVERIFY(it == c.end()); } void tst_QSharedPointer::map() diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index 4eabd61025..1905c9c049 100644 --- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp @@ -41,6 +41,7 @@ #include <QtTest/QtTest> #include <qregexp.h> +#include <qregularexpression.h> #include <qtextcodec.h> #include <qtextstream.h> #include <qstringlist.h> @@ -50,6 +51,7 @@ #include <qlocale.h> #include <locale.h> +#include <qhash.h> Q_DECLARE_METATYPE(qlonglong) @@ -194,6 +196,7 @@ private slots: void localeAwareCompare(); void split_data(); void split(); + void split_regexp_data(); void split_regexp(); void fromUtf16_data(); void fromUtf16(); @@ -227,6 +230,65 @@ private slots: void assignQLatin1String(); }; +template <class T> const T &verifyZeroTermination(const T &t) { return t; } + +QString verifyZeroTermination(const QString &str) +{ + // This test does some evil stuff, it's all supposed to work. + + QString::DataPtr strDataPtr = const_cast<QString &>(str).data_ptr(); + + // Skip if isStatic() or fromRawData(), as those offer no guarantees + if (strDataPtr->ref.isStatic() + || strDataPtr->offset != QString().data_ptr()->offset) + return str; + + int strSize = str.size(); + QChar strTerminator = str.constData()[strSize]; + if (QChar('\0') != strTerminator) + return QString::fromAscii( + "*** Result ('%1') not null-terminated: 0x%2 ***").arg(str) + .arg(strTerminator.unicode(), 4, 16, QChar('0')); + + // Skip mutating checks on shared strings + if (strDataPtr->ref.isShared()) + return str; + + const QChar *strData = str.constData(); + const QString strCopy(strData, strSize); // Deep copy + + const_cast<QChar *>(strData)[strSize] = QChar('x'); + if (QChar('x') != str.constData()[strSize]) { + return QString::fromAscii("*** Failed to replace null-terminator in " + "result ('%1') ***").arg(str); + } + if (str != strCopy) { + return QString::fromAscii( "*** Result ('%1') differs from its copy " + "after null-terminator was replaced ***").arg(str); + } + const_cast<QChar *>(strData)[strSize] = QChar('\0'); // Restore sanity + + return str; +} + +// Overriding QTest's QCOMPARE, to check QString for null termination +#undef QCOMPARE +#define QCOMPARE(actual, expected) \ + do { \ + if (!QTest::qCompare(verifyZeroTermination(actual), expected, \ + #actual, #expected, __FILE__, __LINE__)) \ + return; \ + } while (0) \ + /**/ +#undef QTEST +#define QTEST(actual, testElement) \ + do { \ + if (!QTest::qTest(verifyZeroTermination(actual), testElement, \ + #actual, #testElement, __FILE__, __LINE__)) \ + return; \ + } while (0) \ + /**/ + typedef QList<int> IntList; Q_DECLARE_METATYPE(QList<QVariant>) @@ -823,7 +885,6 @@ void tst_QString::constructorQByteArray() void tst_QString::STL() { -#ifndef QT_NO_STL #ifndef QT_NO_CAST_TO_ASCII QString qt( "QString" ); @@ -867,9 +928,6 @@ void tst_QString::STL() QCOMPARE(s, QString::fromLatin1("hello")); QCOMPARE(stlStr, s.toStdWString()); -#else - QSKIP( "Not tested without STL support"); -#endif } void tst_QString::truncate() @@ -1089,6 +1147,17 @@ void tst_QString::indexOf() QCOMPARE( rx2.matchedLength(), -1 ); } + { + QRegularExpression::PatternOptions options = QRegularExpression::NoPatternOption; + if (!bcs) + options |= QRegularExpression::CaseInsensitiveOption; + + QRegularExpression re(QRegularExpression::escape(needle), options); + QEXPECT_FAIL("data58", "QRegularExpression does not support case folding", Continue); + QEXPECT_FAIL("data59", "QRegularExpression does not support case folding", Continue); + QCOMPARE( haystack.indexOf(re, startpos), resultpos ); + } + if (cs == Qt::CaseSensitive) { QCOMPARE( haystack.indexOf(needle, startpos), resultpos ); QCOMPARE( haystack.indexOf(ref, startpos), resultpos ); @@ -1276,6 +1345,15 @@ void tst_QString::lastIndexOf() QCOMPARE(rx1.matchedLength(), -1); QCOMPARE(rx2.matchedLength(), -1); } + + { + QRegularExpression::PatternOptions options = QRegularExpression::NoPatternOption; + if (!caseSensitive) + options |= QRegularExpression::CaseInsensitiveOption; + + QRegularExpression re(QRegularExpression::escape(needle), options); + QCOMPARE(haystack.lastIndexOf(re, from), expected); + } } if (cs == Qt::CaseSensitive) { @@ -1311,6 +1389,9 @@ void tst_QString::count() QCOMPARE(a.count( "", Qt::CaseInsensitive), 16); QCOMPARE(a.count(QRegExp("[FG][HI]")),1); QCOMPARE(a.count(QRegExp("[G][HE]")),2); + QCOMPARE(a.count(QRegularExpression("[FG][HI]")), 1); + QCOMPARE(a.count(QRegularExpression("[G][HE]")), 2); + CREATE_REF(QLatin1String("FG")); QCOMPARE(a.count(ref),2); @@ -1336,6 +1417,8 @@ void tst_QString::contains() QVERIFY(a.contains( "", Qt::CaseInsensitive)); QVERIFY(a.contains(QRegExp("[FG][HI]"))); QVERIFY(a.contains(QRegExp("[G][HE]"))); + QVERIFY(a.contains(QRegularExpression("[FG][HI]"))); + QVERIFY(a.contains(QRegularExpression("[G][HE]"))); CREATE_REF(QLatin1String("FG")); QVERIFY(a.contains(ref)); @@ -1430,16 +1513,69 @@ void tst_QString::mid() QVERIFY(a.mid(9999).isNull()); QVERIFY(a.mid(9999,1).isNull()); + QCOMPARE(a.mid(-1, 6), a.mid(0, 5)); + QVERIFY(a.mid(-100, 6).isEmpty()); + QVERIFY(a.mid(INT_MIN, 0).isEmpty()); + QCOMPARE(a.mid(INT_MIN, -1), a); + QVERIFY(a.mid(INT_MIN, INT_MAX).isNull()); + QVERIFY(a.mid(INT_MIN + 1, INT_MAX).isEmpty()); + QCOMPARE(a.mid(INT_MIN + 2, INT_MAX), a.left(1)); + QCOMPARE(a.mid(INT_MIN + a.size() + 1, INT_MAX), a); + QVERIFY(a.mid(INT_MAX).isNull()); + QVERIFY(a.mid(INT_MAX, INT_MAX).isNull()); + QCOMPARE(a.mid(-5, INT_MAX), a); + QCOMPARE(a.mid(-1, INT_MAX), a); + QCOMPARE(a.mid(0, INT_MAX), a); + QCOMPARE(a.mid(1, INT_MAX), QString("BCDEFGHIEfGEFG")); + QCOMPARE(a.mid(5, INT_MAX), QString("FGHIEfGEFG")); + QVERIFY(a.mid(20, INT_MAX).isNull()); + QCOMPARE(a.mid(-1, -1), a); + QString n; QVERIFY(n.mid(3,3).isNull()); QVERIFY(n.mid(0,0).isNull()); QVERIFY(n.mid(9999,0).isNull()); QVERIFY(n.mid(9999,1).isNull()); + QVERIFY(n.mid(-1, 6).isNull()); + QVERIFY(n.mid(-100, 6).isNull()); + QVERIFY(n.mid(INT_MIN, 0).isNull()); + QVERIFY(n.mid(INT_MIN, -1).isNull()); + QVERIFY(n.mid(INT_MIN, INT_MAX).isNull()); + QVERIFY(n.mid(INT_MIN + 1, INT_MAX).isNull()); + QVERIFY(n.mid(INT_MIN + 2, INT_MAX).isNull()); + QVERIFY(n.mid(INT_MIN + n.size() + 1, INT_MAX).isNull()); + QVERIFY(n.mid(INT_MAX).isNull()); + QVERIFY(n.mid(INT_MAX, INT_MAX).isNull()); + QVERIFY(n.mid(-5, INT_MAX).isNull()); + QVERIFY(n.mid(-1, INT_MAX).isNull()); + QVERIFY(n.mid(0, INT_MAX).isNull()); + QVERIFY(n.mid(1, INT_MAX).isNull()); + QVERIFY(n.mid(5, INT_MAX).isNull()); + QVERIFY(n.mid(20, INT_MAX).isNull()); + QVERIFY(n.mid(-1, -1).isNull()); + QString x = "Nine pineapples"; QCOMPARE(x.mid(5, 4), QString("pine")); QCOMPARE(x.mid(5), QString("pineapples")); + QCOMPARE(x.mid(-1, 6), x.mid(0, 5)); + QVERIFY(x.mid(-100, 6).isEmpty()); + QVERIFY(x.mid(INT_MIN, 0).isEmpty()); + QCOMPARE(x.mid(INT_MIN, -1), x); + QVERIFY(x.mid(INT_MIN, INT_MAX).isNull()); + QVERIFY(x.mid(INT_MIN + 1, INT_MAX).isEmpty()); + QCOMPARE(x.mid(INT_MIN + 2, INT_MAX), x.left(1)); + QCOMPARE(x.mid(INT_MIN + x.size() + 1, INT_MAX), x); + QVERIFY(x.mid(INT_MAX).isNull()); + QVERIFY(x.mid(INT_MAX, INT_MAX).isNull()); + QCOMPARE(x.mid(-5, INT_MAX), x); + QCOMPARE(x.mid(-1, INT_MAX), x); + QCOMPARE(x.mid(0, INT_MAX), x); + QCOMPARE(x.mid(1, INT_MAX), QString("ine pineapples")); + QCOMPARE(x.mid(5, INT_MAX), QString("pineapples")); + QVERIFY(x.mid(20, INT_MAX).isNull()); + QCOMPARE(x.mid(-1, -1), x); } void tst_QString::midRef() @@ -1456,16 +1592,69 @@ void tst_QString::midRef() QVERIFY(a.midRef(9999).toString().isEmpty()); QVERIFY(a.midRef(9999,1).toString().isEmpty()); + QCOMPARE(a.midRef(-1, 6), a.midRef(0, 5)); + QVERIFY(a.midRef(-100, 6).isEmpty()); + QVERIFY(a.midRef(INT_MIN, 0).isEmpty()); + QCOMPARE(a.midRef(INT_MIN, -1).toString(), a); + QVERIFY(a.midRef(INT_MIN, INT_MAX).isNull()); + QVERIFY(a.midRef(INT_MIN + 1, INT_MAX).isEmpty()); + QCOMPARE(a.midRef(INT_MIN + 2, INT_MAX), a.leftRef(1)); + QCOMPARE(a.midRef(INT_MIN + a.size() + 1, INT_MAX).toString(), a); + QVERIFY(a.midRef(INT_MAX).isNull()); + QVERIFY(a.midRef(INT_MAX, INT_MAX).isNull()); + QCOMPARE(a.midRef(-5, INT_MAX).toString(), a); + QCOMPARE(a.midRef(-1, INT_MAX).toString(), a); + QCOMPARE(a.midRef(0, INT_MAX).toString(), a); + QCOMPARE(a.midRef(1, INT_MAX).toString(), QString("BCDEFGHIEfGEFG")); + QCOMPARE(a.midRef(5, INT_MAX).toString(), QString("FGHIEfGEFG")); + QVERIFY(a.midRef(20, INT_MAX).isNull()); + QCOMPARE(a.midRef(-1, -1).toString(), a); + QString n; QVERIFY(n.midRef(3,3).toString().isEmpty()); QVERIFY(n.midRef(0,0).toString().isEmpty()); QVERIFY(n.midRef(9999,0).toString().isEmpty()); QVERIFY(n.midRef(9999,1).toString().isEmpty()); + QVERIFY(n.midRef(-1, 6).isNull()); + QVERIFY(n.midRef(-100, 6).isNull()); + QVERIFY(n.midRef(INT_MIN, 0).isNull()); + QVERIFY(n.midRef(INT_MIN, -1).isNull()); + QVERIFY(n.midRef(INT_MIN, INT_MAX).isNull()); + QVERIFY(n.midRef(INT_MIN + 1, INT_MAX).isNull()); + QVERIFY(n.midRef(INT_MIN + 2, INT_MAX).isNull()); + QVERIFY(n.midRef(INT_MIN + n.size() + 1, INT_MAX).isNull()); + QVERIFY(n.midRef(INT_MAX).isNull()); + QVERIFY(n.midRef(INT_MAX, INT_MAX).isNull()); + QVERIFY(n.midRef(-5, INT_MAX).isNull()); + QVERIFY(n.midRef(-1, INT_MAX).isNull()); + QVERIFY(n.midRef(0, INT_MAX).isNull()); + QVERIFY(n.midRef(1, INT_MAX).isNull()); + QVERIFY(n.midRef(5, INT_MAX).isNull()); + QVERIFY(n.midRef(20, INT_MAX).isNull()); + QVERIFY(n.midRef(-1, -1).isNull()); + QString x = "Nine pineapples"; QCOMPARE(x.midRef(5, 4).toString(), QString("pine")); QCOMPARE(x.midRef(5).toString(), QString("pineapples")); + QCOMPARE(x.midRef(-1, 6), x.midRef(0, 5)); + QVERIFY(x.midRef(-100, 6).isEmpty()); + QVERIFY(x.midRef(INT_MIN, 0).isEmpty()); + QCOMPARE(x.midRef(INT_MIN, -1).toString(), x); + QVERIFY(x.midRef(INT_MIN, INT_MAX).isNull()); + QVERIFY(x.midRef(INT_MIN + 1, INT_MAX).isEmpty()); + QCOMPARE(x.midRef(INT_MIN + 2, INT_MAX), x.leftRef(1)); + QCOMPARE(x.midRef(INT_MIN + x.size() + 1, INT_MAX).toString(), x); + QVERIFY(x.midRef(INT_MAX).isNull()); + QVERIFY(x.midRef(INT_MAX, INT_MAX).isNull()); + QCOMPARE(x.midRef(-5, INT_MAX).toString(), x); + QCOMPARE(x.midRef(-1, INT_MAX).toString(), x); + QCOMPARE(x.midRef(0, INT_MAX).toString(), x); + QCOMPARE(x.midRef(1, INT_MAX).toString(), QString("ine pineapples")); + QCOMPARE(x.midRef(5, INT_MAX).toString(), QString("pineapples")); + QVERIFY(x.midRef(20, INT_MAX).isNull()); + QCOMPARE(x.midRef(-1, -1).toString(), x); } void tst_QString::stringRef() @@ -2075,6 +2264,9 @@ void tst_QString::replace_regexp() QString s2 = string; s2.replace( QRegExp(regexp), after ); QTEST( s2, "result" ); + s2 = string; + s2.replace( QRegularExpression(regexp), after ); + QTEST( s2, "result" ); } void tst_QString::remove_uint_uint() @@ -2139,8 +2331,13 @@ void tst_QString::remove_regexp() QFETCH( QString, after ); if ( after.length() == 0 ) { - string.remove( QRegExp(regexp) ); - QTEST( string, "result" ); + QString s2 = string; + s2.remove( QRegExp(regexp) ); + QTEST( s2, "result" ); + + s2 = string; + s2.remove( QRegularExpression(regexp) ); + QTEST( s2, "result" ); } else { QCOMPARE( 0, 0 ); // shut QtTest } @@ -3168,7 +3365,6 @@ void tst_QString::fromStdString() #ifdef Q_CC_HPACC QSKIP("This test crashes on HP-UX with aCC"); #endif -#if !defined(QT_NO_STL) std::string stroustrup = "foo"; QString eng = QString::fromStdString( stroustrup ); QCOMPARE( eng, QString("foo") ); @@ -3176,7 +3372,6 @@ void tst_QString::fromStdString() std::string stdnull( cnull, sizeof(cnull)-1 ); QString qtnull = QString::fromStdString( stdnull ); QCOMPARE( qtnull.size(), int(stdnull.size()) ); -#endif } void tst_QString::toStdString() @@ -3184,7 +3379,6 @@ void tst_QString::toStdString() #ifdef Q_CC_HPACC QSKIP("This test crashes on HP-UX with aCC"); #endif -#if !defined(QT_NO_STL) QString nord = "foo"; std::string stroustrup1 = nord.toStdString(); QVERIFY( qstrcmp(stroustrup1.c_str(), "foo") == 0 ); @@ -3198,7 +3392,6 @@ void tst_QString::toStdString() QString qtnull( qcnull, sizeof(qcnull)/sizeof(QChar) ); std::string stdnull = qtnull.toStdString(); QCOMPARE( int(stdnull.size()), qtnull.size() ); -#endif } void tst_QString::utf8() @@ -3881,8 +4074,12 @@ void tst_QString::section() QFETCH( bool, regexp ); if (regexp) { QCOMPARE( wholeString.section( QRegExp(sep), start, end, QString::SectionFlag(flags) ), sectionString ); + QCOMPARE( wholeString.section( QRegularExpression(sep), start, end, QString::SectionFlag(flags) ), sectionString ); } else { QCOMPARE( wholeString.section( sep, start, end, QString::SectionFlag(flags) ), sectionString ); + QCOMPARE( wholeString.section( QRegExp(QRegExp::escape(sep)), start, end, QString::SectionFlag(flags) ), sectionString ); + QCOMPARE( wholeString.section( QRegularExpression(QRegularExpression::escape(sep)), start, end, QString::SectionFlag(flags) ), sectionString ); + } } @@ -4403,6 +4600,7 @@ void tst_QString::split() QFETCH(QStringList, result); QRegExp rx = QRegExp(QRegExp::escape(sep)); + QRegularExpression re(QRegularExpression::escape(sep)); QStringList list; @@ -4410,6 +4608,8 @@ void tst_QString::split() QVERIFY(list == result); list = str.split(rx); QVERIFY(list == result); + list = str.split(re); + QVERIFY(list == result); if (sep.size() == 1) { list = str.split(sep.at(0)); QVERIFY(list == result); @@ -4419,6 +4619,8 @@ void tst_QString::split() QVERIFY(list == result); list = str.split(rx, QString::KeepEmptyParts); QVERIFY(list == result); + list = str.split(re, QString::KeepEmptyParts); + QVERIFY(list == result); if (sep.size() == 1) { list = str.split(sep.at(0), QString::KeepEmptyParts); QVERIFY(list == result); @@ -4429,39 +4631,51 @@ void tst_QString::split() QVERIFY(list == result); list = str.split(rx, QString::SkipEmptyParts); QVERIFY(list == result); + list = str.split(re, QString::SkipEmptyParts); + QVERIFY(list == result); if (sep.size() == 1) { list = str.split(sep.at(0), QString::SkipEmptyParts); QVERIFY(list == result); } } +void tst_QString::split_regexp_data() +{ + QTest::addColumn<QString>("string"); + QTest::addColumn<QString>("pattern"); + QTest::addColumn<QStringList>("result"); + + QTest::newRow("data01") << "Some text\n\twith strange whitespace." + << "\\s+" + << (QStringList() << "Some" << "text" << "with" << "strange" << "whitespace." ); + + QTest::newRow("data02") << "This time, a normal English sentence." + << "\\W+" + << (QStringList() << "This" << "time" << "a" << "normal" << "English" << "sentence" << ""); + + QTest::newRow("data03") << "Now: this sentence fragment." + << "\\b" + << (QStringList() << "" << "Now" << ": " << "this" << " " << "sentence" << " " << "fragment" << "."); +} + void tst_QString::split_regexp() { - QString str1 = "Some text\n\twith strange whitespace."; - QStringList list1 = str1.split(QRegExp("\\s+")); - QStringList result1; - result1 << "Some" << "text" << "with" << "strange" << "whitespace."; - QVERIFY(list1 == result1); - list1 = str1.split(QRegExp("\\s"), QString::SkipEmptyParts); - QVERIFY(list1 == result1); - - QString str2 = "This time, a normal English sentence."; - QStringList list2 = str2.split(QRegExp("\\W+")); - QStringList result2; - result2 << "This" << "time" << "a" << "normal" << "English" << "sentence" << ""; - QVERIFY(list2 == result2); - list2 = str2.split(QRegExp("\\W"), QString::SkipEmptyParts); - result2.removeAll(QString()); - QVERIFY(list2 == result2); - - QString str3 = "Now: this sentence fragment."; - QStringList list3 = str3.split(QRegExp("\\b")); - QStringList result3; - result3 << "" << "Now" << ": " << "this" << " " << "sentence" << " " << "fragment" << "."; - QVERIFY(list3 == result3); - list3 = str3.split(QRegExp("\\b"), QString::SkipEmptyParts); - result3.removeAll(QString()); - QVERIFY(list3 == result3); + QFETCH(QString, string); + QFETCH(QString, pattern); + QFETCH(QStringList, result); + + QStringList list; + list = string.split(QRegExp(pattern)); + QCOMPARE(list, result); + list = string.split(QRegularExpression(pattern)); + QCOMPARE(list, result); + + result.removeAll(QString()); + + list = string.split(QRegExp(pattern), QString::SkipEmptyParts); + QCOMPARE(list, result); + list = string.split(QRegularExpression(pattern), QString::SkipEmptyParts); + QCOMPARE(list, result); } void tst_QString::fromUtf16_data() @@ -4717,6 +4931,13 @@ void tst_QString::compare() QCOMPARE(sign(QStringRef::compare(r1, r2, Qt::CaseSensitive)), csr); QCOMPARE(sign(QStringRef::compare(r1, r2, Qt::CaseInsensitive)), cir); + if (csr == 0) { + QVERIFY(qHash(s1) == qHash(s2)); + QVERIFY(qHash(s1) == qHash(r2)); + QVERIFY(qHash(r1) == qHash(s2)); + QVERIFY(qHash(r1) == qHash(r2)); + } + if (!cir) { QCOMPARE(s1.toCaseFolded(), s2.toCaseFolded()); } @@ -5065,8 +5286,8 @@ void tst_QString::literals() QVERIFY(str.length() == 4); QVERIFY(str == QLatin1String("abcd")); - QVERIFY(str.data_ptr()->ref == -1); - QVERIFY(str.data_ptr()->offset == 0); + QVERIFY(str.data_ptr()->ref.isStatic()); + QVERIFY(str.data_ptr()->offset == sizeof(QStringData)); const QChar *s = str.constData(); QString str2 = str; diff --git a/tests/auto/corelib/tools/qstringbuilder/qstringbuilder1/stringbuilder.cpp b/tests/auto/corelib/tools/qstringbuilder/qstringbuilder1/stringbuilder.cpp index 556b9ac16a..862789cc73 100644 --- a/tests/auto/corelib/tools/qstringbuilder/qstringbuilder1/stringbuilder.cpp +++ b/tests/auto/corelib/tools/qstringbuilder/qstringbuilder1/stringbuilder.cpp @@ -107,6 +107,21 @@ void runScenario() QCOMPARE(r, r3); #endif + { + static const QStaticStringData<12> literalData = { + Q_STATIC_STRING_DATA_HEADER_INITIALIZER(12), + { 's', 'o', 'm', 'e', ' ', 'l', 'i', 't', 'e', 'r', 'a', 'l' } + }; + static QStringDataPtr literal = { literalData.data_ptr() }; + + r = literal; + QCOMPARE(r, string); + r = r Q literal; + QCOMPARE(r, r2); + r = literal Q literal; + QCOMPARE(r, r2); + } + #ifndef QT_NO_CAST_FROM_ASCII r = string P LITERAL; QCOMPARE(r, r2); @@ -211,6 +226,21 @@ void runScenario() QCOMPARE(r, ba); } + { + static const QStaticByteArrayData<12> literalData = { + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER(12), + { 's', 'o', 'm', 'e', ' ', 'l', 'i', 't', 'e', 'r', 'a', 'l' } + }; + static QByteArrayDataPtr literal = { literalData.data_ptr() }; + + QByteArray ba = literal; + QCOMPARE(ba, QByteArray(LITERAL)); + ba = ba Q literal; + QCOMPARE(ba, QByteArray(LITERAL LITERAL)); + ba = literal Q literal; + QCOMPARE(ba, QByteArray(LITERAL LITERAL)); + } + //operator QString += { QString str = QString::fromUtf8(UTF8_LITERAL); diff --git a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp index 6066f7c8e0..16a329f1dd 100644 --- a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp +++ b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp @@ -41,12 +41,16 @@ #include <QtTest/QtTest> #include <qregexp.h> +#include <qregularexpression.h> #include <qstringlist.h> +#include <locale.h> + class tst_QStringList : public QObject { Q_OBJECT private slots: + void sort(); void filter(); void replaceInStrings(); void removeDuplicates(); @@ -72,18 +76,37 @@ void tst_QStringList::indexOf_regExp() { QStringList list; list << "harald" << "trond" << "vohi" << "harald"; + { + QRegExp re(".*o.*"); + + QCOMPARE(list.indexOf(re), 1); + QCOMPARE(list.indexOf(re, 2), 2); + QCOMPARE(list.indexOf(re, 3), -1); + + QCOMPARE(list.indexOf(QRegExp(".*x.*")), -1); + QCOMPARE(list.indexOf(re, -1), -1); + QCOMPARE(list.indexOf(re, -3), 1); + QCOMPARE(list.indexOf(re, -9999), 1); + QCOMPARE(list.indexOf(re, 9999), -1); - QRegExp re(".*o.*"); + QCOMPARE(list.indexOf(QRegExp("[aeiou]")), -1); + } - QCOMPARE(list.indexOf(re), 1); - QCOMPARE(list.indexOf(re, 2), 2); - QCOMPARE(list.indexOf(re, 3), -1); + { + QRegularExpression re(".*o.*"); - QCOMPARE(list.indexOf(QRegExp(".*x.*")), -1); - QCOMPARE(list.indexOf(re, -1), -1); - QCOMPARE(list.indexOf(re, -3), 1); - QCOMPARE(list.indexOf(re, -9999), 1); - QCOMPARE(list.indexOf(re, 9999), -1); + QCOMPARE(list.indexOf(re), 1); + QCOMPARE(list.indexOf(re, 2), 2); + QCOMPARE(list.indexOf(re, 3), -1); + + QCOMPARE(list.indexOf(QRegularExpression(".*x.*")), -1); + QCOMPARE(list.indexOf(re, -1), -1); + QCOMPARE(list.indexOf(re, -3), 1); + QCOMPARE(list.indexOf(re, -9999), 1); + QCOMPARE(list.indexOf(re, 9999), -1); + + QCOMPARE(list.indexOf(QRegularExpression("[aeiou]")), -1); + } } void tst_QStringList::lastIndexOf_regExp() @@ -91,17 +114,39 @@ void tst_QStringList::lastIndexOf_regExp() QStringList list; list << "harald" << "trond" << "vohi" << "harald"; - QRegExp re(".*o.*"); + { + QRegExp re(".*o.*"); + + QCOMPARE(list.lastIndexOf(re), 2); + QCOMPARE(list.lastIndexOf(re, 2), 2); + QCOMPARE(list.lastIndexOf(re, 1), 1); + + QCOMPARE(list.lastIndexOf(QRegExp(".*x.*")), -1); + QCOMPARE(list.lastIndexOf(re, -1), 2); + QCOMPARE(list.lastIndexOf(re, -3), 1); + QCOMPARE(list.lastIndexOf(re, -9999), -1); + QCOMPARE(list.lastIndexOf(re, 9999), 2); + + QCOMPARE(list.lastIndexOf(QRegExp("[aeiou]")), -1); + } + + { + QRegularExpression re(".*o.*"); + + QCOMPARE(list.lastIndexOf(re), 2); + QCOMPARE(list.lastIndexOf(re, 2), 2); + QCOMPARE(list.lastIndexOf(re, 1), 1); + + QCOMPARE(list.lastIndexOf(QRegularExpression(".*x.*")), -1); + QCOMPARE(list.lastIndexOf(re, -1), 2); + QCOMPARE(list.lastIndexOf(re, -3), 1); + QCOMPARE(list.lastIndexOf(re, -9999), -1); + QCOMPARE(list.lastIndexOf(re, 9999), 2); + + QCOMPARE(list.lastIndexOf(QRegularExpression("[aeiou]")), -1); + } - QCOMPARE(list.lastIndexOf(re), 2); - QCOMPARE(list.lastIndexOf(re, 2), 2); - QCOMPARE(list.lastIndexOf(re, 1), 1); - QCOMPARE(list.lastIndexOf(QRegExp(".*x.*")), -1); - QCOMPARE(list.lastIndexOf(re, -1), 2); - QCOMPARE(list.lastIndexOf(re, -3), 1); - QCOMPARE(list.lastIndexOf(re, -9999), -1); - QCOMPARE(list.lastIndexOf(re, 9999), 2); } void tst_QStringList::indexOf() @@ -149,6 +194,29 @@ void tst_QStringList::filter() list3 = list3.filter( QRegExp("[i]ll") ); list4 << "Bill Gates" << "Bill Clinton"; QCOMPARE( list3, list4 ); + + QStringList list5, list6; + list5 << "Bill Gates" << "Joe Blow" << "Bill Clinton"; + list5 = list5.filter( QRegularExpression("[i]ll") ); + list6 << "Bill Gates" << "Bill Clinton"; + QCOMPARE( list5, list6 ); +} + +void tst_QStringList::sort() +{ + QStringList list1, list2; + list1 << "alpha" << "beta" << "BETA" << "gamma" << "Gamma" << "gAmma" << "epsilon"; + list1.sort(); + list2 << "BETA" << "Gamma" << "alpha" << "beta" << "epsilon" << "gAmma" << "gamma"; + QCOMPARE( list1, list2 ); + + char *current_locale = setlocale(LC_ALL, "C"); + QStringList list3, list4; + list3 << "alpha" << "beta" << "BETA" << "gamma" << "Gamma" << "gAmma" << "epsilon"; + list3.sort(Qt::CaseInsensitive); + list4 << "alpha" << "beta" << "BETA" << "epsilon" << "Gamma" << "gAmma" << "gamma"; + QCOMPARE( list3, list4 ); + setlocale(LC_ALL, current_locale); } void tst_QStringList::replaceInStrings() @@ -170,6 +238,18 @@ void tst_QStringList::replaceInStrings() list6 << "Bill Clinton" << "Bill Gates"; list5.replaceInStrings( QRegExp("^(.*), (.*)$"), "\\2 \\1" ); QCOMPARE( list5, list6 ); + + QStringList list7, list8; + list7 << "alpha" << "beta" << "gamma" << "epsilon"; + list7.replaceInStrings( QRegularExpression("^a"), "o" ); + list8 << "olpha" << "beta" << "gamma" << "epsilon"; + QCOMPARE( list7, list8 ); + + QStringList list9, list10; + list9 << "Bill Clinton" << "Gates, Bill"; + list10 << "Bill Clinton" << "Bill Gates"; + list9.replaceInStrings( QRegularExpression("^(.*), (.*)$"), "\\2 \\1" ); + QCOMPARE( list9, list10 ); } void tst_QStringList::contains() diff --git a/tests/auto/corelib/tools/qvector/tst_qvector.cpp b/tests/auto/corelib/tools/qvector/tst_qvector.cpp index c79aee4187..67ca547736 100644 --- a/tests/auto/corelib/tools/qvector/tst_qvector.cpp +++ b/tests/auto/corelib/tools/qvector/tst_qvector.cpp @@ -85,6 +85,8 @@ private slots: void initializeList(); void const_shared_null(); + void setSharable_data(); + void setSharable(); }; void tst_QVector::constructors() const @@ -946,5 +948,97 @@ void tst_QVector::const_shared_null() QVERIFY(!v2.isDetached()); } +Q_DECLARE_METATYPE(QVector<int>); + +void tst_QVector::setSharable_data() +{ + QTest::addColumn<QVector<int> >("vector"); + QTest::addColumn<int>("size"); + QTest::addColumn<int>("capacity"); + QTest::addColumn<bool>("isCapacityReserved"); + + QVector<int> null; + QVector<int> empty(0, 5); + QVector<int> emptyReserved; + QVector<int> nonEmpty; + QVector<int> nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + + nonEmpty << 0 << 1 << 2 << 3 << 4; + nonEmptyReserved << 0 << 1 << 2 << 3 << 4 << 5 << 6; + + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + QTest::newRow("null") << null << 0 << 0 << false; + QTest::newRow("empty") << empty << 0 << 0 << false; + QTest::newRow("empty, Reserved") << emptyReserved << 0 << 10 << true; + QTest::newRow("non-empty") << nonEmpty << 5 << 0 << false; + QTest::newRow("non-empty, Reserved") << nonEmptyReserved << 7 << 15 << true; +} + +void tst_QVector::setSharable() +{ + QFETCH(QVector<int>, vector); + QFETCH(int, size); + QFETCH(int, capacity); + QFETCH(bool, isCapacityReserved); + + QVERIFY(!vector.isDetached()); // Shared with QTest + + vector.setSharable(true); + + QCOMPARE(vector.size(), size); + if (isCapacityReserved) + QVERIFY2(vector.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); + + { + QVector<int> copy(vector); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(vector)); + } + + vector.setSharable(false); + QVERIFY(vector.isDetached() || vector.isSharedWith(QVector<int>())); + + { + QVector<int> copy(vector); + + QVERIFY(copy.isDetached() || copy.isSharedWith(QVector<int>())); + QCOMPARE(copy.size(), size); + if (isCapacityReserved) + QVERIFY2(copy.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); + QCOMPARE(copy, vector); + } + + vector.setSharable(true); + + { + QVector<int> copy(vector); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(vector)); + } + + for (int i = 0; i < vector.size(); ++i) + QCOMPARE(vector[i], i); + + QCOMPARE(vector.size(), size); + if (isCapacityReserved) + QVERIFY2(vector.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); +} + QTEST_APPLESS_MAIN(tst_QVector) #include "tst_qvector.moc" diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro index d8961559e5..38225e12f7 100644 --- a/tests/auto/corelib/tools/tools.pro +++ b/tests/auto/corelib/tools/tools.pro @@ -1,6 +1,7 @@ TEMPLATE=subdirs SUBDIRS=\ qalgorithms \ + qarraydata \ qbitarray \ qbytearray \ qbytearraymatcher \ |