Skip to content

Commit

Permalink
tst_QCryptographicHash: split 4GiB tests to a separate unit test
Browse files Browse the repository at this point in the history
With those tests split, tst_QCryptographicHash takes about 4ms.
When FEATURE_openssl_hash is enabled those tests take about 15s on
their own, but when openssl_hash is disabled they take about 2 minutes.
That makes running the tests locally a bit of a hassle when hacking
code ... test ... hack ... test.

This is with a debug build, GCC, `-O0 -g` flags.

Change-Id: I8b8f5d1954feb1f9eb8115e27635610a41b42f47
Reviewed-by: Thiago Macieira <[email protected]>
(cherry picked from commit fff2178)
Reviewed-by: Qt Cherry-pick Bot <[email protected]>
  • Loading branch information
Ahmad Samir authored and Qt Cherry-pick Bot committed Dec 29, 2024
1 parent 6790d8d commit bbf78c9
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 113 deletions.
6 changes: 6 additions & 0 deletions tests/auto/corelib/tools/qcryptographichash/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ qt_internal_add_test(tst_qcryptographichash
TESTDATA ${test_data}
)

qt_internal_add_test(tst_qcryptographichash_bigdata
SOURCES
tst_qcryptographichash_bigdata.cpp
TESTDATA ${test_data}
)

if(QT_FEATURE_sanitize_address)
set_property(TEST tst_qcryptographichash APPEND PROPERTY ENVIRONMENT "QTEST_FUNCTION_TIMEOUT=900000")
endif()
113 changes: 0 additions & 113 deletions tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
#include <QCryptographicHash>
#include <QtCore/QMetaEnum>

#include <thread>

Q_DECLARE_METATYPE(QCryptographicHash::Algorithm)

class tst_QCryptographicHash : public QObject
Expand Down Expand Up @@ -37,10 +35,6 @@ private slots:
void addDataAcceptsNullByteArrayView();
void move();
void swap();
// keep last
void moreThan4GiBOfData_data();
void moreThan4GiBOfData();
void keccakBufferOverflow();
private:
void all_methods(bool includingNumAlgorithms) const;
void ensureLargeData();
Expand Down Expand Up @@ -577,112 +571,5 @@ void tst_QCryptographicHash::swap()
QCOMPARE(hash1.result(), QCryptographicHash::hash("test", QCryptographicHash::Sha256));
}

void tst_QCryptographicHash::ensureLargeData()
{
#if QT_POINTER_SIZE > 4
QElapsedTimer timer;
timer.start();
const size_t GiB = 1024 * 1024 * 1024;
if (large.size() == 4 * GiB + 1)
return;
try {
large.resize(4 * GiB + 1, '\0');
} catch (const std::bad_alloc &) {
QSKIP("Could not allocate 4GiB plus one byte of RAM.");
}
QCOMPARE(large.size(), 4 * GiB + 1);
large.back() = '\1';
qDebug("created dataset in %lld ms", timer.elapsed());
#endif
}

void tst_QCryptographicHash::moreThan4GiBOfData_data()
{
#if QT_POINTER_SIZE > 4
if (ensureLargeData(); large.empty())
return;
QTest::addColumn<QCryptographicHash::Algorithm>("algorithm");
auto me = QMetaEnum::fromType<QCryptographicHash::Algorithm>();
auto row = [me] (QCryptographicHash::Algorithm algo) {
QTest::addRow("%s", me.valueToKey(int(algo))) << algo;
};
// these are reasonably fast (O(secs))
row(QCryptographicHash::Md4);
row(QCryptographicHash::Md5);
row(QCryptographicHash::Sha1);
if (!qgetenv("QTEST_ENVIRONMENT").split(' ').contains("ci")) {
// This is important but so slow (O(minute)) that, on CI, it tends to time out.
// Retain it for manual runs, all the same, as most dev machines will be fast enough.
row(QCryptographicHash::Sha512);
}
// the rest is just too slow
#else
QSKIP("This test is 64-bit only.");
#endif
}

void tst_QCryptographicHash::moreThan4GiBOfData()
{
QFETCH(const QCryptographicHash::Algorithm, algorithm);

using MaybeThread = std::thread;

QElapsedTimer timer;
timer.start();
const auto sg = qScopeGuard([&] {
qDebug() << algorithm << "test finished in" << timer.restart() << "ms";
});

const auto view = QByteArrayView{large};
const auto first = view.first(view.size() / 2);
const auto last = view.sliced(view.size() / 2);

QByteArray single;
QByteArray chunked;

auto t = MaybeThread{[&] {
QCryptographicHash h(algorithm);
h.addData(view);
single = h.result();
}};
{
QCryptographicHash h(algorithm);
h.addData(first);
h.addData(last);
chunked = h.result();
}
t.join();

QCOMPARE(single, chunked);
}

