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.

Task-number: QTBUG-116077
Task-number: QTBUG-116080
Change-Id: I664b9f014ffc48cbb49bfffd17b021719e6d612f
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
(cherry picked from commit 1845d433277348542e496d3c38175ad0c5cbddde)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Thiago Macieira 2024-02-02 11:10:30 -08:00 committed by Qt Cherry-pick Bot
parent be03e8523d
commit c4aa3b0dcb

View File

@ -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,80 +69,6 @@ private Q_SLOTS:
#endif
};
void tst_QHashFunctions::consistent()
{
// QString-like
const QString s = QStringLiteral("abcdefghijklmnopqrstuvxyz").repeated(16);
QCOMPARE(qHash(s, seed), qHash(QStringView(s), seed));
// 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:
#ifdef __cpp_char8_t
const auto hc8 = qHash(char8_t(ae), seed);
#endif
const auto hc16 = qHash(char16_t(ae), seed);
const auto hc32 = qHash(char32_t(ae), seed);
#ifdef __cpp_char8_t
QCOMPARE(hc8, h8);
#endif
QCOMPARE(hc16, h16);
QCOMPARE(hc32, h32);
}
// 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);
}
// mixed signed/unsigned
{
const auto hu8 = qHash(quint8(42), seed);
const auto hs8 = qHash(qint8(42), seed);
QCOMPARE(hu8, hs8);
const auto hu16 = qHash(quint16(4242), seed);
const auto hs16 = qHash(qint16(4242), seed);
QCOMPARE(hu16, hs16);
const auto hu32 = qHash(quint32(4242'4242), seed);
const auto hs32 = qHash(qint32(4242'4242), seed);
QCOMPARE(hu32, hs32);
const auto hu64 = qHash(quint64(4242'424242), seed);
const auto hs64 = qHash(qint64(4242'424242), seed);
QCOMPARE(hu64, hs64);
}
// 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);
}
}
void tst_QHashFunctions::initTestCase()
{
QTest::addColumn<quint64>("seedValue");
@ -156,6 +90,214 @@ void tst_QHashFunctions::init()
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(value), seed);
#endif
const auto hc16 = qHash(char16_t(value), seed);
const auto hc32 = qHash(char32_t(value), seed);
#ifdef __cpp_char8_t
QCOMPARE(hc8, hu8);
#endif
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);
}
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
}
}
void tst_QHashFunctions::floatingPointConsistency_data()
{
QTest::addColumn<double>("value");
QTest::addRow("zero") << 0.0;
QTest::addRow("1.0") << 1.0;
QTest::addRow("infinity") << std::numeric_limits<double>::infinity();
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());
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());
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::floatingPointConsistency()
{
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);
}
// 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::stringConsistency_data()
{
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()
{
{