diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2024-02-02 11:10:30 -0800 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2024-02-12 17:01:33 -0800 |
commit | 1845d433277348542e496d3c38175ad0c5cbddde (patch) | |
tree | 0eace3b4d8a526e6eed5aac356da3845c92ab171 /tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp | |
parent | fee15182949e88682131399c50b679be83a75385 (diff) |
tst_QHashFunctions: make the consistency check table-driven
So we can test more values. Because we are testing more values, we can't
use QEXPECT_FAIL, because we can't guarantee a mismatch.
Pick-to: 6.7
Task-number: QTBUG-116077
Task-number: QTBUG-116080
Change-Id: I664b9f014ffc48cbb49bfffd17b021719e6d612f
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Diffstat (limited to 'tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp')
-rw-r--r-- | tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp | 286 |
1 files changed, 214 insertions, 72 deletions
diff --git a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp index cd38b0a6e9..424e869d98 100644 --- a/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp +++ b/tests/auto/corelib/tools/qhashfunctions/tst_qhashfunctions.cpp @@ -1,4 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2024 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -30,7 +31,14 @@ public slots: void init(); private Q_SLOTS: - void consistent(); + void unsignedIntegerConsistency_data(); + void unsignedIntegerConsistency(); + void signedIntegerConsistency_data(); + void signedIntegerConsistency(); + void floatingPointConsistency_data(); + void floatingPointConsistency(); + void stringConsistency_data(); + void stringConsistency(); void qhash(); void qhash_of_empty_and_null_qstring(); void qhash_of_empty_and_null_qbytearray(); @@ -61,99 +69,233 @@ private Q_SLOTS: #endif }; -void tst_QHashFunctions::consistent() +void tst_QHashFunctions::initTestCase() { - // QString-like - const QString s = QStringLiteral("abcdefghijklmnopqrstuvxyz").repeated(16); - QCOMPARE(qHash(s, seed), qHash(QStringView(s), seed)); + QTest::addColumn<quint64>("seedValue"); - // unsigned integers - { - constexpr unsigned char ae = 0xE4; // LATIN SMALL LETTER A WITH DIAERESIS - const auto h8 = qHash(quint8(ae), seed); - const auto h16 = qHash(quint16(ae), seed); - const auto h32 = qHash(quint32(ae), seed); - const auto h64 = qHash(quint64(ae), seed); - QCOMPARE(h8, h16); - QCOMPARE(h16, h32); - QCOMPARE(h32, h64); - // there are a few more unsigned types: + QTest::newRow("zero-seed") << ZeroSeed; + QTest::newRow("zero-seed-negated") << ~ZeroSeed; + QTest::newRow("non-zero-seed-32bit") << RandomSeed32; + QTest::newRow("non-zero-seed-32bit-negated") + << quint64{~quint32(RandomSeed32)}; // ensure this->seed gets same value on 32/64-bit + if constexpr (sizeof(size_t) == sizeof(quint64)) { + QTest::newRow("non-zero-seed-64bit") << RandomSeed64; + QTest::newRow("non-zero-seed-64bit-negated") << ~RandomSeed64; + } +} + +void tst_QHashFunctions::init() +{ + QFETCH_GLOBAL(quint64, seedValue); + seed = size_t(seedValue); +} + +template <typename T> static void addPositiveCommonRows() +{ + QTest::addRow("zero") << T(0); + QTest::addRow("positive_7bit") << T(42); + QTest::addRow("positive_15bit") << T(0x1f3f); + QTest::addRow("positive_31bit") << T(0x4b3d'93c4); + QTest::addRow("positive_63bit") << T(Q_INT64_C(0x39df'7338'4b14'fcb0)); + + QTest::addRow("SCHAR_MAX") << T(SCHAR_MAX); + QTest::addRow("SHRT_MAX") << T(SHRT_MAX); + QTest::addRow("INT_MAX") << T(INT_MAX); + QTest::addRow("LLONG_MAX") << T(LLONG_MAX); +} + +void tst_QHashFunctions::signedIntegerConsistency_data() +{ + QTest::addColumn<qint64>("value"); + addPositiveCommonRows<qint64>(); + QTest::addRow("negative_7bit") << Q_INT64_C(-28); + QTest::addRow("negative_15bit") << Q_INT64_C(-0x387c); + QTest::addRow("negative_31bit") << qint64(-0x7713'30f9); + + QTest::addRow("SCHAR_MIN") << qint64(SCHAR_MIN); + QTest::addRow("SHRT_MIN") << qint64(SHRT_MIN); + QTest::addRow("INT_MIN") << qint64(INT_MIN); + QTest::addRow("LLONG_MIN") << LLONG_MIN; +} + +void tst_QHashFunctions::unsignedIntegerConsistency_data() +{ + QTest::addColumn<quint64>("value"); + addPositiveCommonRows<quint64>(); + + QTest::addRow("positive_8bit") << Q_UINT64_C(0xE4); + QTest::addRow("positive_16bit") << Q_UINT64_C(0xcafe); + QTest::addRow("positive_32bit") << quint64(0xcafe'babe); + + QTest::addRow("UCHAR_MAX") << quint64(UCHAR_MAX); + QTest::addRow("UHRT_MAX") << quint64(USHRT_MAX); + QTest::addRow("UINT_MAX") << quint64(UINT_MAX); + QTest::addRow("ULLONG_MAX") << ULLONG_MAX; +} + +static void unsignedIntegerConsistency(quint64 value, size_t seed) +{ + quint8 v8 = quint8(value); + quint16 v16 = quint16(value); + quint32 v32 = quint32(value); + + const auto hu8 = qHash(v8, seed); + const auto hu16 = qHash(v16, seed); + const auto hu32 = qHash(v32, seed); + const auto hu64 = qHash(value, seed); + + if (v8 == value) + QCOMPARE(hu8, hu32); + if (v16 == value) + QCOMPARE(hu16, hu32); + if (v32 == value) + QCOMPARE(hu64, hu32); + + // there are a few more unsigned types: #ifdef __cpp_char8_t - const auto hc8 = qHash(char8_t(ae), seed); + const auto hc8 = qHash(char8_t(value), seed); #endif - const auto hc16 = qHash(char16_t(ae), seed); - const auto hc32 = qHash(char32_t(ae), seed); + const auto hc16 = qHash(char16_t(value), seed); + const auto hc32 = qHash(char32_t(value), seed); #ifdef __cpp_char8_t - QCOMPARE(hc8, h8); + QCOMPARE(hc8, hu8); #endif - QCOMPARE(hc16, h16); - QCOMPARE(hc32, h32); + QCOMPARE(hc16, hu16); + QCOMPARE(hc32, hu32); +} + +void tst_QHashFunctions::unsignedIntegerConsistency() +{ + QFETCH(quint64, value); + ::unsignedIntegerConsistency(value, seed); +} + +void tst_QHashFunctions::signedIntegerConsistency() +{ + QFETCH(qint64, value); + qint8 v8 = qint8(value); + qint16 v16 = qint16(value); + qint32 v32 = qint32(value); + + const auto hs8 = qHash(v8, seed); + const auto hs16 = qHash(v16, seed); + const auto hs32 = qHash(v32, seed); + const auto hs64 = qHash(value, seed); + + if (v8 == value) + QCOMPARE(hs8, hs32); + if (v16 == value) + QCOMPARE(hs16, hs32); + if (v32 == value) { + // because of QTBUG-116080, this may not match, but we can't guarantee + // it mismatches 100% of the time either + if constexpr (sizeof(size_t) > sizeof(int)) + QCOMPARE(hs64, hs32); } - // signed integers - { - constexpr signed char ae = 0xE4; // LATIN SMALL LETTER A WITH DIAERESIS - const auto h8 = qHash(qint8(ae), seed); - const auto h16 = qHash(qint16(ae), seed); - const auto h32 = qHash(qint32(ae), seed); - const auto h64 = qHash(qint64(ae), seed); - QCOMPARE(h8, h16); - QCOMPARE(h16, h32); - if constexpr (sizeof(size_t) == sizeof(int)) // 32-bit - QEXPECT_FAIL("", "QTBUG-116080", Continue); - QCOMPARE(h32, h64); + if (value > 0) { + quint64 u64 = quint64(value); + const auto hu64 = qHash(u64, seed); + QCOMPARE(hu64, hs64); + ::unsignedIntegerConsistency(u64, seed); + // by A == B && B == C -> A == C, we've shown hsXX == huXX for all XX } +} - // mixed signed/unsigned - { - const auto hu8 = qHash(quint8(42), seed); - const auto hs8 = qHash(qint8(42), seed); - QCOMPARE(hu8, hs8); +void tst_QHashFunctions::floatingPointConsistency_data() +{ + QTest::addColumn<double>("value"); + QTest::addRow("zero") << 0.0; - const auto hu16 = qHash(quint16(4242), seed); - const auto hs16 = qHash(qint16(4242), seed); - QCOMPARE(hu16, hs16); + QTest::addRow("1.0") << 1.0; + QTest::addRow("infinity") << std::numeric_limits<double>::infinity(); - const auto hu32 = qHash(quint32(4242'4242), seed); - const auto hs32 = qHash(qint32(4242'4242), seed); - QCOMPARE(hu32, hs32); + QTest::addRow("fp16_epsilon") << double(std::numeric_limits<qfloat16>::epsilon()); + QTest::addRow("fp16_min") << double(std::numeric_limits<qfloat16>::min()); + QTest::addRow("fp16_max") << double(std::numeric_limits<qfloat16>::max()); - const auto hu64 = qHash(quint64(4242'424242), seed); - const auto hs64 = qHash(qint64(4242'424242), seed); - QCOMPARE(hu64, hs64); - } + QTest::addRow("float_epsilon") << double(std::numeric_limits<float>::epsilon()); + QTest::addRow("float_min") << double(std::numeric_limits<float>::min()); + QTest::addRow("float_max") << double(std::numeric_limits<float>::max()); - // floats - { - const/*expr broken: QTBUG-116079*/ qfloat16 f16 = qfloat16(-42.f); - const auto h16 = qHash(f16, seed); - const auto h32 = qHash(float(f16), seed); - const auto h64 = qHash(double(f16), seed); - QCOMPARE(h16, h32); - QEXPECT_FAIL("", "QTBUG-116077", Continue); - QCOMPARE(h32, h64); - } + QTest::addRow("double_epsilon") << double(std::numeric_limits<double>::epsilon()); + QTest::addRow("double_min") << double(std::numeric_limits<double>::min()); + QTest::addRow("double_max") << double(std::numeric_limits<double>::max()); } -void tst_QHashFunctions::initTestCase() +void tst_QHashFunctions::floatingPointConsistency() { - QTest::addColumn<quint64>("seedValue"); + QFETCH(double, value); + long double lvalue = value; + float fp32 = float(value); + qfloat16 fp16 = qfloat16(value); + + const auto hfld = qHash(lvalue, seed); + const auto hf64 = qHash(value, seed); + const auto hf32 = qHash(fp32, seed); + const auto hf16 = qHash(fp16, seed); + + const auto hnfld = qHash(-lvalue, seed); + const auto hnf64 = qHash(-value, seed); + const auto hnf32 = qHash(-fp32, seed); + const auto hnf16 = qHash(-fp16, seed); + + if (fp16 == fp32) { + QCOMPARE(hf16, hf32); + QCOMPARE(hnf16, hnf32); + } - QTest::newRow("zero-seed") << ZeroSeed; - QTest::newRow("zero-seed-negated") << ~ZeroSeed; - QTest::newRow("non-zero-seed-32bit") << RandomSeed32; - QTest::newRow("non-zero-seed-32bit-negated") - << quint64{~quint32(RandomSeed32)}; // ensure this->seed gets same value on 32/64-bit - if constexpr (sizeof(size_t) == sizeof(quint64)) { - QTest::newRow("non-zero-seed-64bit") << RandomSeed64; - QTest::newRow("non-zero-seed-64bit-negated") << ~RandomSeed64; + // See QTBUG-116077; the rest isn't guaranteed to match (but we can't + // guarantee it will mismatch either). + return; + + if (fp32 == value) { + QCOMPARE(hf32, hf64); + QCOMPARE(hnf32, hnf64); } + + QCOMPARE(hfld, hf64); + QCOMPARE(hnfld, hnf64); } -void tst_QHashFunctions::init() +void tst_QHashFunctions::stringConsistency_data() { - QFETCH_GLOBAL(quint64, seedValue); - seed = size_t(seedValue); + QTest::addColumn<QString>("value"); + QTest::newRow("null") << QString(); + QTest::newRow("empty") << ""; + QTest::newRow("withnull") << QStringLiteral("A\0z"); + QTest::newRow("short-ascii") << "Hello"; + QTest::newRow("long-ascii") << QStringLiteral("abcdefghijklmnopqrstuvxyz").repeated(16); + + QTest::newRow("short-latin1") << "Bokmål"; + QTest::newRow("long-latin1") + << R"(Alle mennesker er født frie og med samme menneskeverd og menneskerettigheter. + De er utstyrt med fornuft og samvittighet og bør handle mot hverandre i brorskapets ånd.)"; + + QTest::newRow("short-nonlatin1") << "Ελληνικά"; + QTest::newRow("long-nonlatin1") + << R"('Ολοι οι άνθρωποι γεννιούνται ελεύθεροι και ίσοι στην αξιοπρέπεια και τα + δικαιώματα. Είναι προικισμένοι με λογική και συνείδηση, και οφείλουν να συμπεριφέρονται μεταξύ + τους με πνεύμα αδελφοσύνης.)"; +} + +void tst_QHashFunctions::stringConsistency() +{ + QFETCH(QString, value); + QStringView sv = value; + QByteArray u8ba = value.toUtf8(); + QByteArray u8bav = u8ba; + + // sanity checking: + QCOMPARE(sv.isNull(), value.isNull()); + QCOMPARE(sv.isEmpty(), value.isEmpty()); + QCOMPARE(u8ba.isNull(), value.isNull()); + QCOMPARE(u8ba.isEmpty(), value.isEmpty()); + QCOMPARE(u8bav.isNull(), value.isNull()); + QCOMPARE(u8bav.isEmpty(), value.isEmpty()); + + QCOMPARE(qHash(sv, seed), qHash(value, seed)); + QCOMPARE(qHash(u8bav, seed), qHash(u8ba, seed)); } void tst_QHashFunctions::qhash() |