diff options
Diffstat (limited to 'tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp')
-rw-r--r-- | tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp | 438 |
1 files changed, 361 insertions, 77 deletions
diff --git a/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp b/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp index 73dc02b082..0eaadb870c 100644 --- a/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp +++ b/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp @@ -1,37 +1,18 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QAnyStringView> #include <QChar> +#include <QDebug> #include <QList> #include <QString> #include <QStringBuilder> #include <QVarLengthArray> +#if QT_CONFIG(cpp_winrt) +# include <private/qt_winrtbase_p.h> +#endif +#include <private/qxmlstream_p.h> +#include <private/qcomparisontesthelper_p.h> #include <QTest> @@ -67,6 +48,16 @@ # define ONLY_WIN(expr) QSKIP("This is a Windows-only test") #endif +#ifdef __cpp_impl_three_way_comparison +# define ONLY_3WAY(expr) expr +#else +# define ONLY_3WAY(expr) \ + QSKIP("This test requires C++20 spaceship operator (<=>) " \ + "support enabled in the standard library.") +#endif + +using namespace Qt::StringLiterals; + template <typename T> constexpr inline bool CanConvert = std::is_convertible_v<T, QAnyStringView>; @@ -74,6 +65,16 @@ static_assert(CanConvert<QLatin1String>); static_assert(CanConvert<const char*>); static_assert(CanConvert<QByteArray>); +template <typename T> +struct ImplicitlyConvertibleTo +{ + operator T() const; +}; + +static_assert(CanConvert<ImplicitlyConvertibleTo<QString>>); +static_assert(CanConvert<ImplicitlyConvertibleTo<QByteArray>>); +static_assert(!CanConvert<ImplicitlyConvertibleTo<QLatin1StringView>>); + // QAnyStringView qchar_does_not_compile() { return QAnyStringView(QChar('a')); } // QAnyStringView qlatin1string_does_not_compile() { return QAnyStringView(QLatin1String("a")); } // QAnyStringView const_char_star_does_not_compile() { return QAnyStringView("a"); } @@ -170,6 +171,8 @@ static_assert(CanConvert<std::array<char16_t, 123>>); static_assert(!CanConvert<std::deque<char16_t>>); static_assert(!CanConvert<std::list<char16_t>>); +static_assert(CanConvert<QtPrivate::XmlStringRef>); + // // char32_t // @@ -238,6 +241,81 @@ static_assert(!CanConvert<std::list<wchar_t>>); static_assert(CanConvert<QStringBuilder<QString, QString>>); +#if QT_CONFIG(cpp_winrt) + +// +// winrt::hstring (QTBUG-111886) +// + +static_assert(CanConvert< winrt::hstring >); +static_assert(CanConvert<const winrt::hstring >); +static_assert(CanConvert< winrt::hstring&>); +static_assert(CanConvert<const winrt::hstring&>); + +#endif // QT_CONFIG(cpp_winrt) + +// In bootstrapped build and in Qt 7+, two lower bits of size() are used as a +// mask, so check that it is handled correctly, and the mask does not break the +// actual size +template <typename Char> struct SampleStrings +{ + static constexpr char emptyString[] = ""; + static constexpr char oneChar[] = "a"; + static constexpr char twoChars[] = "ab"; + static constexpr char threeChars[] = "abc"; + static constexpr char regularString[] = "Hello World!"; + static constexpr char regularLongString[] = R"(Lorem ipsum dolor sit amet, consectetur +adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna +aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in +voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint +occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim +id est laborum.)"; + static constexpr char stringWithNulls[] = "Hello\0World\0!"; + static constexpr qsizetype stringWithNullsLength = std::size(stringWithNulls) -1; +}; + +template <> struct SampleStrings<char16_t> +{ + static constexpr char16_t emptyString[] = u""; + static constexpr char16_t oneChar[] = u"a"; + static constexpr char16_t twoChars[] = u"ab"; + static constexpr char16_t threeChars[] = u"abc"; + static constexpr char16_t regularString[] = u"Hello World!"; + static constexpr char16_t regularLongString[] = uR"(Lorem ipsum dolor sit amet, consectetur +adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna +aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in +voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint +occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim +id est laborum.)"; + static constexpr char16_t stringWithNulls[] = u"Hello\0World\0!"; + static constexpr qsizetype stringWithNullsLength = std::size(stringWithNulls) -1; +}; + +template <> struct SampleStrings<QChar> +{ + static constexpr QChar emptyString[] = { {} }; // this one is easy + static const QChar *const oneChar; + static const QChar *const twoChars; + static const QChar *const threeChars; + static const QChar *const regularString; + static const QChar *const regularLongString; + static const QChar *const stringWithNulls; + static constexpr qsizetype stringWithNullsLength = SampleStrings<char16_t>::stringWithNullsLength; +}; +const QChar *const SampleStrings<QChar>::oneChar = + reinterpret_cast<const QChar *>(SampleStrings<char16_t>::oneChar); +const QChar *const SampleStrings<QChar>::twoChars = + reinterpret_cast<const QChar *>(SampleStrings<char16_t>::twoChars); +const QChar *const SampleStrings<QChar>::threeChars = + reinterpret_cast<const QChar *>(SampleStrings<char16_t>::threeChars); +const QChar *const SampleStrings<QChar>::regularString = + reinterpret_cast<const QChar *>(SampleStrings<char16_t>::regularString); +const QChar *const SampleStrings<QChar>::regularLongString = + reinterpret_cast<const QChar *>(SampleStrings<char16_t>::regularLongString); +const QChar *const SampleStrings<QChar>::stringWithNulls = + reinterpret_cast<const QChar *>(SampleStrings<char16_t>::stringWithNulls); class tst_QAnyStringView : public QObject { @@ -246,9 +324,14 @@ class tst_QAnyStringView : public QObject private Q_SLOTS: void constExpr() const; void basics() const; + void debug() const; + void asciiLiteralIsLatin1() const; void fromQString() const { fromQStringOrByteArray<QString>(); } void fromQByteArray() const { fromQStringOrByteArray<QByteArray>(); } + void fromQStringView() const { fromQStringOrByteArray<QStringView>(); } + void fromQUtf8StringView() const { fromQStringOrByteArray<QUtf8StringView>(); } + void fromQLatin1StringView() const { fromQStringOrByteArray<QLatin1StringView>(); } void fromCharArray() const { fromArray<char>(); } void fromChar8Array() const { ONLY_IF_CHAR_8_T(fromArray<char8_t>()); } @@ -276,29 +359,12 @@ private Q_SLOTS: void fromChar16TStar() const { fromLiteral(u"Hello, World!"); } void fromWCharTStar() const { ONLY_WIN(fromLiteral(L"Hello, World!")); } - void fromQCharRange() const - { - const QChar str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; - fromRange(std::begin(str), std::end(str)); - } - - void fromUShortRange() const - { - const ushort str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; - fromRange(std::begin(str), std::end(str)); - } - - void fromChar16TRange() const - { - const char16_t str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; - fromRange(std::begin(str), std::end(str)); - } - - void fromWCharTRange() const - { - [[maybe_unused]] const wchar_t str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; - ONLY_WIN(fromRange(std::begin(str), std::end(str))); - } + void fromCharRange() const { fromRange<char>(); } + void fromChar8TRange() const { ONLY_IF_CHAR_8_T(fromRange<char8_t>()); } + void fromQCharRange() const { fromRange<QChar>(); } + void fromUShortRange() const { fromRange<ushort>(); } + void fromChar16TRange() const { fromRange<char16_t>(); } + void fromWCharTRange() const { ONLY_WIN(fromRange<wchar_t>()); } // std::basic_string void fromStdStringChar() const { fromStdString<char>(); } @@ -311,8 +377,10 @@ private Q_SLOTS: void fromChar16TContainers() const { fromContainers<char16_t>(); } void fromWCharTContainers() const { ONLY_WIN(fromContainers<wchar_t>()); } - void fromQStringBuilder_QString_QString() const { fromQStringBuilder(u"1"_qs % u"2"_qs, u"12"); } + void fromQStringBuilder_QString_QString() const { fromQStringBuilder(u"1"_s % u"2"_s, u"12"); } + void comparisonCompiles(); + void comparison_data(); void comparison(); private: @@ -325,7 +393,7 @@ private: template <typename Char> void fromLiteral(const Char *arg) const; template <typename Char> - void fromRange(const Char *first, const Char *last) const; + void fromRange() const; template <typename Char, typename Container> void fromContainer() const; template <typename Char> @@ -431,6 +499,100 @@ void tst_QAnyStringView::basics() const QVERIFY(!(sv2 != sv1)); } +void tst_QAnyStringView::debug() const +{ + #ifdef QT_SUPPORTS_IS_CONSTANT_EVALUATED + # define MAYBE_L1(str) str "_L1" + # define VERIFY_L1(s) QVERIFY(s.isLatin1()) + #else + # define MAYBE_L1(str) "u8" str + # define VERIFY_L1(s) QVERIFY(s.isUtf8()) + #endif + #define CHECK1(s, mod, expected) do { \ + QString result; \ + QDebug(&result) mod << "X"_L1 << s << "Y"_L1; \ + /* QDebug appends an eager ' ', so trim before comparison */ \ + /* We use X and Y affixes so we can still check spacing */ \ + /* around the QAnyStringView itself. */ \ + QCOMPARE(result.trimmed(), expected); \ + } while (false) + #define CHECK(init, esq, eq, es, e) do { \ + QAnyStringView s = init; \ + CHECK1(s, , esq); \ + CHECK1(s, .nospace(), eq); \ + CHECK1(s, .noquote(), es); \ + CHECK1(s, .nospace().noquote(), e); \ + } while (false) + + CHECK(nullptr, + R"("X" u8"" "Y")", + R"("X"u8"""Y")", + R"(X Y)", + R"(XY)"); + CHECK(QLatin1StringView(nullptr), + R"("X" ""_L1 "Y")", + R"("X"""_L1"Y")", + R"(X Y)", + R"(XY)"); + CHECK(QUtf8StringView(nullptr), + R"("X" u8"" "Y")", + R"("X"u8"""Y")", + R"(X Y)", + R"(XY)"); + CHECK(QStringView(nullptr), + R"("X" u"" "Y")", + R"("X"u"""Y")", + R"(X Y)", + R"(XY)"); + { + constexpr QAnyStringView asv = "hello"; + VERIFY_L1(asv); // ### fails when asv isn't constexpr + CHECK(asv, + R"("X" )" MAYBE_L1(R"("hello")") R"( "Y")", + R"("X")" MAYBE_L1(R"("hello")") R"("Y")", + R"(X hello Y)", + R"(XhelloY)"); + } + CHECK(u8"hällo", + R"("X" u8"h\xC3\xA4llo" "Y")", + R"("X"u8"h\xC3\xA4llo""Y")", + R"(X hällo Y)", + R"(XhälloY)"); + CHECK(u"hällo", + R"("X" u"hällo" "Y")", + R"("X"u"hällo""Y")", + R"(X hällo Y)", + R"(XhälloY)"); + + #undef CHECK + #undef CHECK1 + #undef VERIFY_L1 + #undef MAYBE_L1 +} + +void tst_QAnyStringView::asciiLiteralIsLatin1() const +{ + if constexpr (QAnyStringView::detects_US_ASCII_at_compile_time) { + constexpr bool asciiCstringIsLatin1 = QAnyStringView("Hello, World").isLatin1(); + QVERIFY(asciiCstringIsLatin1); + constexpr bool asciiUtf8stringIsLatin1 = QAnyStringView(u8"Hello, World").isLatin1(); + QVERIFY(asciiUtf8stringIsLatin1); + constexpr bool utf8StringIsNotLatin1 = !QAnyStringView(u8"Tørrfisk").isLatin1(); + QVERIFY(utf8StringIsNotLatin1); + constexpr bool asciiCstringArrayIsLatin1 = + QAnyStringView::fromArray("Hello, World").isLatin1(); + QVERIFY(asciiCstringArrayIsLatin1); + constexpr bool asciiUtfstringArrayIsLatin1 = + QAnyStringView::fromArray(u8"Hello, World").isLatin1(); + QVERIFY(asciiUtfstringArrayIsLatin1); + constexpr bool utf8StringArrayIsNotLatin1 = + !QAnyStringView::fromArray(u8"Tørrfisk").isLatin1(); + QVERIFY(utf8StringArrayIsNotLatin1); + } else { + QSKIP("Compile-detection of US-ASCII strings not possible with this compiler"); + } +} + template <typename StringBuilder> void tst_QAnyStringView::fromQStringBuilder(StringBuilder &&sb, QStringView expected) const { @@ -458,18 +620,37 @@ void tst_QAnyStringView::fromArray() const QCOMPARE(sv2.back(), u'c'); } + template <typename QStringOrByteArray> void tst_QAnyStringView::fromQStringOrByteArray() const { + using Char = std::remove_cv_t<typename QStringOrByteArray::value_type>; + using Strings = SampleStrings<Char>; + QStringOrByteArray null; - QStringOrByteArray empty = ""; + QStringOrByteArray empty(Strings::emptyString); QVERIFY( QAnyStringView(null).isNull()); QVERIFY( QAnyStringView(null).isEmpty()); QVERIFY( QAnyStringView(empty).isEmpty()); QVERIFY(!QAnyStringView(empty).isNull()); - conversion_tests(QStringOrByteArray("Hello World!")); + conversion_tests(QStringOrByteArray(Strings::oneChar)); + if (QTest::currentTestFailed()) + return; + conversion_tests(QStringOrByteArray(Strings::twoChars)); + if (QTest::currentTestFailed()) + return; + conversion_tests(QStringOrByteArray(Strings::threeChars)); + if (QTest::currentTestFailed()) + return; + conversion_tests(QStringOrByteArray(Strings::regularString)); + if (QTest::currentTestFailed()) + return; + conversion_tests(QStringOrByteArray(Strings::regularLongString)); + if (QTest::currentTestFailed()) + return; + conversion_tests(QStringOrByteArray(Strings::stringWithNulls, Strings::stringWithNullsLength)); } template <typename Char> @@ -493,27 +674,48 @@ void tst_QAnyStringView::fromLiteral(const Char *arg) const } template <typename Char> -void tst_QAnyStringView::fromRange(const Char *first, const Char *last) const +void tst_QAnyStringView::fromRange() const { + auto doTest = [](const Char *first, const Char *last) { + QCOMPARE(QAnyStringView(first, first).size(), 0); + QCOMPARE(static_cast<const void*>(QAnyStringView(first, first).data()), + static_cast<const void*>(first)); + + const auto sv = QAnyStringView(first, last); + QCOMPARE(sv.size(), last - first); + QCOMPARE(static_cast<const void*>(sv.data()), + static_cast<const void*>(first)); + + // can't call conversion_tests() here, as it requires a single object + }; const Char *null = nullptr; + using RealChar = std::conditional_t<sizeof(Char) == 1, char, char16_t>; + using Strings = SampleStrings<RealChar>; + QCOMPARE(QAnyStringView(null, null).size(), 0); QCOMPARE(QAnyStringView(null, null).data(), nullptr); - QCOMPARE(QAnyStringView(first, first).size(), 0); - QCOMPARE(static_cast<const void*>(QAnyStringView(first, first).data()), - static_cast<const void*>(first)); - const auto sv = QAnyStringView(first, last); - QCOMPARE(sv.size(), last - first); - QCOMPARE(static_cast<const void*>(sv.data()), - static_cast<const void*>(first)); + doTest(reinterpret_cast<const Char *>(std::begin(Strings::regularString)), + reinterpret_cast<const Char *>(std::end(Strings::regularString))); + if (QTest::currentTestFailed()) + return; - // can't call conversion_tests() here, as it requires a single object + doTest(reinterpret_cast<const Char *>(std::begin(Strings::regularLongString)), + reinterpret_cast<const Char *>(std::end(Strings::regularLongString))); + if (QTest::currentTestFailed()) + return; + + doTest(reinterpret_cast<const Char *>(std::begin(Strings::stringWithNulls)), + reinterpret_cast<const Char *>(std::end(Strings::stringWithNulls))); + if (QTest::currentTestFailed()) + return; } template <typename Char, typename Container> void tst_QAnyStringView::fromContainer() const { const std::string s = "Hello World!"; + const std::string n(SampleStrings<char>::stringWithNulls, SampleStrings<char>::stringWithNullsLength); Container c; // unspecified whether empty containers make null QAnyStringViews @@ -521,6 +723,13 @@ void tst_QAnyStringView::fromContainer() const std::copy(s.begin(), s.end(), std::back_inserter(c)); conversion_tests(std::move(c)); + if (QTest::currentTestFailed()) + return; + + // repeat with nulls + c = {}; + std::copy(n.begin(), n.end(), std::back_inserter(c)); + conversion_tests(std::move(c)); } template <typename Char> @@ -612,22 +821,97 @@ void tst_QAnyStringView::conversion_tests(String string) const } } +void tst_QAnyStringView::comparisonCompiles() +{ + QTestPrivate::testAllComparisonOperatorsCompile<QAnyStringView>(); + QTestPrivate::testAllComparisonOperatorsCompile<QAnyStringView, char16_t>(); + QTestPrivate::testAllComparisonOperatorsCompile<QAnyStringView, QChar>(); + QTestPrivate::testAllComparisonOperatorsCompile<QAnyStringView, const char16_t *>(); + QTestPrivate::testAllComparisonOperatorsCompile<QAnyStringView, const char *>(); + QTestPrivate::testAllComparisonOperatorsCompile<QAnyStringView, QByteArray>(); + QTestPrivate::testAllComparisonOperatorsCompile<QAnyStringView, QByteArrayView>(); + QTestPrivate::testAllComparisonOperatorsCompile<QAnyStringView, QString>(); + QTestPrivate::testAllComparisonOperatorsCompile<QAnyStringView, QStringView>(); + QTestPrivate::testAllComparisonOperatorsCompile<QAnyStringView, QUtf8StringView>(); + QTestPrivate::testAllComparisonOperatorsCompile<QAnyStringView, QLatin1StringView>(); +} + +void tst_QAnyStringView::comparison_data() +{ + QTest::addColumn<QAnyStringView>("lhs"); + QTest::addColumn<QAnyStringView>("rhs"); + QTest::addColumn<int>("csr"); // case sensitive result + QTest::addColumn<int>("cir"); // case insensitive result + + auto row = [&](QAnyStringView l, QAnyStringView r, int csr, int cir) { + QTest::addRow("%s_vs_%s", qPrintable(l.toString()), qPrintable(r.toString())) + << l << r << csr << cir; + }; + row(u"aa", u"aa", 0, 0); + row(u"aa", u"AA", 1, 0); + row(u"ab", u"b", -1, -1); + row(u"ab", u"aBb", 1, -1); + row(u"ab", u"B", 1, -1); +} + +static int sign(int x) +{ + return x == 0 ? 0 : (x < 0 ? -1 : 1); +} + void tst_QAnyStringView::comparison() { - const QAnyStringView aa = u"aa"; - const QAnyStringView upperAa = u"AA"; - const QAnyStringView bb = u"bb"; - - QVERIFY(aa == aa); - QVERIFY(aa != bb); - QVERIFY(aa < bb); - QVERIFY(bb > aa); - - QCOMPARE(QAnyStringView::compare(aa, aa), 0); - QVERIFY(QAnyStringView::compare(aa, upperAa) != 0); - QCOMPARE(QAnyStringView::compare(aa, upperAa, Qt::CaseInsensitive), 0); - QVERIFY(QAnyStringView::compare(aa, bb) < 0); - QVERIFY(QAnyStringView::compare(bb, aa) > 0); + QFETCH(const QAnyStringView, lhs); + QFETCH(const QAnyStringView, rhs); + QFETCH(const int, csr); + QFETCH(const int, cir); + + QCOMPARE(sign(QAnyStringView::compare(lhs, rhs)), csr); + QCOMPARE(sign(QAnyStringView::compare(lhs, rhs, Qt::CaseInsensitive)), cir); + + const Qt::strong_ordering ordering = [&csr] { + if (csr == 0) + return Qt::strong_ordering::equal; + else if (csr < 0) + return Qt::strong_ordering::less; + else + return Qt::strong_ordering::greater; + }(); + QT_TEST_ALL_COMPARISON_OPS(lhs, rhs, ordering); + + const QString rhs_str = rhs.toString(); + QT_TEST_ALL_COMPARISON_OPS(lhs, rhs_str, ordering); + + const QStringView rhs_sv(rhs_str); + QT_TEST_ALL_COMPARISON_OPS(lhs, rhs_sv, ordering); + + if (!rhs_str.contains(QChar(u'\0'))) { + const char16_t *utfData = reinterpret_cast<const char16_t*>(rhs_str.constData()); + QT_TEST_ALL_COMPARISON_OPS(lhs, utfData, ordering); + } + + if (rhs_str.size() == 1) { + const QChar ch = rhs_str.front(); + QT_TEST_ALL_COMPARISON_OPS(lhs, ch, ordering); + } + + if (rhs.isLatin1()) { + const QLatin1StringView rhs_l1 = rhs.asLatin1StringView(); + QT_TEST_ALL_COMPARISON_OPS(lhs, rhs_l1, ordering); + } + + const QByteArray rhs_u8 = rhs_str.toUtf8(); + + const QUtf8StringView rhs_u8sv(rhs_u8.data(), rhs_u8.size()); + QT_TEST_ALL_COMPARISON_OPS(lhs, rhs_u8sv, ordering); + + QT_TEST_ALL_COMPARISON_OPS(lhs, rhs_u8, ordering); + const QByteArrayView rhs_u8view{rhs_u8.begin(), rhs_u8.size()}; + QT_TEST_ALL_COMPARISON_OPS(lhs, rhs_u8view, ordering); + if (!rhs_str.contains(QChar(u'\0'))) { + const char *rhs_u8data = rhs_u8.constData(); + QT_TEST_ALL_COMPARISON_OPS(lhs, rhs_u8data, ordering); + } } QTEST_APPLESS_MAIN(tst_QAnyStringView) |