// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include #include #include #include #include #include #include // These tests are way too slow to be part of automatic unit tests class tst_QDataStream : public QObject { Q_OBJECT template void fill(T &input); void fill(QSet &input); void fill(QMap &input); void fill(QHash &input); template void stream_big(); public slots: void initTestCase(); private slots: void stream_bigQString(); void stream_bigQByteArray(); void stream_bigQList(); void stream_bigQSet(); void stream_bigQMap(); void stream_bigQHash(); void stream_bigCString(); }; void tst_QDataStream::initTestCase() { qputenv("QTEST_FUNCTION_TIMEOUT", "9000000"); QTest::addColumn("streamVersion"); QTest::addRow("current") << QDataStream::Qt_DefaultCompiledVersion; QTest::addRow("Qt_6_6") << QDataStream::Qt_6_6; } template void tst_QDataStream::fill(T &input) { constexpr qsizetype GiB = 1024 * 1024 * 1024; constexpr qsizetype BaseSize = 4 * GiB + 1; qDebug("Filling container with %lld entries", qint64(BaseSize)); QElapsedTimer timer; timer.start(); try { input.reserve(BaseSize); input.resize(BaseSize, 'a'); } catch (const std::bad_alloc &) { QSKIP("Could not allocate 4 GiB of RAM."); } qDebug("Created dataset in %lld ms", timer.elapsed()); } void tst_QDataStream::fill(QSet &input) { constexpr qsizetype GiB = 1024 * 1024 * 1024; constexpr qsizetype BaseSize = 4 * GiB + 1; qDebug("Filling container with %lld entries", qint64(BaseSize)); QElapsedTimer timer; timer.start(); try { input.reserve(BaseSize); for (qsizetype i = 0; i < BaseSize; ++i) input.insert(i); } catch (const std::bad_alloc &) { QSKIP("Could not allocate 4 Gi entries."); } qDebug("Created dataset in %lld ms", timer.elapsed()); } void tst_QDataStream::fill(QMap &input) { constexpr qsizetype GiB = 1024 * 1024 * 1024; constexpr qsizetype BaseSize = 4 * GiB + 1; qDebug("Filling container with %lld entries", qint64(BaseSize)); QElapsedTimer timer; timer.start(); try { for (qsizetype i = 0; i < BaseSize; ++i) input.insert(i, i); } catch (const std::bad_alloc &) { QSKIP("Could not allocate 4 Gi entries."); } qDebug("Created dataset in %lld ms", timer.elapsed()); } void tst_QDataStream::fill(QHash &input) { constexpr qsizetype GiB = 1024 * 1024 * 1024; constexpr qsizetype BaseSize = 4 * GiB + 1; qDebug("Filling container with %lld entries", qint64(BaseSize)); QElapsedTimer timer; timer.start(); try { input.reserve(BaseSize); for (qsizetype i = 0; i < BaseSize; ++i) input.emplace(i, i); } catch (const std::bad_alloc &) { QSKIP("Could not allocate 4 Gi entries."); } qDebug("Created dataset in %lld ms", timer.elapsed()); } template void tst_QDataStream::stream_big() { #if QT_POINTER_SIZE > 4 QFETCH_GLOBAL(const QDataStream::Version, streamVersion); QElapsedTimer timer; T input; fill(input); QByteArray ba; QDataStream inputstream(&ba, QIODevice::WriteOnly); inputstream.setVersion(streamVersion); timer.start(); try { inputstream << input; } catch (const std::bad_alloc &) { QSKIP("Not enough memory to copy into QDataStream."); } qDebug("Streamed into QDataStream in %lld ms", timer.elapsed()); if (streamVersion < QDataStream::Qt_6_7) { // old versions do not support data size more than 4 GiB QCOMPARE(inputstream.status(), QDataStream::SizeLimitExceeded); QVERIFY(ba.isEmpty()); } else { T output; QDataStream outputstream(ba); timer.start(); try { outputstream >> output; } catch (const std::bad_alloc &) { QSKIP("Not enough memory to copy out of QDataStream."); } qDebug("Streamed out of QDataStream in %lld ms", timer.elapsed()); QCOMPARE(input.size(), output.size()); QCOMPARE(input, output); } #else QSKIP("This test is 64-bit only."); #endif } void tst_QDataStream::stream_bigQString() { stream_big(); } void tst_QDataStream::stream_bigQByteArray() { stream_big(); } void tst_QDataStream::stream_bigQList() { stream_big>(); } void tst_QDataStream::stream_bigQSet() { stream_big>(); } void tst_QDataStream::stream_bigQMap() { stream_big>(); } void tst_QDataStream::stream_bigQHash() { stream_big>(); } void tst_QDataStream::stream_bigCString() { if constexpr (sizeof(void*) == sizeof(int)) QSKIP("This test is 64-bit only."); QFETCH_GLOBAL(const QDataStream::Version, streamVersion); constexpr qint64 GiB = 1024 * 1024 * 1024; constexpr qint64 BaseSize = 4 * GiB + 1; std::string input; qDebug("Creating an array with %lld entries", BaseSize); QElapsedTimer timer; timer.start(); try { input.resize(BaseSize, 'a'); } catch (const std::bad_alloc &) { QSKIP("Could not allocate 4 Gi + 2 entries."); } qDebug("Created dataset in %lld ms", timer.elapsed()); QByteArray ba; QDataStream inputstream(&ba, QIODevice::WriteOnly); inputstream.setVersion(streamVersion); timer.start(); try { inputstream << input.data(); } catch (const std::bad_alloc &) { QSKIP("Not enough memory to copy into QDataStream."); } qDebug("Streamed into QDataStream in %lld ms", timer.elapsed()); if (streamVersion < QDataStream::Qt_6_7) { // old versions do not support data size more than 4 GiB QCOMPARE(inputstream.status(), QDataStream::SizeLimitExceeded); QVERIFY(ba.isEmpty()); } else { char *output = nullptr; auto cleanup = qScopeGuard([&output] { delete [] output; }); QDataStream outputstream(ba); timer.start(); try { outputstream >> output; } catch (const std::bad_alloc &) { QSKIP("Not enough memory to copy out of QDataStream."); } qDebug("Streamed out of QDataStream in %lld ms", timer.elapsed()); QCOMPARE(qstrlen(output), input.size()); QVERIFY(memcmp(input.data(), output, BaseSize + 1) == 0); } } QTEST_MAIN(tst_QDataStream) #include "tst_manualqdatastream.moc"