void tst_QCryptographicHash::keccakBufferOverflow()
{
#if QT_POINTER_SIZE == 4
QSKIP("This is a 64-bit-only test");
#else

if (ensureLargeData(); large.empty())
return;

QElapsedTimer timer;
timer.start();
const auto sg = qScopeGuard([&] {
qDebug() << "test finished in" << timer.restart() << "ms";
});

constexpr qsizetype magic = INT_MAX/4;
QCOMPARE_GE(large.size(), size_t(magic + 1));

QCryptographicHash hash(QCryptographicHash::Algorithm::Keccak_224);
const auto first = QByteArrayView{large}.first(1);
const auto second = QByteArrayView{large}.sliced(1, magic);
hash.addData(first);
hash.addData(second);
(void)hash.resultView();
QVERIFY(true); // didn't crash
#endif
}

QTEST_MAIN(tst_QCryptographicHash)
#include "tst_qcryptographichash.moc"
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include <QtCore/QCoreApplication>
#include <QTest>
#include <QScopeGuard>
#include <QCryptographicHash>

#include <thread>

Q_DECLARE_METATYPE(QCryptographicHash::Algorithm)

class tst_QCryptographicHashBigData : public QObject
{
Q_OBJECT
private slots:
void moreThan4GiBOfData_data();
void moreThan4GiBOfData();
void keccakBufferOverflow();
private:
void ensureLargeData();
std::vector<char> large;
};

void tst_QCryptographicHashBigData::ensureLargeData()
{
#if QT_POINTER_SIZE > 4
QElapsedTimer timer;
timer.start();
const size_t GiB = 1024 * 1024 * 1024;
if (large.size() == 4 * GiB + 1)
return;
try {
large.resize(4 * GiB + 1, '\0');
} catch (const std::bad_alloc &) {
QSKIP("Could not allocate 4GiB plus one byte of RAM.");
}
QCOMPARE(large.size(), 4 * GiB + 1);
large.back() = '\1';
qDebug("created dataset in %lld ms", timer.elapsed());
#endif
}

void tst_QCryptographicHashBigData::moreThan4GiBOfData_data()
{
#if QT_POINTER_SIZE > 4
if (ensureLargeData(); large.empty())
return;
QTest::addColumn<QCryptographicHash::Algorithm>("algorithm");
auto me = QMetaEnum::fromType<QCryptographicHash::Algorithm>();
auto row = [me] (QCryptographicHash::Algorithm algo) {
QTest::addRow("%s", me.valueToKey(int(algo))) << algo;
};
// these are reasonably fast (O(secs))
row(QCryptographicHash::Md4);
row(QCryptographicHash::Md5);
row(QCryptographicHash::Sha1);
if (!qgetenv("QTEST_ENVIRONMENT").split(' ').contains("ci")) {
// This is important but so slow (O(minute)) that, on CI, it tends to time out.
// Retain it for manual runs, all the same, as most dev machines will be fast enough.
row(QCryptographicHash::Sha512);
}
// the rest is just too slow
#else
QSKIP("This test is 64-bit only.");
#endif
}

void tst_QCryptographicHashBigData::moreThan4GiBOfData()
{
QFETCH(const QCryptographicHash::Algorithm, algorithm);

using MaybeThread = std::thread;

QElapsedTimer timer;
timer.start();
const auto sg = qScopeGuard([&] {
qDebug() << algorithm << "test finished in" << timer.restart() << "ms";
});

const auto view = QByteArrayView{large};
const auto first = view.first(view.size() / 2);
const auto last = view.sliced(view.size() / 2);

QByteArray single;
QByteArray chunked;

auto t = MaybeThread{[&] {
QCryptographicHash h(algorithm);
h.addData(view);
single = h.result();
}};
{
QCryptographicHash h(algorithm);
h.addData(first);
h.addData(last);
chunked = h.result();
}
t.join();

QCOMPARE(single, chunked);
}

void tst_QCryptographicHashBigData::keccakBufferOverflow()
{
#if QT_POINTER_SIZE == 4
QSKIP("This is a 64-bit-only test");
#else

if (ensureLargeData(); large.empty())
return;

QElapsedTimer timer;
timer.start();
const auto sg = qScopeGuard([&] {
qDebug() << "test finished in" << timer.restart() << "ms";
});

constexpr qsizetype magic = INT_MAX/4;
QCOMPARE_GE(large.size(), size_t(magic + 1));

QCryptographicHash hash(QCryptographicHash::Algorithm::Keccak_224);
const auto first = QByteArrayView{large}.first(1);
const auto second = QByteArrayView{large}.sliced(1, magic);
hash.addData(first);
hash.addData(second);
(void)hash.resultView();
QVERIFY(true); // didn't crash
#endif
}

QTEST_MAIN(tst_QCryptographicHashBigData)
#include "tst_qcryptographichash_bigdata.moc"

0 comments on commit bbf78c9

Please sign in to comment.