mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-04 08:15:30 +08:00
qt 6.6.0 clean
This commit is contained in:
@ -32,15 +32,9 @@ endif()
|
||||
if(TARGET Qt::Network AND NOT ANDROID AND NOT UIKIT)
|
||||
add_subdirectory(qobject)
|
||||
endif()
|
||||
if(QT_FEATURE_private_tests AND NOT ANDROID AND NOT UIKIT)
|
||||
add_subdirectory(qsharedmemory)
|
||||
endif()
|
||||
if(QT_FEATURE_private_tests AND TARGET Qt::Network)
|
||||
add_subdirectory(qsocketnotifier)
|
||||
endif()
|
||||
if(QT_FEATURE_systemsemaphore AND NOT ANDROID AND NOT UIKIT)
|
||||
add_subdirectory(qsystemsemaphore)
|
||||
endif()
|
||||
if(WIN32)
|
||||
add_subdirectory(qwineventnotifier)
|
||||
add_subdirectory(qwinregistrykey)
|
||||
|
@ -27,52 +27,6 @@ template<> char *toString(const QDeadlineTimer &dt)
|
||||
dt.hasExpired() ? " (expired)" : "");
|
||||
return buf;
|
||||
}
|
||||
|
||||
template <typename Rep, typename Period> char *toString(std::chrono::duration<Rep, Period> dur)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
static_assert(sizeof(double) == sizeof(qlonglong));
|
||||
|
||||
if constexpr (Period::num == 1 && sizeof(Rep) <= sizeof(qlonglong)) {
|
||||
// typical case: second or sub-multiple of second, in a representation
|
||||
// we can directly use
|
||||
char *buf = new char[128];
|
||||
if constexpr (std::is_integral_v<Rep>) {
|
||||
char unit[] = "ss";
|
||||
if constexpr (std::is_same_v<Period, std::atto>) { // from Norwegian "atten", 18
|
||||
unit[0] = 'a';
|
||||
} else if constexpr (std::is_same_v<Period, std::femto>) { // Norwegian "femten", 15
|
||||
unit[0] = 'f';
|
||||
} else if constexpr (std::is_same_v<Period, std::pico>) {
|
||||
unit[0] = 'p';
|
||||
} else if constexpr (std::is_same_v<Period, std::nano>) {
|
||||
unit[0] = 'n';
|
||||
} else if constexpr (std::is_same_v<Period, std::micro>) {
|
||||
unit[0] = 'u'; // µ, really, but the output may not be UTF-8-safe
|
||||
} else if constexpr (std::is_same_v<Period, std::milli>) {
|
||||
unit[0] = 'm';
|
||||
} else {
|
||||
// deci, centi, cycles of something (60 Hz, 8000 Hz, etc.)
|
||||
static_assert(Period::den == 1,
|
||||
"Unsupported std::chrono::duration of a sub-multiple of second");
|
||||
unit[1] = '\0';
|
||||
}
|
||||
|
||||
// cast to qlonglong in case Rep is not int64_t
|
||||
qsnprintf(buf, 128, "%lld %s", qlonglong(dur.count()), unit);
|
||||
} else {
|
||||
auto secs = duration_cast<duration<double>>(dur);
|
||||
qsnprintf(buf, 128, "%g s", secs.count());
|
||||
}
|
||||
return buf;
|
||||
} else if constexpr (std::is_integral_v<Rep> && Period::den == 1) {
|
||||
// multiple of second, so just print it in seconds
|
||||
return toString(std::chrono::seconds(dur));
|
||||
} else {
|
||||
// something else, use floating-point seconds
|
||||
return toString(std::chrono::duration_cast<double>(dur));
|
||||
}
|
||||
}
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
@ -81,7 +35,6 @@ class tst_QDeadlineTimer : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase_data();
|
||||
void basics();
|
||||
void foreverness();
|
||||
void current();
|
||||
@ -92,20 +45,13 @@ private Q_SLOTS:
|
||||
void stdchrono();
|
||||
};
|
||||
|
||||
void tst_QDeadlineTimer::initTestCase_data()
|
||||
{
|
||||
qRegisterMetaType<Qt::TimerType>();
|
||||
QTest::addColumn<Qt::TimerType>("timerType");
|
||||
QTest::newRow("precise") << Qt::PreciseTimer;
|
||||
QTest::newRow("coarse") << Qt::CoarseTimer;
|
||||
}
|
||||
static constexpr auto timerType = Qt::PreciseTimer;
|
||||
|
||||
void tst_QDeadlineTimer::basics()
|
||||
{
|
||||
QDeadlineTimer deadline;
|
||||
QCOMPARE(deadline.timerType(), Qt::CoarseTimer);
|
||||
|
||||
QFETCH_GLOBAL(Qt::TimerType, timerType);
|
||||
deadline = QDeadlineTimer(timerType);
|
||||
QCOMPARE(deadline.timerType(), timerType);
|
||||
QVERIFY(!deadline.isForever());
|
||||
@ -159,9 +105,6 @@ void tst_QDeadlineTimer::basics()
|
||||
|
||||
void tst_QDeadlineTimer::foreverness()
|
||||
{
|
||||
QFETCH_GLOBAL(Qt::TimerType, timerType);
|
||||
// we don't check whether timerType() is our type since it's possible it detects it's forever
|
||||
|
||||
QDeadlineTimer deadline = QDeadlineTimer::Forever;
|
||||
QCOMPARE(deadline.timerType(), Qt::CoarseTimer);
|
||||
QVERIFY(deadline.isForever());
|
||||
@ -271,7 +214,6 @@ void tst_QDeadlineTimer::foreverness()
|
||||
|
||||
void tst_QDeadlineTimer::current()
|
||||
{
|
||||
QFETCH_GLOBAL(Qt::TimerType, timerType);
|
||||
auto deadline = QDeadlineTimer::current(timerType);
|
||||
QVERIFY(deadline.hasExpired());
|
||||
QVERIFY(!deadline.isForever());
|
||||
@ -307,8 +249,6 @@ void tst_QDeadlineTimer::current()
|
||||
|
||||
void tst_QDeadlineTimer::deadlines()
|
||||
{
|
||||
QFETCH_GLOBAL(Qt::TimerType, timerType);
|
||||
|
||||
QDeadlineTimer deadline(4 * minResolution, timerType);
|
||||
QVERIFY(!deadline.hasExpired());
|
||||
QVERIFY(!deadline.isForever());
|
||||
@ -405,7 +345,6 @@ void tst_QDeadlineTimer::deadlines()
|
||||
|
||||
void tst_QDeadlineTimer::setDeadline()
|
||||
{
|
||||
QFETCH_GLOBAL(Qt::TimerType, timerType);
|
||||
auto now = QDeadlineTimer::current(timerType);
|
||||
QDeadlineTimer deadline;
|
||||
|
||||
@ -457,7 +396,6 @@ void tst_QDeadlineTimer::setDeadline()
|
||||
|
||||
void tst_QDeadlineTimer::overflow()
|
||||
{
|
||||
QFETCH_GLOBAL(Qt::TimerType, timerType);
|
||||
// Check the constructor for overflows (should also cover saturating the result of the deadline() method if overflowing)
|
||||
QDeadlineTimer now = QDeadlineTimer::current(timerType), deadline(std::numeric_limits<qint64>::max() - 1, timerType);
|
||||
QVERIFY(deadline.isForever() || deadline.deadline() >= now.deadline());
|
||||
@ -520,22 +458,43 @@ void tst_QDeadlineTimer::overflow()
|
||||
|
||||
// Make sure setRemainingTime underflows gracefully
|
||||
deadline.setPreciseRemainingTime(std::numeric_limits<qint64>::min() / 10, 0, timerType);
|
||||
QVERIFY(!deadline.isForever()); // On Win/macOS the above underflows, make sure we don't saturate to Forever
|
||||
QVERIFY(deadline.remainingTime() == 0);
|
||||
QVERIFY(deadline.isForever()); // The above could underflow, so make sure we did set to Forever
|
||||
QCOMPARE(deadline.remainingTimeNSecs(), -1);
|
||||
QCOMPARE(deadline.remainingTime(), -1);
|
||||
// If the timer is saturated we don't want to get a valid number of milliseconds
|
||||
QVERIFY(deadline.deadline() == std::numeric_limits<qint64>::min());
|
||||
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
|
||||
|
||||
// Check that the conversion to milliseconds and nanoseconds underflows gracefully
|
||||
deadline.setPreciseDeadline(std::numeric_limits<qint64>::min() / 10, 0, timerType);
|
||||
QVERIFY(!deadline.isForever()); // On Win/macOS the above underflows, make sure we don't saturate to Forever
|
||||
QVERIFY(!deadline.isForever()); // The above underflows, make sure we don't saturate to Forever
|
||||
QVERIFY(deadline.deadline() == std::numeric_limits<qint64>::min());
|
||||
QVERIFY(deadline.deadlineNSecs() == std::numeric_limits<qint64>::min());
|
||||
|
||||
// Check that subtracting max() twice doesn't make it become positive
|
||||
deadline.setPreciseDeadline(0);
|
||||
deadline -= std::numeric_limits<qint64>::max();
|
||||
deadline -= std::numeric_limits<qint64>::max();
|
||||
QVERIFY(!deadline.isForever());
|
||||
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::min());
|
||||
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::min());
|
||||
|
||||
// Ditto for adding max()
|
||||
deadline.setPreciseDeadline(0);
|
||||
deadline += std::numeric_limits<qint64>::max();
|
||||
deadline += std::numeric_limits<qint64>::max();
|
||||
QVERIFY(deadline.isForever()); // it's so far in the future it's effectively forever
|
||||
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
|
||||
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
|
||||
|
||||
// But we don't un-become forever after saturation
|
||||
deadline -= std::numeric_limits<qint64>::max();
|
||||
QVERIFY(deadline.isForever());
|
||||
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
|
||||
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
|
||||
}
|
||||
|
||||
void tst_QDeadlineTimer::expire()
|
||||
{
|
||||
QFETCH_GLOBAL(Qt::TimerType, timerType);
|
||||
|
||||
QDeadlineTimer deadline(minResolution, timerType);
|
||||
QVERIFY(!deadline.hasExpired());
|
||||
QVERIFY(!deadline.isForever());
|
||||
@ -557,7 +516,6 @@ void tst_QDeadlineTimer::stdchrono()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
using namespace std::chrono_literals;
|
||||
QFETCH_GLOBAL(Qt::TimerType, timerType);
|
||||
|
||||
// create some forevers
|
||||
QDeadlineTimer deadline = milliseconds::max();
|
||||
@ -630,16 +588,13 @@ void tst_QDeadlineTimer::stdchrono()
|
||||
QTRY_VERIFY2_WITH_TIMEOUT(timersExecuted,
|
||||
"Looks like timers didn't fire on time.", 4 * minResolution);
|
||||
|
||||
#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || (defined(Q_CC_MSVC) && Q_CC_MSVC >= 1900)
|
||||
{
|
||||
// We know for these OS/compilers that the std::chrono::steady_clock uses the same
|
||||
// reference time as QDeadlineTimer
|
||||
qint64 before = duration_cast<nanoseconds>(steady_before.time_since_epoch()).count();
|
||||
qint64 after = duration_cast<nanoseconds>(steady_after.time_since_epoch()).count();
|
||||
QCOMPARE_GT(now.deadlineNSecs(), before);
|
||||
QCOMPARE_LT(now.deadlineNSecs(), after);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
auto diff = duration_cast<milliseconds>(steady_after - steady_deadline);
|
||||
QCOMPARE_GT(diff.count(), minResolution / 2);
|
||||
@ -681,18 +636,24 @@ void tst_QDeadlineTimer::stdchrono()
|
||||
QCOMPARE_LT(deadline.deadline<steady_clock>(), (steady_clock::now() + 5ms * minResolution));
|
||||
QCOMPARE_GT(deadline.deadline<system_clock>(), (system_clock::now() + 3ms * minResolution));
|
||||
QCOMPARE_LT(deadline.deadline<system_clock>(), (system_clock::now() + 5ms * minResolution));
|
||||
if (timerType == Qt::CoarseTimer) {
|
||||
QCOMPARE_GT(deadline, now + 3ms * minResolution);
|
||||
QCOMPARE_LT(deadline, now + 5ms * minResolution);
|
||||
QCOMPARE_GT(deadline, now + 3000000ns * minResolution);
|
||||
QCOMPARE_LT(deadline, now + 5000000ns * minResolution);
|
||||
QCOMPARE_GT(deadline, 3ms * minResolution);
|
||||
QCOMPARE_LT(deadline, 5ms * minResolution);
|
||||
QCOMPARE_GT(deadline, 3000000ns * minResolution);
|
||||
QCOMPARE_LT(deadline, 5000000ns * minResolution);
|
||||
QCOMPARE_GE(deadline, steady_clock::now());
|
||||
QCOMPARE_GE(deadline, system_clock::now());
|
||||
}
|
||||
QCOMPARE_GT((deadline.deadline<steady_clock, milliseconds>()),
|
||||
steady_clock::now() + 3ms * minResolution);
|
||||
QCOMPARE_LT((deadline.deadline<steady_clock, milliseconds>()),
|
||||
steady_clock::now() + 5ms * minResolution);
|
||||
QCOMPARE_GT((deadline.deadline<system_clock, milliseconds>()),
|
||||
system_clock::now() + 3ms * minResolution);
|
||||
QCOMPARE_LT((deadline.deadline<system_clock, milliseconds>()),
|
||||
system_clock::now() + 5ms * minResolution);
|
||||
QCOMPARE_GT(deadline, now + 3ms * minResolution);
|
||||
QCOMPARE_LT(deadline, now + 5ms * minResolution);
|
||||
QCOMPARE_GT(deadline, now + 3000000ns * minResolution);
|
||||
QCOMPARE_LT(deadline, now + 5000000ns * minResolution);
|
||||
QCOMPARE_GT(deadline, 3ms * minResolution);
|
||||
QCOMPARE_LT(deadline, 5ms * minResolution);
|
||||
QCOMPARE_GT(deadline, 3000000ns * minResolution);
|
||||
QCOMPARE_LT(deadline, 5000000ns * minResolution);
|
||||
QCOMPARE_GE(deadline, steady_clock::now());
|
||||
QCOMPARE_GE(deadline, system_clock::now());
|
||||
|
||||
now = QDeadlineTimer::current(timerType);
|
||||
deadline = QDeadlineTimer(1s, timerType);
|
||||
@ -705,10 +666,8 @@ void tst_QDeadlineTimer::stdchrono()
|
||||
QCOMPARE_LE(deadline.deadline<steady_clock>(), steady_clock::now() + 1s + 1ms * minResolution);
|
||||
QCOMPARE_GT(deadline.deadline<system_clock>(), system_clock::now() + 1s - 1ms * minResolution);
|
||||
QCOMPARE_LE(deadline.deadline<system_clock>(), system_clock::now() + 1s + 1ms * minResolution);
|
||||
if (timerType == Qt::CoarseTimer) {
|
||||
QCOMPARE_GT(deadline, 1s - 1ms * minResolution);
|
||||
QCOMPARE_LE(deadline, 1s);
|
||||
}
|
||||
QCOMPARE_GT(deadline, 1s - 1ms * minResolution);
|
||||
QCOMPARE_LE(deadline, 1s);
|
||||
|
||||
now = QDeadlineTimer::current(timerType);
|
||||
deadline.setRemainingTime(1h, timerType);
|
||||
|
@ -1,8 +1,8 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QTime>
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QTest>
|
||||
#include <QTimer>
|
||||
@ -31,11 +31,25 @@ private Q_SLOTS:
|
||||
|
||||
void tst_QElapsedTimer::statics()
|
||||
{
|
||||
qDebug() << "Clock type is" << QElapsedTimer::clockType();
|
||||
qDebug() << "Said clock is" << (QElapsedTimer::isMonotonic() ? "monotonic" : "not monotonic");
|
||||
// these have been required since Qt 6.6
|
||||
QCOMPARE(QElapsedTimer::clockType(), QElapsedTimer::MonotonicClock);
|
||||
QVERIFY(QElapsedTimer::isMonotonic());
|
||||
|
||||
QElapsedTimer t;
|
||||
t.start();
|
||||
qDebug() << "Current time is" << t.msecsSinceReference();
|
||||
qint64 system_now = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
auto setprecision = +[](QTextStream &s) -> QTextStream & {
|
||||
s.setRealNumberNotation(QTextStream::FixedNotation);
|
||||
s.setRealNumberPrecision(3);
|
||||
return s;
|
||||
};
|
||||
qDebug() << setprecision
|
||||
<< "Current monotonic time is" << (t.msecsSinceReference() / 1000.)
|
||||
<< "s and current system time is" << (system_now / 1000.) << 's';
|
||||
if (qAbs(system_now - t.msecsSinceReference()) < 5 * minResolution)
|
||||
qWarning() << "The monotonic clock is awfully close to the system clock"
|
||||
" (it may not be monotonic at all!)";
|
||||
}
|
||||
|
||||
void tst_QElapsedTimer::validity()
|
||||
|
@ -355,7 +355,7 @@ void tst_QEventDispatcher::postEventFromThread()
|
||||
threadPool->start([&]{
|
||||
int loop = 1000 / 10; // give it a second
|
||||
while (!done && --loop)
|
||||
QThread::msleep(10);
|
||||
QThread::sleep(std::chrono::milliseconds{10});
|
||||
if (done)
|
||||
return;
|
||||
hadToQuit = true;
|
||||
@ -393,7 +393,7 @@ void tst_QEventDispatcher::postEventFromEventHandler()
|
||||
threadPool->start([&]{
|
||||
int loop = 250 / 10; // give it 250ms
|
||||
while (!done && --loop)
|
||||
QThread::msleep(10);
|
||||
QThread::sleep(std::chrono::milliseconds{10});
|
||||
if (done)
|
||||
return;
|
||||
hadToQuit = true;
|
||||
|
@ -497,6 +497,10 @@ void tst_QMetaContainer::testSequence()
|
||||
QVERIFY(metaSequence.compareConstIterator(constIt, constEnd));
|
||||
metaSequence.destroyConstIterator(constIt);
|
||||
metaSequence.destroyConstIterator(constEnd);
|
||||
|
||||
QVERIFY(metaSequence.iface() != nullptr);
|
||||
QMetaSequence defaultConstructed;
|
||||
QVERIFY(defaultConstructed.iface() == nullptr);
|
||||
}
|
||||
|
||||
void tst_QMetaContainer::testAssociation_data()
|
||||
@ -722,6 +726,10 @@ void tst_QMetaContainer::testAssociation()
|
||||
QVERIFY(metaAssociation.compareConstIterator(constIt, constEnd));
|
||||
metaAssociation.destroyConstIterator(constIt);
|
||||
metaAssociation.destroyConstIterator(constEnd);
|
||||
|
||||
QVERIFY(metaAssociation.iface() != nullptr);
|
||||
QMetaSequence defaultConstructed;
|
||||
QVERIFY(defaultConstructed.iface() == nullptr);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QMetaContainer)
|
||||
|
@ -302,6 +302,8 @@ private slots:
|
||||
void invokeTypedefTypes();
|
||||
void invokeException();
|
||||
void invokeQueuedAutoRegister();
|
||||
void invokeFreeFunction();
|
||||
void invokeBind();
|
||||
void qtMetaObjectInheritance();
|
||||
void normalizedSignature_data();
|
||||
void normalizedSignature();
|
||||
@ -1997,6 +1999,49 @@ void tst_QMetaObject::invokeQueuedAutoRegister()
|
||||
QString("slotWithRegistrableArgument:myShared-myShared-myShared-myShared-00"));
|
||||
}
|
||||
|
||||
namespace FunctionTest {
|
||||
static void function0() {}
|
||||
static int functionNoExcept() noexcept
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QMetaObject::invokeFreeFunction()
|
||||
{
|
||||
using namespace FunctionTest;
|
||||
QtTestObject obj;
|
||||
QMetaObject::invokeMethod(&obj, function0);
|
||||
int retInt = -1;
|
||||
QMetaObject::invokeMethod(&obj, functionNoExcept, &retInt);
|
||||
QCOMPARE(retInt, functionNoExcept());
|
||||
}
|
||||
|
||||
void tst_QMetaObject::invokeBind()
|
||||
{
|
||||
QtTestObject obj;
|
||||
|
||||
struct {
|
||||
int number;
|
||||
QString string;
|
||||
} results;
|
||||
|
||||
const auto function = [&results](int number, const QString &string) -> bool {
|
||||
results.number = number;
|
||||
results.string = string;
|
||||
return true;
|
||||
};
|
||||
|
||||
const int number = 42;
|
||||
const QString string("Test");
|
||||
const auto binding = std::bind(function, number, string);
|
||||
bool ret = false;
|
||||
QMetaObject::invokeMethod(&obj, binding, &ret);
|
||||
QVERIFY(ret);
|
||||
QCOMPARE(results.number, number);
|
||||
QCOMPARE(results.string, string);
|
||||
}
|
||||
|
||||
void tst_QMetaObject::normalizedSignature_data()
|
||||
{
|
||||
QTest::addColumn<QString>("signature");
|
||||
|
@ -779,6 +779,26 @@ void tst_QMetaObjectBuilder::notifySignal()
|
||||
|
||||
void tst_QMetaObjectBuilder::enumerator()
|
||||
{
|
||||
static const QtPrivate::QMetaTypeInterface fooFlagMetaType = {
|
||||
0,
|
||||
8,
|
||||
8,
|
||||
QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration | QMetaType::RelocatableType,
|
||||
{},
|
||||
nullptr,
|
||||
"fooFlag",
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
};
|
||||
|
||||
QMetaObjectBuilder builder;
|
||||
|
||||
// Add an enumerator and check its attributes.
|
||||
@ -810,6 +830,7 @@ void tst_QMetaObjectBuilder::enumerator()
|
||||
enum1.setIsFlag(true);
|
||||
enum1.setIsScoped(true);
|
||||
enum1.setEnumName(QByteArrayLiteral("fooFlag"));
|
||||
enum1.setMetaType(QMetaType(&fooFlagMetaType));
|
||||
QCOMPARE(enum1.addKey("ABC", 0), 0);
|
||||
QCOMPARE(enum1.addKey("DEF", 1), 1);
|
||||
QCOMPARE(enum1.addKey("GHI", -1), 2);
|
||||
@ -819,6 +840,7 @@ void tst_QMetaObjectBuilder::enumerator()
|
||||
QVERIFY(enum1.isFlag());
|
||||
QVERIFY(enum1.isScoped());
|
||||
QCOMPARE(enum1.enumName(), QByteArray("fooFlag"));
|
||||
QCOMPARE(enum1.metaType(), QMetaType(&fooFlagMetaType));
|
||||
QCOMPARE(enum1.keyCount(), 3);
|
||||
QCOMPARE(enum1.index(), 0);
|
||||
QCOMPARE(enum1.key(0), QByteArray("ABC"));
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <QtCore/qflags.h>
|
||||
|
||||
Q_DECLARE_METATYPE(QMetaType::Type)
|
||||
Q_DECLARE_METATYPE(QPartialOrdering)
|
||||
|
||||
@ -1858,6 +1860,58 @@ void tst_QMetaType::isEnum()
|
||||
QVERIFY((QMetaType(type6).flags() & QMetaType::IsEnumeration) == QMetaType::IsEnumeration);
|
||||
}
|
||||
|
||||
enum E1 : unsigned char {};
|
||||
enum E2 : qlonglong {};
|
||||
enum class E3 : unsigned short {};
|
||||
|
||||
namespace myflags {
|
||||
|
||||
Q_NAMESPACE
|
||||
|
||||
enum Flag1 : int { A, B };
|
||||
enum Flag2 : short { X, Y };
|
||||
|
||||
Q_DECLARE_FLAGS(Flags1, myflags::Flag1);
|
||||
Q_FLAG_NS(Flags1)
|
||||
Q_DECLARE_FLAGS(Flags2, myflags::Flag2);
|
||||
Q_FLAG_NS(Flags2)
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using getUnderlyingTypeNormalized = std::conditional_t<
|
||||
std::is_signed_v<std::underlying_type_t<T>>,
|
||||
typename QIntegerForSize<sizeof(T)>::Signed,
|
||||
typename QIntegerForSize<sizeof(T)>::Unsigned
|
||||
>;
|
||||
|
||||
void tst_QMetaType::underlyingType_data()
|
||||
{
|
||||
QTest::addColumn<QMetaType>("source");
|
||||
QTest::addColumn<QMetaType>("underlying");
|
||||
|
||||
QTest::newRow("invalid") << QMetaType() << QMetaType();
|
||||
QTest::newRow("plain") << QMetaType::fromType<isEnumTest_Enum1>()
|
||||
<< QMetaType::fromType<getUnderlyingTypeNormalized<isEnumTest_Enum1>>();
|
||||
QTest::newRow("uchar") << QMetaType::fromType<E1>()
|
||||
<< QMetaType::fromType<getUnderlyingTypeNormalized<E1>>();
|
||||
QTest::newRow("long") << QMetaType::fromType<E2>()
|
||||
<< QMetaType::fromType<getUnderlyingTypeNormalized<E2>>();
|
||||
QTest::newRow("class_ushort") << QMetaType::fromType<E3>()
|
||||
<< QMetaType::fromType<getUnderlyingTypeNormalized<E3>>();
|
||||
QTest::newRow("flags_int") << QMetaType::fromType<myflags::Flags1>()
|
||||
<< QMetaType::fromType<int>();
|
||||
QTest::newRow("flags_short") << QMetaType::fromType<myflags::Flags2>()
|
||||
<< QMetaType::fromType<int>(); // sic, not short!
|
||||
}
|
||||
|
||||
void tst_QMetaType::underlyingType()
|
||||
{
|
||||
QFETCH(QMetaType, source);
|
||||
QFETCH(QMetaType, underlying);
|
||||
QCOMPARE(source.underlyingType(), underlying);
|
||||
}
|
||||
|
||||
void tst_QMetaType::isRegisteredStaticLess_data()
|
||||
{
|
||||
isRegistered_data();
|
||||
|
@ -91,6 +91,8 @@ private slots:
|
||||
void isRegisteredStaticLess();
|
||||
void isNotRegistered();
|
||||
void isEnum();
|
||||
void underlyingType_data();
|
||||
void underlyingType();
|
||||
void automaticTemplateRegistration_1();
|
||||
void automaticTemplateRegistration_2(); // defined in tst_qmetatype3.cpp
|
||||
void saveAndLoadBuiltin_data();
|
||||
|
@ -149,6 +149,7 @@ private slots:
|
||||
void objectNameBinding();
|
||||
void emitToDestroyedClass();
|
||||
void declarativeData();
|
||||
void asyncCallbackHelper();
|
||||
};
|
||||
|
||||
struct QObjectCreatedOnShutdown
|
||||
@ -6074,6 +6075,7 @@ void tst_QObject::connectFunctorArgDifference()
|
||||
|
||||
connect(&timer, &QTimer::timeout, [=](){});
|
||||
connect(&timer, &QTimer::objectNameChanged, [=](const QString &){});
|
||||
connect(&timer, &QTimer::objectNameChanged, this, [](){});
|
||||
connect(qApp, &QCoreApplication::aboutToQuit, [=](){});
|
||||
|
||||
connect(&timer, &QTimer::objectNameChanged, [=](){});
|
||||
@ -6812,7 +6814,11 @@ struct QmlReceiver : public QtPrivate::QSlotObjectBase
|
||||
, magic(0)
|
||||
{}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
||||
static void impl(int which, QSlotObjectBase *this_, QObject *, void **metaArgs, bool *ret)
|
||||
#else
|
||||
static void impl(QSlotObjectBase *this_, QObject *, void **metaArgs, int which, bool *ret)
|
||||
#endif
|
||||
{
|
||||
switch (which) {
|
||||
case Destroy: delete static_cast<QmlReceiver*>(this_); return;
|
||||
@ -8365,5 +8371,240 @@ void tst_QObject::declarativeData()
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
Compile-time test for the helpers in qobjectdefs_impl.h.
|
||||
*/
|
||||
class AsyncCaller : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~AsyncCaller()
|
||||
{
|
||||
if (slotObject)
|
||||
slotObject->destroyIfLastRef();
|
||||
}
|
||||
void callback0() {}
|
||||
void callback1(const QString &) {}
|
||||
void callbackInt(int) {}
|
||||
int returnInt() const { return 0; }
|
||||
|
||||
static int staticCallback0() { return 0; }
|
||||
static void staticCallback1(const QString &) {}
|
||||
|
||||
using Prototype0 = int(*)();
|
||||
using Prototype1 = void(*)(QString);
|
||||
|
||||
template<typename Functor>
|
||||
bool callMe0(const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *, Functor &&func)
|
||||
{
|
||||
if (slotObject) {
|
||||
slotObject->destroyIfLastRef();
|
||||
slotObject = nullptr;
|
||||
}
|
||||
QtPrivate::AssertCompatibleFunctions<Prototype0, Functor>();
|
||||
slotObject = QtPrivate::makeCallableObject<Prototype0>(std::forward<Functor>(func));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
bool callMe0(Functor &&func)
|
||||
{
|
||||
return callMe0(nullptr, std::forward<Functor>(func));
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
bool callMe1(const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *, Functor &&func)
|
||||
{
|
||||
if (slotObject) {
|
||||
slotObject->destroyIfLastRef();
|
||||
slotObject = nullptr;
|
||||
}
|
||||
QtPrivate::AssertCompatibleFunctions<Prototype1, Functor>();
|
||||
slotObject = QtPrivate::makeCallableObject<Prototype1>(std::forward<Functor>(func));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Functor>
|
||||
bool callMe1(Functor &&func)
|
||||
{
|
||||
return callMe1(nullptr, std::forward<Functor>(func));
|
||||
}
|
||||
|
||||
QtPrivate::QSlotObjectBase *slotObject = nullptr;
|
||||
};
|
||||
|
||||
static void freeFunction0() {}
|
||||
static void freeFunction1(QString) {}
|
||||
static void freeFunctionVariant(QVariant) {}
|
||||
|
||||
template<typename Prototype, typename Functor>
|
||||
inline constexpr bool compiles(Functor &&) {
|
||||
return QtPrivate::AreFunctionsCompatible<Prototype, Functor>::value;
|
||||
}
|
||||
|
||||
void tst_QObject::asyncCallbackHelper()
|
||||
{
|
||||
int result = 0;
|
||||
QString arg1 = "Parameter";
|
||||
void *argv[] = { &result, &arg1 };
|
||||
|
||||
auto lambda0 = []{};
|
||||
auto lambda1 = [](const QString &) {};
|
||||
auto lambda2 = [](const QString &, int) {};
|
||||
const auto constLambda = [](const QString &) {};
|
||||
auto moveOnlyLambda = [u = std::unique_ptr<int>()]{};
|
||||
auto moveOnlyLambda1 = [u = std::unique_ptr<int>()](const QString &){};
|
||||
|
||||
SlotFunctor functor0;
|
||||
SlotFunctorString functor1;
|
||||
|
||||
// no parameters provided or needed
|
||||
static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::callback0));
|
||||
static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::staticCallback0));
|
||||
static_assert(compiles<AsyncCaller::Prototype0>(lambda0));
|
||||
static_assert(compiles<AsyncCaller::Prototype0>(std::move(moveOnlyLambda)));
|
||||
static_assert(compiles<AsyncCaller::Prototype0>(freeFunction0));
|
||||
static_assert(compiles<AsyncCaller::Prototype0>(functor0));
|
||||
|
||||
// more parameters than needed
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::callback0));
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::staticCallback0));
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(lambda0));
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(freeFunction0));
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(functor0));
|
||||
|
||||
// matching parameter
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::callback1));
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::staticCallback1));
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(lambda1));
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(std::move(moveOnlyLambda1)));
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(constLambda));
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(freeFunction1));
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(functor1));
|
||||
|
||||
// not enough parameters
|
||||
static_assert(!compiles<AsyncCaller::Prototype0>(&AsyncCaller::callback1));
|
||||
static_assert(!compiles<AsyncCaller::Prototype0>(&AsyncCaller::staticCallback1));
|
||||
static_assert(!compiles<AsyncCaller::Prototype0>(lambda1));
|
||||
static_assert(!compiles<AsyncCaller::Prototype0>(constLambda));
|
||||
static_assert(!compiles<AsyncCaller::Prototype0>(lambda2));
|
||||
static_assert(!compiles<AsyncCaller::Prototype0>(freeFunction1));
|
||||
static_assert(!compiles<AsyncCaller::Prototype0>(functor1));
|
||||
|
||||
// wrong parameter type
|
||||
static_assert(!compiles<AsyncCaller::Prototype1>(&AsyncCaller::callbackInt));
|
||||
|
||||
// old-style slot name
|
||||
static_assert(!compiles<AsyncCaller::Prototype0>("callback1"));
|
||||
|
||||
// slot with return value is ok, we just don't pass
|
||||
// the return value through to anything.
|
||||
static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::returnInt));
|
||||
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(freeFunctionVariant));
|
||||
|
||||
std::function<int()> stdFunction0(&AsyncCaller::staticCallback0);
|
||||
std::function<void(QString)> stdFunction1(&AsyncCaller::staticCallback1);
|
||||
static_assert(compiles<AsyncCaller::Prototype0>(stdFunction0));
|
||||
static_assert(compiles<AsyncCaller::Prototype1>(stdFunction1));
|
||||
|
||||
AsyncCaller caller;
|
||||
// with context
|
||||
QVERIFY(caller.callMe0(&caller, &AsyncCaller::callback0));
|
||||
QVERIFY(caller.callMe0(&caller, &AsyncCaller::returnInt));
|
||||
QVERIFY(caller.callMe0(&caller, &AsyncCaller::staticCallback0));
|
||||
QVERIFY(caller.callMe0(&caller, lambda0));
|
||||
QVERIFY(caller.callMe0(&caller, freeFunction0));
|
||||
QVERIFY(caller.callMe0(&caller, std::move(moveOnlyLambda)));
|
||||
QVERIFY(caller.callMe0(&caller, stdFunction0));
|
||||
|
||||
QVERIFY(caller.callMe1(&caller, &AsyncCaller::callback1));
|
||||
QVERIFY(caller.callMe1(&caller, &AsyncCaller::staticCallback1));
|
||||
QVERIFY(caller.callMe1(&caller, lambda1));
|
||||
QVERIFY(caller.callMe1(&caller, freeFunction1));
|
||||
QVERIFY(caller.callMe1(&caller, constLambda));
|
||||
QVERIFY(caller.callMe1(&caller, stdFunction1));
|
||||
|
||||
// without context
|
||||
QVERIFY(caller.callMe0(&AsyncCaller::staticCallback0));
|
||||
QVERIFY(caller.callMe0(lambda0));
|
||||
QVERIFY(caller.callMe0(freeFunction0));
|
||||
QVERIFY(caller.callMe0(stdFunction0));
|
||||
|
||||
QVERIFY(caller.callMe1(&AsyncCaller::staticCallback1));
|
||||
QVERIFY(caller.callMe1(lambda1));
|
||||
QVERIFY(caller.callMe1(constLambda));
|
||||
QVERIFY(caller.callMe1(std::move(moveOnlyLambda1)));
|
||||
QVERIFY(caller.callMe1(freeFunction1));
|
||||
QVERIFY(caller.callMe1(stdFunction1));
|
||||
|
||||
static const char *expectedPayload = "Hello World!";
|
||||
{
|
||||
struct MoveOnlyFunctor {
|
||||
MoveOnlyFunctor() = default;
|
||||
MoveOnlyFunctor(MoveOnlyFunctor &&) = default;
|
||||
MoveOnlyFunctor(const MoveOnlyFunctor &) = delete;
|
||||
~MoveOnlyFunctor() = default;
|
||||
|
||||
int operator()() const {
|
||||
qDebug().noquote() << payload;
|
||||
return int(payload.length());
|
||||
}
|
||||
QString payload = expectedPayload;
|
||||
} moveOnlyFunctor;
|
||||
QVERIFY(caller.callMe0(std::move(moveOnlyFunctor)));
|
||||
}
|
||||
QTest::ignoreMessage(QtDebugMsg, expectedPayload);
|
||||
caller.slotObject->call(nullptr, argv);
|
||||
QCOMPARE(result, QLatin1String(expectedPayload).length());
|
||||
|
||||
// mutable lambda; same behavior as mutableFunctor - we copy the functor
|
||||
// in the QCallableObject, so the original is not modified
|
||||
int status = 0;
|
||||
auto mutableLambda1 = [&status, calls = 0]() mutable { status = ++calls; };
|
||||
|
||||
mutableLambda1();
|
||||
QCOMPARE(status, 1);
|
||||
QVERIFY(caller.callMe0(mutableLambda1)); // this copies the lambda with count == 1
|
||||
caller.slotObject->call(nullptr, argv); // this doesn't change mutableLambda1, but the copy
|
||||
QCOMPARE(status, 2);
|
||||
mutableLambda1();
|
||||
QCOMPARE(status, 2); // and we are still at two
|
||||
|
||||
auto mutableLambda2 = [calls = 0]() mutable { return ++calls; };
|
||||
QCOMPARE(mutableLambda2(), 1);
|
||||
QVERIFY(caller.callMe0(mutableLambda2)); // this copies the lambda
|
||||
caller.slotObject->call(nullptr, argv); // this call doesn't change mutableLambda2
|
||||
QCOMPARE(mutableLambda2(), 2); // so we are still at 2
|
||||
|
||||
{
|
||||
int called = -1;
|
||||
struct MutableFunctor {
|
||||
void operator()() { called = 0; }
|
||||
int &called;
|
||||
};
|
||||
struct ConstFunctor
|
||||
{
|
||||
void operator()() const { called = 1; }
|
||||
int &called;
|
||||
};
|
||||
|
||||
MutableFunctor mf{called};
|
||||
QMetaObject::invokeMethod(this, mf);
|
||||
QCOMPARE(called, 0);
|
||||
ConstFunctor cf{called};
|
||||
QMetaObject::invokeMethod(this, cf);
|
||||
QCOMPARE(called, 1);
|
||||
QMetaObject::invokeMethod(this, [&called, u = std::unique_ptr<int>()]{ called = 2; });
|
||||
QCOMPARE(called, 2);
|
||||
QMetaObject::invokeMethod(this, [&called, count = 0]() mutable {
|
||||
if (!count)
|
||||
called = 3;
|
||||
++count;
|
||||
});
|
||||
QCOMPARE(called, 3);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QObject)
|
||||
#include "tst_qobject.moc"
|
||||
|
@ -26,6 +26,7 @@ private Q_SLOTS:
|
||||
|
||||
void conversionMaintainsState() const;
|
||||
|
||||
void functorWithoutContext();
|
||||
void functorWithContextInThread();
|
||||
void receiverInThread();
|
||||
void destroyedContextObject();
|
||||
@ -145,6 +146,47 @@ void tst_QPermission::conversionMaintainsState() const
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func,
|
||||
typename T = std::void_t<decltype(qApp->requestPermission(std::declval<DummyPermission>(),
|
||||
std::declval<Func>()))>
|
||||
>
|
||||
void wrapRequestPermission(const QPermission &p, Func &&f)
|
||||
{
|
||||
qApp->requestPermission(p, std::forward<Func>(f));
|
||||
}
|
||||
|
||||
template <typename Functor>
|
||||
using CompatibleTest = decltype(wrapRequestPermission(std::declval<QPermission>(), std::declval<Functor>()));
|
||||
|
||||
|
||||
// Compile test for context-less functor overloads
|
||||
void tst_QPermission::functorWithoutContext()
|
||||
{
|
||||
int argc = 0;
|
||||
char *argv = nullptr;
|
||||
QCoreApplication app(argc, &argv);
|
||||
|
||||
DummyPermission dummy;
|
||||
#ifdef Q_OS_DARWIN
|
||||
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Could not find permission plugin for DummyPermission.*"));
|
||||
#endif
|
||||
|
||||
qApp->requestPermission(dummy, [](const QPermission &permission){
|
||||
QVERIFY(permission.value<DummyPermission>());
|
||||
});
|
||||
wrapRequestPermission(dummy, [](const QPermission &permission){
|
||||
QVERIFY(permission.value<DummyPermission>());
|
||||
});
|
||||
|
||||
auto compatible = [](const QPermission &) {};
|
||||
using Compatible = decltype(compatible);
|
||||
auto incompatible = [](const QString &) {};
|
||||
using Incompatible = decltype(incompatible);
|
||||
|
||||
static_assert(qxp::is_detected_v<CompatibleTest, Compatible>);
|
||||
static_assert(!qxp::is_detected_v<CompatibleTest, Incompatible>);
|
||||
}
|
||||
|
||||
void tst_QPermission::functorWithContextInThread()
|
||||
{
|
||||
int argc = 0;
|
||||
@ -209,6 +251,11 @@ void tst_QPermission::receiverInThread()
|
||||
|
||||
qApp->requestPermission(dummy, &receiver, &Receiver::handlePermission);
|
||||
QTRY_COMPARE(receiver.permissionReceiverThread, &receiverThread);
|
||||
|
||||
// compile tests: none of these work and the error output isn't horrible
|
||||
// qApp->requestPermission(dummy, &receiver, "&tst_QPermission::receiverInThread");
|
||||
// qApp->requestPermission(dummy, &receiver, &tst_QPermission::receiverInThread);
|
||||
// qApp->requestPermission(dummy, &receiver, &QObject::destroyed);
|
||||
}
|
||||
|
||||
void tst_QPermission::destroyedContextObject()
|
||||
|
@ -19,6 +19,7 @@ public:
|
||||
|
||||
private slots:
|
||||
void constructors();
|
||||
void conversion();
|
||||
void destructor();
|
||||
void assignment_operators();
|
||||
void equality_operators();
|
||||
@ -44,6 +45,54 @@ void tst_QPointer::constructors()
|
||||
QCOMPARE(p3, QPointer<QObject>(this));
|
||||
}
|
||||
|
||||
void tst_QPointer::conversion()
|
||||
{
|
||||
// copy-conversion:
|
||||
{
|
||||
QFile file;
|
||||
QPointer<QFile> pf = &file;
|
||||
QCOMPARE_EQ(pf, &file);
|
||||
QPointer<const QIODevice> pio = pf;
|
||||
QCOMPARE_EQ(pio, &file);
|
||||
QCOMPARE_EQ(pio.get(), &file);
|
||||
QCOMPARE_EQ(pio, pf);
|
||||
QCOMPARE_EQ(pio.get(), pf.get());
|
||||
|
||||
// reset
|
||||
pio = nullptr;
|
||||
QCOMPARE_EQ(pio, nullptr);
|
||||
QCOMPARE_EQ(pio.get(), nullptr);
|
||||
|
||||
// copy-assignment
|
||||
QCOMPARE_EQ(pf, &file);
|
||||
pio = pf;
|
||||
QCOMPARE_EQ(pio, &file);
|
||||
QCOMPARE_EQ(pio.get(), &file);
|
||||
QCOMPARE_EQ(pio, pf);
|
||||
QCOMPARE_EQ(pio.get(), pf.get());
|
||||
}
|
||||
// move-conversion:
|
||||
{
|
||||
QFile file;
|
||||
QPointer<QFile> pf = &file;
|
||||
QCOMPARE_EQ(pf, &file);
|
||||
QPointer<const QIODevice> pio = std::move(pf);
|
||||
QCOMPARE_EQ(pf, nullptr);
|
||||
QCOMPARE_EQ(pio, &file);
|
||||
QCOMPARE_EQ(pio.get(), &file);
|
||||
|
||||
// reset
|
||||
pio = nullptr;
|
||||
QCOMPARE_EQ(pio, nullptr);
|
||||
QCOMPARE_EQ(pio.get(), nullptr);
|
||||
|
||||
// move-assignment
|
||||
pio = QPointer<QFile>(&file);
|
||||
QCOMPARE_EQ(pio, &file);
|
||||
QCOMPARE_EQ(pio.get(), &file);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QPointer::destructor()
|
||||
{
|
||||
// Make two QPointer's to the same object
|
||||
|
@ -1827,13 +1827,14 @@ void tst_QProperty::propertyAdaptorBinding()
|
||||
QCOMPARE(object.fooChangedCount, 7);
|
||||
|
||||
// Check update group
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
source.setValue(23);
|
||||
source2.setValue(22);
|
||||
QCOMPARE(object.foo(), 43);
|
||||
QCOMPARE(dest1.value(), 43);
|
||||
QCOMPARE(object.fooChangedCount, 7);
|
||||
Qt::endPropertyUpdateGroup();
|
||||
{
|
||||
const QScopedPropertyUpdateGroup guard;
|
||||
source.setValue(23);
|
||||
source2.setValue(22);
|
||||
QCOMPARE(object.foo(), 43);
|
||||
QCOMPARE(dest1.value(), 43);
|
||||
QCOMPARE(object.fooChangedCount, 7);
|
||||
}
|
||||
QCOMPARE(object.foo(), 45);
|
||||
QCOMPARE(dest1.value(), 45);
|
||||
QCOMPARE(object.fooChangedCount, 8);
|
||||
@ -2130,27 +2131,29 @@ void tst_QProperty::groupedNotifications()
|
||||
QCOMPARE(nNotifications, 1);
|
||||
|
||||
expected = 2;
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
a = 1;
|
||||
QCOMPARE(b.value(), 0);
|
||||
QCOMPARE(c.value(), 0);
|
||||
QCOMPARE(d.value(), 0);
|
||||
QCOMPARE(nNotifications, 1);
|
||||
Qt::endPropertyUpdateGroup();
|
||||
{
|
||||
const QScopedPropertyUpdateGroup guard;
|
||||
a = 1;
|
||||
QCOMPARE(b.value(), 0);
|
||||
QCOMPARE(c.value(), 0);
|
||||
QCOMPARE(d.value(), 0);
|
||||
QCOMPARE(nNotifications, 1);
|
||||
}
|
||||
QCOMPARE(b.value(), 1);
|
||||
QCOMPARE(c.value(), 1);
|
||||
QCOMPARE(e.value(), 2);
|
||||
QCOMPARE(nNotifications, 2);
|
||||
|
||||
expected = 7;
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
a = 2;
|
||||
d = 3;
|
||||
QCOMPARE(b.value(), 1);
|
||||
QCOMPARE(c.value(), 1);
|
||||
QCOMPARE(d.value(), 3);
|
||||
QCOMPARE(nNotifications, 2);
|
||||
Qt::endPropertyUpdateGroup();
|
||||
{
|
||||
const QScopedPropertyUpdateGroup guard;
|
||||
a = 2;
|
||||
d = 3;
|
||||
QCOMPARE(b.value(), 1);
|
||||
QCOMPARE(c.value(), 1);
|
||||
QCOMPARE(d.value(), 3);
|
||||
QCOMPARE(nNotifications, 2);
|
||||
}
|
||||
QCOMPARE(b.value(), 2);
|
||||
QCOMPARE(c.value(), 2);
|
||||
QCOMPARE(e.value(), 7);
|
||||
@ -2173,10 +2176,11 @@ void tst_QProperty::groupedNotificationConsistency()
|
||||
j = 1;
|
||||
QVERIFY(!areEqual); // value changed runs before j = 1
|
||||
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
i = 2;
|
||||
j = 2;
|
||||
Qt::endPropertyUpdateGroup();
|
||||
{
|
||||
const QScopedPropertyUpdateGroup guard;
|
||||
i = 2;
|
||||
j = 2;
|
||||
}
|
||||
QVERIFY(areEqual); // value changed runs after everything has been evaluated
|
||||
}
|
||||
|
||||
@ -2311,6 +2315,8 @@ void tst_QProperty::selfBindingShouldNotCrash()
|
||||
|
||||
void tst_QProperty::qpropertyAlias()
|
||||
{
|
||||
#if QT_DEPRECATED_SINCE(6, 6)
|
||||
QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
|
||||
std::unique_ptr<QProperty<int>> i {new QProperty<int>};
|
||||
QPropertyAlias<int> alias(i.get());
|
||||
QVERIFY(alias.isValid());
|
||||
@ -2325,6 +2331,8 @@ void tst_QProperty::qpropertyAlias()
|
||||
QCOMPARE(alias.value(), 42);
|
||||
i.reset();
|
||||
QVERIFY(!alias.isValid());
|
||||
QT_WARNING_POP
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QProperty::scheduleNotify()
|
||||
|
@ -1,27 +0,0 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(QT_FEATURE_sharedmemory)
|
||||
#####################################################################
|
||||
## tst_qsharedmemory Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qsharedmemory
|
||||
SOURCES
|
||||
tst_qsharedmemory.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
)
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_extend_target(tst_qsharedmemory CONDITION LINUX
|
||||
LIBRARIES
|
||||
rt
|
||||
)
|
||||
add_subdirectory(producerconsumer)
|
||||
if(QT_FEATURE_process)
|
||||
add_dependencies(tst_qsharedmemory producerconsumer_helper)
|
||||
endif()
|
||||
endif()
|
@ -1,13 +0,0 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## producerconsumer_helper Binary:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test_helper(producerconsumer_helper
|
||||
SOURCES
|
||||
main.cpp
|
||||
LIBRARIES
|
||||
Qt::Test
|
||||
)
|
@ -1,173 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QSharedMemory>
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
#include <QTest>
|
||||
#include <stdio.h>
|
||||
|
||||
void set(QSharedMemory &sm, int pos, char value)
|
||||
{
|
||||
((char*)sm.data())[pos] = value;
|
||||
}
|
||||
|
||||
QChar get(QSharedMemory &sm, int i)
|
||||
{
|
||||
return QChar::fromLatin1(((char*)sm.data())[i]);
|
||||
}
|
||||
|
||||
int readonly_segfault()
|
||||
{
|
||||
QSharedMemory sharedMemory;
|
||||
sharedMemory.setKey("readonly_segfault");
|
||||
sharedMemory.create(1024, QSharedMemory::ReadOnly);
|
||||
sharedMemory.lock();
|
||||
set(sharedMemory, 0, 'a');
|
||||
sharedMemory.unlock();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int producer()
|
||||
{
|
||||
QSharedMemory producer;
|
||||
producer.setKey("market");
|
||||
|
||||
int size = 1024;
|
||||
if (!producer.create(size)) {
|
||||
if (producer.error() == QSharedMemory::AlreadyExists) {
|
||||
if (!producer.attach()) {
|
||||
qWarning() << "Could not attach to" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Could not create" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
// tell parent we're ready
|
||||
//qDebug("producer created and attached");
|
||||
puts("");
|
||||
fflush(stdout);
|
||||
|
||||
if (!producer.lock()) {
|
||||
qWarning() << "Could not lock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
set(producer, 0, 'Q');
|
||||
if (!producer.unlock()) {
|
||||
qWarning() << "Could not lock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
while (i < 5) {
|
||||
if (!producer.lock()) {
|
||||
qWarning() << "Could not lock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (get(producer, 0) == 'Q') {
|
||||
if (!producer.unlock()) {
|
||||
qWarning() << "Could not unlock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
QTest::qSleep(1);
|
||||
continue;
|
||||
}
|
||||
//qDebug() << "producer:" << i);
|
||||
++i;
|
||||
set(producer, 0, 'Q');
|
||||
if (!producer.unlock()) {
|
||||
qWarning() << "Could not unlock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
QTest::qSleep(1);
|
||||
}
|
||||
if (!producer.lock()) {
|
||||
qWarning() << "Could not lock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
set(producer, 0, 'E');
|
||||
if (!producer.unlock()) {
|
||||
qWarning() << "Could not unlock" << producer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
//qDebug("producer done");
|
||||
|
||||
// Sleep for a bit to let all consumers exit
|
||||
getchar();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int consumer()
|
||||
{
|
||||
QSharedMemory consumer;
|
||||
consumer.setKey("market");
|
||||
|
||||
//qDebug("consumer starting");
|
||||
int tries = 0;
|
||||
while (!consumer.attach()) {
|
||||
if (tries == 5000) {
|
||||
qWarning() << "consumer exiting, waiting too long";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
++tries;
|
||||
QTest::qSleep(1);
|
||||
}
|
||||
//qDebug("consumer attached");
|
||||
|
||||
|
||||
int i = 0;
|
||||
while (true) {
|
||||
if (!consumer.lock()) {
|
||||
qWarning() << "Could not lock" << consumer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (get(consumer, 0) == 'Q') {
|
||||
set(consumer, 0, ++i);
|
||||
//qDebug() << "consumer sets" << i;
|
||||
}
|
||||
if (get(consumer, 0) == 'E') {
|
||||
if (!consumer.unlock()) {
|
||||
qWarning() << "Could not unlock" << consumer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!consumer.unlock()) {
|
||||
qWarning() << "Could not unlock" << consumer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
QTest::qSleep(10);
|
||||
}
|
||||
|
||||
//qDebug("consumer detaching");
|
||||
if (!consumer.detach()) {
|
||||
qWarning() << "Could not detach" << consumer.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
QStringList arguments = app.arguments();
|
||||
if (app.arguments().size() != 2) {
|
||||
qWarning("Please call the helper with the function to call as argument");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
QString function = arguments.at(1);
|
||||
if (function == QLatin1String("readonly_segfault"))
|
||||
return readonly_segfault();
|
||||
else if (function == QLatin1String("producer"))
|
||||
return producer();
|
||||
else if (function == QLatin1String("consumer"))
|
||||
return consumer();
|
||||
else
|
||||
qWarning() << "Unknown function" << arguments.at(1);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -1,807 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#if QT_CONFIG(process)
|
||||
# include <QProcess>
|
||||
#endif
|
||||
#include <QSharedMemory>
|
||||
#include <QTest>
|
||||
#include <QThread>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#define EXISTING_SHARE "existing"
|
||||
#define EXISTING_SIZE 1024
|
||||
|
||||
Q_DECLARE_METATYPE(QSharedMemory::SharedMemoryError)
|
||||
Q_DECLARE_METATYPE(QSharedMemory::AccessMode)
|
||||
|
||||
class tst_QSharedMemory : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QSharedMemory();
|
||||
virtual ~tst_QSharedMemory();
|
||||
|
||||
public Q_SLOTS:
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
|
||||
private slots:
|
||||
// basics
|
||||
void constructor();
|
||||
void key_data();
|
||||
void key();
|
||||
void create_data();
|
||||
void create();
|
||||
void attach_data();
|
||||
void attach();
|
||||
void lock();
|
||||
|
||||
// custom edge cases
|
||||
#ifndef Q_OS_HPUX
|
||||
void removeWhileAttached();
|
||||
#endif
|
||||
void emptyMemory();
|
||||
#if !defined(Q_OS_WIN)
|
||||
void readOnly();
|
||||
#endif
|
||||
|
||||
// basics all together
|
||||
#ifndef Q_OS_HPUX
|
||||
void simpleProducerConsumer_data();
|
||||
void simpleProducerConsumer();
|
||||
void simpleDoubleProducerConsumer();
|
||||
#endif
|
||||
|
||||
// with threads
|
||||
void simpleThreadedProducerConsumer_data();
|
||||
void simpleThreadedProducerConsumer();
|
||||
|
||||
// with processes
|
||||
void simpleProcessProducerConsumer_data();
|
||||
void simpleProcessProducerConsumer();
|
||||
|
||||
// extreme cases
|
||||
void useTooMuchMemory();
|
||||
#if !defined(Q_OS_HPUX)
|
||||
void attachTooMuch();
|
||||
#endif
|
||||
|
||||
// unique keys
|
||||
void uniqueKey_data();
|
||||
void uniqueKey();
|
||||
|
||||
protected:
|
||||
int remove(const QString &key);
|
||||
|
||||
QString rememberKey(const QString &key)
|
||||
{
|
||||
if (key == EXISTING_SHARE)
|
||||
return key;
|
||||
if (!keys.contains(key)) {
|
||||
keys.append(key);
|
||||
remove(key);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
QStringList keys;
|
||||
QList<QSharedMemory*> jail;
|
||||
QSharedMemory *existingSharedMemory;
|
||||
|
||||
private:
|
||||
const QString m_helperBinary;
|
||||
};
|
||||
|
||||
tst_QSharedMemory::tst_QSharedMemory()
|
||||
: existingSharedMemory(0)
|
||||
, m_helperBinary("./producerconsumer_helper")
|
||||
{
|
||||
}
|
||||
|
||||
tst_QSharedMemory::~tst_QSharedMemory()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::init()
|
||||
{
|
||||
existingSharedMemory = new QSharedMemory(EXISTING_SHARE);
|
||||
if (!existingSharedMemory->create(EXISTING_SIZE)) {
|
||||
QCOMPARE(existingSharedMemory->error(), QSharedMemory::AlreadyExists);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::cleanup()
|
||||
{
|
||||
delete existingSharedMemory;
|
||||
qDeleteAll(jail.begin(), jail.end());
|
||||
jail.clear();
|
||||
|
||||
keys.append(EXISTING_SHARE);
|
||||
for (int i = 0; i < keys.size(); ++i) {
|
||||
QSharedMemory sm(keys.at(i));
|
||||
if (!sm.create(1024)) {
|
||||
//if (sm.error() != QSharedMemory::KeyError)
|
||||
// qWarning() << "test cleanup: remove failed:" << keys.at(i) << sm.error() << sm.errorString();
|
||||
sm.attach();
|
||||
sm.detach();
|
||||
remove(keys.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#include <private/qsharedmemory_p.h>
|
||||
#include <sys/types.h>
|
||||
#ifndef QT_POSIX_IPC
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif // QT_POSIX_IPC
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
int tst_QSharedMemory::remove(const QString &key)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
Q_UNUSED(key);
|
||||
return 0;
|
||||
#else
|
||||
// On unix the shared memory might exists from a previously failed test
|
||||
// or segfault, remove it it does
|
||||
if (key.isEmpty())
|
||||
return -1;
|
||||
|
||||
QString fileName = QSharedMemoryPrivate::makePlatformSafeKey(key);
|
||||
|
||||
#ifndef QT_POSIX_IPC
|
||||
// ftok requires that an actual file exists somewhere
|
||||
if (!QFile::exists(fileName)) {
|
||||
//qDebug() << "exits failed";
|
||||
return -2;
|
||||
}
|
||||
|
||||
int unix_key = ftok(fileName.toLatin1().constData(), 'Q');
|
||||
if (-1 == unix_key) {
|
||||
qDebug() << "ftok failed";
|
||||
return -3;
|
||||
}
|
||||
|
||||
int id = shmget(unix_key, 0, 0600);
|
||||
if (-1 == id) {
|
||||
qDebug() << "shmget failed" << strerror(errno);
|
||||
return -4;
|
||||
}
|
||||
|
||||
struct shmid_ds shmid_ds;
|
||||
if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
|
||||
qDebug() << "shmctl failed";
|
||||
return -5;
|
||||
}
|
||||
#else
|
||||
if (shm_unlink(QFile::encodeName(fileName).constData()) == -1) {
|
||||
if (errno != ENOENT) {
|
||||
qDebug() << "shm_unlink failed";
|
||||
return -5;
|
||||
}
|
||||
}
|
||||
#endif // QT_POSIX_IPC
|
||||
|
||||
return QFile::remove(fileName);
|
||||
#endif // Q_OS_WIN
|
||||
}
|
||||
|
||||
/*!
|
||||
Tests the default values
|
||||
*/
|
||||
void tst_QSharedMemory::constructor()
|
||||
{
|
||||
QSharedMemory sm;
|
||||
QCOMPARE(sm.key(), QString());
|
||||
QVERIFY(!sm.isAttached());
|
||||
QVERIFY(!sm.data());
|
||||
QCOMPARE(sm.size(), 0);
|
||||
QCOMPARE(sm.error(), QSharedMemory::NoError);
|
||||
QCOMPARE(sm.errorString(), QString());
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::key_data()
|
||||
{
|
||||
QTest::addColumn<QString>("constructorKey");
|
||||
QTest::addColumn<QString>("setKey");
|
||||
QTest::addColumn<QString>("setNativeKey");
|
||||
|
||||
QTest::newRow("null, null, null") << QString() << QString() << QString();
|
||||
QTest::newRow("one, null, null") << QString("one") << QString() << QString();
|
||||
QTest::newRow("null, one, null") << QString() << QString("one") << QString();
|
||||
QTest::newRow("null, null, one") << QString() << QString() << QString("one");
|
||||
QTest::newRow("one, two, null") << QString("one") << QString("two") << QString();
|
||||
QTest::newRow("one, null, two") << QString("one") << QString() << QString("two");
|
||||
QTest::newRow("null, one, two") << QString() << QString("one") << QString("two");
|
||||
QTest::newRow("one, two, three") << QString("one") << QString("two") << QString("three");
|
||||
QTest::newRow("invalid") << QString("o/e") << QString("t/o") << QString("|x");
|
||||
}
|
||||
|
||||
/*!
|
||||
Basic key testing
|
||||
*/
|
||||
void tst_QSharedMemory::key()
|
||||
{
|
||||
QFETCH(QString, constructorKey);
|
||||
QFETCH(QString, setKey);
|
||||
QFETCH(QString, setNativeKey);
|
||||
|
||||
QSharedMemory sm(constructorKey);
|
||||
QCOMPARE(sm.key(), constructorKey);
|
||||
QCOMPARE(sm.nativeKey().isEmpty(), constructorKey.isEmpty());
|
||||
sm.setKey(setKey);
|
||||
QCOMPARE(sm.key(), setKey);
|
||||
QCOMPARE(sm.nativeKey().isEmpty(), setKey.isEmpty());
|
||||
sm.setNativeKey(setNativeKey);
|
||||
QVERIFY(sm.key().isNull());
|
||||
QCOMPARE(sm.nativeKey(), setNativeKey);
|
||||
QCOMPARE(sm.isAttached(), false);
|
||||
|
||||
QCOMPARE(sm.error(), QSharedMemory::NoError);
|
||||
QCOMPARE(sm.errorString(), QString());
|
||||
QVERIFY(!sm.data());
|
||||
QCOMPARE(sm.size(), 0);
|
||||
|
||||
QCOMPARE(sm.detach(), false);
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::create_data()
|
||||
{
|
||||
QTest::addColumn<QString>("key");
|
||||
QTest::addColumn<int>("size");
|
||||
QTest::addColumn<bool>("canCreate");
|
||||
QTest::addColumn<QSharedMemory::SharedMemoryError>("error");
|
||||
|
||||
QTest::newRow("null key") << QString() << 1024
|
||||
<< false << QSharedMemory::KeyError;
|
||||
QTest::newRow("-1 size") << QString("negsize") << -1
|
||||
<< false << QSharedMemory::InvalidSize;
|
||||
QTest::newRow("nor size") << QString("norsize") << 1024
|
||||
<< true << QSharedMemory::NoError;
|
||||
QTest::newRow("already exists") << QString(EXISTING_SHARE) << EXISTING_SIZE
|
||||
<< false << QSharedMemory::AlreadyExists;
|
||||
}
|
||||
|
||||
/*!
|
||||
Basic create testing
|
||||
*/
|
||||
void tst_QSharedMemory::create()
|
||||
{
|
||||
QFETCH(QString, key);
|
||||
QFETCH(int, size);
|
||||
QFETCH(bool, canCreate);
|
||||
QFETCH(QSharedMemory::SharedMemoryError, error);
|
||||
|
||||
QSharedMemory sm(rememberKey(key));
|
||||
QCOMPARE(sm.create(size), canCreate);
|
||||
if (sm.error() != error)
|
||||
qDebug() << sm.errorString();
|
||||
QCOMPARE(sm.key(), key);
|
||||
if (canCreate) {
|
||||
QCOMPARE(sm.errorString(), QString());
|
||||
QVERIFY(sm.data() != 0);
|
||||
QVERIFY(sm.size() != 0);
|
||||
} else {
|
||||
QVERIFY(!sm.data());
|
||||
QVERIFY(sm.errorString() != QString());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::attach_data()
|
||||
{
|
||||
QTest::addColumn<QString>("key");
|
||||
QTest::addColumn<bool>("exists");
|
||||
QTest::addColumn<QSharedMemory::SharedMemoryError>("error");
|
||||
|
||||
QTest::newRow("null key") << QString() << false << QSharedMemory::KeyError;
|
||||
QTest::newRow("doesn't exists") << QString("doesntexists") << false << QSharedMemory::NotFound;
|
||||
|
||||
// HPUX doesn't allow for multiple attaches per process.
|
||||
#ifndef Q_OS_HPUX
|
||||
QTest::newRow("already exists") << QString(EXISTING_SHARE) << true << QSharedMemory::NoError;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Basic attach/detach testing
|
||||
*/
|
||||
void tst_QSharedMemory::attach()
|
||||
{
|
||||
QFETCH(QString, key);
|
||||
QFETCH(bool, exists);
|
||||
QFETCH(QSharedMemory::SharedMemoryError, error);
|
||||
|
||||
QSharedMemory sm(key);
|
||||
QCOMPARE(sm.attach(), exists);
|
||||
QCOMPARE(sm.isAttached(), exists);
|
||||
QCOMPARE(sm.error(), error);
|
||||
QCOMPARE(sm.key(), key);
|
||||
if (exists) {
|
||||
QVERIFY(sm.data() != 0);
|
||||
QVERIFY(sm.size() != 0);
|
||||
QCOMPARE(sm.errorString(), QString());
|
||||
QVERIFY(sm.detach());
|
||||
// Make sure detach doesn't screw up something and we can't re-attach.
|
||||
QVERIFY(sm.attach());
|
||||
QVERIFY(sm.data() != 0);
|
||||
QVERIFY(sm.size() != 0);
|
||||
QVERIFY(sm.detach());
|
||||
QCOMPARE(sm.size(), 0);
|
||||
QVERIFY(!sm.data());
|
||||
} else {
|
||||
QVERIFY(!sm.data());
|
||||
QCOMPARE(sm.size(), 0);
|
||||
QVERIFY(sm.errorString() != QString());
|
||||
QVERIFY(!sm.detach());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::lock()
|
||||
{
|
||||
QSharedMemory shm;
|
||||
QVERIFY(!shm.lock());
|
||||
QCOMPARE(shm.error(), QSharedMemory::LockError);
|
||||
|
||||
shm.setKey(rememberKey(QLatin1String("qsharedmemory")));
|
||||
|
||||
QVERIFY(!shm.lock());
|
||||
QCOMPARE(shm.error(), QSharedMemory::LockError);
|
||||
|
||||
QVERIFY(shm.create(100));
|
||||
QVERIFY(shm.lock());
|
||||
QTest::ignoreMessage(QtWarningMsg, "QSharedMemory::lock: already locked");
|
||||
QVERIFY(shm.lock());
|
||||
// we didn't unlock(), so ignore the warning from auto-detach in destructor
|
||||
QTest::ignoreMessage(QtWarningMsg, "QSharedMemory::lock: already locked");
|
||||
}
|
||||
|
||||
/*!
|
||||
Other shared memory are allowed to be attached after we remove,
|
||||
but new shared memory are not allowed to attach after a remove.
|
||||
*/
|
||||
// HPUX doesn't allow for multiple attaches per process.
|
||||
#ifndef Q_OS_HPUX
|
||||
void tst_QSharedMemory::removeWhileAttached()
|
||||
{
|
||||
rememberKey("one");
|
||||
|
||||
// attach 1
|
||||
QSharedMemory *smOne = new QSharedMemory(QLatin1String("one"));
|
||||
QVERIFY(smOne->create(1024));
|
||||
QVERIFY(smOne->isAttached());
|
||||
|
||||
// attach 2
|
||||
QSharedMemory *smTwo = new QSharedMemory(QLatin1String("one"));
|
||||
QVERIFY(smTwo->attach());
|
||||
QVERIFY(smTwo->isAttached());
|
||||
|
||||
// detach 1 and remove, remove one first to catch another error.
|
||||
delete smOne;
|
||||
delete smTwo;
|
||||
|
||||
#ifdef QT_POSIX_IPC
|
||||
// POSIX IPC doesn't guarantee that the shared memory is removed
|
||||
remove("one");
|
||||
#endif
|
||||
|
||||
// three shouldn't be able to attach
|
||||
QSharedMemory smThree(QLatin1String("one"));
|
||||
QVERIFY(!smThree.attach());
|
||||
QCOMPARE(smThree.error(), QSharedMemory::NotFound);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
The memory should be set to 0 after created.
|
||||
*/
|
||||
void tst_QSharedMemory::emptyMemory()
|
||||
{
|
||||
QSharedMemory sm(rememberKey(QLatin1String("voidland")));
|
||||
int size = 1024;
|
||||
QVERIFY(sm.create(size, QSharedMemory::ReadOnly));
|
||||
char *get = (char*)sm.data();
|
||||
char null = 0;
|
||||
for (int i = 0; i < size; ++i)
|
||||
QCOMPARE(get[i], null);
|
||||
}
|
||||
|
||||
/*!
|
||||
Verify that attach with ReadOnly is actually read only
|
||||
by writing to data and causing a segfault.
|
||||
*/
|
||||
// This test opens a crash dialog on Windows.
|
||||
#if !defined(Q_OS_WIN)
|
||||
void tst_QSharedMemory::readOnly()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#elif defined(Q_OS_MACOS)
|
||||
QSKIP("QTBUG-59936: Times out on macOS", SkipAll);
|
||||
#elif defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
|
||||
QSKIP("ASan prevents the crash this test is looking for.", SkipAll);
|
||||
#else
|
||||
rememberKey("readonly_segfault");
|
||||
// ### on windows disable the popup somehow
|
||||
QProcess p;
|
||||
p.start(m_helperBinary, QStringList("readonly_segfault"));
|
||||
p.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
p.waitForFinished();
|
||||
QCOMPARE(p.error(), QProcess::Crashed);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Keep making shared memory until the kernel stops us.
|
||||
*/
|
||||
void tst_QSharedMemory::useTooMuchMemory()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
bool success = true;
|
||||
int count = 0;
|
||||
while (success) {
|
||||
QString key = QLatin1String("maxmemorytest_") + QString::number(count++);
|
||||
QSharedMemory *sm = new QSharedMemory(rememberKey(key));
|
||||
QVERIFY(sm);
|
||||
jail.append(sm);
|
||||
int size = 32768 * 1024;
|
||||
success = sm->create(size);
|
||||
if (!success && sm->error() == QSharedMemory::AlreadyExists) {
|
||||
// left over from a crash, clean it up
|
||||
sm->attach();
|
||||
sm->detach();
|
||||
success = sm->create(size);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
QVERIFY(!sm->isAttached());
|
||||
QCOMPARE(sm->key(), key);
|
||||
QCOMPARE(sm->size(), 0);
|
||||
QVERIFY(!sm->data());
|
||||
if (sm->error() != QSharedMemory::OutOfResources)
|
||||
qDebug() << sm->error() << sm->errorString();
|
||||
// ### Linux won't return OutOfResources if there are not enough semaphores to use.
|
||||
QVERIFY(sm->error() == QSharedMemory::OutOfResources
|
||||
|| sm->error() == QSharedMemory::LockError);
|
||||
QVERIFY(sm->errorString() != QString());
|
||||
QVERIFY(!sm->attach());
|
||||
QVERIFY(!sm->detach());
|
||||
} else {
|
||||
QVERIFY(sm->isAttached());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Create one shared memory (government) and see how many other shared memories (wars) we can
|
||||
attach before the system runs out of resources.
|
||||
*/
|
||||
// HPUX doesn't allow for multiple attaches per process.
|
||||
#if !defined(Q_OS_HPUX)
|
||||
void tst_QSharedMemory::attachTooMuch()
|
||||
{
|
||||
QSKIP("disabled");
|
||||
|
||||
QSharedMemory government(rememberKey("government"));
|
||||
QVERIFY(government.create(1024));
|
||||
while (true) {
|
||||
QSharedMemory *war = new QSharedMemory(government.key());
|
||||
QVERIFY(war);
|
||||
jail.append(war);
|
||||
if (!war->attach()) {
|
||||
QVERIFY(!war->isAttached());
|
||||
QCOMPARE(war->key(), government.key());
|
||||
QCOMPARE(war->size(), 0);
|
||||
QVERIFY(!war->data());
|
||||
QCOMPARE(war->error(), QSharedMemory::OutOfResources);
|
||||
QVERIFY(war->errorString() != QString());
|
||||
QVERIFY(!war->detach());
|
||||
break;
|
||||
} else {
|
||||
QVERIFY(war->isAttached());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPUX doesn't allow for multiple attaches per process.
|
||||
#ifndef Q_OS_HPUX
|
||||
void tst_QSharedMemory::simpleProducerConsumer_data()
|
||||
{
|
||||
QTest::addColumn<QSharedMemory::AccessMode>("mode");
|
||||
|
||||
QTest::newRow("readonly") << QSharedMemory::ReadOnly;
|
||||
QTest::newRow("readwrite") << QSharedMemory::ReadWrite;
|
||||
}
|
||||
|
||||
/*!
|
||||
The basic consumer producer that rounds out the basic testing.
|
||||
If this fails then any muli-threading/process might fail (but be
|
||||
harder to debug)
|
||||
|
||||
This doesn't require nor test any locking system.
|
||||
*/
|
||||
void tst_QSharedMemory::simpleProducerConsumer()
|
||||
{
|
||||
QFETCH(QSharedMemory::AccessMode, mode);
|
||||
|
||||
rememberKey(QLatin1String("market"));
|
||||
QSharedMemory producer(QLatin1String("market"));
|
||||
QSharedMemory consumer(QLatin1String("market"));
|
||||
int size = 512;
|
||||
QVERIFY(producer.create(size));
|
||||
QVERIFY(consumer.attach(mode));
|
||||
|
||||
char *put = (char*)producer.data();
|
||||
char *get = (char*)consumer.data();
|
||||
// On Windows CE you always have ReadWrite access. Thus
|
||||
// ViewMapOfFile returns the same pointer
|
||||
QVERIFY(put != get);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
put[i] = 'Q';
|
||||
QCOMPARE(get[i], 'Q');
|
||||
}
|
||||
QVERIFY(consumer.detach());
|
||||
}
|
||||
#endif
|
||||
|
||||
// HPUX doesn't allow for multiple attaches per process.
|
||||
#ifndef Q_OS_HPUX
|
||||
void tst_QSharedMemory::simpleDoubleProducerConsumer()
|
||||
{
|
||||
rememberKey(QLatin1String("market"));
|
||||
QSharedMemory producer(QLatin1String("market"));
|
||||
int size = 512;
|
||||
QVERIFY(producer.create(size));
|
||||
QVERIFY(producer.detach());
|
||||
#ifdef QT_POSIX_IPC
|
||||
// POSIX IPC doesn't guarantee that the shared memory is removed
|
||||
remove("market");
|
||||
#endif
|
||||
QVERIFY(producer.create(size));
|
||||
|
||||
{
|
||||
QSharedMemory consumer(QLatin1String("market"));
|
||||
QVERIFY(consumer.attach());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
class Consumer : public QThread
|
||||
{
|
||||
|
||||
public:
|
||||
void run() override
|
||||
{
|
||||
QSharedMemory consumer(QLatin1String("market"));
|
||||
while (!consumer.attach()) {
|
||||
if (consumer.error() != QSharedMemory::NotFound)
|
||||
qDebug() << "consumer: failed to connect" << consumer.error() << consumer.errorString();
|
||||
QVERIFY(consumer.error() == QSharedMemory::NotFound || consumer.error() == QSharedMemory::KeyError);
|
||||
QTest::qWait(1);
|
||||
}
|
||||
|
||||
char *memory = (char*)consumer.data();
|
||||
|
||||
int i = 0;
|
||||
while (true) {
|
||||
if (!consumer.lock())
|
||||
break;
|
||||
if (memory[0] == 'Q')
|
||||
memory[0] = ++i;
|
||||
if (memory[0] == 'E') {
|
||||
memory[1]++;
|
||||
QVERIFY(consumer.unlock());
|
||||
break;
|
||||
}
|
||||
QVERIFY(consumer.unlock());
|
||||
QTest::qWait(1);
|
||||
}
|
||||
|
||||
QVERIFY(consumer.detach());
|
||||
}
|
||||
};
|
||||
|
||||
class Producer : public QThread
|
||||
{
|
||||
|
||||
public:
|
||||
Producer() : producer(QLatin1String("market"))
|
||||
{
|
||||
int size = 1024;
|
||||
if (!producer.create(size)) {
|
||||
// left over from a crash...
|
||||
if (producer.error() == QSharedMemory::AlreadyExists) {
|
||||
producer.attach();
|
||||
producer.detach();
|
||||
QVERIFY(producer.create(size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
|
||||
char *memory = (char*)producer.data();
|
||||
memory[1] = '0';
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
int i = 0;
|
||||
while (i < 5 && timer.elapsed() < 5000) {
|
||||
QVERIFY(producer.lock());
|
||||
if (memory[0] == 'Q') {
|
||||
QVERIFY(producer.unlock());
|
||||
QTest::qWait(1);
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
memory[0] = 'Q';
|
||||
QVERIFY(producer.unlock());
|
||||
QTest::qWait(1);
|
||||
}
|
||||
|
||||
// tell everyone to quit
|
||||
QVERIFY(producer.lock());
|
||||
memory[0] = 'E';
|
||||
QVERIFY(producer.unlock());
|
||||
|
||||
}
|
||||
|
||||
QSharedMemory producer;
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
void tst_QSharedMemory::simpleThreadedProducerConsumer_data()
|
||||
{
|
||||
QTest::addColumn<bool>("producerIsThread");
|
||||
QTest::addColumn<int>("threads");
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
QTest::newRow("1 consumer, producer is thread") << true << 1;
|
||||
QTest::newRow("1 consumer, producer is this") << false << 1;
|
||||
QTest::newRow("5 consumers, producer is thread") << true << 5;
|
||||
QTest::newRow("5 consumers, producer is this") << false << 5;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
The basic producer/consumer, but this time using threads.
|
||||
*/
|
||||
void tst_QSharedMemory::simpleThreadedProducerConsumer()
|
||||
{
|
||||
QFETCH(bool, producerIsThread);
|
||||
QFETCH(int, threads);
|
||||
rememberKey(QLatin1String("market"));
|
||||
|
||||
Producer p;
|
||||
QVERIFY(p.producer.isAttached());
|
||||
if (producerIsThread)
|
||||
p.start();
|
||||
|
||||
QList<Consumer*> consumers;
|
||||
for (int i = 0; i < threads; ++i) {
|
||||
consumers.append(new Consumer());
|
||||
consumers.last()->start();
|
||||
}
|
||||
|
||||
if (!producerIsThread)
|
||||
p.run();
|
||||
|
||||
p.wait(5000);
|
||||
while (!consumers.isEmpty()) {
|
||||
Consumer *c = consumers.first();
|
||||
QVERIFY(c->isFinished() || c->wait(5000));
|
||||
delete consumers.takeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::simpleProcessProducerConsumer_data()
|
||||
{
|
||||
#if QT_CONFIG(process)
|
||||
QTest::addColumn<int>("processes");
|
||||
int tries = 5;
|
||||
for (int i = 0; i < tries; ++i) {
|
||||
QTest::newRow("1 process") << 1;
|
||||
QTest::newRow("5 processes") << 5;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Create external processes that produce and consume.
|
||||
*/
|
||||
void tst_QSharedMemory::simpleProcessProducerConsumer()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#else
|
||||
QFETCH(int, processes);
|
||||
|
||||
QSKIP("This test is unstable: QTBUG-25655");
|
||||
|
||||
rememberKey("market");
|
||||
|
||||
QProcess producer;
|
||||
producer.start(m_helperBinary, QStringList("producer"));
|
||||
QVERIFY2(producer.waitForStarted(), "Could not start helper binary");
|
||||
QVERIFY2(producer.waitForReadyRead(), "Helper process failed to create shared memory segment: " +
|
||||
producer.readAllStandardError());
|
||||
|
||||
QList<QProcess*> consumers;
|
||||
unsigned int failedProcesses = 0;
|
||||
const QStringList consumerArguments = QStringList("consumer");
|
||||
for (int i = 0; i < processes; ++i) {
|
||||
QProcess *p = new QProcess;
|
||||
p->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
p->start(m_helperBinary, consumerArguments);
|
||||
if (p->waitForStarted(2000))
|
||||
consumers.append(p);
|
||||
else
|
||||
++failedProcesses;
|
||||
}
|
||||
|
||||
bool consumerFailed = false;
|
||||
|
||||
while (!consumers.isEmpty()) {
|
||||
QVERIFY(consumers.first()->waitForFinished(3000));
|
||||
if (consumers.first()->state() == QProcess::Running ||
|
||||
consumers.first()->exitStatus() != QProcess::NormalExit ||
|
||||
consumers.first()->exitCode() != 0) {
|
||||
consumerFailed = true;
|
||||
}
|
||||
delete consumers.takeFirst();
|
||||
}
|
||||
QCOMPARE(consumerFailed, false);
|
||||
QCOMPARE(failedProcesses, (unsigned int)(0));
|
||||
|
||||
// tell the producer to exit now
|
||||
producer.write("", 1);
|
||||
producer.waitForBytesWritten();
|
||||
QVERIFY(producer.waitForFinished(5000));
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::uniqueKey_data()
|
||||
{
|
||||
QTest::addColumn<QString>("key1");
|
||||
QTest::addColumn<QString>("key2");
|
||||
|
||||
QTest::newRow("null == null") << QString() << QString();
|
||||
QTest::newRow("key == key") << QString("key") << QString("key");
|
||||
QTest::newRow("key1 == key1") << QString("key1") << QString("key1");
|
||||
QTest::newRow("key != key1") << QString("key") << QString("key1");
|
||||
QTest::newRow("ke1y != key1") << QString("ke1y") << QString("key1");
|
||||
QTest::newRow("key1 != key2") << QString("key1") << QString("key2");
|
||||
QTest::newRow("Noël -> Nol") << QString::fromUtf8("N\xc3\xabl") << QString("Nol");
|
||||
}
|
||||
|
||||
void tst_QSharedMemory::uniqueKey()
|
||||
{
|
||||
QFETCH(QString, key1);
|
||||
QFETCH(QString, key2);
|
||||
|
||||
QSharedMemory sm1(key1);
|
||||
QSharedMemory sm2(key2);
|
||||
|
||||
bool setEqual = (key1 == key2);
|
||||
bool keyEqual = (sm1.key() == sm2.key());
|
||||
bool nativeEqual = (sm1.nativeKey() == sm2.nativeKey());
|
||||
|
||||
QCOMPARE(keyEqual, setEqual);
|
||||
QCOMPARE(nativeEqual, setEqual);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QSharedMemory)
|
||||
#include "tst_qsharedmemory.moc"
|
||||
|
@ -24,6 +24,7 @@
|
||||
# undef min
|
||||
#endif // Q_CC_MSVC
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class tst_QSocketNotifier : public QObject
|
||||
{
|
||||
@ -375,7 +376,7 @@ void tst_QSocketNotifier::asyncMultipleDatagram()
|
||||
&tst_QSocketNotifier::async_readDatagramSlot);
|
||||
|
||||
// activate socket notifiers
|
||||
QTestEventLoop::instance().enterLoopMSecs(100);
|
||||
QTestEventLoop::instance().enterLoop(100ms);
|
||||
|
||||
m_asyncSender->writeDatagram("1", makeNonAny(m_asyncReceiver->localAddress()), port);
|
||||
m_asyncSender->writeDatagram("2", makeNonAny(m_asyncReceiver->localAddress()), port);
|
||||
|
@ -1,16 +0,0 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qsystemsemaphore Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qsystemsemaphore
|
||||
SOURCES
|
||||
tst_qsystemsemaphore.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(acquirerelease)
|
||||
if(QT_FEATURE_process)
|
||||
add_dependencies(tst_qsystemsemaphore acquirerelease_helper)
|
||||
endif()
|
@ -1,13 +0,0 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## acquirerelease_helper Binary:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test_helper(acquirerelease_helper
|
||||
SOURCES
|
||||
main.cpp
|
||||
LIBRARIES
|
||||
Qt::Test
|
||||
)
|
@ -1,76 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
#include <QSystemSemaphore>
|
||||
|
||||
int acquire(int count = 1)
|
||||
{
|
||||
QSystemSemaphore sem("store");
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (!sem.acquire()) {
|
||||
qWarning() << "Could not acquire" << sem.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
qDebug("done aquiring");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int release()
|
||||
{
|
||||
QSystemSemaphore sem("store");
|
||||
if (!sem.release()) {
|
||||
qWarning() << "Could not release" << sem.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
qDebug("done releasing");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int acquirerelease()
|
||||
{
|
||||
QSystemSemaphore sem("store");
|
||||
if (!sem.acquire()) {
|
||||
qWarning() << "Could not acquire" << sem.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!sem.release()) {
|
||||
qWarning() << "Could not release" << sem.key();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
QStringList arguments = app.arguments();
|
||||
// binary name is not used here
|
||||
arguments.takeFirst();
|
||||
if (arguments.size() < 1) {
|
||||
qWarning("Please call the helper with the function to call as argument");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
QString function = arguments.takeFirst();
|
||||
if (function == QLatin1String("acquire")) {
|
||||
int count = 1;
|
||||
bool ok = true;
|
||||
if (arguments.size())
|
||||
count = arguments.takeFirst().toInt(&ok);
|
||||
if (!ok)
|
||||
count = 1;
|
||||
return acquire(count);
|
||||
} else if (function == QLatin1String("release")) {
|
||||
return release();
|
||||
} else if (function == QLatin1String("acquirerelease")) {
|
||||
return acquirerelease();
|
||||
} else {
|
||||
qWarning() << "Unknown function" << function;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -1,271 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
#if QT_CONFIG(process)
|
||||
#include <QProcess>
|
||||
#endif
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QSystemSemaphore>
|
||||
#include <QtCore/QTemporaryDir>
|
||||
|
||||
#define EXISTING_SHARE "existing"
|
||||
#define HELPERWAITTIME 10000
|
||||
|
||||
class tst_QSystemSemaphore : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QSystemSemaphore();
|
||||
|
||||
public Q_SLOTS:
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
private slots:
|
||||
void key_data();
|
||||
void key();
|
||||
|
||||
void basicacquire();
|
||||
void complexacquire();
|
||||
void release();
|
||||
|
||||
void basicProcesses();
|
||||
|
||||
void processes_data();
|
||||
void processes();
|
||||
|
||||
#if !defined(Q_OS_WIN) && !defined(QT_POSIX_IPC)
|
||||
void undo();
|
||||
#endif
|
||||
void initialValue();
|
||||
|
||||
private:
|
||||
QSystemSemaphore *existingLock;
|
||||
|
||||
const QString m_helperBinary;
|
||||
};
|
||||
|
||||
tst_QSystemSemaphore::tst_QSystemSemaphore()
|
||||
: m_helperBinary("./acquirerelease_helper")
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::init()
|
||||
{
|
||||
existingLock = new QSystemSemaphore(EXISTING_SHARE, 1, QSystemSemaphore::Create);
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::cleanup()
|
||||
{
|
||||
delete existingLock;
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::key_data()
|
||||
{
|
||||
QTest::addColumn<QString>("constructorKey");
|
||||
QTest::addColumn<QString>("setKey");
|
||||
|
||||
QTest::newRow("null, null") << QString() << QString();
|
||||
QTest::newRow("null, one") << QString() << QString("one");
|
||||
QTest::newRow("one, two") << QString("one") << QString("two");
|
||||
}
|
||||
|
||||
/*!
|
||||
Basic key testing
|
||||
*/
|
||||
void tst_QSystemSemaphore::key()
|
||||
{
|
||||
QFETCH(QString, constructorKey);
|
||||
QFETCH(QString, setKey);
|
||||
|
||||
QSystemSemaphore sem(constructorKey);
|
||||
QCOMPARE(sem.key(), constructorKey);
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
|
||||
sem.setKey(setKey);
|
||||
QCOMPARE(sem.key(), setKey);
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::basicacquire()
|
||||
{
|
||||
QSystemSemaphore sem("QSystemSemaphore_basicacquire", 1, QSystemSemaphore::Create);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::complexacquire()
|
||||
{
|
||||
QSystemSemaphore sem("QSystemSemaphore_complexacquire", 2, QSystemSemaphore::Create);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::release()
|
||||
{
|
||||
QSystemSemaphore sem("QSystemSemaphore_release", 0, QSystemSemaphore::Create);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.acquire());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QVERIFY(sem.release());
|
||||
QCOMPARE(sem.error(), QSystemSemaphore::NoError);
|
||||
QCOMPARE(sem.errorString(), QString());
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::basicProcesses()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#else
|
||||
QSystemSemaphore sem("store", 0, QSystemSemaphore::Create);
|
||||
|
||||
QProcess acquire;
|
||||
acquire.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
QProcess release;
|
||||
release.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
acquire.start(m_helperBinary, QStringList("acquire"));
|
||||
QVERIFY2(acquire.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
QCOMPARE(acquire.state(), QProcess::Running);
|
||||
acquire.kill();
|
||||
release.start(m_helperBinary, QStringList("release"));
|
||||
QVERIFY2(release.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
release.waitForFinished(HELPERWAITTIME);
|
||||
QCOMPARE(acquire.state(), QProcess::NotRunning);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::processes_data()
|
||||
{
|
||||
QTest::addColumn<int>("processes");
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
QTest::addRow("1 process (%d)", i) << 1;
|
||||
QTest::addRow("3 process (%d)", i) << 3;
|
||||
QTest::addRow("10 process (%d)", i) << 10;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QSystemSemaphore::processes()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#else
|
||||
QSystemSemaphore sem("store", 1, QSystemSemaphore::Create);
|
||||
|
||||
QFETCH(int, processes);
|
||||
QList<QString> scripts(processes, "acquirerelease");
|
||||
|
||||
QList<QProcess*> consumers;
|
||||
for (int i = 0; i < scripts.size(); ++i) {
|
||||
QProcess *p = new QProcess;
|
||||
p->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
consumers.append(p);
|
||||
p->start(m_helperBinary, QStringList(scripts.at(i)));
|
||||
}
|
||||
|
||||
while (!consumers.isEmpty()) {
|
||||
consumers.first()->waitForFinished();
|
||||
QCOMPARE(consumers.first()->exitStatus(), QProcess::NormalExit);
|
||||
QCOMPARE(consumers.first()->exitCode(), 0);
|
||||
delete consumers.takeFirst();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// This test only checks a system v unix behavior.
|
||||
#if !defined(Q_OS_WIN) && !defined(QT_POSIX_IPC)
|
||||
void tst_QSystemSemaphore::undo()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#else
|
||||
QSystemSemaphore sem("store", 1, QSystemSemaphore::Create);
|
||||
|
||||
QStringList acquireArguments = QStringList("acquire");
|
||||
QProcess acquire;
|
||||
acquire.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
acquire.start(m_helperBinary, acquireArguments);
|
||||
QVERIFY2(acquire.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
QVERIFY(acquire.state()== QProcess::NotRunning);
|
||||
|
||||
// At process exit the kernel should auto undo
|
||||
|
||||
acquire.start(m_helperBinary, acquireArguments);
|
||||
QVERIFY2(acquire.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
QVERIFY(acquire.state()== QProcess::NotRunning);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_QSystemSemaphore::initialValue()
|
||||
{
|
||||
#if !QT_CONFIG(process)
|
||||
QSKIP("No qprocess support", SkipAll);
|
||||
#else
|
||||
QSystemSemaphore sem("store", 1, QSystemSemaphore::Create);
|
||||
|
||||
QStringList acquireArguments = QStringList("acquire");
|
||||
QStringList releaseArguments = QStringList("release");
|
||||
QProcess acquire;
|
||||
acquire.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
QProcess release;
|
||||
release.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
acquire.start(m_helperBinary, acquireArguments);
|
||||
QVERIFY2(acquire.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
QVERIFY(acquire.state()== QProcess::NotRunning);
|
||||
|
||||
acquire.start(m_helperBinary, acquireArguments << QLatin1String("2"));
|
||||
QVERIFY2(acquire.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
QVERIFY(acquire.state()== QProcess::Running);
|
||||
acquire.kill();
|
||||
|
||||
release.start(m_helperBinary, releaseArguments);
|
||||
QVERIFY2(release.waitForStarted(), "Could not start helper binary");
|
||||
acquire.waitForFinished(HELPERWAITTIME);
|
||||
release.waitForFinished(HELPERWAITTIME);
|
||||
QVERIFY(acquire.state()== QProcess::NotRunning);
|
||||
#endif
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QSystemSemaphore)
|
||||
#include "tst_qsystemsemaphore.moc"
|
||||
|
@ -150,14 +150,14 @@ void tst_QTimer::singleShotNormalizes_data()
|
||||
void tst_QTimer::singleShotNormalizes()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
static constexpr int TestTimeout = 250 * 1000;
|
||||
static constexpr auto TestTimeout = 250ms;
|
||||
QFETCH(QByteArray, slotName);
|
||||
QEventLoop loop;
|
||||
|
||||
// control test: regular connection
|
||||
{
|
||||
QTimer timer;
|
||||
QVERIFY(QObject::connect(&timer, SIGNAL(timeout()), &QTestEventLoop::instance(), slotName.constData()));
|
||||
QVERIFY(QObject::connect(&timer, SIGNAL(timeout()), &QTestEventLoop::instance(), slotName));
|
||||
timer.setSingleShot(true);
|
||||
timer.start(1);
|
||||
QTestEventLoop::instance().enterLoop(TestTimeout);
|
||||
@ -165,20 +165,20 @@ void tst_QTimer::singleShotNormalizes()
|
||||
}
|
||||
|
||||
// non-zero time
|
||||
QTimer::singleShot(1, &QTestEventLoop::instance(), slotName.constData());
|
||||
QTimer::singleShot(1, &QTestEventLoop::instance(), slotName);
|
||||
QTestEventLoop::instance().enterLoop(TestTimeout);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
QTimer::singleShot(1ms, &QTestEventLoop::instance(), slotName.constData());
|
||||
QTimer::singleShot(1ms, &QTestEventLoop::instance(), slotName);
|
||||
QTestEventLoop::instance().enterLoop(TestTimeout);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
// zero time
|
||||
QTimer::singleShot(0, &QTestEventLoop::instance(), slotName.constData());
|
||||
QTimer::singleShot(0, &QTestEventLoop::instance(), slotName);
|
||||
QTestEventLoop::instance().enterLoop(TestTimeout);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
QTimer::singleShot(0ms, &QTestEventLoop::instance(), slotName.constData());
|
||||
QTimer::singleShot(0ms, &QTestEventLoop::instance(), slotName);
|
||||
QTestEventLoop::instance().enterLoop(TestTimeout);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
}
|
||||
@ -1175,11 +1175,22 @@ void tst_QTimer::crossThreadSingleShotToFunctor()
|
||||
DummyFunctor::callThread = nullptr;
|
||||
|
||||
QThread t;
|
||||
t.start();
|
||||
|
||||
std::unique_ptr<QObject> o(new QObject());
|
||||
o->moveToThread(&t);
|
||||
|
||||
QTimer::singleShot(timeout, o.get(), DummyFunctor());
|
||||
|
||||
// wait enough time for the timer to have timed out before the timer
|
||||
// could be start in the receiver's thread.
|
||||
QTest::qWait(10 + timeout * 10);
|
||||
t.start();
|
||||
t.wait();
|
||||
QCOMPARE(DummyFunctor::callThread, &t);
|
||||
|
||||
// continue with a stress test - the calling thread is busy, the
|
||||
// timer should still fire and no crashes.
|
||||
DummyFunctor::callThread = nullptr;
|
||||
t.start();
|
||||
for (int i = 0; i < 10000; i++)
|
||||
QTimer::singleShot(timeout, o.get(), DummyFunctor());
|
||||
|
||||
|
@ -5,6 +5,48 @@
|
||||
|
||||
#include <qvariant.h>
|
||||
|
||||
// don't assume <type_traits>
|
||||
template <typename T, typename U>
|
||||
constexpr inline bool my_is_same_v = false;
|
||||
template <typename T>
|
||||
constexpr inline bool my_is_same_v<T, T> = true;
|
||||
|
||||
#define CHECK_IMPL(func, arg, Variant, cvref, R) \
|
||||
static_assert(my_is_same_v<decltype( func < arg >(std::declval< Variant cvref >())), R cvref >)
|
||||
|
||||
#define CHECK_GET_IF(Variant, cvref) \
|
||||
CHECK_IMPL(get_if, int, Variant, cvref *, int)
|
||||
|
||||
#define CHECK_GET(Variant, cvref) \
|
||||
CHECK_IMPL(get, int, Variant, cvref, int)
|
||||
|
||||
CHECK_GET_IF(QVariant, /* unadorned */);
|
||||
CHECK_GET_IF(QVariant, const);
|
||||
|
||||
CHECK_GET(QVariant, &);
|
||||
CHECK_GET(QVariant, const &);
|
||||
CHECK_GET(QVariant, &&);
|
||||
CHECK_GET(QVariant, const &&);
|
||||
|
||||
// check for a type derived from QVariant:
|
||||
|
||||
struct MyVariant : QVariant
|
||||
{
|
||||
using QVariant::QVariant;
|
||||
};
|
||||
|
||||
CHECK_GET_IF(MyVariant, /* unadorned */);
|
||||
CHECK_GET_IF(MyVariant, const);
|
||||
|
||||
CHECK_GET(MyVariant, &);
|
||||
CHECK_GET(MyVariant, const &);
|
||||
CHECK_GET(MyVariant, &&);
|
||||
CHECK_GET(MyVariant, const &&);
|
||||
|
||||
#undef CHECK_GET_IF
|
||||
#undef CHECK_GET
|
||||
#undef CHECK_IMPL
|
||||
|
||||
#include <QTest>
|
||||
|
||||
// Please stick to alphabetic order.
|
||||
@ -44,7 +86,10 @@
|
||||
#include <variant>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
class CustomNonQObject;
|
||||
struct NonDefaultConstructible;
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct QVariantFromValueCompiles
|
||||
@ -342,7 +387,29 @@ private slots:
|
||||
void constructFromQtLT65MetaType();
|
||||
void copyNonDefaultConstructible();
|
||||
|
||||
void inplaceConstruct();
|
||||
void emplace();
|
||||
|
||||
void getIf_int() { getIf_impl(42); }
|
||||
void getIf_QString() { getIf_impl(u"string"_s); };
|
||||
void getIf_NonDefaultConstructible();
|
||||
void getIfSpecial();
|
||||
|
||||
void get_int() { get_impl(42); }
|
||||
void get_QString() { get_impl(u"string"_s); }
|
||||
void get_NonDefaultConstructible();
|
||||
|
||||
private:
|
||||
using StdVariant = std::variant<std::monostate,
|
||||
// list here all the types with which we instantiate getIf_impl:
|
||||
int,
|
||||
QString,
|
||||
NonDefaultConstructible
|
||||
>;
|
||||
template <typename T>
|
||||
void getIf_impl(T t) const;
|
||||
template <typename T>
|
||||
void get_impl(T t) const;
|
||||
template<typename T>
|
||||
void canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPointerAndValue_impl(const QByteArray &typeName);
|
||||
void dataStream_data(QDataStream::Version version);
|
||||
@ -5428,6 +5495,16 @@ void tst_QVariant::shouldDeleteVariantDataWorksForAssociative()
|
||||
|
||||
void tst_QVariant::fromStdVariant()
|
||||
{
|
||||
#define CHECK_EQUAL(lhs, rhs, type) do { \
|
||||
QCOMPARE(lhs.typeId(), rhs.typeId()); \
|
||||
if (lhs.isNull()) { \
|
||||
QVERIFY(rhs.isNull()); \
|
||||
} else { \
|
||||
QVERIFY(!rhs.isNull()); \
|
||||
QCOMPARE(get< type >(lhs), get< type >(rhs)); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
{
|
||||
typedef std::variant<int, bool> intorbool_t;
|
||||
intorbool_t stdvar = 5;
|
||||
@ -5435,21 +5512,38 @@ void tst_QVariant::fromStdVariant()
|
||||
QVERIFY(!qvar.isNull());
|
||||
QCOMPARE(qvar.typeId(), QMetaType::Int);
|
||||
QCOMPARE(qvar.value<int>(), std::get<int>(stdvar));
|
||||
{
|
||||
const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
|
||||
CHECK_EQUAL(qv2, qvar, int);
|
||||
}
|
||||
|
||||
stdvar = true;
|
||||
qvar = QVariant::fromStdVariant(stdvar);
|
||||
QVERIFY(!qvar.isNull());
|
||||
QCOMPARE(qvar.typeId(), QMetaType::Bool);
|
||||
QCOMPARE(qvar.value<bool>(), std::get<bool>(stdvar));
|
||||
{
|
||||
const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
|
||||
CHECK_EQUAL(qv2, qvar, bool);
|
||||
}
|
||||
}
|
||||
{
|
||||
std::variant<std::monostate, int> stdvar;
|
||||
QVariant qvar = QVariant::fromStdVariant(stdvar);
|
||||
QVERIFY(!qvar.isValid());
|
||||
{
|
||||
const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
|
||||
CHECK_EQUAL(qv2, qvar, int); // fake type, they're empty
|
||||
}
|
||||
stdvar = -4;
|
||||
qvar = QVariant::fromStdVariant(stdvar);
|
||||
QVERIFY(!qvar.isNull());
|
||||
QCOMPARE(qvar.typeId(), QMetaType::Int);
|
||||
QCOMPARE(qvar.value<int>(), std::get<int>(stdvar));
|
||||
{
|
||||
const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
|
||||
CHECK_EQUAL(qv2, qvar, int);
|
||||
}
|
||||
}
|
||||
{
|
||||
std::variant<int, bool, QChar> stdvar = QChar::fromLatin1(' ');
|
||||
@ -5457,7 +5551,25 @@ void tst_QVariant::fromStdVariant()
|
||||
QVERIFY(!qvar.isNull());
|
||||
QCOMPARE(qvar.typeId(), QMetaType::QChar);
|
||||
QCOMPARE(qvar.value<QChar>(), std::get<QChar>(stdvar));
|
||||
{
|
||||
const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
|
||||
CHECK_EQUAL(qv2, qvar, QChar);
|
||||
}
|
||||
}
|
||||
// rvalue fromStdVariant() actually moves:
|
||||
{
|
||||
const auto foo = u"foo"_s;
|
||||
std::variant<QString, QByteArray> stdvar = foo;
|
||||
QVariant qvar = QVariant::fromStdVariant(std::move(stdvar));
|
||||
const auto ps = get_if<QString>(&stdvar);
|
||||
QVERIFY(ps);
|
||||
QVERIFY(ps->isNull()); // QString was moved from
|
||||
QVERIFY(!qvar.isNull());
|
||||
QCOMPARE(qvar.typeId(), QMetaType::QString);
|
||||
QCOMPARE(get<QString>(qvar), foo);
|
||||
}
|
||||
|
||||
#undef CHECK_EQUAL
|
||||
}
|
||||
|
||||
void tst_QVariant::qt4UuidDataStream()
|
||||
@ -5641,6 +5753,16 @@ void tst_QVariant::canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPoi
|
||||
#undef ADD_TEST_IMPL
|
||||
}
|
||||
|
||||
struct MoveTester
|
||||
{
|
||||
bool wasMoved = false;
|
||||
MoveTester() = default;
|
||||
MoveTester(const MoveTester &) {}; // non-trivial on purpose
|
||||
MoveTester(MoveTester &&other) { other.wasMoved = true; }
|
||||
MoveTester& operator=(const MoveTester &) = default;
|
||||
MoveTester& operator=(MoveTester &&other) {other.wasMoved = true; return *this;}
|
||||
};
|
||||
|
||||
void tst_QVariant::moveOperations()
|
||||
{
|
||||
{
|
||||
@ -5662,6 +5784,19 @@ void tst_QVariant::moveOperations()
|
||||
v = QVariant::fromValue(list);
|
||||
v2 = std::move(v);
|
||||
QVERIFY(v2.value<std::list<int>>() == list);
|
||||
|
||||
{
|
||||
MoveTester tester;
|
||||
QVariant::fromValue(tester);
|
||||
QVERIFY(!tester.wasMoved);
|
||||
QVariant::fromValue(std::move(tester));
|
||||
QVERIFY(tester.wasMoved);
|
||||
}
|
||||
{
|
||||
const MoveTester tester;
|
||||
QVariant::fromValue(std::move(tester));
|
||||
QVERIFY(!tester.wasMoved); // we don't want to move from const variables
|
||||
}
|
||||
}
|
||||
|
||||
class NoMetaObject : public QObject {};
|
||||
@ -5816,14 +5951,317 @@ void tst_QVariant::copyNonDefaultConstructible()
|
||||
QVERIFY(var.constData() != &ndc);
|
||||
|
||||
// qvariant_cast<T> and QVariant::value<T> don't compile
|
||||
QCOMPARE(*static_cast<const NonDefaultConstructible *>(var.constData()), ndc);
|
||||
QCOMPARE(get<NonDefaultConstructible>(std::as_const(var)), ndc);
|
||||
|
||||
QVariant var2 = var;
|
||||
var2.detach(); // force another copy
|
||||
QVERIFY(var2.isDetached());
|
||||
QVERIFY(var2.constData() != var.constData());
|
||||
QCOMPARE(get<NonDefaultConstructible>(std::as_const(var2)),
|
||||
get<NonDefaultConstructible>(std::as_const(var)));
|
||||
QCOMPARE(var2, var);
|
||||
}
|
||||
|
||||
void tst_QVariant::inplaceConstruct()
|
||||
{
|
||||
{
|
||||
NonDefaultConstructible ndc(42);
|
||||
QVariant var(std::in_place_type<NonDefaultConstructible>, 42);
|
||||
QVERIFY(get_if<NonDefaultConstructible>(&var));
|
||||
QCOMPARE(get<NonDefaultConstructible>(var), ndc);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<int> vec {1, 2, 3, 4};
|
||||
QVariant var(std::in_place_type<std::vector<int>>, {1, 2, 3, 4});
|
||||
QVERIFY(get_if<std::vector<int>>(&var));
|
||||
QCOMPARE(get<std::vector<int>>(var), vec);
|
||||
}
|
||||
}
|
||||
|
||||
struct LargerThanInternalQVariantStorage {
|
||||
char data[6 * sizeof(void *)];
|
||||
};
|
||||
|
||||
struct alignas(256) LargerThanInternalQVariantStorageOveraligned {
|
||||
char data[6 * sizeof(void *)];
|
||||
};
|
||||
|
||||
struct alignas(128) SmallerAlignmentEvenLargerSize {
|
||||
char data[17 * sizeof(void *)];
|
||||
};
|
||||
|
||||
void tst_QVariant::emplace()
|
||||
{
|
||||
{
|
||||
// can emplace non default constructible + can emplace on null variant
|
||||
NonDefaultConstructible ndc(42);
|
||||
QVariant var;
|
||||
var.emplace<NonDefaultConstructible>(42);
|
||||
QVERIFY(get_if<NonDefaultConstructible>(&var));
|
||||
QCOMPARE(get<NonDefaultConstructible>(var), ndc);
|
||||
}
|
||||
{
|
||||
// can emplace using ctor taking initializer_list
|
||||
QVariant var;
|
||||
var.emplace<std::vector<int>>({0, 1, 2, 3, 4});
|
||||
auto vecPtr = get_if<std::vector<int>>(&var);
|
||||
QVERIFY(vecPtr);
|
||||
QCOMPARE(vecPtr->size(), 5U);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
QCOMPARE(vecPtr->at(size_t(i)), i);
|
||||
}
|
||||
// prequisites for the test
|
||||
QCOMPARE_LE(sizeof(std::vector<int>), sizeof(std::string));
|
||||
QCOMPARE(alignof(std::vector<int>), alignof(std::string));
|
||||
{
|
||||
// emplace can reuse storage
|
||||
auto var = QVariant::fromValue(std::string{});
|
||||
QVERIFY(var.data_ptr().is_shared);
|
||||
auto data = var.constData();
|
||||
std::vector<int> &vec = var.emplace<std::vector<int>>(3, 42);
|
||||
/* alignment is the same, so the pointer is exactly the same;
|
||||
no offset change */
|
||||
auto expected = std::vector<int>{42, 42, 42};
|
||||
QCOMPARE(get_if<std::vector<int>>(&var), &vec);
|
||||
QCOMPARE(get<std::vector<int>>(var), expected);
|
||||
QCOMPARE(var.constData(), data);
|
||||
}
|
||||
{
|
||||
// emplace can't reuse storage if the variant is shared
|
||||
auto var = QVariant::fromValue(std::string{});
|
||||
[[maybe_unused]] QVariant causesSharing = var;
|
||||
QVERIFY(var.data_ptr().is_shared);
|
||||
auto data = var.constData();
|
||||
var.emplace<std::vector<int>>(3, 42);
|
||||
auto expected = std::vector<int>{42, 42, 42};
|
||||
QVERIFY(get_if<std::vector<int>>(&var));
|
||||
QCOMPARE(get<std::vector<int>>(var), expected);
|
||||
QCOMPARE_NE(var.constData(), data);
|
||||
}
|
||||
{
|
||||
// emplace puts element into the correct place - non-shared
|
||||
QVERIFY(QVariant::Private::canUseInternalSpace(QMetaType::fromType<QString>().iface()));
|
||||
QVariant var;
|
||||
var.emplace<QString>(QChar('x'));
|
||||
QVERIFY(!var.data_ptr().is_shared);
|
||||
}
|
||||
{
|
||||
// emplace puts element into the correct place - shared
|
||||
QVERIFY(!QVariant::Private::canUseInternalSpace(QMetaType::fromType<std::string>().iface()));
|
||||
QVariant var;
|
||||
var.emplace<std::string>(42, 'x');
|
||||
QVERIFY(var.data_ptr().is_shared);
|
||||
}
|
||||
{
|
||||
// emplace does not reuse the storage if alignment is too large
|
||||
auto iface = QMetaType::fromType<LargerThanInternalQVariantStorage>().iface();
|
||||
QVERIFY(!QVariant::Private::canUseInternalSpace(iface));
|
||||
auto var = QVariant::fromValue(LargerThanInternalQVariantStorage{});
|
||||
auto data = var.constData();
|
||||
var.emplace<LargerThanInternalQVariantStorageOveraligned>();
|
||||
QCOMPARE_NE(var.constData(), data);
|
||||
}
|
||||
{
|
||||
// emplace does reuse the storage if new alignment and size are together small enough
|
||||
auto iface = QMetaType::fromType<LargerThanInternalQVariantStorageOveraligned>().iface();
|
||||
QVERIFY(!QVariant::Private::canUseInternalSpace(iface));
|
||||
auto var = QVariant::fromValue(LargerThanInternalQVariantStorageOveraligned{});
|
||||
auto data = var.constData();
|
||||
var.emplace<SmallerAlignmentEvenLargerSize>();
|
||||
// no exact match below - the alignment is after all different
|
||||
QCOMPARE_LE(quintptr(var.constData()), quintptr(data));
|
||||
QCOMPARE_LE(quintptr(var.constData()),
|
||||
quintptr(data) + sizeof(LargerThanInternalQVariantStorageOveraligned));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QVariant::getIf_NonDefaultConstructible()
|
||||
{
|
||||
getIf_impl(NonDefaultConstructible{42});
|
||||
}
|
||||
|
||||
void tst_QVariant::getIfSpecial()
|
||||
{
|
||||
QVariant v{QString{}}; // used to be a null QVariant in Qt 5
|
||||
QCOMPARE_NE(get_if<QString>(&v), nullptr); // not anymore...
|
||||
}
|
||||
|
||||
void tst_QVariant::get_NonDefaultConstructible()
|
||||
{
|
||||
get_impl(NonDefaultConstructible{42});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T mutate(const T &t) { return t + t; }
|
||||
template <>
|
||||
NonDefaultConstructible mutate(const NonDefaultConstructible &t)
|
||||
{
|
||||
return NonDefaultConstructible{t.i + t.i};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QVariant make_null_QVariant_of_type()
|
||||
{
|
||||
return QVariant(QMetaType::fromType<T>());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void tst_QVariant::getIf_impl(T t) const
|
||||
{
|
||||
QVariant v = QVariant::fromValue(t);
|
||||
|
||||
QVariant null;
|
||||
QVERIFY(null.isNull());
|
||||
|
||||
[[maybe_unused]]
|
||||
QVariant nulT;
|
||||
if constexpr (std::is_default_constructible_v<T>) {
|
||||
// typed null QVariants don't work with non-default-constuctable types
|
||||
nulT = make_null_QVariant_of_type<T>();
|
||||
QVERIFY(nulT.isNull());
|
||||
}
|
||||
|
||||
QVariant date = QDate(2023, 3, 3);
|
||||
static_assert(!std::is_same_v<T, QDate>);
|
||||
|
||||
// for behavioral comparison:
|
||||
StdVariant stdn = {}, stdv = t;
|
||||
|
||||
// returns nullptr on type mismatch:
|
||||
{
|
||||
// const
|
||||
QCOMPARE_EQ(get_if<T>(&std::as_const(stdn)), nullptr);
|
||||
QCOMPARE_EQ(get_if<T>(&std::as_const(date)), nullptr);
|
||||
// mutable
|
||||
QCOMPARE_EQ(get_if<T>(&stdn), nullptr);
|
||||
QCOMPARE_EQ(get_if<T>(&date), nullptr);
|
||||
}
|
||||
|
||||
// returns nullptr on null variant (QVariant only):
|
||||
{
|
||||
QCOMPARE_EQ(get_if<T>(&std::as_const(null)), nullptr);
|
||||
QCOMPARE_EQ(get_if<T>(&null), nullptr);
|
||||
if constexpr (std::is_default_constructible_v<T>) {
|
||||
// const access return nullptr
|
||||
QCOMPARE_EQ(get_if<T>(&std::as_const(nulT)), nullptr);
|
||||
// but mutable access makes typed null QVariants non-null (like data())
|
||||
QCOMPARE_NE(get_if<T>(&nulT), nullptr);
|
||||
QVERIFY(!nulT.isNull());
|
||||
nulT = make_null_QVariant_of_type<T>(); // reset to null state
|
||||
}
|
||||
}
|
||||
|
||||
// const access:
|
||||
{
|
||||
auto ps = get_if<T>(&std::as_const(stdv));
|
||||
static_assert(std::is_same_v<decltype(ps), const T*>);
|
||||
QCOMPARE_NE(ps, nullptr);
|
||||
QCOMPARE_EQ(*ps, t);
|
||||
|
||||
auto pv = get_if<T>(&std::as_const(v));
|
||||
static_assert(std::is_same_v<decltype(ps), const T*>);
|
||||
QCOMPARE_NE(pv, nullptr);
|
||||
QCOMPARE_EQ(*pv, t);
|
||||
}
|
||||
|
||||
// mutable access:
|
||||
{
|
||||
T t2 = mutate(t);
|
||||
|
||||
auto ps = get_if<T>(&stdv);
|
||||
static_assert(std::is_same_v<decltype(ps), T*>);
|
||||
QCOMPARE_NE(ps, nullptr);
|
||||
QCOMPARE_EQ(*ps, t);
|
||||
*ps = t2;
|
||||
auto ps2 = get_if<T>(&stdv);
|
||||
QCOMPARE_NE(ps2, nullptr);
|
||||
QCOMPARE_EQ(*ps2, t2);
|
||||
|
||||
auto pv = get_if<T>(&v);
|
||||
static_assert(std::is_same_v<decltype(pv), T*>);
|
||||
QCOMPARE_NE(pv, nullptr);
|
||||
QCOMPARE_EQ(*pv, t);
|
||||
*pv = t2;
|
||||
auto pv2 = get_if<T>(&v);
|
||||
QCOMPARE_NE(pv2, nullptr);
|
||||
QCOMPARE_EQ(*pv2, t2);
|
||||
|
||||
// typed null QVariants become non-null (data() behavior):
|
||||
if constexpr (std::is_default_constructible_v<T>) {
|
||||
QVERIFY(nulT.isNull());
|
||||
auto pn = get_if<T>(&nulT);
|
||||
QVERIFY(!nulT.isNull());
|
||||
static_assert(std::is_same_v<decltype(pn), T*>);
|
||||
QCOMPARE_NE(pn, nullptr);
|
||||
QCOMPARE_EQ(*pn, T{});
|
||||
*pn = t2;
|
||||
auto pn2 = get_if<T>(&nulT);
|
||||
QCOMPARE_NE(pn2, nullptr);
|
||||
QCOMPARE_EQ(*pn2, t2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void tst_QVariant::get_impl(T t) const
|
||||
{
|
||||
QVariant v = QVariant::fromValue(t);
|
||||
|
||||
// for behavioral comparison:
|
||||
StdVariant stdv = t;
|
||||
|
||||
#define FOR_EACH_CVREF(op) \
|
||||
op(/*unadorned*/, &&) \
|
||||
op(&, &) \
|
||||
op(&&, &&) \
|
||||
op(const, const &&) \
|
||||
op(const &, const &) \
|
||||
op(const &&, const &&) \
|
||||
/* end */
|
||||
|
||||
|
||||
#define CHECK_RETURN_TYPE_OF(Variant, cvref_in, cvref_out) \
|
||||
static_assert(std::is_same_v< \
|
||||
decltype(get<T>(std::declval<Variant cvref_in >())), \
|
||||
T cvref_out \
|
||||
>); \
|
||||
/* end */
|
||||
#define CHECK_RETURN_TYPE(cvref_in, cvref_out) \
|
||||
CHECK_RETURN_TYPE_OF(StdVariant, cvref_in, cvref_out) \
|
||||
CHECK_RETURN_TYPE_OF(QVariant, cvref_in, cvref_out) \
|
||||
/* end */
|
||||
FOR_EACH_CVREF(CHECK_RETURN_TYPE)
|
||||
#undef CHECK_RETURN_TYPE
|
||||
|
||||
#undef FOR_EACH_CVREF
|
||||
|
||||
// const access:
|
||||
{
|
||||
auto &&rs = get<T>(std::as_const(stdv));
|
||||
QCOMPARE_EQ(rs, t);
|
||||
|
||||
auto &&rv = get<T>(std::as_const(v));
|
||||
QCOMPARE_EQ(rv, t);
|
||||
}
|
||||
|
||||
// mutable access:
|
||||
{
|
||||
T t2 = mutate(t);
|
||||
|
||||
auto &&rs = get<T>(stdv);
|
||||
QCOMPARE_EQ(rs, t);
|
||||
rs = t2;
|
||||
auto &&rs2 = get<T>(stdv);
|
||||
QCOMPARE_EQ(rs2, t2);
|
||||
|
||||
auto &&rv = get<T>(v);
|
||||
QCOMPARE_EQ(rv, t);
|
||||
rv = t2;
|
||||
auto &&rv2 = get<T>(v);
|
||||
QCOMPARE_EQ(rv2, t2);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QVariant)
|
||||
#include "tst_qvariant.moc"
|
||||
|
Reference in New Issue
Block a user