/**************************************************************************** ** ** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include // for negative testing (can't convert from) #include #include template constexpr bool CanConvert = std::is_convertible_v; static_assert(!CanConvert); static_assert(!CanConvert); static_assert(!CanConvert); static_assert(!CanConvert); static_assert(CanConvert); static_assert(CanConvert); static_assert(CanConvert); static_assert(CanConvert); static_assert(!CanConvert); static_assert(!CanConvert); static_assert(!CanConvert); static_assert(CanConvert); static_assert(CanConvert); static_assert(!CanConvert); static_assert(!CanConvert); static_assert(!CanConvert); static_assert(CanConvert); static_assert(CanConvert); static_assert(!CanConvert); static_assert(!CanConvert); static_assert(!CanConvert); static_assert(CanConvert); static_assert(CanConvert); static_assert(CanConvert< QByteArray >); static_assert(CanConvert); static_assert(CanConvert< QByteArray&>); static_assert(CanConvert); static_assert(CanConvert< std::string >); static_assert(CanConvert); static_assert(CanConvert< std::string&>); static_assert(CanConvert); static_assert(CanConvert< std::string_view >); static_assert(CanConvert); static_assert(CanConvert< std::string_view&>); static_assert(CanConvert); static_assert(CanConvert< QVector >); static_assert(CanConvert >); static_assert(CanConvert< QVector&>); static_assert(CanConvert&>); static_assert(CanConvert< QVarLengthArray >); static_assert(CanConvert >); static_assert(CanConvert< QVarLengthArray&>); static_assert(CanConvert&>); static_assert(CanConvert< std::vector >); static_assert(CanConvert >); static_assert(CanConvert< std::vector&>); static_assert(CanConvert&>); static_assert(CanConvert< std::array >); static_assert(CanConvert >); static_assert(CanConvert< std::array&>); static_assert(CanConvert&>); static_assert(!CanConvert>); static_assert(!CanConvert>); class tst_QByteArrayView : public QObject { Q_OBJECT private slots: void constExpr() const; void basics() const; void literals() const; void fromArray() const; void literalsWithInternalNulls() const; void at() const; void fromQByteArray() const; void fromCharStar() const { fromEmptyLiteral(); conversionTests("Hello, World!"); } void fromUCharStar() const { fromEmptyLiteral(); const uchar data[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; conversionTests(data); } void fromSignedCharStar() const { fromEmptyLiteral(); const signed char data[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; conversionTests(data); } void fromStdByteArray() const { fromEmptyLiteral(); const std::byte data[] = {std::byte{'H'}, std::byte{'e'}, std::byte{'l'}, std::byte{'l'}, std::byte{'o'}, std::byte{0}}; conversionTests(data); } void fromCharRange() const { const char data[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; fromRange(std::begin(data), std::end(data)); } void fromUCharRange() const { const uchar data[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; fromRange(std::begin(data), std::end(data)); } void fromSignedCharRange() const { const signed char data[] = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 0 }; fromRange(std::begin(data), std::end(data)); } void fromStdByteRange() const { const std::byte data[] = {std::byte{'H'}, std::byte{'e'}, std::byte{'l'}, std::byte{'l'}, std::byte{'o'}, std::byte{0}}; fromRange(std::begin(data), std::end(data)); } void fromCharContainers() const { fromContainers(); } void fromUCharContainers() const { fromContainers(); } void fromSignedCharContainers() const { fromContainers(); } void fromStdByteContainers() const { fromContainers(); } void comparison() const; private: template void conversionTests(Data arg) const; template void fromEmptyLiteral() const; template void fromRange(const Char *first, const Char *last) const; template void fromContainer() const; template void fromContainers() const; }; void tst_QByteArrayView::constExpr() const { // compile-time checks { constexpr QByteArrayView bv; static_assert(bv.size() == 0); static_assert(bv.isNull()); static_assert(bv.empty()); static_assert(bv.isEmpty()); static_assert(bv.data() == nullptr); constexpr QByteArrayView bv2(bv.data(), bv.data() + bv.size()); static_assert(bv2.isNull()); static_assert(bv2.empty()); } { constexpr QByteArrayView bv = ""; static_assert(bv.size() == 0); static_assert(!bv.isNull()); static_assert(bv.empty()); static_assert(bv.isEmpty()); static_assert(bv.data() != nullptr); constexpr QByteArrayView bv2(bv.data(), bv.data() + bv.size()); static_assert(!bv2.isNull()); static_assert(bv2.empty()); } { static_assert(QByteArrayView("Hello").size() == 5); constexpr QByteArrayView bv = "Hello"; static_assert(bv.size() == 5); static_assert(!bv.empty()); static_assert(!bv.isEmpty()); static_assert(!bv.isNull()); static_assert(*bv.data() == 'H'); static_assert(bv[0] == 'H'); static_assert(bv.at(0) == 'H'); static_assert(bv.front() == 'H'); static_assert(bv.first() == 'H'); static_assert(bv[4] == 'o'); static_assert(bv.at(4) == 'o'); static_assert(bv.back() == 'o'); static_assert(bv.last() == 'o'); static_assert(*bv.begin() == 'H' ); static_assert(*(bv.end() - 1) == 'o' ); static_assert(*bv.cbegin() == 'H' ); static_assert(*(bv.cend() - 1) == 'o' ); static_assert(*bv.rbegin() == 'o' ); static_assert(*bv.crbegin() == 'o' ); // This is just to test that rend()/crend() are constexpr. static_assert(bv.rbegin() != bv.rend()); static_assert(bv.crbegin() != bv.crend()); constexpr QByteArrayView bv2(bv.data(), bv.data() + bv.size()); static_assert(!bv2.isNull()); static_assert(!bv2.empty()); static_assert(bv2.size() == 5); } { static constexpr char hello[] = "Hello"; constexpr QByteArrayView bv(hello); static_assert(bv.size() == 5); static_assert(!bv.empty()); static_assert(!bv.isEmpty()); static_assert(!bv.isNull()); static_assert(*bv.data() == 'H'); static_assert(bv[0] == 'H'); static_assert(bv.at(0) == 'H'); static_assert(bv.front() == 'H'); static_assert(bv.first() == 'H'); static_assert(bv[4] == 'o'); static_assert(bv.at(4) == 'o'); static_assert(bv.back() == 'o'); static_assert(bv.last() == 'o'); } { static constexpr char hello[] = { 'H', 'e', 'l', 'l', 'o' }; constexpr QByteArrayView bv(hello, std::size(hello)); static_assert(bv.size() == 5); static_assert(!bv.empty()); static_assert(!bv.isEmpty()); static_assert(!bv.isNull()); static_assert(*bv.data() == 'H'); static_assert(bv[0] == 'H'); static_assert(bv.at(0) == 'H'); static_assert(bv.front() == 'H'); static_assert(bv.first() == 'H'); static_assert(bv[4] == 'o'); static_assert(bv.at(4) == 'o'); static_assert(bv.back() == 'o'); static_assert(bv.last() == 'o'); } { constexpr char *null = nullptr; constexpr QByteArrayView bv(null); static_assert(bv.isNull()); static_assert(bv.isEmpty()); static_assert(bv.size() == 0); } } void tst_QByteArrayView::basics() const { QByteArrayView bv1; // a default-constructed QByteArrayView is null: QVERIFY(bv1.isNull()); // which implies it's empty(); QVERIFY(bv1.isEmpty()); QByteArrayView bv2; QVERIFY(bv2 == bv1); QVERIFY(!(bv2 != bv1)); } // Note: initially the size would be deduced from the array literal, // but it caused source compatibility issues so this is currently not the case. void tst_QByteArrayView::literals() const { const char hello[] = "Hello\0This shouldn't be found"; QCOMPARE(QByteArrayView(hello).size(), 5); QCOMPARE(QByteArrayView(hello + 0).size(), 5); // forces decay to pointer QByteArrayView bv = hello; QCOMPARE(bv.size(), 5); QVERIFY(!bv.empty()); QVERIFY(!bv.isEmpty()); QVERIFY(!bv.isNull()); QCOMPARE(*bv.data(), 'H'); QCOMPARE(bv[0], 'H'); QCOMPARE(bv.at(0), 'H'); QCOMPARE(bv.front(), 'H'); QCOMPARE(bv.first(), 'H'); QCOMPARE(bv[4], 'o'); QCOMPARE(bv.at(4), 'o'); QCOMPARE(bv.back(), 'o'); QCOMPARE(bv.last(), 'o'); QByteArrayView bv2(bv.data(), bv.data() + bv.size()); QVERIFY(!bv2.isNull()); QVERIFY(!bv2.empty()); QCOMPARE(bv2.size(), 5); const char abc[] = "abc"; bv = abc; QCOMPARE(bv.size(), 3); const char def[3] = {'d', 'e', 'f'}; bv = def; QCOMPARE(bv.size(), 3); } void tst_QByteArrayView::fromArray() const { static constexpr char hello[] = "Hello\0abc\0\0."; constexpr QByteArrayView bv = QByteArrayView::fromArray(hello); QCOMPARE(bv.size(), 13); QVERIFY(!bv.empty()); QVERIFY(!bv.isEmpty()); QVERIFY(!bv.isNull()); QCOMPARE(*bv.data(), 'H'); QCOMPARE(bv[0], 'H'); QCOMPARE(bv.at(0), 'H'); QCOMPARE(bv.front(), 'H'); QCOMPARE(bv.first(), 'H'); QCOMPARE(bv[4], 'o'); QCOMPARE(bv.at(4), 'o'); QCOMPARE(bv[5], '\0'); QCOMPARE(bv.at(5), '\0'); QCOMPARE(*(bv.data() + bv.size() - 2), '.'); QCOMPARE(bv.back(), '\0'); QCOMPARE(bv.last(), '\0'); const std::byte bytes[] = {std::byte(0x0), std::byte(0x1), std::byte(0x2)}; QByteArrayView bbv = QByteArrayView::fromArray(bytes); QCOMPARE(bbv.data(), reinterpret_cast(bytes + 0)); QCOMPARE(bbv.size(), 3); QCOMPARE(bbv.first(), 0x0); QCOMPARE(bbv.last(), 0x2); } void tst_QByteArrayView::literalsWithInternalNulls() const { const char withnull[] = "a\0zzz"; // these are different results QCOMPARE(size_t(QByteArrayView::fromArray(withnull).size()), std::size(withnull)); QCOMPARE(QByteArrayView(withnull + 0).size(), 1); QByteArrayView nulled = QByteArrayView::fromArray(withnull); QCOMPARE(nulled.last(), '\0'); nulled.chop(1); // cut off trailing \0 QCOMPARE(nulled[1], '\0'); QCOMPARE(nulled.indexOf('\0'), 1); QCOMPARE(nulled.indexOf('z'), 2); QCOMPARE(nulled.lastIndexOf('z'), 4); QCOMPARE(nulled.lastIndexOf('a'), 0); QVERIFY(nulled.startsWith("a\0z")); QVERIFY(nulled.startsWith("a\0y")); QVERIFY(!nulled.startsWith(QByteArrayView("a\0y", 3))); QVERIFY(nulled.endsWith("zz")); QVERIFY(nulled.contains("z")); QVERIFY(nulled.contains(QByteArrayView("\0z", 2))); QVERIFY(!nulled.contains(QByteArrayView("\0y", 2))); QCOMPARE(nulled.first(5), QByteArrayView(withnull, 5)); QCOMPARE(nulled.last(5), QByteArrayView(withnull, 5)); QCOMPARE(nulled.sliced(0), QByteArrayView(withnull, 5)); QCOMPARE(nulled.sliced(2, 2), "zz"); QCOMPARE(nulled.chopped(2), QByteArrayView("a\0z", 3)); QVERIFY(nulled.chopped(2) != QByteArrayView("a\0y", 3)); QCOMPARE(nulled.count('z'), 3); const char nullfirst[] = "\0buzz"; QByteArrayView fromnull = QByteArrayView::fromArray(nullfirst); QVERIFY(!fromnull.isEmpty()); const char nullNotEnd[] = { 'b', 'o', 'w', '\0', 'a', 'f', 't', 'z' }; QByteArrayView midNull = QByteArrayView::fromArray(nullNotEnd); QCOMPARE(midNull.back(), 'z'); } void tst_QByteArrayView::at() const { QByteArray hello("Hello"); QByteArrayView bv(hello); QCOMPARE(bv.at(0), 'H'); QCOMPARE(bv[0], 'H'); QCOMPARE(bv.at(1), 'e'); QCOMPARE(bv[1], 'e'); QCOMPARE(bv.at(2), 'l'); QCOMPARE(bv[2], 'l'); QCOMPARE(bv.at(3), 'l'); QCOMPARE(bv[3], 'l'); QCOMPARE(bv.at(4), 'o'); QCOMPARE(bv[4], 'o'); } void tst_QByteArrayView::fromQByteArray() const { QByteArray null; QByteArray empty = ""; QVERIFY(QByteArrayView(null).isNull()); QVERIFY(!qToByteArrayViewIgnoringNull(null).isNull()); QVERIFY(QByteArrayView(null).isEmpty()); QVERIFY(qToByteArrayViewIgnoringNull(null).isEmpty()); QVERIFY(QByteArrayView(empty).isEmpty()); QVERIFY(qToByteArrayViewIgnoringNull(empty).isEmpty()); QVERIFY(!QByteArrayView(empty).isNull()); QVERIFY(!qToByteArrayViewIgnoringNull(empty).isNull()); conversionTests(QByteArray("Hello World!")); } namespace help { template size_t size(const T &t) { return size_t(t.size()); } template size_t size(const T *t) { return std::char_traits::length(t); } template decltype(auto) cbegin(const T &t) { return t.begin(); } template const T * cbegin(const T *t) { return t; } template decltype(auto) cend(const T &t) { return t.end(); } template const T * cend(const T *t) { return t + size(t); } template decltype(auto) crbegin(const T &t) { return t.rbegin(); } template std::reverse_iterator crbegin(const T *t) { return std::reverse_iterator(cend(t)); } template decltype(auto) crend(const T &t) { return t.rend(); } template std::reverse_iterator crend(const T *t) { return std::reverse_iterator(cbegin(t)); } } // namespace help template void tst_QByteArrayView::conversionTests(Data data) const { // copy-construct: { QByteArrayView bv = data; QCOMPARE(help::size(bv), help::size(data)); const auto compare = [](auto v1, auto v2) { if constexpr (std::is_same_v) return std::to_integer(v1) == v2; else return v1 == v2; }; QVERIFY(std::equal(help::cbegin(data), help::cend(data), QT_MAKE_CHECKED_ARRAY_ITERATOR(bv.cbegin(), bv.size()), compare)); QVERIFY(std::equal(help::cbegin(data), help::cend(data), QT_MAKE_CHECKED_ARRAY_ITERATOR(bv.begin(), bv.size()), compare)); QVERIFY(std::equal(help::crbegin(data), help::crend(data), bv.crbegin(), compare)); QVERIFY(std::equal(help::crbegin(data), help::crend(data), bv.rbegin(), compare)); QCOMPARE(bv, data); } QByteArrayView bv; // copy-assign: { bv = data; QCOMPARE(help::size(bv), help::size(data)); // check relational operators: QCOMPARE(bv, data); QCOMPARE(data, bv); QVERIFY(!(bv != data)); QVERIFY(!(data != bv)); QVERIFY(!(bv < data)); QVERIFY(bv <= data); QVERIFY(!(bv > data)); QVERIFY(bv >= data); QVERIFY(!(data < bv)); QVERIFY(data <= bv); QVERIFY(!(data > bv)); QVERIFY(data >= bv); } // copy-construct from rvalue (QByteArrayView never assumes ownership): { QByteArrayView bv2 = std::move(data); QCOMPARE(bv2, bv); QCOMPARE(bv2, data); } // copy-assign from rvalue (QByteArrayView never assumes ownership): { QByteArrayView bv2; bv2 = std::move(data); QCOMPARE(bv2, bv); QCOMPARE(bv2, data); } } template void tst_QByteArrayView::fromEmptyLiteral() const { const Char *null = nullptr; const Char empty[] = { Char{0} }; QCOMPARE(QByteArrayView(null).size(), 0); QCOMPARE(QByteArrayView(null).data(), nullptr); QCOMPARE(QByteArrayView::fromArray(empty).size(), 1); QCOMPARE(static_cast(QByteArrayView::fromArray(empty).data()), static_cast(empty)); QVERIFY(QByteArrayView(null).isNull()); QVERIFY(QByteArrayView(null).isEmpty()); QVERIFY(!QByteArrayView::fromArray(empty).isEmpty()); QVERIFY(!QByteArrayView::fromArray(empty).isNull()); } template void tst_QByteArrayView::fromRange(const Char *first, const Char *last) const { const Char *null = nullptr; QCOMPARE(QByteArrayView(null, null).size(), 0); QCOMPARE(QByteArrayView(null, null).data(), nullptr); QCOMPARE(QByteArrayView(first, first).size(), 0); QCOMPARE(static_cast(QByteArrayView(first, first).data()), static_cast(first)); const auto bv = QByteArrayView(first, last); QCOMPARE(bv.size(), last - first); QCOMPARE(static_cast(bv.data()), static_cast(first)); QCOMPARE(static_cast(bv.last(0).data()), static_cast(last)); QCOMPARE(static_cast(bv.sliced(bv.size()).data()), static_cast(last)); // can't call conversionTests() here, as it requires a single object } template void tst_QByteArrayView::fromContainer() const { const QByteArray s = "Hello World!"; Container c; // unspecified whether empty containers make null QByteArrayView QVERIFY(QByteArrayView(c).isEmpty()); QCOMPARE(sizeof(Char), sizeof(char)); const auto *data = reinterpret_cast(s.data()); std::copy(data, data + s.size(), std::back_inserter(c)); conversionTests(std::move(c)); } template void tst_QByteArrayView::fromContainers() const { fromContainer>(); fromContainer>(); fromContainer>(); fromContainer>(); } void tst_QByteArrayView::comparison() const { const QByteArrayView aa = "aa"; const QByteArrayView bb = "bb"; QVERIFY(aa == aa); QVERIFY(aa != bb); QVERIFY(aa < bb); QVERIFY(bb > aa); } QTEST_APPLESS_MAIN(tst_QByteArrayView) #include "tst_qbytearrayview.moc"