diff options
author | Marc Mutz <marc.mutz@qt.io> | 2021-11-16 10:59:24 +0100 |
---|---|---|
committer | Marc Mutz <marc.mutz@qt.io> | 2021-11-18 17:13:19 +0000 |
commit | 0e245c158f59f5517201d139b31a7a1b98ed09c6 (patch) | |
tree | 37de31662e1af7feb180337196ba63849319f20c /tests | |
parent | 0fa6e6b0ab71a00864dbe22879c1d811d3ae2c9f (diff) |
Add an initial tst_QAnyStringView
It's incomplet, but at least something.
Task-number: QTBUG-98138
Pick-to: 6.2
Change-Id: I4630a44b62b190dee8a8cc07822dd6ec67dbdc84
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Øystein Heskestad <oystein.heskestad@qt.io>
Diffstat (limited to 'tests')
4 files changed, 641 insertions, 0 deletions
diff --git a/tests/auto/corelib/text/CMakeLists.txt b/tests/auto/corelib/text/CMakeLists.txt index bbe86a12a5..9b80e43bc6 100644 --- a/tests/auto/corelib/text/CMakeLists.txt +++ b/tests/auto/corelib/text/CMakeLists.txt @@ -1,5 +1,6 @@ # Generated from text.pro. +add_subdirectory(qanystringview) add_subdirectory(qbytearray) add_subdirectory(qbytearrayapisymmetry) add_subdirectory(qbytearraylist) diff --git a/tests/auto/corelib/text/qanystringview/.gitignore b/tests/auto/corelib/text/qanystringview/.gitignore new file mode 100644 index 0000000000..f127febb38 --- /dev/null +++ b/tests/auto/corelib/text/qanystringview/.gitignore @@ -0,0 +1 @@ +tst_qanystringview diff --git a/tests/auto/corelib/text/qanystringview/CMakeLists.txt b/tests/auto/corelib/text/qanystringview/CMakeLists.txt new file mode 100644 index 0000000000..6a01874c5d --- /dev/null +++ b/tests/auto/corelib/text/qanystringview/CMakeLists.txt @@ -0,0 +1,11 @@ +##################################################################### +## tst_qstringview Test: +##################################################################### + +qt_internal_add_test(tst_qanystringview + SOURCES + tst_qanystringview.cpp +) + +## Scopes: +##################################################################### diff --git a/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp b/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp new file mode 100644 index 0000000000..bbc0bdacfd --- /dev/null +++ b/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp @@ -0,0 +1,628 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include <QAnyStringView> +#include <QChar> +#include <QList> +#include <QString> +#include <QVarLengthArray> + +#include <QTest> + +#include <string> +#include <string_view> +#include <array> +#include <vector> +#include <algorithm> +#include <memory> + +// for negative testing (can't convert from) +#include <deque> +#include <list> + +#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 + +#ifdef __cpp_lib_char8_t +# define ONLY_IF_LIB_CHAR_8_T(expr) expr +#else +# define ONLY_IF_LIB_CHAR_8_T(expr) \ + QSKIP("This test requires C++20 char8_t support enabled in the standard library.") +#endif + +#ifdef Q_OS_WIN +# define ONLY_WIN(expr) expr +#else +# define ONLY_WIN(expr) QSKIP("This is a Windows-only test") +#endif + +template <typename T> +constexpr inline bool CanConvert = std::is_convertible_v<T, QAnyStringView>; + +static_assert(CanConvert<QLatin1String>); +static_assert(CanConvert<const char*>); +static_assert(CanConvert<QByteArray>); + +// 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"); } +// QAnyStringView qbytearray_does_not_compile() { return QAnyStringView(QByteArray("a")); } + +// +// QChar +// + +static_assert(CanConvert<QChar>); + +static_assert(CanConvert<QChar[123]>); + +static_assert(CanConvert< QString >); +static_assert(CanConvert<const QString >); +static_assert(CanConvert< QString&>); +static_assert(CanConvert<const QString&>); + +// +// ushort +// + +static_assert(CanConvert<ushort>); + +static_assert(CanConvert<ushort[123]>); + +static_assert(CanConvert< ushort*>); +static_assert(CanConvert<const ushort*>); + +static_assert(CanConvert<QList<ushort>>); +static_assert(CanConvert<QVarLengthArray<ushort>>); +static_assert(CanConvert<std::vector<ushort>>); +static_assert(CanConvert<std::array<ushort, 123>>); +static_assert(!CanConvert<std::deque<ushort>>); +static_assert(!CanConvert<std::list<ushort>>); + +#ifdef __cpp_char8_t + +// +// char8_t +// + +static_assert(CanConvert<char8_t>); + +static_assert(CanConvert< char8_t*>); +static_assert(CanConvert<const char8_t*>); + +#ifdef __cpp_lib_char8_t + +static_assert(CanConvert< std::u8string >); +static_assert(CanConvert<const std::u8string >); +static_assert(CanConvert< std::u8string&>); +static_assert(CanConvert<const std::u8string&>); + +static_assert(CanConvert< std::u8string_view >); +static_assert(CanConvert<const std::u8string_view >); +static_assert(CanConvert< std::u8string_view&>); +static_assert(CanConvert<const std::u8string_view&>); + +#endif // __cpp_lib_char8_t + +static_assert(CanConvert<QList<char8_t>>); +static_assert(CanConvert<QVarLengthArray<char8_t>>); +static_assert(CanConvert<std::vector<char8_t>>); +static_assert(CanConvert<std::array<char8_t, 123>>); +static_assert(!CanConvert<std::deque<char8_t>>); +static_assert(!CanConvert<std::list<char8_t>>); + +#endif // __cpp_char8_t + +// +// char16_t +// + +static_assert(CanConvert<char16_t>); + +static_assert(CanConvert< char16_t*>); +static_assert(CanConvert<const char16_t*>); + +static_assert(CanConvert< std::u16string >); +static_assert(CanConvert<const std::u16string >); +static_assert(CanConvert< std::u16string&>); +static_assert(CanConvert<const std::u16string&>); + +static_assert(CanConvert< std::u16string_view >); +static_assert(CanConvert<const std::u16string_view >); +static_assert(CanConvert< std::u16string_view&>); +static_assert(CanConvert<const std::u16string_view&>); + +static_assert(CanConvert<QList<char16_t>>); +static_assert(CanConvert<QVarLengthArray<char16_t>>); +static_assert(CanConvert<std::vector<char16_t>>); +static_assert(CanConvert<std::array<char16_t, 123>>); +static_assert(!CanConvert<std::deque<char16_t>>); +static_assert(!CanConvert<std::list<char16_t>>); + +// +// char32_t +// + +// Qt Policy: char32_t isn't supported + +static_assert(CanConvert<char32_t>); // ... except here + +static_assert(!CanConvert< char32_t*>); +static_assert(!CanConvert<const char32_t*>); + +static_assert(!CanConvert< std::u32string >); +static_assert(!CanConvert<const std::u32string >); +static_assert(!CanConvert< std::u32string&>); +static_assert(!CanConvert<const std::u32string&>); + +static_assert(!CanConvert< std::u32string_view >); +static_assert(!CanConvert<const std::u32string_view >); +static_assert(!CanConvert< std::u32string_view&>); +static_assert(!CanConvert<const std::u32string_view&>); + +static_assert(!CanConvert<QList<char32_t>>); +static_assert(!CanConvert<QVarLengthArray<char32_t>>); +static_assert(!CanConvert<std::vector<char32_t>>); +static_assert(!CanConvert<std::array<char32_t, 123>>); +static_assert(!CanConvert<std::deque<char32_t>>); +static_assert(!CanConvert<std::list<char32_t>>); + +// +// wchar_t +// + +constexpr bool CanConvertFromWCharT = +#ifdef Q_OS_WIN + true +#else + false +#endif + ; + +static_assert(CanConvert<wchar_t> == CanConvertFromWCharT); // ### FIXME: should work everywhere + +static_assert(CanConvert< wchar_t*> == CanConvertFromWCharT); +static_assert(CanConvert<const wchar_t*> == CanConvertFromWCharT); + +static_assert(CanConvert< std::wstring > == CanConvertFromWCharT); +static_assert(CanConvert<const std::wstring > == CanConvertFromWCharT); +static_assert(CanConvert< std::wstring&> == CanConvertFromWCharT); +static_assert(CanConvert<const std::wstring&> == CanConvertFromWCharT); + +static_assert(CanConvert< std::wstring_view > == CanConvertFromWCharT); +static_assert(CanConvert<const std::wstring_view > == CanConvertFromWCharT); +static_assert(CanConvert< std::wstring_view&> == CanConvertFromWCharT); +static_assert(CanConvert<const std::wstring_view&> == CanConvertFromWCharT); + +static_assert(CanConvert<QList<wchar_t>> == CanConvertFromWCharT); +static_assert(CanConvert<QVarLengthArray<wchar_t>> == CanConvertFromWCharT); +static_assert(CanConvert<std::vector<wchar_t>> == CanConvertFromWCharT); +static_assert(CanConvert<std::array<wchar_t, 123>> == CanConvertFromWCharT); +static_assert(!CanConvert<std::deque<wchar_t>>); +static_assert(!CanConvert<std::list<wchar_t>>); + + +class tst_QAnyStringView : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void constExpr() const; + void basics() const; + + void fromQString() const { fromQStringOrByteArray<QString>(); } + void fromQByteArray() const { fromQStringOrByteArray<QByteArray>(); } + + void fromCharArray() const { fromArray<char>(); } + void fromChar8Array() const { ONLY_IF_CHAR_8_T(fromArray<char8_t>()); } + void fromChar16Array() const { fromArray<char16_t>(); } + void fromQCharArray() const { fromArray<QChar>(); } + void fromWCharTArray() const { ONLY_WIN(fromArray<wchar_t>()); } + + void fromQCharStar() const + { + const QChar str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0' }; + fromLiteral(str); + } + + void fromUShortStar() const + { + const ushort str[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0' }; + fromLiteral(str); + } + + void fromChar8TStar() const + { + fromLiteral(u8"Hello, World!"); // char[] in <= C++17, char8_t[] in >= C++20 + } + + 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))); + } + + // std::basic_string + void fromStdStringChar() const { fromStdString<char>(); } + void fromStdStringChar8T() const { ONLY_IF_LIB_CHAR_8_T(fromStdString<char8_t>()); } + void fromStdStringWCharT() const { ONLY_WIN(fromStdString<wchar_t>()); } + void fromStdStringChar16T() const { fromStdString<char16_t>(); } + + void fromUShortContainers() const { fromContainers<ushort>(); } + void fromQCharContainers() const { fromContainers<QChar>(); } + void fromChar16TContainers() const { fromContainers<char16_t>(); } + void fromWCharTContainers() const { ONLY_WIN(fromContainers<wchar_t>()); } + + void comparison(); + +private: + template <typename Char> + void fromArray() const; + template <typename String> + void conversion_tests(String arg) const; + template <typename Char> + void fromLiteral(const Char *arg) const; + template <typename Char> + void fromRange(const Char *first, const Char *last) const; + template <typename Char, typename Container> + void fromContainer() const; + template <typename Char> + void fromContainers() const; + template <typename Char> + void fromStdString() const { fromContainer<Char, std::basic_string<Char> >(); } + template <typename QStringOrByteArray> + void fromQStringOrByteArray() const; +}; + +void tst_QAnyStringView::constExpr() const +{ +#define IS_NULL(sv) \ + do { \ + static_assert(sv.size() == 0); \ + static_assert(sv.isNull()); \ + static_assert(sv.empty()); \ + static_assert(sv.isEmpty()); \ + static_assert(sv.data() == nullptr); \ + } while (false) \ + /*end*/ +#define IS_EMPTY(sv) \ + do { \ + static_assert(sv.size() == 0); \ + static_assert(!sv.isNull()); \ + static_assert(sv.empty()); \ + static_assert(sv.isEmpty()); \ + static_assert(sv.data() != nullptr); \ + } while (false) \ + /*end*/ +#define IS_OF_SIZE(sv, sz) \ + do { \ + static_assert(sv.size() == sz); \ + static_assert(!sv.isNull()); \ + static_assert(!sv.empty()); \ + static_assert(!sv.isEmpty()); \ + static_assert(sv.data() != nullptr); \ + } while (false) \ + /*end*/ + + // compile-time checks + { + constexpr QAnyStringView sv; + IS_NULL(sv); + } + { + constexpr const char *nul = nullptr; + constexpr QAnyStringView sv(nul, 0); + IS_NULL(sv); + } + { + constexpr const char16_t *nul = nullptr; + constexpr QAnyStringView sv(nul, 0); + IS_NULL(sv); + } +#ifdef __cpp_char8_t + { + constexpr const char8_t *nul = nullptr; + constexpr QAnyStringView sv(nul, 0); + IS_NULL(sv); + } +#endif // __cpp_char8_t + { + constexpr QAnyStringView sv = nullptr; + IS_NULL(sv); + } + { + constexpr QAnyStringView sv = ""; + IS_EMPTY(sv); + } + { + constexpr QAnyStringView sv = u8""; + IS_EMPTY(sv); + } + { + constexpr QAnyStringView sv = u""; + IS_EMPTY(sv); + } + { + constexpr QAnyStringView sv = u"Hello"; + IS_OF_SIZE(sv, 5); + + constexpr QAnyStringView sv2 = sv; + IS_OF_SIZE(sv2, 5); + } +#undef IS_OF_SIZE +#undef IS_EMPTY +#undef IS_NULL +} + +void tst_QAnyStringView::basics() const +{ + QAnyStringView sv1; + + // a default-constructed QAnyStringView is null: + QVERIFY(sv1.isNull()); + // which implies it's empty(); + QVERIFY(sv1.isEmpty()); + + QAnyStringView sv2; + + QVERIFY(sv2 == sv1); + QVERIFY(!(sv2 != sv1)); +} + +template <typename Char> +void tst_QAnyStringView::fromArray() const +{ + constexpr Char hello[] = {'H', 'e', 'l', 'l', 'o', '\0', 'a', 'b', 'c', '\0', '\0', '.', '\0'}; + + QAnyStringView sv = QAnyStringView::fromArray(hello); + QCOMPARE(sv.size(), 13); + QVERIFY(!sv.empty()); + QVERIFY(!sv.isEmpty()); + QVERIFY(!sv.isNull()); + QCOMPARE(sv.front(), 'H'); + QCOMPARE(sv.back(), '\0'); + + const Char bytes[] = {'a', 'b', 'c'}; + QAnyStringView sv2 = QAnyStringView::fromArray(bytes); + QCOMPARE(sv2.data(), static_cast<const void *>(bytes + 0)); + QCOMPARE(sv2.size(), 3); + QCOMPARE(sv2.back(), u'c'); +} + +template <typename QStringOrByteArray> +void tst_QAnyStringView::fromQStringOrByteArray() const +{ + QStringOrByteArray null; + QStringOrByteArray empty = ""; + + QVERIFY( QAnyStringView(null).isNull()); + QVERIFY( QAnyStringView(null).isEmpty()); + QVERIFY( QAnyStringView(empty).isEmpty()); + QVERIFY(!QAnyStringView(empty).isNull()); + + conversion_tests(QStringOrByteArray("Hello World!")); +} + +template <typename Char> +void tst_QAnyStringView::fromLiteral(const Char *arg) const +{ + const Char *null = nullptr; + const Char empty[] = { Char{} }; + + QCOMPARE(QAnyStringView(null).size(), qsizetype(0)); + QCOMPARE(QAnyStringView(null).data(), nullptr); + QCOMPARE(QAnyStringView(empty).size(), qsizetype(0)); + QCOMPARE(static_cast<const void*>(QAnyStringView(empty).data()), + static_cast<const void*>(empty)); + + QVERIFY( QAnyStringView(null).isNull()); + QVERIFY( QAnyStringView(null).isEmpty()); + QVERIFY( QAnyStringView(empty).isEmpty()); + QVERIFY(!QAnyStringView(empty).isNull()); + + conversion_tests(arg); +} + +template <typename Char> +void tst_QAnyStringView::fromRange(const Char *first, const Char *last) const +{ + const Char *null = nullptr; + 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)); + + // can't call conversion_tests() here, as it requires a single object +} + +template <typename Char, typename Container> +void tst_QAnyStringView::fromContainer() const +{ + const std::string s = "Hello World!"; + + Container c; + // unspecified whether empty containers make null QAnyStringViews + QVERIFY(QAnyStringView(c).isEmpty()); + + std::copy(s.begin(), s.end(), std::back_inserter(c)); + conversion_tests(std::move(c)); +} + +template <typename Char> +void tst_QAnyStringView::fromContainers() const +{ + fromContainer<Char, QList<Char>>(); + fromContainer<Char, QVarLengthArray<Char>>(); + fromContainer<Char, std::vector<Char>>(); +} + +namespace q20 { +#ifdef __cpp_lib_ssize + using std::ssize; +#else + template<class C> constexpr auto ssize(const C& c) + -> std::common_type_t<std::ptrdiff_t, std::make_signed_t<decltype(c.size())>> + { return static_cast<std::common_type_t<ptrdiff_t, std::make_signed_t<decltype(c.size())>>>(c.size()); } + + template<class T, ptrdiff_t N> constexpr ptrdiff_t ssize(const T (&array)[N]) noexcept + { return N; } +#endif +} + +namespace help { + template <typename T> + auto ssize(T &t) { return q20::ssize(t); } + + template <typename T> + qsizetype ssize(const T *t) + { + qsizetype result = 0; + if (t) { + while (*t++) + ++result; + } + return result; + } + + qsizetype ssize(const QChar *t) + { + qsizetype result = 0; + if (t) { + while (!t++->isNull()) + ++result; + } + return result; + } +} + +template <typename String> +void tst_QAnyStringView::conversion_tests(String string) const +{ + // copy-construct: + { + QAnyStringView sv = string; + + QCOMPARE(help::ssize(sv), help::ssize(string)); + + QCOMPARE(sv, string); + } + + QAnyStringView sv; + + // copy-assign: + { + sv = string; + + QCOMPARE(help::ssize(sv), help::ssize(string)); + + // check relational operators: + + QCOMPARE(sv, string); + QCOMPARE(string, sv); + + QVERIFY(!(sv != string)); + QVERIFY(!(string != sv)); + + QVERIFY(!(sv < string)); + QVERIFY(sv <= string); + QVERIFY(!(sv > string)); + QVERIFY(sv >= string); + + QVERIFY(!(string < sv)); + QVERIFY(string <= sv); + QVERIFY(!(string > sv)); + QVERIFY(string >= sv); + } + + // copy-construct from rvalue (QAnyStringView never assumes ownership): + { + QAnyStringView sv2 = std::move(string); + QCOMPARE(sv2, sv); + QCOMPARE(sv2, string); + } + + // copy-assign from rvalue (QAnyStringView never assumes ownership): + { + QAnyStringView sv2; + sv2 = std::move(string); + QCOMPARE(sv2, sv); + QCOMPARE(sv2, string); + } +} + +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); +} + +QTEST_APPLESS_MAIN(tst_QAnyStringView) +#include "tst_qanystringview.moc" |