From 9319f790604c89acafdefd8c7dd97f90bcc9a37b Mon Sep 17 00:00:00 2001 From: Matthias Rauter Date: Wed, 5 Feb 2025 14:48:53 +0100 Subject: [PATCH] Add tests for QUtf8StringView This is a port from QLatin1StringView to QUtf8StringView. Some functions of QL1SV have no counterpart to QU8SV: * first() and last() * count() * indexOf() * toUtf8() that were not included. Tests for user defined literals have also been removed. Some tests were extended for utf8 string literals u8"". Pick-to: 6.9 6.8 6.5 Task-number: QTBUG-132097 Change-Id: I0ceee00a7457360b430d13c3199f642f8e4c0036 Reviewed-by: Mate Barany --- tests/auto/corelib/text/CMakeLists.txt | 1 + .../corelib/text/qutf8stringview/.gitignore | 1 + .../text/qutf8stringview/CMakeLists.txt | 20 + .../qutf8stringview/tst_qutf8stringview.cpp | 409 ++++++++++++++++++ 4 files changed, 431 insertions(+) create mode 100644 tests/auto/corelib/text/qutf8stringview/.gitignore create mode 100644 tests/auto/corelib/text/qutf8stringview/CMakeLists.txt create mode 100644 tests/auto/corelib/text/qutf8stringview/tst_qutf8stringview.cpp diff --git a/tests/auto/corelib/text/CMakeLists.txt b/tests/auto/corelib/text/CMakeLists.txt index 3c392cc47ae..2bb9b89ff80 100644 --- a/tests/auto/corelib/text/CMakeLists.txt +++ b/tests/auto/corelib/text/CMakeLists.txt @@ -33,3 +33,4 @@ add_subdirectory(qstringtokenizer) add_subdirectory(qstringview) add_subdirectory(qtextboundaryfinder) add_subdirectory(qunicodetools) +add_subdirectory(qutf8stringview) diff --git a/tests/auto/corelib/text/qutf8stringview/.gitignore b/tests/auto/corelib/text/qutf8stringview/.gitignore new file mode 100644 index 00000000000..ee82d2ee365 --- /dev/null +++ b/tests/auto/corelib/text/qutf8stringview/.gitignore @@ -0,0 +1 @@ +tst_qutf8stringview diff --git a/tests/auto/corelib/text/qutf8stringview/CMakeLists.txt b/tests/auto/corelib/text/qutf8stringview/CMakeLists.txt new file mode 100644 index 00000000000..86562fba860 --- /dev/null +++ b/tests/auto/corelib/text/qutf8stringview/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qutf8stringview Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qutf8stringview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qutf8stringview + SOURCES + tst_qutf8stringview.cpp +) + +## Scopes: +##################################################################### diff --git a/tests/auto/corelib/text/qutf8stringview/tst_qutf8stringview.cpp b/tests/auto/corelib/text/qutf8stringview/tst_qutf8stringview.cpp new file mode 100644 index 00000000000..fec6fddd131 --- /dev/null +++ b/tests/auto/corelib/text/qutf8stringview/tst_qutf8stringview.cpp @@ -0,0 +1,409 @@ +// Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include +#include + +#ifdef __cpp_char8_t +# define ONLY_IF_CHAR_8_T(expr) expr +#else +# define ONLY_IF_CHAR_8_T(expr) \ + QSKIP("This test requires C++20 char8_t support enabled in the compiler.") +#endif + +// QTBUG-112746 +namespace { +extern const char string_array[]; +static void from_array_of_unknown_size() +{ + auto sv = QUtf8StringView{string_array}; + QCOMPARE(sv.size(), 3); +} +const char string_array[] = "abc\0def"; + +#ifdef __cpp_char8_t +extern const char8_t string_array_8t[]; +static void from_array_of_unknown_size_8t() +{ + auto sv = QUtf8StringView{string_array_8t}; + QCOMPARE(sv.size(), 3); +} +const char8_t string_array_8t[] = u8"abc\0def"; +#endif +} // unnamed namespace + +template +const void *as_const_void_star(T *p) { return p; } + +class tst_QUtf8StringView : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void fromArraysOfUnknownSize(); + void constExpr(); + void construction(); + void at(); + void arg() const; + void midLeftRight(); + void nullString(); + void emptyString(); + void iterators(); + void relationalOperators_data(); + void relationalOperators(); +}; + +void tst_QUtf8StringView::fromArraysOfUnknownSize() +{ + from_array_of_unknown_size(); + ONLY_IF_CHAR_8_T(from_array_of_unknown_size_8t()); +} + +void tst_QUtf8StringView::constExpr() +{ + // compile-time checks + { + constexpr QUtf8StringView utf8s; + static_assert(utf8s.size() == 0); + static_assert(utf8s.isNull()); + static_assert(utf8s.empty()); + static_assert(utf8s.isEmpty()); + static_assert(utf8s.data() == nullptr); + + constexpr QUtf8StringView utf8s2(utf8s.data(), utf8s.data() + utf8s.size()); + static_assert(utf8s2.isNull()); + static_assert(utf8s2.empty()); + } + { + constexpr QUtf8StringView utf8s = nullptr; + static_assert(utf8s.size() == 0); + static_assert(utf8s.isNull()); + static_assert(utf8s.empty()); + static_assert(utf8s.isEmpty()); + static_assert(utf8s.data() == nullptr); + } + { + constexpr QUtf8StringView utf8s(""); + static_assert(utf8s.size() == 0); + static_assert(!utf8s.isNull()); + static_assert(utf8s.empty()); + static_assert(utf8s.isEmpty()); + + constexpr QUtf8StringView utf8s2(utf8s.data(), utf8s.data() + utf8s.size()); + static_assert(!utf8s2.isNull()); + static_assert(utf8s2.empty()); + } + { + static_assert(QUtf8StringView("Hello").size() == 5); + constexpr QUtf8StringView utf8s("Hello"); + static_assert(utf8s.size() == 5); + static_assert(!utf8s.empty()); + static_assert(!utf8s.isEmpty()); + static_assert(!utf8s.isNull()); + static_assert(*utf8s.data() == 'H'); + + static_assert(utf8s[0] == char('H')); + static_assert(utf8s.at(0) == char('H')); + static_assert(utf8s.front() == char('H')); + static_assert(utf8s[4] == char('o')); + static_assert(utf8s.at(4) == char('o')); + static_assert(utf8s.back() == char('o')); + +#ifdef __cpp_char8_t + static_assert(utf8s[0] == char8_t('H')); + static_assert(utf8s.at(0) == char8_t('H')); + static_assert(utf8s.front() == char8_t('H')); + static_assert(utf8s[4] == char8_t('o')); + static_assert(utf8s.at(4) == char8_t('o')); + static_assert(utf8s.back() == char8_t('o')); +#endif + + constexpr QUtf8StringView utf8s2(utf8s.data(), utf8s.data() + utf8s.size()); + static_assert(!utf8s2.isNull()); + static_assert(!utf8s2.empty()); + static_assert(utf8s2.size() == 5); + } +} + +void tst_QUtf8StringView::construction() +{ + { + auto str = u8"hello"; + QUtf8StringView utf8s(str); + QCOMPARE(utf8s.size(), 5); + QCOMPARE(utf8s.data(), as_const_void_star(&str[0])); + ONLY_IF_CHAR_8_T(QCOMPARE(as_const_void_star(utf8s.utf8()), as_const_void_star(&str[0]))); + ONLY_IF_CHAR_8_T(QCOMPARE(as_const_void_star(utf8s.utf8()), utf8s.data())); + + QUtf8StringView s1 = {str, 5}; + QCOMPARE(s1, utf8s); + QUtf8StringView s2 = {str, str + 5}; + QCOMPARE(s2, utf8s); + +#ifdef __cpp_char8_t + QByteArrayView helloView(reinterpret_cast(str)); +#else + QByteArrayView helloView(str); +#endif + helloView = helloView.first(4); + utf8s = QUtf8StringView(helloView); + QCOMPARE(utf8s.data(), helloView.data()); + QCOMPARE(utf8s.data(), as_const_void_star(helloView.data())); + QCOMPARE(utf8s.size(), helloView.size()); + ONLY_IF_CHAR_8_T(QCOMPARE(utf8s.utf8(), as_const_void_star(helloView.data()))); + ONLY_IF_CHAR_8_T(QCOMPARE(utf8s.size(), helloView.size())); + } + + { + const char str[6] = "hello"; + QUtf8StringView utf8s(str); + QCOMPARE(utf8s.size(), 5); + QCOMPARE(utf8s.data(), as_const_void_star(&str[0])); + QCOMPARE(utf8s.data(), "hello"); + ONLY_IF_CHAR_8_T(QCOMPARE(as_const_void_star(utf8s.utf8()), as_const_void_star(&str[0]))); + ONLY_IF_CHAR_8_T(QCOMPARE(as_const_void_star(utf8s.utf8()), utf8s.data())); + + QUtf8StringView s1 = {str, 5}; + QCOMPARE(s1, utf8s); + QUtf8StringView s2 = {str, str + 5}; + QCOMPARE(s2, utf8s); + + QByteArrayView helloView(str); + helloView = helloView.first(4); + utf8s = QUtf8StringView(helloView); + QCOMPARE(utf8s.data(), helloView.data()); + QCOMPARE(utf8s.data(), as_const_void_star(helloView.data())); + QCOMPARE(utf8s.size(), helloView.size()); + ONLY_IF_CHAR_8_T(QCOMPARE(utf8s.utf8(), as_const_void_star(helloView.data()))); + ONLY_IF_CHAR_8_T(QCOMPARE(utf8s.size(), helloView.size())); + } + + { + const QByteArray helloArray("hello"); + QUtf8StringView utf8s(helloArray); + QCOMPARE(utf8s.data(), helloArray.data()); + ONLY_IF_CHAR_8_T(QCOMPARE(utf8s.utf8(), as_const_void_star(helloArray.data()))); + QCOMPARE(utf8s.size(), helloArray.size()); + + QByteArrayView helloView(helloArray); + helloView = helloView.first(4); + utf8s = QUtf8StringView(helloView); + QCOMPARE(utf8s.data(), helloView.data()); + ONLY_IF_CHAR_8_T(QCOMPARE(utf8s.utf8(), as_const_void_star(helloView.data()))); + QCOMPARE(utf8s.size(), helloView.size()); + } +} + +void tst_QUtf8StringView::at() +{ + const QUtf8StringView utf8("Hello World"); + QCOMPARE(utf8.at(0), 'H'); + QCOMPARE(utf8.at(utf8.size() - 1), 'd'); + QCOMPARE(utf8[0], 'H'); + QCOMPARE(utf8[utf8.size() - 1], 'd'); +} + +void tst_QUtf8StringView::arg() const +{ +#define CHECK1(pattern, arg1, expected) \ + do { \ + auto p = QUtf8StringView(pattern); \ + QCOMPARE(p.arg(QUtf8StringView(arg1)), expected); \ + QCOMPARE(p.arg(u"" arg1), expected); \ + QCOMPARE(p.arg(QStringLiteral(arg1)), expected); \ + QCOMPARE(p.arg(QLatin1StringView(arg1).toString()), expected); \ + QCOMPARE(p.arg(u8"" arg1), expected); \ + } while (false) \ + /*end*/ +#define CHECK2(pattern, arg1, arg2, expected) \ + do { \ + auto p = QUtf8StringView(pattern); \ + QCOMPARE(p.arg(QUtf8StringView(arg1), QUtf8StringView(arg2)), expected); \ + QCOMPARE(p.arg(u"" arg1, QUtf8StringView(arg2)), expected); \ + QCOMPARE(p.arg(QUtf8StringView(arg1), u"" arg2), expected); \ + QCOMPARE(p.arg(u"" arg1, u"" arg2), expected); \ + QCOMPARE(p.arg(u8"" arg1, u8"" arg2), expected); \ + QCOMPARE(p.arg(u"" arg1, u8"" arg2), expected); \ + } while (false) \ + /*end*/ + + CHECK1("", "World", ""); + CHECK1("%1", "World", "World"); + CHECK1("!%1?", "World", "!World?"); + CHECK1("%1%1", "World", "WorldWorld"); + CHECK1("%1%2", "World", "World%2"); + CHECK1("%2%1", "World", "%2World"); + + CHECK2("", "Hello", "World", ""); + CHECK2("%1", "Hello", "World", "Hello"); + CHECK2("!%1, %2?", "Hello", "World", "!Hello, World?"); + CHECK2("%1%1", "Hello", "World", "HelloHello"); + CHECK2("%1%2", "Hello", "World", "HelloWorld"); + CHECK2("%2%1", "Hello", "World", "WorldHello"); + +#undef CHECK2 +#undef CHECK1 + + QCOMPARE(QUtf8StringView(" %2 %2 %1 %3 ").arg('c', QChar::CarriageReturn, u'C'), + " \r \r c C "); +} + +void tst_QUtf8StringView::midLeftRight() +{ + const QUtf8StringView utf8("Hello World"); + QCOMPARE(utf8.mid(0), utf8); + QCOMPARE(utf8.mid(0, utf8.size()), utf8); + QCOMPARE(utf8.left(utf8.size()), utf8); + QCOMPARE(utf8.right(utf8.size()), utf8); + + QCOMPARE(utf8.mid(6), QUtf8StringView("World")); + QCOMPARE(utf8.mid(6, 5), QUtf8StringView("World")); + QCOMPARE(utf8.right(5), QUtf8StringView("World")); + + QCOMPARE(utf8.mid(6, 1), QUtf8StringView("W")); + QCOMPARE(utf8.right(5).left(1), QUtf8StringView("W")); + + QCOMPARE(utf8.left(5), QUtf8StringView("Hello")); +} + +void tst_QUtf8StringView::nullString() +{ + // default ctor + { + QUtf8StringView utf8; + QCOMPARE(as_const_void_star(utf8.data()), nullptr); + QCOMPARE(utf8.size(), 0); + + QString s = utf8.toString(); //no operator= + QVERIFY(s.isNull()); + } + + // from nullptr + { + const char *null = nullptr; + QUtf8StringView utf8(null); + QCOMPARE(as_const_void_star(utf8.data()), nullptr); + QCOMPARE(utf8.size(), 0); + + QString s = utf8.toString(); //no operator= + QVERIFY(s.isNull()); + } + + // from null QByteArray + { + const QByteArray null; + QVERIFY(null.isNull()); + + QUtf8StringView utf8(null); + QCOMPARE(as_const_void_star(utf8.data()), nullptr); + QCOMPARE(utf8.size(), 0); + + QString s = utf8.toString(); //no operator= + QVERIFY(s.isNull()); + } +} + +void tst_QUtf8StringView::emptyString() +{ + { + const char *empty = ""; + QUtf8StringView utf8(empty); + QCOMPARE(as_const_void_star(utf8.data()), as_const_void_star(empty)); + QCOMPARE(utf8.size(), 0); + + QString s = utf8.toString(); //no operator= + QVERIFY(s.isEmpty()); + QVERIFY(!s.isNull()); + } + + { + const char *notEmpty = "foo"; + QUtf8StringView utf8(notEmpty, qsizetype(0)); + QCOMPARE(as_const_void_star(utf8.data()), as_const_void_star(notEmpty)); + QCOMPARE(utf8.size(), 0); + + QString s = utf8.toString(); //no operator= + QVERIFY(s.isEmpty()); + QVERIFY(!s.isNull()); + } + + { + const QByteArray empty = ""; + QUtf8StringView utf8(empty); + QCOMPARE(as_const_void_star(utf8.data()), as_const_void_star(empty.constData())); + QCOMPARE(utf8.size(), 0); + + QString s = utf8.toString(); //no operator= + QVERIFY(s.isEmpty()); + QVERIFY(!s.isNull()); + } +} + +void tst_QUtf8StringView::iterators() +{ + QUtf8StringView hello("hello"); + QUtf8StringView olleh("olleh"); + + QVERIFY(std::equal(hello.begin(), hello.end(), + olleh.rbegin())); + QVERIFY(std::equal(hello.rbegin(), hello.rend(), + QT_MAKE_CHECKED_ARRAY_ITERATOR(olleh.begin(), olleh.size()))); + + QVERIFY(std::equal(hello.cbegin(), hello.cend(), + olleh.rbegin())); + QVERIFY(std::equal(hello.crbegin(), hello.crend(), + QT_MAKE_CHECKED_ARRAY_ITERATOR(olleh.begin(), olleh.size()))); +} + +void tst_QUtf8StringView::relationalOperators_data() +{ + QTest::addColumn("lhs"); + QTest::addColumn("lhsOrderNumber"); + QTest::addColumn("rhs"); + QTest::addColumn("rhsOrderNumber"); + + struct Data { + QUtf8StringView l1; + int order; + } data[] = { + { QUtf8StringView(), 0 }, + { QUtf8StringView(""), 0 }, + { QUtf8StringView("a"), 1 }, + { QUtf8StringView("aa"), 2 }, + { QUtf8StringView("b"), 3 }, + }; + + for (Data *lhs = data; lhs != data + sizeof data / sizeof *data; ++lhs) { + for (Data *rhs = data; rhs != data + sizeof data / sizeof *data; ++rhs) { + QUtf8StringView l = { lhs->l1 }, r = { rhs->l1 }; + QTest::addRow("\"%s\" <> \"%s\"", + lhs->l1.data() ? lhs->l1.data() : "nullptr", + rhs->l1.data() ? rhs->l1.data() : "nullptr") + << l << lhs->order << r << rhs->order; + } + } +} + +void tst_QUtf8StringView::relationalOperators() +{ + QFETCH(QUtf8StringView, lhs); + QFETCH(int, lhsOrderNumber); + QFETCH(QUtf8StringView, rhs); + QFETCH(int, rhsOrderNumber); + +#define CHECK(op) \ + QCOMPARE(lhs op rhs, lhsOrderNumber op rhsOrderNumber) \ + /*end*/ + CHECK(==); + CHECK(!=); + CHECK(< ); + CHECK(> ); + CHECK(<=); + CHECK(>=); +#undef CHECK +} + +QTEST_APPLESS_MAIN(tst_QUtf8StringView) + +#include "tst_qutf8stringview.moc"