qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,58 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(qapplicationstatic)
add_subdirectory(qcoreapplication)
add_subdirectory(qdeadlinetimer)
add_subdirectory(qelapsedtimer)
add_subdirectory(qmath)
add_subdirectory(qmetacontainer)
add_subdirectory(qmetaobject)
add_subdirectory(qmetaobjectbuilder)
add_subdirectory(qmetamethod)
add_subdirectory(qmetaproperty)
add_subdirectory(qmetaenum)
add_subdirectory(qsignalblocker)
add_subdirectory(qsignalmapper)
add_subdirectory(qtimer)
add_subdirectory(qtranslator)
# QTBUG-88135
if(NOT ANDROID)
add_subdirectory(qeventdispatcher)
endif()
if(TARGET Qt::Network)
add_subdirectory(qeventloop)
endif()
if(TARGET Qt::Gui)
add_subdirectory(qmetatype)
add_subdirectory(qmimedata)
add_subdirectory(qpointer)
add_subdirectory(qvariant)
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)
endif()
if(QT_FEATURE_permissions)
add_subdirectory(qpermission)
endif()
if(QT_FEATURE_private_tests)
add_subdirectory(qproperty)
endif()
if(ANDROID)
add_subdirectory(qjnienvironment)
add_subdirectory(qjniobject)
add_subdirectory(qjnitypes)
endif()

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qapplicationstatic Test:
#####################################################################
qt_internal_add_test(tst_qapplicationstatic
SOURCES
tst_qapplicationstatic.cpp
)

View File

@ -0,0 +1,40 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QPointer>
#include "QtCore/qapplicationstatic.h"
Q_APPLICATION_STATIC(QObject, tstObject)
class tst_qapplicationstatic : public QObject
{
Q_OBJECT
private slots:
void testCreateMultipleApplications() const;
};
void tst_qapplicationstatic::testCreateMultipleApplications() const
{
for (int i = 0; i < 5; i++) {
int argc = 1;
char *argv[] = { (char *)"tst_qapplicationstatic" };
auto app = new QCoreApplication(argc, argv);
QVERIFY(tstObject);
QPointer<QObject> tstObjectPointer(tstObject);
QVERIFY(tstObjectPointer.get());
QVERIFY2(tstObject->objectName().isEmpty(), "Got QObject from previous iteration, not correctly recreated");
tstObject->setObjectName(QStringLiteral("tstObject"));
QVERIFY(!tstObject->objectName().isEmpty());
delete app;
QVERIFY2(!tstObjectPointer.get(), "QObject wasn't destroyed on QCoreApplication destruction");
}
}
QTEST_APPLESS_MAIN(tst_qapplicationstatic)
#include "tst_qapplicationstatic.moc"

View File

@ -0,0 +1,33 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(NOT QT_FEATURE_private_tests)
return()
endif()
#####################################################################
## tst_qcoreapplication Test:
#####################################################################
if (WIN32)
set(target_version "1.2.3.4")
else()
set(target_version "1.2.3")
endif()
qt_internal_add_test(tst_qcoreapplication
VERSION ${target_version}
SOURCES
tst_qcoreapplication.cpp tst_qcoreapplication.h
LIBRARIES
Qt::CorePrivate
)
if (APPLE)
set_property(TARGET tst_qcoreapplication PROPERTY MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist")
set_property(TARGET tst_qcoreapplication PROPERTY PROPERTY MACOSX_BUNDLE TRUE)
endif()
if (ANDROID)
set_property(TARGET tst_qcoreapplication PROPERTY QT_ANDROID_VERSION_NAME ${target_version})
endif()

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleVersion</key>
<string>1.2.3</string>
</dict>
</plist>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef TST_QCOREAPPLICATION_H
#define TST_QCOREAPPLICATION_H
#include <QtCore/QtCore>
class tst_QCoreApplication: public QObject
{
Q_OBJECT
private slots:
void sendEventsOnProcessEvents(); // this must be the first test
void getSetCheck();
void qAppName();
void qAppVersion();
void argc();
void postEvent();
void removePostedEvents();
#if QT_CONFIG(thread)
void deliverInDefinedOrder();
#endif
void applicationPid();
#ifdef QT_BUILD_INTERNAL
void globalPostedEventsCount();
#endif
void processEventsAlwaysSendsPostedEvents();
#ifdef Q_OS_WIN
void sendPostedEventsInNativeLoop();
#endif
void quit();
void reexec();
void execAfterExit();
void eventLoopExecAfterExit();
void customEventDispatcher();
void testQuitLock();
void QTBUG31606_QEventDestructorDeadLock();
void applicationEventFilters_mainThread();
void applicationEventFilters_auxThread();
void threadedEventDelivery_data();
void threadedEventDelivery();
void testTrWithPercantegeAtTheEnd();
#if QT_CONFIG(library)
void addRemoveLibPaths();
#endif
void theMainThread();
};
#endif // TST_QCOREAPPLICATION_H

View File

@ -0,0 +1,2 @@
[stdchrono]
windows clang ci

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qdeadlinetimer Test:
#####################################################################
qt_internal_add_test(tst_qdeadlinetimer
SOURCES
tst_qdeadlinetimer.cpp
)

View File

@ -0,0 +1,751 @@
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/QString>
#include <QtCore/QTime>
#include <QtCore/QDeadlineTimer>
#include <QtCore/QElapsedTimer>
#include <QTest>
#include <QTimer>
#include <chrono>
#include <inttypes.h>
static const int minResolution = 400; // the minimum resolution for the tests
QT_BEGIN_NAMESPACE
namespace QTest {
template<> char *toString(const QDeadlineTimer &dt)
{
if (dt.isForever())
return qstrdup("QDeadlineTimer::Forever");
qint64 deadline = dt.deadlineNSecs();
char *buf = new char[256];
qsnprintf(buf, 256, "%lld.%09d%s",
deadline / 1000 / 1000 / 1000, qAbs(deadline) % (1000 * 1000 * 1000),
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
class tst_QDeadlineTimer : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase_data();
void basics();
void foreverness();
void current();
void deadlines();
void setDeadline();
void overflow();
void expire();
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;
}
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());
QCOMPARE(deadline, QDeadlineTimer(timerType));
QVERIFY(!(deadline != QDeadlineTimer(timerType)));
QVERIFY(!(deadline < QDeadlineTimer()));
QCOMPARE_LE(deadline, QDeadlineTimer());
QCOMPARE_GE(deadline, QDeadlineTimer());
QVERIFY(!(deadline > QDeadlineTimer()));
QVERIFY(!(deadline < deadline));
QCOMPARE_LE(deadline, deadline);
QCOMPARE_GE(deadline, deadline);
QVERIFY(!(deadline > deadline));
// should have expired, but we may be running too early after boot
QTRY_VERIFY_WITH_TIMEOUT(deadline.hasExpired(), 100);
QCOMPARE(deadline.remainingTime(), qint64(0));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(0));
QCOMPARE(deadline.deadline(), qint64(0));
QCOMPARE(deadline.deadlineNSecs(), qint64(0));
deadline.setRemainingTime(0, timerType);
QCOMPARE(deadline.remainingTime(), qint64(0));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(0));
QCOMPARE_NE(deadline.deadline(), 0);
QCOMPARE_NE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE_NE(deadline.deadlineNSecs(), 0);
QCOMPARE_NE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
deadline.setPreciseRemainingTime(0, 0, timerType);
QCOMPARE(deadline.remainingTime(), qint64(0));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(0));
QCOMPARE_NE(deadline.deadline(), 0);
QCOMPARE_NE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE_NE(deadline.deadlineNSecs(), 0);
QCOMPARE_NE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
deadline.setDeadline(0, timerType);
QCOMPARE(deadline.remainingTime(), qint64(0));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(0));
QCOMPARE(deadline.deadline(), qint64(0));
QCOMPARE(deadline.deadlineNSecs(), qint64(0));
deadline.setPreciseDeadline(0, 0, timerType);
QCOMPARE(deadline.remainingTime(), qint64(0));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(0));
QCOMPARE(deadline.deadline(), qint64(0));
QCOMPARE(deadline.deadlineNSecs(), qint64(0));
}
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());
QVERIFY(!deadline.hasExpired());
QCOMPARE(deadline.remainingTime(), qint64(-1));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(-1));
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
deadline = QDeadlineTimer(-1, timerType);
QVERIFY(deadline.isForever());
QVERIFY(!deadline.hasExpired());
QCOMPARE(deadline.remainingTime(), qint64(-1));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(-1));
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
deadline.setRemainingTime(-1, timerType);
QVERIFY(deadline.isForever());
QVERIFY(!deadline.hasExpired());
QCOMPARE(deadline.remainingTime(), qint64(-1));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(-1));
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
deadline.setPreciseRemainingTime(-1, 0, timerType);
QVERIFY(deadline.isForever());
QVERIFY(!deadline.hasExpired());
QCOMPARE(deadline.remainingTime(), qint64(-1));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(-1));
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
deadline.setPreciseRemainingTime(-1, -1, timerType);
QVERIFY(deadline.isForever());
QVERIFY(!deadline.hasExpired());
QCOMPARE(deadline.remainingTime(), qint64(-1));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(-1));
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
deadline.setDeadline(std::numeric_limits<qint64>::max(), timerType);
QVERIFY(deadline.isForever());
QVERIFY(!deadline.hasExpired());
QCOMPARE(deadline.remainingTime(), qint64(-1));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(-1));
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
deadline.setPreciseDeadline(std::numeric_limits<qint64>::max(), 0, timerType);
QVERIFY(deadline.isForever());
QVERIFY(!deadline.hasExpired());
QCOMPARE(deadline.remainingTime(), qint64(-1));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(-1));
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline, deadline);
QVERIFY(!(deadline < deadline));
QCOMPARE_LE(deadline, deadline);
QCOMPARE_GE(deadline, deadline);
QVERIFY(!(deadline > deadline));
// adding to forever must still be forever
QDeadlineTimer deadline2 = deadline + 1;
QVERIFY(deadline2.isForever());
QVERIFY(!deadline2.hasExpired());
QCOMPARE(deadline2.remainingTime(), qint64(-1));
QCOMPARE(deadline2.remainingTimeNSecs(), qint64(-1));
QCOMPARE(deadline2.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline2.deadlineNSecs(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline2.timerType(), deadline.timerType());
QCOMPARE(deadline2 - deadline, qint64(0));
QCOMPARE(deadline2, deadline);
QVERIFY(!(deadline2 < deadline));
QCOMPARE_LE(deadline2, deadline);
QCOMPARE_GE(deadline2, deadline);
QVERIFY(!(deadline2 > deadline));
// subtracting from forever is *also* forever
deadline2 = deadline - 1;
QVERIFY(deadline2.isForever());
QVERIFY(!deadline2.hasExpired());
QCOMPARE(deadline2.remainingTime(), qint64(-1));
QCOMPARE(deadline2.remainingTimeNSecs(), qint64(-1));
QCOMPARE(deadline2.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline2.deadlineNSecs(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline2.timerType(), deadline.timerType());
QCOMPARE(deadline2 - deadline, qint64(0));
QCOMPARE(deadline2, deadline);
QVERIFY(!(deadline2 < deadline));
QCOMPARE_LE(deadline2, deadline);
QCOMPARE_GE(deadline2, deadline);
QVERIFY(!(deadline2 > deadline));
// compare and order against a default-constructed object
QDeadlineTimer expired;
QVERIFY(!(deadline == expired));
QCOMPARE_NE(deadline, expired);
QVERIFY(!(deadline < expired));
QVERIFY(!(deadline <= expired));
QCOMPARE_GE(deadline, expired);
QCOMPARE_GT(deadline, expired);
}
void tst_QDeadlineTimer::current()
{
QFETCH_GLOBAL(Qt::TimerType, timerType);
auto deadline = QDeadlineTimer::current(timerType);
QVERIFY(deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE(deadline.remainingTime(), qint64(0));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(0));
QCOMPARE_NE(deadline.deadline(), 0);
QCOMPARE_NE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE_NE(deadline.deadlineNSecs(), 0);
QCOMPARE_NE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
// subtracting from current should be "more expired"
QDeadlineTimer earlierDeadline = deadline - 1;
QVERIFY(earlierDeadline.hasExpired());
QVERIFY(!earlierDeadline.isForever());
QCOMPARE(earlierDeadline.timerType(), timerType);
QCOMPARE(earlierDeadline.remainingTime(), qint64(0));
QCOMPARE(earlierDeadline.remainingTimeNSecs(), qint64(0));
QCOMPARE_NE(earlierDeadline.deadline(), 0);
QCOMPARE_NE(earlierDeadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE_NE(earlierDeadline.deadlineNSecs(), 0);
QCOMPARE_NE(earlierDeadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
QCOMPARE(earlierDeadline.deadline(), deadline.deadline() - 1);
QCOMPARE(earlierDeadline.deadlineNSecs(), deadline.deadlineNSecs() - 1000*1000);
QCOMPARE(earlierDeadline - deadline, qint64(-1));
QCOMPARE_NE(earlierDeadline, deadline);
QCOMPARE_LT(earlierDeadline, deadline);
QCOMPARE_LE(earlierDeadline, deadline);
QVERIFY(!(earlierDeadline >= deadline));
QVERIFY(!(earlierDeadline > deadline));
}
void tst_QDeadlineTimer::deadlines()
{
QFETCH_GLOBAL(Qt::TimerType, timerType);
QDeadlineTimer deadline(4 * minResolution, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE_GT(deadline.remainingTime(), (3 * minResolution));
QCOMPARE_LE(deadline.remainingTime(), (4 * minResolution));
QCOMPARE_GT(deadline.remainingTimeNSecs(), (3000000 * minResolution));
QCOMPARE_LE(deadline.remainingTimeNSecs(), (4000000 * minResolution));
QCOMPARE_NE(deadline.deadline(), 0);
QCOMPARE_NE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE_NE(deadline.deadlineNSecs(), 0);
QCOMPARE_NE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
deadline.setRemainingTime(4 * minResolution, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE_GT(deadline.remainingTime(), (3 * minResolution));
QCOMPARE_LE(deadline.remainingTime(), (4 * minResolution));
QCOMPARE_GT(deadline.remainingTimeNSecs(), (3000000 * minResolution));
QCOMPARE_LE(deadline.remainingTimeNSecs(), (4000000 * minResolution));
QCOMPARE_NE(deadline.deadline(), 0);
QCOMPARE_NE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE_NE(deadline.deadlineNSecs(), 0);
QCOMPARE_NE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
deadline.setPreciseRemainingTime(0, 4000000 * minResolution, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE_GT(deadline.remainingTime(), (3 * minResolution));
QCOMPARE_LE(deadline.remainingTime(), (4 * minResolution));
QCOMPARE_GT(deadline.remainingTimeNSecs(), (3000000 * minResolution));
QCOMPARE_LE(deadline.remainingTimeNSecs(), (4000000 * minResolution));
QCOMPARE_NE(deadline.deadline(), 0);
QCOMPARE_NE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE_NE(deadline.deadlineNSecs(), 0);
QCOMPARE_NE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
deadline.setPreciseRemainingTime(1, 0, timerType); // 1 sec
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE_GT(deadline.remainingTime(), (1000 - minResolution));
QCOMPARE_LE(deadline.remainingTime(), 1000);
QCOMPARE_GT(deadline.remainingTimeNSecs(), (1000 - minResolution)*1000*1000);
QCOMPARE_LE(deadline.remainingTimeNSecs(), (1000*1000*1000));
QCOMPARE_NE(deadline.deadline(), 0);
QCOMPARE_NE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE_NE(deadline.deadlineNSecs(), 0);
QCOMPARE_NE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
// adding to a future deadline must still be further in the future
QDeadlineTimer laterDeadline = deadline + 1;
QVERIFY(!laterDeadline.hasExpired());
QVERIFY(!laterDeadline.isForever());
QCOMPARE(laterDeadline.timerType(), timerType);
QCOMPARE_GT(laterDeadline.remainingTime(), (1000 - minResolution));
QCOMPARE_LE(laterDeadline.remainingTime(), 1001);
QCOMPARE_GT(laterDeadline.remainingTimeNSecs(), (1001 - minResolution)*1000*1000);
QCOMPARE_LE(laterDeadline.remainingTimeNSecs(), (1001*1000*1000));
QCOMPARE_NE(laterDeadline.deadline(), 0);
QCOMPARE_NE(laterDeadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE_NE(laterDeadline.deadlineNSecs(), 0);
QCOMPARE_NE(laterDeadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
QCOMPARE(laterDeadline.deadline(), deadline.deadline() + 1);
QCOMPARE(laterDeadline.deadlineNSecs(), deadline.deadlineNSecs() + 1000*1000);
QCOMPARE(laterDeadline - deadline, qint64(1));
QCOMPARE_NE(laterDeadline, deadline);
QVERIFY(!(laterDeadline < deadline));
QVERIFY(!(laterDeadline <= deadline));
QCOMPARE_GE(laterDeadline, deadline);
QCOMPARE_GT(laterDeadline, deadline);
// compare and order against a default-constructed object
QDeadlineTimer expired;
QVERIFY(!(deadline == expired));
QCOMPARE_NE(deadline, expired);
QVERIFY(!(deadline < expired));
QVERIFY(!(deadline <= expired));
QCOMPARE_GE(deadline, expired);
QCOMPARE_GT(deadline, expired);
// compare and order against a forever deadline
QDeadlineTimer forever_(QDeadlineTimer::Forever);
QVERIFY(!(deadline == forever_));
QCOMPARE_NE(deadline, forever_);
QCOMPARE_LT(deadline, forever_);
QCOMPARE_LE(deadline, forever_);
QVERIFY(!(deadline >= forever_));
QVERIFY(!(deadline > forever_));
}
void tst_QDeadlineTimer::setDeadline()
{
QFETCH_GLOBAL(Qt::TimerType, timerType);
auto now = QDeadlineTimer::current(timerType);
QDeadlineTimer deadline;
deadline.setDeadline(now.deadline(), timerType);
QVERIFY(deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE(deadline.remainingTime(), qint64(0));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(0));
QCOMPARE(deadline.deadline(), now.deadline());
// don't check deadlineNSecs!
deadline.setPreciseDeadline(now.deadlineNSecs() / (1000 * 1000 * 1000),
now.deadlineNSecs() % (1000 * 1000 * 1000), timerType);
QVERIFY(deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE(deadline.remainingTime(), qint64(0));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(0));
QCOMPARE(deadline.deadline(), now.deadline());
QCOMPARE(deadline.deadlineNSecs(), now.deadlineNSecs());
now = QDeadlineTimer::current(timerType);
deadline.setDeadline(now.deadline() + 4 * minResolution, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE_GT(deadline.remainingTime(), (3 * minResolution));
QCOMPARE_LE(deadline.remainingTime(), (4 * minResolution));
QCOMPARE_GT(deadline.remainingTimeNSecs(), (3000000 * minResolution));
QCOMPARE_LE(deadline.remainingTimeNSecs(), (4000000 * minResolution));
QCOMPARE(deadline.deadline(), now.deadline() + 4 * minResolution); // yes, it's exact
// don't check deadlineNSecs!
now = QDeadlineTimer::current(timerType);
qint64 nsec = now.deadlineNSecs() + 4000000 * minResolution;
deadline.setPreciseDeadline(nsec / (1000 * 1000 * 1000),
nsec % (1000 * 1000 * 1000), timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE_GT(deadline.remainingTime(), (3 * minResolution));
QCOMPARE_LE(deadline.remainingTime(), (4 * minResolution));
QCOMPARE_GT(deadline.remainingTimeNSecs(), (3000000 * minResolution));
QCOMPARE_LE(deadline.remainingTimeNSecs(), (4000000 * minResolution));
QCOMPARE(deadline.deadline(), nsec / (1000 * 1000));
QCOMPARE(deadline.deadlineNSecs(), nsec);
}
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());
// Check the setDeadline with milliseconds (should also cover implicitly setting the nanoseconds as qint64 max)
deadline.setDeadline(std::numeric_limits<qint64>::max() - 1, timerType);
QVERIFY(deadline.isForever() || deadline.deadline() >= now.deadline());
// Check the setRemainingTime with milliseconds (should also cover implicitly setting the nanoseconds as qint64 max)
deadline.setRemainingTime(std::numeric_limits<qint64>::max() - 1, timerType);
QVERIFY(deadline.isForever() || deadline.deadline() >= now.deadline());
// Check that the deadline gets saturated when the arguments of setPreciseDeadline are large
deadline.setPreciseDeadline(std::numeric_limits<qint64>::max() - 1, std::numeric_limits<qint64>::max() - 1, timerType);
QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
QVERIFY(deadline.isForever());
// Check that remainingTime gets saturated if we overflow
deadline.setPreciseRemainingTime(std::numeric_limits<qint64>::max() - 1, std::numeric_limits<qint64>::max() - 1, timerType);
QCOMPARE(deadline.remainingTime(), qint64(-1));
QVERIFY(deadline.isForever());
// Check that we saturate the getter for nanoseconds
deadline.setPreciseDeadline(std::numeric_limits<qint64>::max() - 1, 0, timerType);
QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
// Check that adding nanoseconds and overflowing is consistent and saturates the timer
deadline = QDeadlineTimer::addNSecs(deadline, std::numeric_limits<qint64>::max() - 1);
QVERIFY(deadline.isForever());
// Make sure forever is forever, regardless of us subtracting time from it
deadline = QDeadlineTimer(QDeadlineTimer::Forever, timerType);
deadline = QDeadlineTimer::addNSecs(deadline, -10000);
QVERIFY(deadline.isForever());
// Make sure we get the correct result when moving the deadline back and forth in time
QDeadlineTimer current = QDeadlineTimer::current(timerType);
QDeadlineTimer takenNSecs = QDeadlineTimer::addNSecs(current, -1000);
QVERIFY(takenNSecs.deadlineNSecs() - current.deadlineNSecs() == -1000);
QDeadlineTimer addedNSecs = QDeadlineTimer::addNSecs(current, 1000);
QVERIFY(addedNSecs.deadlineNSecs() - current.deadlineNSecs() == 1000);
// Make sure the calculation goes as expected when we need to subtract nanoseconds
// We make use of an additional timer to be certain that
// even when the environment is under load we can track the
// time needed to do the calls
static constexpr qint64 nsExpected = 1000 * 1000 * 1000 - 1000; // 1s - 1000ns, what we pass to setPreciseRemainingTime() later
QElapsedTimer callTimer;
callTimer.start();
deadline = QDeadlineTimer::current(timerType);
qint64 nsDeadline = deadline.deadlineNSecs();
// We adjust in relation to current() here, so we expect the difference to be a tad over the exact number.
// However we are tracking the elapsed time, so it shouldn't be a problem.
deadline.setPreciseRemainingTime(1, -1000, timerType);
qint64 difference = (deadline.deadlineNSecs() - nsDeadline) - nsExpected;
QCOMPARE_GE(difference, 0); // Should always be true, but just in case
QCOMPARE_LE(difference, callTimer.nsecsElapsed()); // Ideally difference should be 0 exactly
// 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);
// If the timer is saturated we don't want to get a valid number of milliseconds
QVERIFY(deadline.deadline() == std::numeric_limits<qint64>::min());
// 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.deadline() == std::numeric_limits<qint64>::min());
QVERIFY(deadline.deadlineNSecs() == std::numeric_limits<qint64>::min());
}
void tst_QDeadlineTimer::expire()
{
QFETCH_GLOBAL(Qt::TimerType, timerType);
QDeadlineTimer deadline(minResolution, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
qint64 previousDeadline = deadline.deadlineNSecs();
QTest::qSleep(2 * minResolution);
QCOMPARE(deadline.remainingTime(), qint64(0));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(0));
QCOMPARE_NE(deadline.deadline(), 0);
QCOMPARE_NE(deadline.deadline(), std::numeric_limits<qint64>::max());
QCOMPARE_NE(deadline.deadlineNSecs(), 0);
QCOMPARE_NE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
QCOMPARE(deadline.deadlineNSecs(), previousDeadline);
}
void tst_QDeadlineTimer::stdchrono()
{
using namespace std::chrono;
QFETCH_GLOBAL(Qt::TimerType, timerType);
// create some forevers
QDeadlineTimer deadline = milliseconds::max();
QVERIFY(deadline.isForever());
deadline = milliseconds::max();
QVERIFY(deadline.isForever());
deadline.setRemainingTime(milliseconds::max(), timerType);
QVERIFY(deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
deadline = nanoseconds::max();
QVERIFY(deadline.isForever());
deadline.setRemainingTime(nanoseconds::max(), timerType);
QVERIFY(deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
deadline = hours::max();
QVERIFY(deadline.isForever());
deadline.setRemainingTime(hours::max(), timerType);
QVERIFY(deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
deadline = time_point<system_clock>::max();
QVERIFY(deadline.isForever());
deadline.setDeadline(time_point<system_clock>::max(), timerType);
QVERIFY(deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
deadline = time_point<steady_clock>::max();
QVERIFY(deadline.isForever());
deadline.setDeadline(time_point<steady_clock>::max(), timerType);
QVERIFY(deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QVERIFY(deadline == time_point<steady_clock>::max());
QVERIFY(deadline == time_point<system_clock>::max());
QCOMPARE(deadline.remainingTimeAsDuration(), nanoseconds::max());
// make it expired
deadline = time_point<system_clock>();
QVERIFY(deadline.hasExpired());
deadline.setDeadline(time_point<system_clock>(), timerType);
QVERIFY(deadline.hasExpired());
QCOMPARE(deadline.timerType(), timerType);
deadline = time_point<steady_clock>();
QVERIFY(deadline.hasExpired());
deadline.setDeadline(time_point<steady_clock>(), timerType);
QVERIFY(deadline.hasExpired());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE(deadline.remainingTimeAsDuration(), nanoseconds::zero());
QDeadlineTimer now;
bool timersExecuted = false;
auto steady_before = steady_clock::now();
auto system_before = system_clock::now();
decltype(steady_before) steady_after, steady_deadline;
decltype(system_before) system_after, system_deadline;
QTimer::singleShot(minResolution, Qt::PreciseTimer, [&]() {
now = QDeadlineTimer::current(timerType);
QTimer::singleShot(minResolution, Qt::PreciseTimer, [&]() {
steady_deadline = now.deadline<steady_clock>();
system_deadline = now.deadline<system_clock>();
steady_after = steady_clock::now();
system_after = system_clock::now();
timersExecuted = true;
});
});
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);
QCOMPARE_LT(diff.count(), 3 * minResolution / 2);
QDeadlineTimer dt_after(steady_after, timerType);
QCOMPARE_LT(now, dt_after);
diff = duration_cast<milliseconds>(steady_deadline - steady_before);
QCOMPARE_GT(diff.count(), minResolution / 2);
QCOMPARE_LT(diff.count(), 3 * minResolution / 2);
QDeadlineTimer dt_before(steady_before, timerType);
QCOMPARE_GT(now, dt_before);
}
{
auto diff = duration_cast<milliseconds>(system_after - system_deadline);
QCOMPARE_GT(diff.count(), minResolution / 2);
QCOMPARE_LT(diff.count(), 3 * minResolution / 2);
QDeadlineTimer dt_after(system_after, timerType);
QCOMPARE_LT(now, dt_after);
diff = duration_cast<milliseconds>(system_deadline - system_before);
QCOMPARE_GT(diff.count(), minResolution / 2);
QCOMPARE_LT(diff.count(), 3 * minResolution / 2);
QDeadlineTimer dt_before(system_before, timerType);
QCOMPARE_GT(now, dt_before);
}
// make it regular
now = QDeadlineTimer::current(timerType);
deadline.setRemainingTime(milliseconds(4 * minResolution), timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE_GT(deadline.remainingTimeAsDuration(), milliseconds(3 * minResolution));
QCOMPARE_LT(deadline.remainingTimeAsDuration(), milliseconds(5 * minResolution));
QCOMPARE_GT(deadline.remainingTimeAsDuration(), nanoseconds(3000000 * minResolution));
QCOMPARE_LT(deadline.remainingTimeAsDuration(), nanoseconds(5000000 * minResolution));
QCOMPARE_GT(deadline.deadline<steady_clock>(), (steady_clock::now() + milliseconds(3 * minResolution)));
QCOMPARE_LT(deadline.deadline<steady_clock>(), (steady_clock::now() + milliseconds(5 * minResolution)));
QCOMPARE_GT(deadline.deadline<system_clock>(), (system_clock::now() + milliseconds(3 * minResolution)));
QCOMPARE_LT(deadline.deadline<system_clock>(), (system_clock::now() + milliseconds(5 * minResolution)));
if (timerType == Qt::CoarseTimer) {
QCOMPARE_GT(deadline, (now + milliseconds(3 * minResolution)));
QCOMPARE_LT(deadline, (now + milliseconds(5 * minResolution)));
QCOMPARE_GT(deadline, (now + nanoseconds(3000000 * minResolution)));
QCOMPARE_LT(deadline, (now + nanoseconds(5000000 * minResolution)));
QCOMPARE_GT(deadline, milliseconds(3 * minResolution));
QCOMPARE_LT(deadline, milliseconds(5 * minResolution));
QCOMPARE_GT(deadline, nanoseconds(3000000 * minResolution));
QCOMPARE_LT(deadline, nanoseconds(5000000 * minResolution));
QCOMPARE_GE(deadline, steady_clock::now());
QCOMPARE_GE(deadline, system_clock::now());
}
now = QDeadlineTimer::current(timerType);
deadline = QDeadlineTimer(seconds(1), timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE_GT(deadline.remainingTimeAsDuration(), (seconds(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.remainingTimeAsDuration(), seconds(1));
QCOMPARE_GT(deadline.deadline<steady_clock>(), (steady_clock::now() + seconds(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.deadline<steady_clock>(), (steady_clock::now() + seconds(1) + milliseconds(minResolution)));
QCOMPARE_GT(deadline.deadline<system_clock>(), (system_clock::now() + seconds(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.deadline<system_clock>(), (system_clock::now() + seconds(1) + milliseconds(minResolution)));
if (timerType == Qt::CoarseTimer) {
QCOMPARE_GT(deadline, (seconds(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline, seconds(1));
}
now = QDeadlineTimer::current(timerType);
deadline.setRemainingTime(hours(1), timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE_GT(deadline.remainingTimeAsDuration(), (hours(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.remainingTimeAsDuration(), hours(1));
QCOMPARE_GT(deadline.deadline<steady_clock>(), (steady_clock::now() + hours(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.deadline<steady_clock>(), (steady_clock::now() + hours(1) + milliseconds(minResolution)));
QCOMPARE_GT(deadline.deadline<system_clock>(), (system_clock::now() + hours(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.deadline<system_clock>(), (system_clock::now() + hours(1) + milliseconds(minResolution)));
now = QDeadlineTimer::current(timerType);
deadline.setDeadline(system_clock::now() + seconds(1), timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE_GT(deadline.remainingTimeAsDuration(), (seconds(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.remainingTimeAsDuration(), seconds(1));
QCOMPARE_GT(deadline.deadline<steady_clock>(), (steady_clock::now() + seconds(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.deadline<steady_clock>(), (steady_clock::now() + seconds(1) + milliseconds(minResolution)));
QCOMPARE_GT(deadline.deadline<system_clock>(), (system_clock::now() + seconds(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.deadline<system_clock>(), (system_clock::now() + seconds(1) + milliseconds(minResolution)));
now = QDeadlineTimer::current(timerType);
deadline.setDeadline(steady_clock::now() + seconds(1), timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
QCOMPARE_GT(deadline.remainingTimeAsDuration(), (seconds(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.remainingTimeAsDuration(), seconds(1));
QCOMPARE_GT(deadline.deadline<steady_clock>(), (steady_clock::now() + seconds(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.deadline<steady_clock>(), (steady_clock::now() + seconds(1) + milliseconds(minResolution)));
QCOMPARE_GT(deadline.deadline<system_clock>(), (system_clock::now() + seconds(1) - milliseconds(minResolution)));
QCOMPARE_LE(deadline.deadline<system_clock>(), (system_clock::now() + seconds(1) + milliseconds(minResolution)));
}
QTEST_MAIN(tst_QDeadlineTimer)
#include "tst_qdeadlinetimer.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qelapsedtimer Test:
#####################################################################
qt_internal_add_test(tst_qelapsedtimer
SOURCES
tst_qelapsedtimer.cpp
)

View File

@ -0,0 +1,143 @@
// 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/QString>
#include <QtCore/QTime>
#include <QtCore/QElapsedTimer>
#include <QTest>
#include <QTimer>
static const int minResolution = 100; // the minimum resolution for the tests
QT_BEGIN_NAMESPACE
QDebug operator<<(QDebug s, const QElapsedTimer &t)
{
s.nospace() << "(" << t.msecsSinceReference() << ")";
return s.space();
}
QT_END_NAMESPACE
class tst_QElapsedTimer : public QObject
{
Q_OBJECT
private Q_SLOTS:
void statics();
void validity();
void basics();
void elapsed();
void msecsTo();
};
void tst_QElapsedTimer::statics()
{
qDebug() << "Clock type is" << QElapsedTimer::clockType();
qDebug() << "Said clock is" << (QElapsedTimer::isMonotonic() ? "monotonic" : "not monotonic");
QElapsedTimer t;
t.start();
qDebug() << "Current time is" << t.msecsSinceReference();
}
void tst_QElapsedTimer::validity()
{
QElapsedTimer t;
QVERIFY(!t.isValid()); // non-POD now, it should always start invalid
t.start();
QVERIFY(t.isValid());
t.invalidate();
QVERIFY(!t.isValid());
}
void tst_QElapsedTimer::basics()
{
QElapsedTimer t1;
t1.start();
QVERIFY(t1.msecsSinceReference() != 0);
QCOMPARE(t1, t1);
QVERIFY(!(t1 != t1));
QVERIFY(!(t1 < t1));
QCOMPARE(t1.msecsTo(t1), qint64(0));
QCOMPARE(t1.secsTo(t1), qint64(0));
quint64 value1 = t1.msecsSinceReference();
qDebug() << "value1:" << value1 << "t1:" << t1;
qint64 nsecs = t1.nsecsElapsed();
qint64 elapsed = t1.restart();
QVERIFY(elapsed < minResolution);
QVERIFY(nsecs / 1000000 < minResolution);
quint64 value2 = t1.msecsSinceReference();
qDebug() << "value2:" << value2 << "t1:" << t1
<< "elapsed:" << elapsed << "nsecs:" << nsecs;
// in theory, elapsed == value2 - value1
// However, since QElapsedTimer keeps internally the full resolution,
// we have here a rounding error due to integer division
QVERIFY(qAbs(elapsed - qint64(value2 - value1)) <= 1);
}
void tst_QElapsedTimer::elapsed()
{
qint64 nsecs = 0;
qint64 msecs = 0;
bool expired1 = false;
bool expired8 = false;
bool expiredInf = false;
qint64 elapsed = 0;
bool timerExecuted = false;
QElapsedTimer t1;
t1.start();
QTimer::singleShot(2 * minResolution, Qt::PreciseTimer, [&](){
nsecs = t1.nsecsElapsed();
msecs = t1.elapsed();
expired1 = t1.hasExpired(minResolution);
expired8 = t1.hasExpired(8 * minResolution);
expiredInf = t1.hasExpired(-1);
elapsed = t1.restart();
timerExecuted = true;
});
QTRY_VERIFY2_WITH_TIMEOUT(timerExecuted,
"Looks like timer didn't fire on time.", 4 * minResolution);
QVERIFY(nsecs > 0);
QVERIFY(msecs > 0);
// the number of elapsed nanoseconds and milliseconds should match
QVERIFY(nsecs - msecs * 1000000 < 1000000);
QVERIFY(expired1);
QVERIFY(!expired8);
QVERIFY(!expiredInf);
QVERIFY(elapsed >= msecs);
QVERIFY(elapsed < msecs + 3 * minResolution);
}
void tst_QElapsedTimer::msecsTo()
{
QElapsedTimer t1;
t1.start();
QTest::qSleep(minResolution);
QElapsedTimer t2;
t2.start();
QVERIFY(t1 != t2);
QVERIFY(!(t1 == t2));
QVERIFY(t1 < t2);
auto diff = t1.msecsTo(t2);
QVERIFY2(diff > 0, QString("difference t1 and t2 is %1").arg(diff).toLatin1());
diff = t2.msecsTo(t1);
QVERIFY2(diff < 0, QString("difference t2 and t1 is %1").arg(diff).toLatin1());
}
QTEST_MAIN(tst_QElapsedTimer);
#include "tst_qelapsedtimer.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qeventdispatcher Test:
#####################################################################
qt_internal_add_test(tst_qeventdispatcher
SOURCES
tst_qeventdispatcher.cpp
)

View File

@ -0,0 +1,535 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifdef QT_GUI_LIB
# include <QtGui/QGuiApplication>
# define tst_QEventDispatcher tst_QGuiEventDispatcher
#else
# include <QtCore/QCoreApplication>
#endif
#include <QTest>
#include <QAbstractEventDispatcher>
#include <QTimer>
#include <QThreadPool>
enum {
PreciseTimerInterval = 10,
CoarseTimerInterval = 200,
VeryCoarseTimerInterval = 1000
};
class tst_QEventDispatcher : public QObject
{
Q_OBJECT
QAbstractEventDispatcher *eventDispatcher;
int receivedEventType = -1;
int timerIdFromEvent = -1;
bool doubleTimer = false;
protected:
bool event(QEvent *e) override;
public:
inline tst_QEventDispatcher()
: QObject(),
eventDispatcher(QAbstractEventDispatcher::instance(thread())),
isGuiEventDispatcher(QCoreApplication::instance()->inherits("QGuiApplication"))
{ }
private slots:
void initTestCase();
void cleanup();
void registerTimer();
/* void registerSocketNotifier(); */ // Not implemented here, see tst_QSocketNotifier instead
/* void registerEventNotifiier(); */ // Not implemented here, see tst_QWinEventNotifier instead
void sendPostedEvents_data();
void sendPostedEvents();
void processEventsOnlySendsQueuedEvents();
// these two tests need to run before postedEventsPingPong
void postEventFromThread();
void postEventFromEventHandler();
// these tests don't leave the event dispatcher in a reliable state
void postedEventsPingPong();
void eventLoopExit();
void interruptTrampling();
private:
const bool isGuiEventDispatcher;
};
bool tst_QEventDispatcher::event(QEvent *e)
{
switch (receivedEventType = e->type()) {
case QEvent::Timer:
{
// sometimes, two timers fire during a single QTRY_xxx wait loop
if (timerIdFromEvent != -1)
doubleTimer = true;
timerIdFromEvent = static_cast<QTimerEvent *>(e)->timerId();
return true;
}
default:
break;
}
return QObject::event(e);
}
// drain the system event queue after the test starts to avoid destabilizing the test functions
void tst_QEventDispatcher::initTestCase()
{
QElapsedTimer elapsedTimer;
elapsedTimer.start();
while (!elapsedTimer.hasExpired(CoarseTimerInterval) && eventDispatcher->processEvents(QEventLoop::AllEvents)) {
;
}
}
// consume pending posted events to avoid impact on the next test function
void tst_QEventDispatcher::cleanup()
{
eventDispatcher->processEvents(QEventLoop::AllEvents);
}
class TimerManager {
Q_DISABLE_COPY(TimerManager)
public:
TimerManager(QAbstractEventDispatcher *eventDispatcher, QObject *parent)
: m_eventDispatcher(eventDispatcher), m_parent(parent)
{
}
~TimerManager()
{
if (!registeredTimers().isEmpty())
m_eventDispatcher->unregisterTimers(m_parent);
}
TimerManager(TimerManager &&) = delete;
TimerManager &operator=(TimerManager &&) = delete;
int preciseTimerId() const { return m_preciseTimerId; }
int coarseTimerId() const { return m_coarseTimerId; }
int veryCoarseTimerId() const { return m_veryCoarseTimerId; }
bool foundPrecise() const { return m_preciseTimerId > 0; }
bool foundCoarse() const { return m_coarseTimerId > 0; }
bool foundVeryCoarse() const { return m_veryCoarseTimerId > 0; }
QList<QAbstractEventDispatcher::TimerInfo> registeredTimers() const
{
return m_eventDispatcher->registeredTimers(m_parent);
}
void registerAll()
{
// start 3 timers, each with the different timer types and different intervals
m_preciseTimerId = m_eventDispatcher->registerTimer(
PreciseTimerInterval, Qt::PreciseTimer, m_parent);
m_coarseTimerId = m_eventDispatcher->registerTimer(
CoarseTimerInterval, Qt::CoarseTimer, m_parent);
m_veryCoarseTimerId = m_eventDispatcher->registerTimer(
VeryCoarseTimerInterval, Qt::VeryCoarseTimer, m_parent);
QVERIFY(m_preciseTimerId > 0);
QVERIFY(m_coarseTimerId > 0);
QVERIFY(m_veryCoarseTimerId > 0);
findTimers();
}
void unregister(int timerId)
{
m_eventDispatcher->unregisterTimer(timerId);
findTimers();
}
void unregisterAll()
{
m_eventDispatcher->unregisterTimers(m_parent);
findTimers();
}
private:
void findTimers()
{
bool foundPrecise = false;
bool foundCoarse = false;
bool foundVeryCoarse = false;
const QList<QAbstractEventDispatcher::TimerInfo> timers = registeredTimers();
for (int i = 0; i < timers.size(); ++i) {
const QAbstractEventDispatcher::TimerInfo &timerInfo = timers.at(i);
if (timerInfo.timerId == m_preciseTimerId) {
QCOMPARE(timerInfo.interval, int(PreciseTimerInterval));
QCOMPARE(timerInfo.timerType, Qt::PreciseTimer);
foundPrecise = true;
} else if (timerInfo.timerId == m_coarseTimerId) {
QCOMPARE(timerInfo.interval, int(CoarseTimerInterval));
QCOMPARE(timerInfo.timerType, Qt::CoarseTimer);
foundCoarse = true;
} else if (timerInfo.timerId == m_veryCoarseTimerId) {
QCOMPARE(timerInfo.interval, int(VeryCoarseTimerInterval));
QCOMPARE(timerInfo.timerType, Qt::VeryCoarseTimer);
foundVeryCoarse = true;
}
}
if (!foundPrecise)
m_preciseTimerId = -1;
if (!foundCoarse)
m_coarseTimerId = -1;
if (!foundVeryCoarse)
m_veryCoarseTimerId = -1;
}
QAbstractEventDispatcher *m_eventDispatcher = nullptr;
int m_preciseTimerId = -1;
int m_coarseTimerId = -1;
int m_veryCoarseTimerId = -1;
QObject *m_parent = nullptr;
};
// test that the eventDispatcher's timer implementation is complete and working
void tst_QEventDispatcher::registerTimer()
{
TimerManager timers(eventDispatcher, this);
timers.registerAll();
if (QTest::currentTestFailed())
return;
// check that all 3 are present in the eventDispatcher's registeredTimer() list
QCOMPARE(timers.registeredTimers().size(), 3);
QVERIFY(timers.foundPrecise());
QVERIFY(timers.foundCoarse());
QVERIFY(timers.foundVeryCoarse());
#ifdef Q_OS_DARWIN
/*
We frequently experience flaky failures on macOS. Assumption is that this is
due to undeterministic VM scheduling, making us process events for significantly
longer than expected and resulting in timers firing in undefined order.
To detect this condition, we use a QElapsedTimer, and skip the test.
*/
QElapsedTimer elapsedTimer;
elapsedTimer.start();
#endif
// process events, waiting for the next event... this should only fire the precise timer
receivedEventType = -1;
timerIdFromEvent = -1;
doubleTimer = false;
QTRY_COMPARE_WITH_TIMEOUT(receivedEventType, int(QEvent::Timer), PreciseTimerInterval * 2);
#ifdef Q_OS_DARWIN
if (doubleTimer)
QSKIP("Double timer during a single timeout - aborting test as flaky on macOS");
if (timerIdFromEvent != timers.preciseTimerId()
&& elapsedTimer.elapsed() > PreciseTimerInterval * 3)
QSKIP("Ignore flaky test behavior due to VM scheduling on macOS");
#endif
QCOMPARE(timerIdFromEvent, timers.preciseTimerId());
// now unregister it and make sure it's gone
timers.unregister(timers.preciseTimerId());
if (QTest::currentTestFailed())
return;
QCOMPARE(timers.registeredTimers().size(), 2);
QVERIFY(!timers.foundPrecise());
QVERIFY(timers.foundCoarse());
QVERIFY(timers.foundVeryCoarse());
// do the same again for the coarse timer
receivedEventType = -1;
timerIdFromEvent = -1;
doubleTimer = false;
QTRY_COMPARE_WITH_TIMEOUT(receivedEventType, int(QEvent::Timer), CoarseTimerInterval * 2);
#ifdef Q_OS_DARWIN
if (doubleTimer)
QSKIP("Double timer during a single timeout - aborting test as flaky on macOS");
if (timerIdFromEvent != timers.coarseTimerId()
&& elapsedTimer.elapsed() > CoarseTimerInterval * 3)
QSKIP("Ignore flaky test behavior due to VM scheduling on macOS");
#endif
QCOMPARE(timerIdFromEvent, timers.coarseTimerId());
// now unregister it and make sure it's gone
timers.unregister(timers.coarseTimerId());
if (QTest::currentTestFailed())
return;
QCOMPARE(timers.registeredTimers().size(), 1);
QVERIFY(!timers.foundPrecise());
QVERIFY(!timers.foundCoarse());
QVERIFY(timers.foundVeryCoarse());
// not going to wait for the VeryCoarseTimer, would take too long, just unregister it
timers.unregisterAll();
if (QTest::currentTestFailed())
return;
QVERIFY(timers.registeredTimers().isEmpty());
}
void tst_QEventDispatcher::sendPostedEvents_data()
{
QTest::addColumn<int>("processEventsFlagsInt");
QTest::newRow("WaitForMoreEvents") << int(QEventLoop::WaitForMoreEvents);
QTest::newRow("AllEvents") << int(QEventLoop::AllEvents);
}
// test that the eventDispatcher sends posted events correctly
void tst_QEventDispatcher::sendPostedEvents()
{
QFETCH(int, processEventsFlagsInt);
QEventLoop::ProcessEventsFlags processEventsFlags = QEventLoop::ProcessEventsFlags(processEventsFlagsInt);
QElapsedTimer elapsedTimer;
elapsedTimer.start();
while (!elapsedTimer.hasExpired(200)) {
receivedEventType = -1;
QCoreApplication::postEvent(this, new QEvent(QEvent::User));
// event shouldn't be delivered as a result of posting
QCOMPARE(receivedEventType, -1);
// since there is a pending posted event, this should not actually block, it should send the posted event and return
QVERIFY(eventDispatcher->processEvents(processEventsFlags));
// event shouldn't be delivered as a result of posting
QCOMPARE(receivedEventType, int(QEvent::User));
}
}
class ProcessEventsOnlySendsQueuedEvents : public QObject
{
Q_OBJECT
public:
int eventsReceived;
inline ProcessEventsOnlySendsQueuedEvents() : eventsReceived(0) {}
bool event(QEvent *event) override
{
++eventsReceived;
if (event->type() == QEvent::User)
QCoreApplication::postEvent(this, new QEvent(QEvent::Type(QEvent::User + 1)));
return QObject::event(event);
}
public slots:
void timerFired()
{
QCoreApplication::postEvent(this, new QEvent(QEvent::Type(QEvent::User + 1)));
}
};
void tst_QEventDispatcher::processEventsOnlySendsQueuedEvents()
{
ProcessEventsOnlySendsQueuedEvents object;
// Posted events during event processing should be handled on
// the next processEvents iteration.
QCoreApplication::postEvent(&object, new QEvent(QEvent::User));
QCoreApplication::processEvents();
QCOMPARE(object.eventsReceived, 1);
QCoreApplication::processEvents();
QCOMPARE(object.eventsReceived, 2);
// The same goes for posted events during timer processing
QTimer::singleShot(0, &object, SLOT(timerFired()));
QCoreApplication::processEvents();
QCOMPARE(object.eventsReceived, 3);
QCoreApplication::processEvents();
QCOMPARE(object.eventsReceived, 4);
}
void tst_QEventDispatcher::postEventFromThread()
{
QThreadPool *threadPool = QThreadPool::globalInstance();
QAtomicInt hadToQuit = false;
QAtomicInt done = false;
threadPool->start([&]{
int loop = 1000 / 10; // give it a second
while (!done && --loop)
QThread::msleep(10);
if (done)
return;
hadToQuit = true;
QCoreApplication::eventDispatcher()->wakeUp();
});
struct EventReceiver : public QObject {
bool event(QEvent* event) override {
if (event->type() == QEvent::User)
return true;
return QObject::event(event);
}
} receiver;
int count = 500;
while (!hadToQuit && --count) {
threadPool->start([&receiver]{
QCoreApplication::postEvent(&receiver, new QEvent(QEvent::User));
});
QAbstractEventDispatcher::instance()->processEvents(QEventLoop::WaitForMoreEvents);
}
done = true;
QVERIFY(!hadToQuit);
QVERIFY(threadPool->waitForDone());
}
void tst_QEventDispatcher::postEventFromEventHandler()
{
QThreadPool *threadPool = QThreadPool::globalInstance();
QAtomicInt hadToQuit = false;
QAtomicInt done = false;
threadPool->start([&]{
int loop = 250 / 10; // give it 250ms
while (!done && --loop)
QThread::msleep(10);
if (done)
return;
hadToQuit = true;
QCoreApplication::eventDispatcher()->wakeUp();
});
struct EventReceiver : public QObject {
int i = 0;
bool event(QEvent* event) override
{
if (event->type() == QEvent::User) {
++i;
if (i < 2)
QCoreApplication::postEvent(this, new QEvent(QEvent::User));
return true;
}
return QObject::event(event);
}
} receiver;
QCoreApplication::postEvent(&receiver, new QEvent(QEvent::User));
while (receiver.i < 2)
QAbstractEventDispatcher::instance()->processEvents(QEventLoop::WaitForMoreEvents);
done = true;
const QByteArrayView eventDispatcherName(QAbstractEventDispatcher::instance()->metaObject()->className());
qDebug() << eventDispatcherName;
// QXcbUnixEventDispatcher and QEventDispatcherUNIX do not do this correctly on any platform;
// both Windows event dispatchers fail as well.
const bool knownToFail = eventDispatcherName.contains("UNIX")
|| eventDispatcherName.contains("Unix")
|| eventDispatcherName.contains("Win32")
|| eventDispatcherName.contains("WindowsGui")
|| eventDispatcherName.contains("Android");
if (knownToFail)
QEXPECT_FAIL("", eventDispatcherName.constData(), Continue);
QVERIFY(!hadToQuit);
QVERIFY(threadPool->waitForDone());
}
void tst_QEventDispatcher::postedEventsPingPong()
{
QEventLoop mainLoop;
// We need to have at least two levels of nested loops
// for the posted event to get stuck (QTBUG-85981).
QMetaObject::invokeMethod(this, [this, &mainLoop]() {
QMetaObject::invokeMethod(this, [&mainLoop]() {
// QEventLoop::quit() should be invoked on the next
// iteration of mainLoop.exec().
QMetaObject::invokeMethod(&mainLoop, &QEventLoop::quit,
Qt::QueuedConnection);
}, Qt::QueuedConnection);
mainLoop.processEvents();
}, Qt::QueuedConnection);
// We should use Qt::CoarseTimer on Windows, to prevent event
// dispatcher from sending a posted event.
QTimer::singleShot(500, Qt::CoarseTimer, &mainLoop, [&mainLoop]() {
mainLoop.exit(1);
});
QCOMPARE(mainLoop.exec(), 0);
}
void tst_QEventDispatcher::eventLoopExit()
{
// This test was inspired by QTBUG-79477. A particular
// implementation detail in QCocoaEventDispatcher allowed
// QEventLoop::exit() to fail to really exit the event loop.
// Thus this test is a part of the dispatcher auto-test.
// Imitates QApplication::exec():
QEventLoop mainLoop;
// The test itself is a lambda:
QTimer::singleShot(0, &mainLoop, [&mainLoop]() {
// Two more single shots, both will be posted as events
// (zero timeout) and supposed to be processes by the
// mainLoop:
QTimer::singleShot(0, &mainLoop, [&mainLoop]() {
// wakeUp triggers QCocoaEventDispatcher into incrementing
// its 'serialNumber':
mainLoop.wakeUp();
// QCocoaEventDispatcher::processEvents() will process
// posted events and execute the second lambda defined below:
QCoreApplication::processEvents();
});
QTimer::singleShot(0, &mainLoop, [&mainLoop]() {
// With QCocoaEventDispatcher this is executed while in the
// processEvents (see above) and would fail to actually
// interrupt the loop.
mainLoop.exit();
});
});
bool timeoutObserved = false;
QTimer::singleShot(500, &mainLoop, [&timeoutObserved, &mainLoop]() {
// In case the QEventLoop::exit above failed, we have to bail out
// early, not wasting time:
mainLoop.exit();
timeoutObserved = true;
});
mainLoop.exec();
QVERIFY(!timeoutObserved);
}
// Based on QTBUG-91539: In the event dispatcher on Windows we overwrite the
// interrupt once we start processing events (this pattern is also in the 'unix' dispatcher)
// which would lead the dispatcher to accidentally ignore certain interrupts and,
// as in the bug report, would not quit, leaving the thread alive and running.
void tst_QEventDispatcher::interruptTrampling()
{
class WorkerThread : public QThread
{
void run() override {
auto dispatcher = eventDispatcher();
QVERIFY(dispatcher);
dispatcher->processEvents(QEventLoop::AllEvents);
QTimer::singleShot(0, dispatcher, [dispatcher]() {
dispatcher->wakeUp();
});
dispatcher->processEvents(QEventLoop::WaitForMoreEvents);
dispatcher->interrupt();
dispatcher->processEvents(QEventLoop::WaitForMoreEvents);
}
};
WorkerThread thread;
thread.start();
QVERIFY(thread.wait(1000));
QVERIFY(thread.isFinished());
}
QTEST_MAIN(tst_QEventDispatcher)
#include "tst_qeventdispatcher.moc"

View File

@ -0,0 +1,27 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qeventloop Test:
#####################################################################
qt_internal_add_test(tst_qeventloop
SOURCES
tst_qeventloop.cpp
LIBRARIES
Qt::CorePrivate
Qt::Network
)
## Scopes:
#####################################################################
qt_internal_extend_target(tst_qeventloop CONDITION WIN32
LIBRARIES
user32
)
qt_internal_extend_target(tst_qeventloop CONDITION QT_FEATURE_glib
DEFINES
HAVE_GLIB
)

View File

@ -0,0 +1,644 @@
// 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>
#include <qabstracteventdispatcher.h>
#include <qcoreapplication.h>
#include <qcoreevent.h>
#include <qeventloop.h>
#include <private/qeventloop_p.h>
#if defined(Q_OS_UNIX)
#include <private/qeventdispatcher_unix_p.h>
#include <QtCore/private/qcore_unix_p.h>
#if defined(HAVE_GLIB)
#include <private/qeventdispatcher_glib_p.h>
#endif
#endif
#include <qmutex.h>
#include <qthread.h>
#include <qtimer.h>
#include <qwaitcondition.h>
#include <QTcpServer>
#include <QTcpSocket>
#include <QSignalSpy>
class EventLoopExiter : public QObject
{
Q_OBJECT
QEventLoop *eventLoop;
public:
inline EventLoopExiter(QEventLoop *el)
: eventLoop(el)
{ }
public slots:
void exit();
void exit1();
void exit2();
};
void EventLoopExiter::exit()
{ eventLoop->exit(); }
void EventLoopExiter::exit1()
{ eventLoop->exit(1); }
void EventLoopExiter::exit2()
{ eventLoop->exit(2); }
class EventLoopThread : public QThread
{
Q_OBJECT
signals:
void checkPoint();
public:
QEventLoop *eventLoop;
void run() override;
};
void EventLoopThread::run()
{
eventLoop = new QEventLoop;
emit checkPoint();
(void) eventLoop->exec();
delete eventLoop;
eventLoop = nullptr;
}
class MultipleExecThread : public QThread
{
Q_OBJECT
signals:
void checkPoint();
public:
QMutex mutex;
QWaitCondition cond;
volatile int result1;
volatile int result2;
MultipleExecThread() : result1(0xdead), result2(0xbeef) {}
void run() override
{
QMutexLocker locker(&mutex);
// this exec should work
cond.wakeOne();
cond.wait(&mutex);
QTimer timer;
connect(&timer, SIGNAL(timeout()), SLOT(quit()), Qt::DirectConnection);
timer.setInterval(1000);
timer.start();
result1 = exec();
// this should return immediately, since exit() has been called
cond.wakeOne();
cond.wait(&mutex);
QEventLoop eventLoop;
result2 = eventLoop.exec();
}
};
class StartStopEvent: public QEvent
{
public:
explicit StartStopEvent(int type, QEventLoop *loop = nullptr)
: QEvent(Type(type)), el(loop)
{ }
QEventLoop *el;
};
class EventLoopExecutor : public QObject
{
Q_OBJECT
QEventLoop *eventLoop;
public:
int returnCode;
EventLoopExecutor(QEventLoop *eventLoop)
: QObject(), eventLoop(eventLoop), returnCode(-42)
{
}
public slots:
void exec()
{
QTimer::singleShot(100, eventLoop, SLOT(quit()));
// this should return immediately, and the timer event should be delivered to
// tst_QEventLoop::exec() test, letting the test complete
returnCode = eventLoop->exec();
}
};
#ifdef QT_GUI_LIB
#define tst_QEventLoop tst_QGuiEventLoop
#endif
class tst_QEventLoop : public QObject
{
Q_OBJECT
private slots:
// This test *must* run first. See the definition for why.
void processEvents();
void exec();
void reexec();
void execAfterExit();
void wakeUp();
void quit();
#if defined(Q_OS_UNIX)
void processEventsExcludeSocket();
#endif
void processEventsExcludeTimers();
void deliverInDefinedOrder();
// keep this test last:
void nestedLoops();
void testQuitLock();
protected:
void customEvent(QEvent *e) override;
};
void tst_QEventLoop::processEvents()
{
QSignalSpy aboutToBlockSpy(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::aboutToBlock);
QSignalSpy awakeSpy(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::awake);
QVERIFY(aboutToBlockSpy.isValid());
QVERIFY(awakeSpy.isValid());
QEventLoop eventLoop;
QCoreApplication::postEvent(&eventLoop, new QEvent(QEvent::User));
// process posted events, QEventLoop::processEvents() should return
// true
QVERIFY(eventLoop.processEvents());
QCOMPARE(aboutToBlockSpy.size(), 0);
QCOMPARE(awakeSpy.size(), 1);
// allow any session manager to complete its handshake, so that
// there are no pending events left. This tests that we are able
// to process all events from the queue, otherwise it will hang.
while (eventLoop.processEvents())
;
// make sure the test doesn't block forever
int timerId = startTimer(100);
// wait for more events to process, QEventLoop::processEvents()
// should return true
aboutToBlockSpy.clear();
awakeSpy.clear();
QVERIFY(eventLoop.processEvents(QEventLoop::WaitForMoreEvents));
// We should get one awake for each aboutToBlock, plus one awake when
// processEvents is entered. There is no guarantee that that the
// processEvents call actually blocked, since the OS may introduce
// native events at any time.
QVERIFY(awakeSpy.size() > 0);
QVERIFY(awakeSpy.size() >= aboutToBlockSpy.size());
killTimer(timerId);
}
#define EXEC_TIMEOUT 100
void tst_QEventLoop::exec()
{
{
QEventLoop eventLoop;
EventLoopExiter exiter(&eventLoop);
int returnCode;
QTimer::singleShot(EXEC_TIMEOUT, &exiter, SLOT(exit()));
returnCode = eventLoop.exec();
QCOMPARE(returnCode, 0);
QTimer::singleShot(EXEC_TIMEOUT, &exiter, SLOT(exit1()));
returnCode = eventLoop.exec();
QCOMPARE(returnCode, 1);
QTimer::singleShot(EXEC_TIMEOUT, &exiter, SLOT(exit2()));
returnCode = eventLoop.exec();
QCOMPARE(returnCode, 2);
}
{
// calling QEventLoop::exec() after a thread loop has exit()ed should return immediately
// Note: this behaviour differs from QCoreApplication and QEventLoop
// see tst_QCoreApplication::eventLoopExecAfterExit, tst_QEventLoop::reexec
MultipleExecThread thread;
// start thread and wait for checkpoint
thread.mutex.lock();
thread.start();
thread.cond.wait(&thread.mutex);
// make sure the eventloop runs
QSignalSpy spy(QAbstractEventDispatcher::instance(&thread), &QAbstractEventDispatcher::awake);
QVERIFY(spy.isValid());
thread.cond.wakeOne();
thread.cond.wait(&thread.mutex);
QVERIFY(spy.size() > 0);
int v = thread.result1;
QCOMPARE(v, 0);
// exec should return immediately
spy.clear();
thread.cond.wakeOne();
thread.mutex.unlock();
thread.wait();
QCOMPARE(spy.size(), 0);
v = thread.result2;
QCOMPARE(v, -1);
}
{
// a single instance of QEventLoop should not be allowed to recurse into exec()
QEventLoop eventLoop;
EventLoopExecutor executor(&eventLoop);
QTimer::singleShot(EXEC_TIMEOUT, &executor, SLOT(exec()));
int returnCode = eventLoop.exec();
QCOMPARE(returnCode, 0);
QCOMPARE(executor.returnCode, -1);
}
}
void tst_QEventLoop::reexec()
{
QEventLoop loop;
// exec once
QMetaObject::invokeMethod(&loop, "quit", Qt::QueuedConnection);
QCOMPARE(loop.exec(), 0);
// and again
QMetaObject::invokeMethod(&loop, "quit", Qt::QueuedConnection);
QCOMPARE(loop.exec(), 0);
}
void tst_QEventLoop::execAfterExit()
{
QEventLoop loop;
EventLoopExiter obj(&loop);
QMetaObject::invokeMethod(&obj, "exit", Qt::QueuedConnection);
loop.exit(1);
QCOMPARE(loop.exec(), 0);
}
void tst_QEventLoop::wakeUp()
{
EventLoopThread thread;
QEventLoop eventLoop;
connect(&thread, SIGNAL(checkPoint()), &eventLoop, SLOT(quit()));
connect(&thread, SIGNAL(finished()), &eventLoop, SLOT(quit()));
thread.start();
(void) eventLoop.exec();
QSignalSpy spy(QAbstractEventDispatcher::instance(&thread), &QAbstractEventDispatcher::awake);
QVERIFY(spy.isValid());
thread.eventLoop->wakeUp();
// give the thread time to wake up
QTimer::singleShot(1000, &eventLoop, SLOT(quit()));
(void) eventLoop.exec();
QVERIFY(spy.size() > 0);
thread.quit();
(void) eventLoop.exec();
}
void tst_QEventLoop::quit()
{
QEventLoop eventLoop;
int returnCode;
QTimer::singleShot(100, &eventLoop, SLOT(quit()));
returnCode = eventLoop.exec();
QCOMPARE(returnCode, 0);
}
void tst_QEventLoop::nestedLoops()
{
QCoreApplication::postEvent(this, new StartStopEvent(QEvent::User));
QCoreApplication::postEvent(this, new StartStopEvent(QEvent::User));
QCoreApplication::postEvent(this, new StartStopEvent(QEvent::User));
// without the fix, this will *wedge* and never return
QTest::qWait(1000);
}
void tst_QEventLoop::customEvent(QEvent *e)
{
if (e->type() == QEvent::User) {
QEventLoop loop;
QCoreApplication::postEvent(this, new StartStopEvent(int(QEvent::User) + 1, &loop));
loop.exec();
} else {
static_cast<StartStopEvent *>(e)->el->exit();
}
}
#if defined(Q_OS_UNIX)
class SocketEventsTester: public QObject
{
Q_OBJECT
public:
SocketEventsTester()
{
socket = 0;
server = 0;
dataSent = false;
dataReadable = false;
testResult = false;
dataArrived = false;
}
~SocketEventsTester()
{
delete socket;
delete server;
}
bool init()
{
bool ret = false;
server = new QTcpServer();
socket = new QTcpSocket();
connect(server, SIGNAL(newConnection()), this, SLOT(sendHello()));
connect(socket, SIGNAL(readyRead()), this, SLOT(sendAck()), Qt::DirectConnection);
if((ret = server->listen(QHostAddress::LocalHost, 0))) {
socket->connectToHost(server->serverAddress(), server->serverPort());
socket->waitForConnected();
}
return ret;
}
QTcpSocket *socket;
QTcpServer *server;
bool dataSent;
bool dataReadable;
bool testResult;
bool dataArrived;
public slots:
void sendAck()
{
dataArrived = true;
}
void sendHello()
{
char data[10] ="HELLO";
qint64 size = sizeof(data);
QTcpSocket *serverSocket = server->nextPendingConnection();
QCoreApplication::processEvents();
serverSocket->write(data, size);
dataSent = serverSocket->waitForBytesWritten(-1);
if (dataSent) {
pollfd pfd = qt_make_pollfd(socket->socketDescriptor(), POLLIN);
dataReadable = (1 == qt_safe_poll(&pfd, 1, nullptr));
}
if (!dataReadable) {
testResult = dataArrived;
} else {
QCoreApplication::processEvents(QEventLoop::ExcludeSocketNotifiers);
testResult = dataArrived;
// to check if the deferred event is processed
QCoreApplication::processEvents();
}
serverSocket->close();
QThread::currentThread()->exit(0);
}
};
class SocketTestThread : public QThread
{
Q_OBJECT
public:
SocketTestThread():QThread(0),testResult(false){};
void run() override
{
SocketEventsTester *tester = new SocketEventsTester();
if (tester->init())
exec();
dataSent = tester->dataSent;
dataReadable = tester->dataReadable;
testResult = tester->testResult;
dataArrived = tester->dataArrived;
delete tester;
}
bool dataSent;
bool dataReadable;
bool testResult;
bool dataArrived;
};
void tst_QEventLoop::processEventsExcludeSocket()
{
SocketTestThread thread;
thread.start();
QVERIFY(thread.wait());
QVERIFY(thread.dataSent);
QVERIFY(thread.dataReadable);
#if defined(HAVE_GLIB)
QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher();
if (qobject_cast<QEventDispatcherGlib *>(eventDispatcher))
QEXPECT_FAIL("", "ExcludeSocketNotifiers is currently broken in the Glib dispatchers", Continue);
#endif
QVERIFY(!thread.testResult);
QVERIFY(thread.dataArrived);
}
#endif
class TimerReceiver : public QObject
{
public:
int gotTimerEvent;
TimerReceiver()
: QObject(), gotTimerEvent(-1)
{ }
void timerEvent(QTimerEvent *event) override
{
gotTimerEvent = event->timerId();
}
};
void tst_QEventLoop::processEventsExcludeTimers()
{
TimerReceiver timerReceiver;
int timerId = timerReceiver.startTimer(0);
QEventLoop eventLoop;
// normal process events will send timers
eventLoop.processEvents();
QCOMPARE(timerReceiver.gotTimerEvent, timerId);
timerReceiver.gotTimerEvent = -1;
// but not if we exclude timers
eventLoop.processEvents(QEventLoop::X11ExcludeTimers);
#if defined(Q_OS_UNIX)
QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher();
if (!qobject_cast<QEventDispatcherUNIX *>(eventDispatcher)
#if defined(HAVE_GLIB)
&& !qobject_cast<QEventDispatcherGlib *>(eventDispatcher)
#endif
)
#endif
QEXPECT_FAIL("", "X11ExcludeTimers only supported in the UNIX/Glib dispatchers", Continue);
QCOMPARE(timerReceiver.gotTimerEvent, -1);
timerReceiver.gotTimerEvent = -1;
// resume timer processing
eventLoop.processEvents();
QCOMPARE(timerReceiver.gotTimerEvent, timerId);
timerReceiver.gotTimerEvent = -1;
}
namespace DeliverInDefinedOrder {
enum { NbThread = 3, NbObject = 500, NbEventQueue = 5, NbEvent = 50 };
struct CustomEvent : public QEvent {
CustomEvent(int q, int v) : QEvent(Type(User + q)), value(v) {}
int value;
};
struct Object : public QObject {
Q_OBJECT
public:
Object() : count(0) {
for (int i = 0; i < NbEventQueue; i++)
lastReceived[i] = -1;
}
int lastReceived[NbEventQueue];
int count;
virtual void customEvent(QEvent* e) override {
QVERIFY(e->type() >= QEvent::User);
QVERIFY(e->type() < QEvent::User + 5);
uint idx = e->type() - QEvent::User;
int value = static_cast<CustomEvent *>(e)->value;
QVERIFY(lastReceived[idx] < value);
lastReceived[idx] = value;
count++;
}
public slots:
void moveToThread(QThread *t) {
QObject::moveToThread(t);
}
};
}
void tst_QEventLoop::deliverInDefinedOrder()
{
using namespace DeliverInDefinedOrder;
qMetaTypeId<QThread*>();
QThread threads[NbThread];
// GHS compiler needs the namespace prefix, despite using above.
DeliverInDefinedOrder::Object objects[NbObject];
for (int t = 0; t < NbThread; t++) {
threads[t].start();
}
int event = 0;
for (int o = 0; o < NbObject; o++) {
objects[o].moveToThread(&threads[o % NbThread]);
for (int e = 0; e < NbEvent; e++) {
int q = e % NbEventQueue;
QCoreApplication::postEvent(&objects[o], new CustomEvent(q, ++event) , q);
if (e % 7)
QMetaObject::invokeMethod(&objects[o], "moveToThread", Qt::QueuedConnection, Q_ARG(QThread*, &threads[(e+o)%NbThread]));
}
}
for (int o = 0; o < NbObject; o++) {
QTRY_COMPARE(objects[o].count, int(NbEvent));
}
for (int t = 0; t < NbThread; t++) {
threads[t].quit();
threads[t].wait();
}
}
class JobObject : public QObject
{
Q_OBJECT
public:
explicit JobObject(QEventLoop *loop, QObject *parent = nullptr)
: QObject(parent), locker(loop)
{
}
explicit JobObject(QObject *parent = nullptr)
: QObject(parent)
{
}
public slots:
void start(int timeout = 200)
{
QTimer::singleShot(timeout, this, SLOT(timeout()));
}
private slots:
void timeout()
{
emit done();
deleteLater();
}
signals:
void done();
private:
QEventLoopLocker locker;
};
void tst_QEventLoop::testQuitLock()
{
QEventLoop eventLoop;
QEventLoopPrivate* privateClass = static_cast<QEventLoopPrivate*>(QObjectPrivate::get(&eventLoop));
QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 0);
JobObject *job1 = new JobObject(&eventLoop, this);
job1->start(500);
QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 1);
eventLoop.exec();
QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 0);
job1 = new JobObject(&eventLoop, this);
job1->start(200);
JobObject *previousJob = job1;
for (int i = 0; i < 9; ++i) {
JobObject *subJob = new JobObject(&eventLoop, this);
connect(previousJob, SIGNAL(done()), subJob, SLOT(start()));
previousJob = subJob;
}
eventLoop.exec();
}
QTEST_MAIN(tst_QEventLoop)
#include "tst_qeventloop.moc"

View File

@ -0,0 +1,17 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qjnienvironment Test:
#####################################################################
qt_internal_add_test(tst_qjnienvironment
SOURCES
tst_qjnienvironment.cpp
)
if(ANDROID)
set_property(TARGET tst_qjnienvironment APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
${CMAKE_CURRENT_SOURCE_DIR}/testdata
)
endif()

View File

@ -0,0 +1,60 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
package org.qtproject.qt.android.testdatapackage;
public class QtJniEnvironmentTestClass
{
private static native void callbackFromJava(String message);
private static native void namedCallbackFromJava(String message);
private static native void memberCallbackFromJava(String message);
private static native void namedMemberCallbackFromJava(String message);
private static native void namespaceCallbackFromJava(String message);
private static native void intCallbackFromJava(int value);
public final int INT_FIELD = 123;
public static final int S_INT_FIELD = 321;
QtJniEnvironmentTestClass() {}
public static void appendJavaToString(String message)
{
callbackFromJava("From Java: " + message);
}
public static void namedAppendJavaToString(String message)
{
namedCallbackFromJava("From Java (named): " + message);
}
public static void memberAppendJavaToString(String message)
{
memberCallbackFromJava("From Java (member): " + message);
}
public static void namedMemberAppendJavaToString(String message)
{
namedMemberCallbackFromJava("From Java (named member): " + message);
}
public static void namespaceAppendJavaToString(String message)
{
namespaceCallbackFromJava("From Java (namespace): " + message);
}
public static void convertToInt(String message)
{
intCallbackFromJava(Integer.parseInt(message));
}
}
class QtJniEnvironmentTestClassNoCtor
{
private static native void callbackFromJavaNoCtor(String message);
public static void appendJavaToString(String message)
{
callbackFromJavaNoCtor("From Java (no ctor): " + message);
}
}

View File

@ -0,0 +1,362 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <jni.h>
#include <QtCore/QJniEnvironment>
#include <QtCore/QJniObject>
#include <QtTest/QtTest>
static const char javaTestClass[] =
"org/qtproject/qt/android/testdatapackage/QtJniEnvironmentTestClass";
static const char javaTestClassNoCtor[] =
"org/qtproject/qt/android/testdatapackage/QtJniEnvironmentTestClassNoCtor";
static QString registerNativesString = QStringLiteral("Qt");
static int registerNativeInteger = 0;
class tst_QJniEnvironment : public QObject
{
Q_OBJECT
private slots:
void jniEnv();
void javaVM();
void registerNativeMethods();
void registerNativeMethodsByJclass();
void findMethod();
void findStaticMethod();
void findField();
void findStaticField();
};
void tst_QJniEnvironment::jniEnv()
{
QJniEnvironment env;
JavaVM *javaVM = env.javaVM();
QVERIFY(javaVM);
{
// JNI environment should now be attached to the current thread
JNIEnv *jni = 0;
QCOMPARE(javaVM->GetEnv((void**)&jni, JNI_VERSION_1_6), JNI_OK);
JNIEnv *e = env.jniEnv();
QVERIFY(e);
QCOMPARE(env->GetVersion(), JNI_VERSION_1_6);
// try to find an existing class
QVERIFY(env->FindClass("java/lang/Object"));
QVERIFY(!env->ExceptionCheck());
// try to find a nonexistent class
QVERIFY(!env->FindClass("this/doesnt/Exist"));
QVERIFY(env->ExceptionCheck());
env->ExceptionClear();
QVERIFY(env->FindClass("java/lang/Object"));
QVERIFY(!QJniEnvironment::checkAndClearExceptions(env.jniEnv()));
// try to find a nonexistent class
QVERIFY(!env->FindClass("this/doesnt/Exist"));
QVERIFY(QJniEnvironment::checkAndClearExceptions(env.jniEnv()));
// try to find an existing class with QJniEnvironment
QJniEnvironment env;
QVERIFY(env.findClass("java/lang/Object"));
QVERIFY(env.findClass<jstring>());
// try to find a nonexistent class
QVERIFY(!env.findClass("this/doesnt/Exist"));
// clear exception with member function
QVERIFY(!env->FindClass("this/doesnt/Exist"));
QVERIFY(env.checkAndClearExceptions());
}
// The env does not detach automatically, even if it goes out of scope. The only way it can
// be detached is if it's done explicitly, or if the thread we attached to gets killed (TLS clean-up).
JNIEnv *jni = nullptr;
QCOMPARE(javaVM->GetEnv((void**)&jni, JNI_VERSION_1_6), JNI_OK);
}
void tst_QJniEnvironment::javaVM()
{
QJniEnvironment env;
JavaVM *javaVM = env.javaVM();
QVERIFY(javaVM);
QCOMPARE(env.javaVM(), javaVM);
JavaVM *vm = 0;
QCOMPARE(env->GetJavaVM(&vm), JNI_OK);
QCOMPARE(env.javaVM(), vm);
}
static void callbackFromJava(JNIEnv *env, jobject /*thiz*/, jstring value)
{
Q_UNUSED(env)
registerNativesString = QJniObject(value).toString();
}
Q_DECLARE_JNI_NATIVE_METHOD(callbackFromJava);
static void tediouslyLongNamed_callbackFromJava(JNIEnv *env, jobject /*thiz*/, jstring value)
{
Q_UNUSED(env)
registerNativesString = QJniObject(value).toString();
}
Q_DECLARE_JNI_NATIVE_METHOD(tediouslyLongNamed_callbackFromJava, namedCallbackFromJava)
static void callbackFromJavaNoCtor(JNIEnv *env, jobject /*thiz*/, jstring value)
{
Q_UNUSED(env)
registerNativesString = QJniObject(value).toString();
}
Q_DECLARE_JNI_NATIVE_METHOD(callbackFromJavaNoCtor);
class CallbackClass {
public:
static void memberCallbackFromJava(JNIEnv *env, jobject /*thiz*/, jstring value)
{
Q_UNUSED(env)
registerNativesString = QJniObject(value).toString();
}
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(memberCallbackFromJava)
static void tediouslyLongNamed_memberCallbackFromJava(JNIEnv *env, jobject /*thiz*/,
jstring value)
{
Q_UNUSED(env)
registerNativesString = QJniObject(value).toString();
}
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(tediouslyLongNamed_memberCallbackFromJava,
namedMemberCallbackFromJava)
};
namespace CallbackNamespace {
static void namespaceCallbackFromJava(JNIEnv *env, jobject /*thiz*/, jstring value)
{
Q_UNUSED(env)
registerNativesString = QJniObject(value).toString();
}
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(namespaceCallbackFromJava)
}
void tst_QJniEnvironment::registerNativeMethods()
{
QJniObject QtString = QJniObject::fromString(registerNativesString);
QJniEnvironment env;
{
QVERIFY(env.registerNativeMethods(javaTestClass, {
Q_JNI_NATIVE_METHOD(callbackFromJava)
}));
QJniObject::callStaticMethod<void>(javaTestClass,
"appendJavaToString",
"(Ljava/lang/String;)V",
QtString.object<jstring>());
QTest::qWait(200);
QVERIFY(registerNativesString == QStringLiteral("From Java: Qt"));
}
// Named native function
{
QVERIFY(env.registerNativeMethods(javaTestClass, {
Q_JNI_NATIVE_METHOD(tediouslyLongNamed_callbackFromJava)
}));
QJniObject::callStaticMethod<void>(javaTestClass,
"namedAppendJavaToString",
"(Ljava/lang/String;)V",
QtString.object<jstring>());
QTest::qWait(200);
QVERIFY(registerNativesString == QStringLiteral("From Java (named): Qt"));
}
// Static class member as callback
{
QVERIFY(env.registerNativeMethods(javaTestClass, {
Q_JNI_NATIVE_SCOPED_METHOD(memberCallbackFromJava, CallbackClass)
}));
QJniObject::callStaticMethod<void>(javaTestClass,
"memberAppendJavaToString",
"(Ljava/lang/String;)V",
QtString.object<jstring>());
QTest::qWait(200);
QVERIFY(registerNativesString == QStringLiteral("From Java (member): Qt"));
}
// Static named class member as callback
{
QVERIFY(env.registerNativeMethods(javaTestClass, {
Q_JNI_NATIVE_SCOPED_METHOD(tediouslyLongNamed_memberCallbackFromJava,
CallbackClass)
}));
QJniObject::callStaticMethod<void>(javaTestClass,
"namedMemberAppendJavaToString",
"(Ljava/lang/String;)V",
QtString.object<jstring>());
QTest::qWait(200);
QVERIFY(registerNativesString == QStringLiteral("From Java (named member): Qt"));
}
// Function generally just in namespace as callback
{
QVERIFY(env.registerNativeMethods(javaTestClass, {
Q_JNI_NATIVE_SCOPED_METHOD(namespaceCallbackFromJava, CallbackNamespace)
}));
QJniObject::callStaticMethod<void>(javaTestClass,
"namespaceAppendJavaToString",
"(Ljava/lang/String;)V",
QtString.object<jstring>());
QTest::qWait(200);
QVERIFY(registerNativesString == QStringLiteral("From Java (namespace): Qt"));
}
// No default constructor in class
{
QVERIFY(env.registerNativeMethods(javaTestClassNoCtor, {
Q_JNI_NATIVE_METHOD(callbackFromJavaNoCtor)
}));
QJniObject::callStaticMethod<void>(javaTestClassNoCtor,
"appendJavaToString",
"(Ljava/lang/String;)V",
QtString.object<jstring>());
QTest::qWait(200);
QVERIFY(registerNativesString == QStringLiteral("From Java (no ctor): Qt"));
}
}
static void intCallbackFromJava(JNIEnv *env, jobject /*thiz*/, jint value)
{
Q_UNUSED(env)
registerNativeInteger = static_cast<int>(value);
}
Q_DECLARE_JNI_NATIVE_METHOD(intCallbackFromJava);
void tst_QJniEnvironment::registerNativeMethodsByJclass()
{
QJniEnvironment env;
jclass clazz = env.findClass(javaTestClass);
QVERIFY(clazz != 0);
QVERIFY(env.registerNativeMethods(clazz, {
Q_JNI_NATIVE_METHOD(intCallbackFromJava)
}));
QCOMPARE(registerNativeInteger, 0);
QJniObject parameter = QJniObject::fromString(QString("123"));
QJniObject::callStaticMethod<void>(clazz, "convertToInt", "(Ljava/lang/String;)V",
parameter.object<jstring>());
QTest::qWait(200);
QCOMPARE(registerNativeInteger, 123);
}
void tst_QJniEnvironment::findMethod()
{
QJniEnvironment env;
jclass clazz = env.findClass("java/lang/Integer");
QVERIFY(clazz != nullptr);
// existing method
jmethodID methodId = env.findMethod(clazz, "toString", "()Ljava/lang/String;");
QVERIFY(methodId != nullptr);
// existing method
methodId = env.findMethod<jstring>(clazz, "toString");
QVERIFY(methodId != nullptr);
// invalid signature
jmethodID invalid = env.findMethod(clazz, "unknown", "()I");
QVERIFY(invalid == nullptr);
// check that all exceptions are already cleared
QVERIFY(!env.checkAndClearExceptions());
}
void tst_QJniEnvironment::findStaticMethod()
{
QJniEnvironment env;
jclass clazz = env.findClass("java/lang/Integer");
QVERIFY(clazz != nullptr);
// existing method
jmethodID staticMethodId = env.findStaticMethod(clazz, "parseInt", "(Ljava/lang/String;)I");
QVERIFY(staticMethodId != nullptr);
// existing method
staticMethodId = env.findStaticMethod<jint, jstring>(clazz, "parseInt");
QVERIFY(staticMethodId != nullptr);
QJniObject parameter = QJniObject::fromString("123");
jint result = QJniObject::callStaticMethod<jint>(clazz, staticMethodId,
parameter.object<jstring>());
QCOMPARE(result, 123);
// invalid method
jmethodID invalid = env.findStaticMethod(clazz, "unknown", "()I");
QVERIFY(invalid == nullptr);
invalid = env.findStaticMethod<jint>(clazz, "unknown");
QVERIFY(invalid == nullptr);
// check that all exceptions are already cleared
QVERIFY(!env.checkAndClearExceptions());
}
void tst_QJniEnvironment::findField()
{
QJniEnvironment env;
jclass clazz = env.findClass(javaTestClass);
QVERIFY(clazz != nullptr);
// valid field
jfieldID validId = env.findField(clazz, "INT_FIELD", "I");
QVERIFY(validId != nullptr);
validId = env.findField<jint>(clazz, "INT_FIELD");
QVERIFY(validId != nullptr);
jmethodID constructorId = env.findMethod(clazz, "<init>", "()V");
QVERIFY(constructorId != nullptr);
jobject obj = env->NewObject(clazz, constructorId);
QVERIFY(!env.checkAndClearExceptions());
int value = env->GetIntField(obj, validId);
QVERIFY(!env.checkAndClearExceptions());
QVERIFY(value == 123);
// invalid signature
jfieldID invalidId = env.findField(clazz, "unknown", "I");
QVERIFY(invalidId == nullptr);
// check that all exceptions are already cleared
QVERIFY(!env.checkAndClearExceptions());
}
void tst_QJniEnvironment::findStaticField()
{
QJniEnvironment env;
jclass clazz = env.findClass(javaTestClass);
QVERIFY(clazz != nullptr);
// valid field
jfieldID validId = env.findStaticField(clazz, "S_INT_FIELD", "I");
QVERIFY(validId != nullptr);
validId = env.findStaticField<jint>(clazz, "S_INT_FIELD");
QVERIFY(validId != nullptr);
int size = env->GetStaticIntField(clazz, validId);
QVERIFY(!env.checkAndClearExceptions());
QVERIFY(size == 321);
// invalid signature
jfieldID invalidId = env.findStaticField(clazz, "unknown", "I");
QVERIFY(invalidId == nullptr);
// check that all exceptions are already cleared
QVERIFY(!env.checkAndClearExceptions());
}
QTEST_MAIN(tst_QJniEnvironment)
#include "tst_qjnienvironment.moc"

View File

@ -0,0 +1,17 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qjniobject Test:
#####################################################################
qt_internal_add_test(tst_qjniobject
SOURCES
tst_qjniobject.cpp
)
if(ANDROID)
set_property(TARGET tst_qjniobject APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
${CMAKE_CURRENT_SOURCE_DIR}/testdata
)
endif()

View File

@ -0,0 +1,174 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
package org.qtproject.qt.android.testdatapackage;
public class QtJniObjectTestClass
{
static final byte A_BYTE_VALUE = 127;
static final short A_SHORT_VALUE = 32767;
static final int A_INT_VALUE = 060701;
static final long A_LONG_VALUE = 060701;
static final float A_FLOAT_VALUE = 1.0f;
static final double A_DOUBLE_VALUE = 1.0;
static final boolean A_BOOLEAN_VALUE = true;
static final char A_CHAR_VALUE = 'Q';
static final String A_STRING_OBJECT = "TEST_DATA_STRING";
static final Class A_CLASS_OBJECT = QtJniObjectTestClass.class;
static final Object A_OBJECT_OBJECT = new QtJniObjectTestClass();
static final Throwable A_THROWABLE_OBJECT = new Throwable(A_STRING_OBJECT);
// --------------------------------------------------------------------------------------------
byte BYTE_VAR;
short SHORT_VAR;
int INT_VAR;
long LONG_VAR;
float FLOAT_VAR;
double DOUBLE_VAR;
boolean BOOLEAN_VAR;
char CHAR_VAR;
String STRING_OBJECT_VAR;
static byte S_BYTE_VAR;
static short S_SHORT_VAR;
static int S_INT_VAR;
static long S_LONG_VAR;
static float S_FLOAT_VAR;
static double S_DOUBLE_VAR;
static boolean S_BOOLEAN_VAR;
static char S_CHAR_VAR;
static String S_STRING_OBJECT_VAR;
// --------------------------------------------------------------------------------------------
public static void staticVoidMethod() { return; }
public static void staticVoidMethodWithArgs(int a, boolean b, char c) { return; }
public void voidMethod() { return; }
public void voidMethodWithArgs(int a, boolean b, char c) { return; }
// --------------------------------------------------------------------------------------------
public static boolean staticBooleanMethod() { return A_BOOLEAN_VALUE; }
public static boolean staticBooleanMethodWithArgs(boolean a, boolean b, boolean c)
{ return staticBooleanMethod(); }
public boolean booleanMethod() { return staticBooleanMethod(); }
public boolean booleanMethodWithArgs(boolean a, boolean b, boolean c)
{ return staticBooleanMethodWithArgs(a, b, c); }
// --------------------------------------------------------------------------------------------
public static byte staticByteMethod() { return A_BYTE_VALUE; }
public static byte staticByteMethodWithArgs(byte a, byte b, byte c) { return staticByteMethod(); }
public byte byteMethod() { return staticByteMethod(); }
public byte byteMethodWithArgs(byte a, byte b, byte c)
{ return staticByteMethodWithArgs(a, b, c); }
// --------------------------------------------------------------------------------------------
public static char staticCharMethod() { return A_CHAR_VALUE; }
public static char staticCharMethodWithArgs(char a, char b, char c) { return staticCharMethod(); }
public char charMethod() { return staticCharMethod(); }
public char charMethodWithArgs(char a, char b, char c)
{ return staticCharMethodWithArgs(a, b, c); }
// --------------------------------------------------------------------------------------------
public static short staticShortMethod() { return A_SHORT_VALUE; }
public static short staticShortMethodWithArgs(short a, short b, short c) { return staticShortMethod(); }
public short shortMethod() { return staticShortMethod(); }
public short shortMethodWithArgs(short a, short b, short c)
{ return staticShortMethodWithArgs(a, b, c); }
// --------------------------------------------------------------------------------------------
public static int staticIntMethod() { return A_INT_VALUE; }
public static int staticIntMethodWithArgs(int a, int b, int c) { return staticIntMethod(); }
public int intMethod() { return staticIntMethod(); }
public int intMethodWithArgs(int a, int b, int c) { return staticIntMethodWithArgs(a, b, c); }
// --------------------------------------------------------------------------------------------
public static long staticLongMethod() { return A_LONG_VALUE; }
public static long staticLongMethodWithArgs(long a, long b, long c) { return staticLongMethod(); }
public long longMethod() { return staticLongMethod(); }
public long longMethodWithArgs(long a, long b, long c)
{ return staticLongMethodWithArgs(a, b, c); }
// --------------------------------------------------------------------------------------------
public static float staticFloatMethod() { return A_FLOAT_VALUE; }
public static float staticFloatMethodWithArgs(float a, float b, float c) { return staticFloatMethod(); }
public float floatMethod() { return staticFloatMethod(); }
public float floatMethodWithArgs(float a, float b, float c)
{ return staticFloatMethodWithArgs(a, b, c); }
// --------------------------------------------------------------------------------------------
public static double staticDoubleMethod() { return A_DOUBLE_VALUE; }
public static double staticDoubleMethodWithArgs(double a, double b, double c)
{ return staticDoubleMethod(); }
public double doubleMethod() { return staticDoubleMethod(); }
public double doubleMethodWithArgs(double a, double b, double c)
{ return staticDoubleMethodWithArgs(a, b, c); }
// --------------------------------------------------------------------------------------------
public static Object staticObjectMethod() { return A_OBJECT_OBJECT; }
public Object objectMethod() { return staticObjectMethod(); }
// --------------------------------------------------------------------------------------------
public static Class staticClassMethod() { return A_CLASS_OBJECT; }
public Class classMethod() { return staticClassMethod(); }
// --------------------------------------------------------------------------------------------
public static String staticStringMethod() { return A_STRING_OBJECT; }
public String stringMethod() { return staticStringMethod(); }
// --------------------------------------------------------------------------------------------
public static Throwable staticThrowableMethod() { return A_THROWABLE_OBJECT; }
public Throwable throwableMethod() { return staticThrowableMethod(); }
// --------------------------------------------------------------------------------------------
public static Object[] staticObjectArrayMethod()
{ Object[] array = { new Object(), new Object(), new Object() }; return array; }
public Object[] objectArrayMethod() { return staticObjectArrayMethod(); }
// --------------------------------------------------------------------------------------------
public static boolean[] staticBooleanArrayMethod()
{ boolean[] array = { true, true, true }; return array; }
public boolean[] booleanArrayMethod() { return staticBooleanArrayMethod(); }
// --------------------------------------------------------------------------------------------
public static byte[] staticByteArrayMethod()
{ byte[] array = { 'a', 'b', 'c' }; return array; }
public byte[] byteArrayMethod() { return staticByteArrayMethod(); }
// --------------------------------------------------------------------------------------------
public static char[] staticCharArrayMethod()
{ char[] array = { 'a', 'b', 'c' }; return array; }
public char[] charArrayMethod() { return staticCharArrayMethod(); }
// --------------------------------------------------------------------------------------------
public static short[] staticShortArrayMethod() { short[] array = { 3, 2, 1 }; return array; }
public short[] shortArrayMethod() { return staticShortArrayMethod(); }
// --------------------------------------------------------------------------------------------
public static int[] staticIntArrayMethod() { int[] array = { 3, 2, 1 }; return array; }
public int[] intArrayMethod() { return staticIntArrayMethod(); }
// --------------------------------------------------------------------------------------------
public static long[] staticLongArrayMethod()
{ long[] array = { 3, 2, 1 }; return array; }
public long[] longArrayMethod() { return staticLongArrayMethod(); }
// --------------------------------------------------------------------------------------------
public static float[] staticFloatArrayMethod()
{ float[] array = { 1.0f, 2.0f, 3.0f }; return array; }
public float[] floatArrayMethod() { return staticFloatArrayMethod(); }
// --------------------------------------------------------------------------------------------
public static double[] staticDoubleArrayMethod()
{ double[] array = { 3.0, 2.0, 1.0 }; return array; }
public double[] doubleArrayMethod() { return staticDoubleArrayMethod(); }
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_test(tst_qjnitypes
SOURCES
tst_qjnitypes.cpp
)

View File

@ -0,0 +1,133 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtTest>
#include <QtCore/qjnitypes.h>
class tst_QJniTypes : public QObject
{
Q_OBJECT
public:
tst_QJniTypes() = default;
private slots:
void initTestCase();
void nativeMethod();
};
struct QtJavaWrapper {};
template<>
constexpr auto QtJniTypes::typeSignature<QtJavaWrapper>()
{
return QtJniTypes::String("Lorg/qtproject/qt/android/QtJavaWrapper;");
}
template<>
constexpr auto QtJniTypes::typeSignature<QJniObject>()
{
return QtJniTypes::String("Ljava/lang/Object;");
}
struct QtCustomJniObject : QJniObject {};
template<>
constexpr auto QtJniTypes::typeSignature<QtCustomJniObject>()
{
return QtJniTypes::String("Lorg/qtproject/qt/android/QtCustomJniObject;");
}
static_assert(QtJniTypes::typeSignature<QtJavaWrapper>() == "Lorg/qtproject/qt/android/QtJavaWrapper;");
static_assert(QtJniTypes::typeSignature<QtJavaWrapper>() != "Ljava/lang/Object;");
static_assert(!(QtJniTypes::typeSignature<QtJavaWrapper>() == "X"));
Q_DECLARE_JNI_TYPE(JavaType, "Lorg/qtproject/qt/JavaType;");
static_assert(QtJniTypes::typeSignature<QtJniTypes::JavaType>() == "Lorg/qtproject/qt/JavaType;");
Q_DECLARE_JNI_TYPE(ArrayType, "[Lorg/qtproject/qt/ArrayType;")
static_assert(QtJniTypes::typeSignature<QtJniTypes::ArrayType>() == "[Lorg/qtproject/qt/ArrayType;");
static_assert(QtJniTypes::className<jstring>() == "java/lang/String");
Q_DECLARE_JNI_CLASS(QtTextToSpeech, "org/qtproject/qt/android/speech/QtTextToSpeech")
static_assert(QtJniTypes::className<QtJniTypes::QtTextToSpeech>() == "org/qtproject/qt/android/speech/QtTextToSpeech");
static_assert(QtJniTypes::fieldSignature<jint>() == "I");
static_assert(QtJniTypes::fieldSignature<jint[]>() == "[I");
static_assert(QtJniTypes::fieldSignature<jint>() != "X");
static_assert(QtJniTypes::fieldSignature<jint>() != "Ljava/lang/Object;");
static_assert(QtJniTypes::fieldSignature<jlong>() == "J");
static_assert(QtJniTypes::fieldSignature<jstring>() == "Ljava/lang/String;");
static_assert(QtJniTypes::fieldSignature<jobject>() == "Ljava/lang/Object;");
static_assert(QtJniTypes::fieldSignature<jobject[]>() == "[Ljava/lang/Object;");
static_assert(QtJniTypes::fieldSignature<jobjectArray>() == "[Ljava/lang/Object;");
static_assert(QtJniTypes::fieldSignature<QJniObject>() == "Ljava/lang/Object;");
static_assert(QtJniTypes::fieldSignature<QtJavaWrapper>() == "Lorg/qtproject/qt/android/QtJavaWrapper;");
static_assert(QtJniTypes::fieldSignature<QtJavaWrapper[]>() == "[Lorg/qtproject/qt/android/QtJavaWrapper;");
static_assert(QtJniTypes::fieldSignature<QtCustomJniObject>() == "Lorg/qtproject/qt/android/QtCustomJniObject;");
static_assert(QtJniTypes::methodSignature<void>() == "()V");
static_assert(QtJniTypes::methodSignature<void>() != "()X");
static_assert(QtJniTypes::methodSignature<void, jint>() == "(I)V");
static_assert(QtJniTypes::methodSignature<void, jint, jstring>() == "(ILjava/lang/String;)V");
static_assert(QtJniTypes::methodSignature<jlong, jint, jclass>() == "(ILjava/lang/Class;)J");
static_assert(QtJniTypes::methodSignature<jobject, jint, jstring>() == "(ILjava/lang/String;)Ljava/lang/Object;");
static_assert(QtJniTypes::methodSignature<QtJniTypes::JavaType, jint, jstring>()
== "(ILjava/lang/String;)Lorg/qtproject/qt/JavaType;");
static_assert(QtJniTypes::isPrimitiveType<jint>());
static_assert(QtJniTypes::isPrimitiveType<void>());
static_assert(!QtJniTypes::isPrimitiveType<jobject>());
static_assert(!QtJniTypes::isPrimitiveType<QtCustomJniObject>());
static_assert(!QtJniTypes::isObjectType<jint>());
static_assert(!QtJniTypes::isObjectType<void>());
static_assert(QtJniTypes::isObjectType<jobject>());
static_assert(QtJniTypes::isObjectType<jobjectArray>());
static_assert(QtJniTypes::isObjectType<QtCustomJniObject>());
static_assert(!QtJniTypes::isArrayType<jint>());
static_assert(QtJniTypes::isArrayType<jint[]>());
static_assert(QtJniTypes::isArrayType<jobject[]>());
static_assert(QtJniTypes::isArrayType<jobjectArray>());
static_assert(QtJniTypes::isArrayType<QtJavaWrapper[]>());
static_assert(QtJniTypes::String("ABCDE").startsWith("ABC"));
static_assert(QtJniTypes::String("ABCDE").startsWith("A"));
static_assert(QtJniTypes::String("ABCDE").startsWith("ABCDE"));
static_assert(!QtJniTypes::String("ABCDE").startsWith("ABCDEF"));
static_assert(!QtJniTypes::String("ABCDE").startsWith("9AB"));
static_assert(QtJniTypes::String("ABCDE").startsWith('A'));
static_assert(!QtJniTypes::String("ABCDE").startsWith('B'));
static_assert(QtJniTypes::String("ABCDE").endsWith("CDE"));
static_assert(QtJniTypes::String("ABCDE").endsWith("E"));
static_assert(QtJniTypes::String("ABCDE").endsWith("ABCDE"));
static_assert(!QtJniTypes::String("ABCDE").endsWith("DEF"));
static_assert(!QtJniTypes::String("ABCDE").endsWith("ABCDEF"));
static_assert(QtJniTypes::String("ABCDE").endsWith('E'));
static_assert(!QtJniTypes::String("ABCDE").endsWith('F'));
void tst_QJniTypes::initTestCase()
{
}
static bool nativeFunction(JNIEnv *, jclass, int, jstring, long)
{
return true;
}
Q_DECLARE_JNI_NATIVE_METHOD(nativeFunction)
static_assert(QtJniTypes::nativeMethodSignature(nativeFunction) == "(ILjava/lang/String;J)Z");
void tst_QJniTypes::nativeMethod()
{
const auto method = Q_JNI_NATIVE_METHOD(nativeFunction);
QVERIFY(method.fnPtr == nativeFunction);
QCOMPARE(method.name, "nativeFunction");
QCOMPARE(method.signature, "(ILjava/lang/String;J)Z");
}
QTEST_MAIN(tst_QJniTypes)
#include "tst_qjnitypes.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmath Test:
#####################################################################
qt_internal_add_test(tst_qmath
SOURCES
tst_qmath.cpp
)

View File

@ -0,0 +1,354 @@
// Copyright (C) 2021 The Qt Company Ltd.
// Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <qmath.h>
#include <qfloat16.h>
class tst_QMath : public QObject
{
Q_OBJECT
private slots:
void fastSinCos();
void degreesToRadians_data();
void degreesToRadians();
void radiansToDegrees_data();
void radiansToDegrees();
void trigonometry_data();
void trigonometry();
void hypotenuse();
void funcs_data();
void funcs();
void qNextPowerOfTwo32S_data();
void qNextPowerOfTwo32S();
void qNextPowerOfTwo64S_data();
void qNextPowerOfTwo64S();
void qNextPowerOfTwo32U_data();
void qNextPowerOfTwo32U();
void qNextPowerOfTwo64U_data();
void qNextPowerOfTwo64U();
};
void tst_QMath::fastSinCos()
{
// Test evenly spaced angles from 0 to 2pi radians.
const int LOOP_COUNT = 100000;
const qreal loopAngle = 2 * M_PI / (LOOP_COUNT - 1);
for (int i = 0; i < LOOP_COUNT; ++i) {
qreal angle = i * loopAngle;
QVERIFY(qAbs(qSin(angle) - qFastSin(angle)) < 1e-5);
QVERIFY(qAbs(qCos(angle) - qFastCos(angle)) < 1e-5);
}
}
void tst_QMath::degreesToRadians_data()
{
QTest::addColumn<float>("degreesFloat");
QTest::addColumn<float>("radiansFloat");
QTest::addColumn<double>("degreesDouble");
QTest::addColumn<double>("radiansDouble");
QTest::newRow( "pi" ) << 180.0f << float(M_PI) << 180.0 << M_PI;
QTest::newRow( "doublepi" ) << 360.0f << float(2 * M_PI) << 360.0 << 2 * M_PI;
QTest::newRow( "halfpi" ) << 90.0f << float(M_PI_2) << 90.0 << M_PI_2;
QTest::newRow( "random" ) << 123.1234567f << 2.1489097058516724f << 123.123456789123456789 << 2.148909707407169856192285627;
QTest::newRow( "bigrandom" ) << 987654321.9876543f << 17237819.79023679f << 987654321987654321.987654321987654321 << 17237819790236794.0;
QTest::newRow( "zero" ) << 0.0f << 0.0f << 0.0 << 0.0;
QTest::newRow( "minuspi" ) << -180.0f << float(-M_PI) << 180.0 << M_PI;
QTest::newRow( "minusdoublepi" ) << -360.0f << float(-2 * M_PI) << -360.0 << -2 * M_PI;
QTest::newRow( "minushalfpi" ) << -90.0f << float(-M_PI_2) << -90.0 << -M_PI_2;
QTest::newRow( "minusrandom" ) << -123.1234567f << -2.1489097058516724f << -123.123456789123456789 << -2.148909707407169856192285627;
QTest::newRow( "minusbigrandom" ) << -987654321.9876543f << -17237819.79023679f << -987654321987654321.987654321987654321 << -17237819790236794.0;
}
void tst_QMath::degreesToRadians()
{
QFETCH(float, degreesFloat);
QFETCH(float, radiansFloat);
QFETCH(double, degreesDouble);
QFETCH(double, radiansDouble);
QCOMPARE(qDegreesToRadians(degreesFloat), radiansFloat);
QCOMPARE(qDegreesToRadians(degreesDouble), radiansDouble);
}
void tst_QMath::radiansToDegrees_data()
{
QTest::addColumn<float>("radiansFloat");
QTest::addColumn<float>("degreesFloat");
QTest::addColumn<double>("radiansDouble");
QTest::addColumn<double>("degreesDouble");
QTest::newRow( "pi" ) << float(M_PI) << 180.0f << M_PI << 180.0;
QTest::newRow( "doublepi" ) << float(2 * M_PI) << 360.0f << 2 * M_PI << 360.0;
QTest::newRow( "halfpi" ) << float(M_PI_2) << 90.0f << M_PI_2 << 90.0;
QTest::newRow( "random" ) << 123.1234567f << 7054.454427971739f << 123.123456789123456789 << 7054.4544330781363896676339209079742431640625;
QTest::newRow( "bigrandom" ) << 987654321.9876543f << 56588424267.74745f << 987654321987654321.987654321987654321 << 56588424267747450880.0;
QTest::newRow( "zero" ) << 0.0f << 0.0f << 0.0 << 0.0;
QTest::newRow( "minuspi" ) << float(-M_PI) << -180.0f << -M_PI << -180.0;
QTest::newRow( "minusdoublepi" ) << float(-2 * M_PI) << -360.0f << -2 * M_PI << -360.0;
QTest::newRow( "minushalfpi" ) << float(-M_PI_2) << -90.0f << -M_PI_2 << -90.0;
QTest::newRow( "minusrandom" ) << -123.1234567f << -7054.454427971739f << -123.123456789123456789 << -7054.4544330781363896676339209079742431640625;
QTest::newRow( "minusbigrandom" ) << -987654321.9876543f << -56588424267.74745f << -987654321987654321.987654321987654321 << -56588424267747450880.0;
}
void tst_QMath::radiansToDegrees()
{
QFETCH(float, radiansFloat);
QFETCH(float, degreesFloat);
QFETCH(double, radiansDouble);
QFETCH(double, degreesDouble);
QCOMPARE(qRadiansToDegrees(radiansFloat), degreesFloat);
QCOMPARE(qRadiansToDegrees(radiansDouble), degreesDouble);
}
void tst_QMath::trigonometry_data()
{
QTest::addColumn<double>("x");
QTest::addColumn<double>("y");
QTest::addColumn<double>("angle");
QTest::newRow("zero") << 1.0 << 0.0 << 0.0;
QTest::newRow("turn/4") << 0.0 << 1.0 << M_PI_2;
QTest::newRow("turn/2") << -1.0 << 0.0 << M_PI;
QTest::newRow("3*turn/4") << 0.0 << -1.0 << -M_PI_2;
}
void tst_QMath::trigonometry()
{
QFETCH(const double, x);
QFETCH(const double, y);
QFETCH(const double, angle);
const double hypot = qHypot(x, y);
QVERIFY(hypot > 0);
QCOMPARE(qAtan2(y, x), angle);
QCOMPARE(qSin(angle), y / hypot);
if (x >= 0 && (y || x)) // aSin() always in right half-plane
QCOMPARE(qAsin(y / hypot), angle);
QCOMPARE(qCos(angle), x / hypot);
if (y >= 0 && (y || x)) // aCos() always in upper half-plane
QCOMPARE(qAcos(x / hypot), angle);
if (x > 0) {
QCOMPARE(qTan(angle), y / x);
QCOMPARE(qAtan(y / x), angle);
}
}
void tst_QMath::hypotenuse()
{
// Correct return-types, particularly when qfloat16 is involved:
static_assert(std::is_same<decltype(qHypot(qfloat16(1), qfloat16(1), qfloat16(1),
qfloat16(1), qfloat16(1), qfloat16(1),
qfloat16(1), qfloat16(1), qfloat16(1))),
qfloat16>::value);
static_assert(std::is_same<decltype(qHypot(qfloat16(3), qfloat16(4), qfloat16(12))),
qfloat16>::value);
static_assert(std::is_same<decltype(qHypot(qfloat16(3), qfloat16(4), 12.0f)), float>::value);
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4.0f, qfloat16(12))), float>::value);
static_assert(std::is_same<decltype(qHypot(3.0f, qfloat16(4), qfloat16(12))), float>::value);
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4.0f, 12.0f)), float>::value);
static_assert(std::is_same<decltype(qHypot(3.0f, qfloat16(4), 12.0f)), float>::value);
static_assert(std::is_same<decltype(qHypot(3.0f, 4.0f, qfloat16(12))), float>::value);
static_assert(std::is_same<decltype(qHypot(qfloat16(3), qfloat16(4))), qfloat16>::value);
static_assert(std::is_same<decltype(qHypot(3.0f, qfloat16(4))), float>::value);
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4.0f)), float>::value);
static_assert(std::is_same<decltype(qHypot(3.0, qfloat16(4))), double>::value);
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4.0)), double>::value);
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4)), double>::value);
static_assert(std::is_same<decltype(qHypot(3, qfloat16(4))), double>::value);
static_assert(std::is_same<decltype(qHypot(qfloat16(3), 4.0L)), long double>::value);
static_assert(std::is_same<decltype(qHypot(3.0L, qfloat16(4))), long double>::value);
static_assert(std::is_same<decltype(qHypot(3.0f, 4.0f)), float>::value);
static_assert(std::is_same<decltype(qHypot(3.0f, 4.0)), double>::value);
static_assert(std::is_same<decltype(qHypot(3.0f, 4)), double>::value);
static_assert(std::is_same<decltype(qHypot(3.0f, 4.0L)), long double>::value);
static_assert(std::is_same<decltype(qHypot(3.0, 4.0f)), double>::value);
static_assert(std::is_same<decltype(qHypot(3, 4.0f)), double>::value);
static_assert(std::is_same<decltype(qHypot(3.0L, 4.0f)), long double>::value);
static_assert(std::is_same<decltype(qHypot(3.0, 4.0L)), long double>::value);
static_assert(std::is_same<decltype(qHypot(3.0L, 4.0)), long double>::value);
static_assert(std::is_same<decltype(qHypot(3.0, 4.0)), double>::value);
static_assert(std::is_same<decltype(qHypot(3, 4.0)), double>::value);
static_assert(std::is_same<decltype(qHypot(3.0, 4)), double>::value);
static_assert(std::is_same<decltype(qHypot(3, 4)), double>::value);
// Works for all numeric types:
QCOMPARE(qHypot(3, 4), 5);
QCOMPARE(qHypot(qfloat16(5), qfloat16(12)), qfloat16(13));
QCOMPARE(qHypot(3.0f, 4.0f, 12.0f), 13.0f);
QCOMPARE(qHypot(3.0, 4.0, 12.0, 84.0), 85.0);
QCOMPARE(qHypot(3.0f, 4.0f, 12.0f, 84.0f, 720.0f), 725.0f);
QCOMPARE(qHypot(3.0, 4.0, 12.0, 84.0, 3612.0), 3613.0);
// Integral gets promoted to double:
QCOMPARE(qHypot(1, 1), M_SQRT2);
// Caller can mix types freely:
QCOMPARE(qHypot(3.0f, 4, 12.0, 84.0f, qfloat16(720), 10500), 10525);
// NaN wins over any finite:
QCOMPARE(qHypot(3, 4.0, 12.0f, qQNaN()), qQNaN());
QCOMPARE(qHypot(3, 4.0, qQNaN(), 12.0f), qQNaN());
QCOMPARE(qHypot(3, qQNaN(), 4.0, 12.0f), qQNaN());
QCOMPARE(qHypot(qQNaN(), 3, 4.0, 12.0f), qQNaN());
// but Infinity beats NaN:
QCOMPARE(qHypot(3, 4.0f, -qInf(), qQNaN()), qInf());
QCOMPARE(qHypot(3, -qInf(), 4.0f, qQNaN()), qInf());
QCOMPARE(qHypot(-qInf(), 3, 4.0f, qQNaN()), qInf());
QCOMPARE(qHypot(qQNaN(), 3, -qInf(), 4.0f), qInf());
QCOMPARE(qHypot(3, qQNaN(), 4.0f, -qInf()), qInf());
QCOMPARE(qHypot(3, 4.0f, qQNaN(), -qInf()), qInf());
// Components whose squares sum to zero don't change the end result:
const double minD = std::numeric_limits<double>::min();
QVERIFY(minD * minD + minD * minD == 0); // *NOT* QCOMPARE
QCOMPARE(qHypot(minD, minD, 12.0), 12.0);
const float minF = std::numeric_limits<float>::min();
QVERIFY(minF * minF + minF * minF == 0.0f); // *NOT* QCOMPARE
QCOMPARE(qHypot(minF, minF, 12.0f), 12.0f);
const qfloat16 minF16 = std::numeric_limits<qfloat16>::min();
QVERIFY(minF16 * minF16 + minF16 * minF16 == qfloat16(0)); // *NOT* QCOMPARE
QCOMPARE(qHypot(minF16, minF16, qfloat16(12)), qfloat16(12));
}
void tst_QMath::funcs_data()
{
QTest::addColumn<double>("value");
QTest::addColumn<int>("floor");
QTest::addColumn<int>("ceil");
QTest::addColumn<double>("abs");
QTest::addColumn<double>("sqrt");
QTest::addColumn<double>("log");
QTest::addColumn<double>("exp");
QTest::addColumn<double>("cube");
const double nan = qQNaN();
QTest::newRow("0") << 0.0 << 0 << 0 << 0.0 << 0.0 << nan << 1.0 << 0.0;
QTest::newRow("1.44")
<< 1.44 << 1 << 2 << 1.44 << 1.2 << 0.36464311358790924 << 4.220695816996552 << 2.985984;
QTest::newRow("-1.44")
<< -1.44 << -2 << -1 << 1.44 << nan << nan << 0.23692775868212176 << -2.985984;
}
void tst_QMath::funcs()
{
QFETCH(double, value);
QTEST(qFloor(value), "floor");
QTEST(qCeil(value), "ceil");
QTEST(qFabs(value), "abs");
if (value >= 0)
QTEST(qSqrt(value), "sqrt");
if (value > 0)
QTEST(qLn(value), "log");
QTEST(qExp(value), "exp");
QTEST(qPow(value, 3), "cube");
}
void tst_QMath::qNextPowerOfTwo32S_data()
{
QTest::addColumn<qint32>("input");
QTest::addColumn<quint32>("output");
QTest::newRow("0") << 0 << 1U;
QTest::newRow("1") << 1 << 2U;
QTest::newRow("2") << 2 << 4U;
QTest::newRow("17") << 17 << 32U;
QTest::newRow("128") << 128 << 256U;
QTest::newRow("65535") << 65535 << 65536U;
QTest::newRow("65536") << 65536 << 131072U;
QTest::newRow("2^30") << (1 << 30) << (1U << 31);
QTest::newRow("2^30 + 1") << (1 << 30) + 1 << (1U << 31);
QTest::newRow("2^31 - 1") << 0x7FFFFFFF << (1U<<31);
}
void tst_QMath::qNextPowerOfTwo32S()
{
QFETCH(qint32, input);
QFETCH(quint32, output);
QCOMPARE(qNextPowerOfTwo(input), output);
}
void tst_QMath::qNextPowerOfTwo32U_data()
{
QTest::addColumn<quint32>("input");
QTest::addColumn<quint32>("output");
QTest::newRow("0") << 0U << 1U;
QTest::newRow("1") << 1U << 2U;
QTest::newRow("2") << 2U << 4U;
QTest::newRow("17") << 17U << 32U;
QTest::newRow("128") << 128U << 256U;
QTest::newRow("65535") << 65535U << 65536U;
QTest::newRow("65536") << 65536U << 131072U;
QTest::newRow("2^30") << (1U << 30) << (1U << 31);
QTest::newRow("2^30 + 1") << (1U << 30) + 1 << (1U << 31);
QTest::newRow("2^31 - 1") << 2147483647U << 2147483648U;
}
void tst_QMath::qNextPowerOfTwo32U()
{
QFETCH(quint32, input);
QFETCH(quint32, output);
QCOMPARE(qNextPowerOfTwo(input), output);
}
void tst_QMath::qNextPowerOfTwo64S_data()
{
QTest::addColumn<qint64>("input");
QTest::addColumn<quint64>("output");
QTest::newRow("0") << Q_INT64_C(0) << Q_UINT64_C(1);
QTest::newRow("1") << Q_INT64_C(1) << Q_UINT64_C(2);
QTest::newRow("2") << Q_INT64_C(2) << Q_UINT64_C(4);
QTest::newRow("17") << Q_INT64_C(17) << Q_UINT64_C(32);
QTest::newRow("128") << Q_INT64_C(128) << Q_UINT64_C(256);
QTest::newRow("65535") << Q_INT64_C(65535) << Q_UINT64_C(65536);
QTest::newRow("65536") << Q_INT64_C(65536) << Q_UINT64_C(131072);
QTest::newRow("2^31 - 1") << Q_INT64_C(2147483647) << Q_UINT64_C(0x80000000);
QTest::newRow("2^31") << Q_INT64_C(2147483648) << Q_UINT64_C(0x100000000);
QTest::newRow("2^31 + 1") << Q_INT64_C(2147483649) << Q_UINT64_C(0x100000000);
QTest::newRow("2^63 - 1") << Q_INT64_C(0x7FFFFFFFFFFFFFFF) << Q_UINT64_C(0x8000000000000000);
}
void tst_QMath::qNextPowerOfTwo64S()
{
QFETCH(qint64, input);
QFETCH(quint64, output);
QCOMPARE(qNextPowerOfTwo(input), output);
}
void tst_QMath::qNextPowerOfTwo64U_data()
{
QTest::addColumn<quint64>("input");
QTest::addColumn<quint64>("output");
QTest::newRow("0") << Q_UINT64_C(0) << Q_UINT64_C(1);
QTest::newRow("1") << Q_UINT64_C(1) << Q_UINT64_C(2);
QTest::newRow("2") << Q_UINT64_C(2) << Q_UINT64_C(4);
QTest::newRow("17") << Q_UINT64_C(17) << Q_UINT64_C(32);
QTest::newRow("128") << Q_UINT64_C(128) << Q_UINT64_C(256);
QTest::newRow("65535") << Q_UINT64_C(65535) << Q_UINT64_C(65536);
QTest::newRow("65536") << Q_UINT64_C(65536) << Q_UINT64_C(131072);
QTest::newRow("2^63 - 1") << Q_UINT64_C(0x7FFFFFFFFFFFFFFF) << Q_UINT64_C(0x8000000000000000);
}
void tst_QMath::qNextPowerOfTwo64U()
{
QFETCH(quint64, input);
QFETCH(quint64, output);
QCOMPARE(qNextPowerOfTwo(input), output);
}
QTEST_APPLESS_MAIN(tst_QMath)
#include "tst_qmath.moc"

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmetacontainer Test:
#####################################################################
qt_internal_add_test(tst_qmetacontainer
SOURCES
tst_qmetacontainer.cpp
LIBRARIES
Qt::CorePrivate
)

View File

@ -0,0 +1,728 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtTest/qtest.h>
#include <QtCore/qcontainerinfo.h>
#include <QtCore/qmetacontainer.h>
#include <QtCore/QMap>
#include <QtCore/QHash>
#include <QtCore/qvector.h>
#include <QtCore/qset.h>
#include <QtCore/qstring.h>
#include <QtCore/qbytearray.h>
#include <vector>
#include <set>
#include <forward_list>
#include <unordered_map>
namespace CheckContainerTraits
{
struct NotAContainer {};
static_assert(QContainerInfo::has_size_v<QVector<int>>);
static_assert(QContainerInfo::has_size_v<QSet<int>>);
static_assert(!QContainerInfo::has_size_v<NotAContainer>);
static_assert(QContainerInfo::has_size_v<std::vector<int>>);
static_assert(QContainerInfo::has_size_v<std::set<int>>);
static_assert(!QContainerInfo::has_size_v<std::forward_list<int>>);
static_assert(QContainerInfo::has_clear_v<QVector<int>>);
static_assert(QContainerInfo::has_clear_v<QSet<int>>);
static_assert(!QContainerInfo::has_clear_v<NotAContainer>);
static_assert(QContainerInfo::has_clear_v<std::vector<int>>);
static_assert(QContainerInfo::has_clear_v<std::set<int>>);
static_assert(QContainerInfo::has_clear_v<std::forward_list<int>>);
static_assert(QContainerInfo::has_at_index_v<QVector<int>>);
static_assert(!QContainerInfo::has_at_index_v<QSet<int>>);
static_assert(!QContainerInfo::has_at_index_v<NotAContainer>);
static_assert(QContainerInfo::has_at_index_v<std::vector<int>>);
static_assert(!QContainerInfo::has_at_index_v<std::set<int>>);
static_assert(!QContainerInfo::has_at_index_v<std::forward_list<int>>);
static_assert(QContainerInfo::can_get_at_index_v<QVector<int>>);
static_assert(!QContainerInfo::can_get_at_index_v<QSet<int>>);
static_assert(!QContainerInfo::can_get_at_index_v<NotAContainer>);
static_assert(QContainerInfo::can_get_at_index_v<std::vector<int>>);
static_assert(!QContainerInfo::can_get_at_index_v<std::set<int>>);
static_assert(!QContainerInfo::can_get_at_index_v<std::forward_list<int>>);
static_assert(QContainerInfo::can_set_at_index_v<QVector<int>>);
static_assert(!QContainerInfo::can_set_at_index_v<QSet<int>>);
static_assert(!QContainerInfo::can_set_at_index_v<NotAContainer>);
static_assert(QContainerInfo::can_set_at_index_v<std::vector<int>>);
static_assert(!QContainerInfo::can_set_at_index_v<std::set<int>>);
static_assert(!QContainerInfo::can_set_at_index_v<std::forward_list<int>>);
static_assert(QContainerInfo::has_push_back_v<QVector<int>>);
static_assert(!QContainerInfo::has_push_back_v<QSet<int>>);
static_assert(!QContainerInfo::has_push_back_v<NotAContainer>);
static_assert(QContainerInfo::has_push_back_v<std::vector<int>>);
static_assert(!QContainerInfo::has_push_back_v<std::set<int>>);
static_assert(!QContainerInfo::has_push_back_v<std::forward_list<int>>);
static_assert(QContainerInfo::has_push_front_v<QVector<int>>);
static_assert(!QContainerInfo::has_push_front_v<QSet<int>>);
static_assert(!QContainerInfo::has_push_front_v<NotAContainer>);
static_assert(!QContainerInfo::has_push_front_v<std::vector<int>>);
static_assert(!QContainerInfo::has_push_front_v<std::set<int>>);
static_assert(QContainerInfo::has_push_front_v<std::forward_list<int>>);
static_assert(!QContainerInfo::has_insert_v<QVector<int>>);
static_assert(QContainerInfo::has_insert_v<QSet<int>>);
static_assert(!QContainerInfo::has_insert_v<NotAContainer>);
static_assert(!QContainerInfo::has_insert_v<std::vector<int>>);
static_assert(QContainerInfo::has_insert_v<std::set<int>>);
static_assert(!QContainerInfo::has_insert_v<std::forward_list<int>>);
static_assert(QContainerInfo::has_pop_back_v<QVector<int>>);
static_assert(!QContainerInfo::has_pop_back_v<QSet<int>>);
static_assert(!QContainerInfo::has_pop_back_v<NotAContainer>);
static_assert(QContainerInfo::has_pop_back_v<std::vector<int>>);
static_assert(!QContainerInfo::has_pop_back_v<std::set<int>>);
static_assert(!QContainerInfo::has_pop_back_v<std::forward_list<int>>);
static_assert(QContainerInfo::has_pop_front_v<QVector<int>>);
static_assert(!QContainerInfo::has_pop_front_v<QSet<int>>);
static_assert(!QContainerInfo::has_pop_front_v<NotAContainer>);
static_assert(!QContainerInfo::has_pop_front_v<std::vector<int>>);
static_assert(!QContainerInfo::has_pop_front_v<std::set<int>>);
static_assert(QContainerInfo::has_pop_front_v<std::forward_list<int>>);
static_assert(QContainerInfo::has_iterator_v<QVector<int>>);
static_assert(QContainerInfo::has_iterator_v<QSet<int>>);
static_assert(!QContainerInfo::has_iterator_v<NotAContainer>);
static_assert(QContainerInfo::has_iterator_v<std::vector<int>>);
static_assert(QContainerInfo::has_iterator_v<std::set<int>>);
static_assert(QContainerInfo::has_iterator_v<std::forward_list<int>>);
static_assert(QContainerInfo::has_const_iterator_v<QVector<int>>);
static_assert(QContainerInfo::has_const_iterator_v<QSet<int>>);
static_assert(!QContainerInfo::has_const_iterator_v<NotAContainer>);
static_assert(QContainerInfo::has_const_iterator_v<std::vector<int>>);
static_assert(QContainerInfo::has_const_iterator_v<std::set<int>>);
static_assert(QContainerInfo::has_const_iterator_v<std::forward_list<int>>);
static_assert(QContainerInfo::iterator_dereferences_to_value_v<QVector<int>>);
static_assert(QContainerInfo::iterator_dereferences_to_value_v<QSet<int>>);
static_assert(!QContainerInfo::iterator_dereferences_to_value_v<NotAContainer>);
static_assert(QContainerInfo::iterator_dereferences_to_value_v<std::vector<int>>);
static_assert(QContainerInfo::iterator_dereferences_to_value_v<std::set<int>>);
static_assert(QContainerInfo::iterator_dereferences_to_value_v<std::forward_list<int>>);
static_assert(QContainerInfo::can_set_value_at_iterator_v<QVector<int>>);
static_assert(!QContainerInfo::can_set_value_at_iterator_v<QSet<int>>);
static_assert(!QContainerInfo::can_set_value_at_iterator_v<NotAContainer>);
static_assert(QContainerInfo::can_set_value_at_iterator_v<std::vector<int>>);
static_assert(!QContainerInfo::can_set_value_at_iterator_v<std::set<int>>);
static_assert(QContainerInfo::can_set_value_at_iterator_v<std::forward_list<int>>);
static_assert(QContainerInfo::can_insert_value_at_iterator_v<QVector<int>>);
static_assert(QContainerInfo::can_insert_value_at_iterator_v<QSet<int>>);
static_assert(!QContainerInfo::can_insert_value_at_iterator_v<NotAContainer>);
static_assert(QContainerInfo::can_insert_value_at_iterator_v<std::vector<int>>);
static_assert(!QContainerInfo::can_insert_value_at_iterator_v<std::forward_list<int>>);
// The iterator is only a hint, but syntactically indistinguishable from others.
// It's explicitly there to be signature compatible with std::vector::insert, though.
// Also, inserting into a set is not guaranteed to actually do anything.
static_assert(QContainerInfo::can_insert_value_at_iterator_v<std::set<int>>);
static_assert(QContainerInfo::can_erase_at_iterator_v<QVector<int>>);
static_assert(QContainerInfo::can_erase_at_iterator_v<QSet<int>>);
static_assert(!QContainerInfo::can_erase_at_iterator_v<NotAContainer>);
static_assert(QContainerInfo::can_erase_at_iterator_v<std::vector<int>>);
static_assert(QContainerInfo::can_erase_at_iterator_v<std::set<int>>);
static_assert(!QContainerInfo::can_erase_at_iterator_v<std::forward_list<int>>);
}
class tst_QMetaContainer: public QObject
{
Q_OBJECT
private:
QVector<QMetaType> qvector;
std::vector<QString> stdvector;
QSet<QByteArray> qset;
std::set<int> stdset;
std::forward_list<QMetaSequence> forwardList;
QHash<int, QMetaType> qhash;
QMap<QByteArray, bool> qmap;
std::map<QString, int> stdmap;
std::unordered_map<int, QMetaAssociation> stdunorderedmap;
private slots:
void init();
void testSequence_data();
void testSequence();
void testAssociation_data();
void testAssociation();
void cleanup();
};
void tst_QMetaContainer::init()
{
qvector = { QMetaType(), QMetaType::fromType<QString>(), QMetaType::fromType<int>() };
stdvector = { QStringLiteral("foo"), QStringLiteral("bar"), QStringLiteral("baz") };
qset = { "aaa", "bbb", "ccc" };
stdset = { 1, 2, 3, 42, 45, 11 };
forwardList = {
QMetaSequence::fromContainer<QVector<QMetaType>>(),
QMetaSequence::fromContainer<std::vector<QString>>(),
QMetaSequence::fromContainer<QSet<QByteArray>>(),
QMetaSequence::fromContainer<std::set<int>>(),
QMetaSequence::fromContainer<std::forward_list<QMetaSequence>>()
};
qhash = {
{ 233, QMetaType() },
{ 11, QMetaType::fromType<QByteArray>() },
{ 6626, QMetaType::fromType<bool>() }
};
qmap = {
{ "eins", true },
{ "zwei", false },
{ "elfundvierzig", true }
};
stdmap = {
{ QStringLiteral("dkdkdkd"), 58583 },
{ QStringLiteral("ooo30393"), 12 },
{ QStringLiteral("2dddd30393"), 999999 },
};
stdunorderedmap = {
{ 11, QMetaAssociation::fromContainer<QHash<int, QMetaType>>() },
{ 12, QMetaAssociation::fromContainer<QMap<QByteArray, bool>>() },
{ 393, QMetaAssociation::fromContainer<std::map<QString, int>>() },
{ 293, QMetaAssociation::fromContainer<std::unordered_map<int, QMetaAssociation>>() }
};
}
void tst_QMetaContainer::cleanup()
{
qvector.clear();
stdvector.clear();
qset.clear();
stdset.clear();
forwardList.clear();
qhash.clear();
qmap.clear();
stdmap.clear();
stdunorderedmap.clear();
}
void tst_QMetaContainer::testSequence_data()
{
QTest::addColumn<void *>("container");
QTest::addColumn<QMetaSequence>("metaSequence");
QTest::addColumn<QMetaType>("metaType");
QTest::addColumn<bool>("hasSize");
QTest::addColumn<bool>("isIndexed");
QTest::addColumn<bool>("canRemove");
QTest::addColumn<bool>("hasBidirectionalIterator");
QTest::addColumn<bool>("hasRandomAccessIterator");
QTest::addColumn<bool>("canInsertAtIterator");
QTest::addColumn<bool>("canEraseAtIterator");
QTest::addColumn<bool>("isSortable");
QTest::addRow("QVector")
<< static_cast<void *>(&qvector)
<< QMetaSequence::fromContainer<QVector<QMetaType>>()
<< QMetaType::fromType<QMetaType>()
<< true << true << true << true << true << true << true << true;
QTest::addRow("std::vector")
<< static_cast<void *>(&stdvector)
<< QMetaSequence::fromContainer<std::vector<QString>>()
<< QMetaType::fromType<QString>()
<< true << true << true << true << true << true << true << true;
QTest::addRow("QSet")
<< static_cast<void *>(&qset)
<< QMetaSequence::fromContainer<QSet<QByteArray>>()
<< QMetaType::fromType<QByteArray>()
<< true << false << false << false << false << true << true << false;
QTest::addRow("std::set")
<< static_cast<void *>(&stdset)
<< QMetaSequence::fromContainer<std::set<int>>()
<< QMetaType::fromType<int>()
<< true << false << false << true << false << true << true << false;
QTest::addRow("std::forward_list")
<< static_cast<void *>(&forwardList)
<< QMetaSequence::fromContainer<std::forward_list<QMetaSequence>>()
<< QMetaType::fromType<QMetaSequence>()
<< false << false << true << false << false << false << false << true;
}
void tst_QMetaContainer::testSequence()
{
QFETCH(void *, container);
QFETCH(QMetaSequence, metaSequence);
QFETCH(QMetaType, metaType);
QFETCH(bool, hasSize);
QFETCH(bool, isIndexed);
QFETCH(bool, canRemove);
QFETCH(bool, hasBidirectionalIterator);
QFETCH(bool, hasRandomAccessIterator);
QFETCH(bool, canInsertAtIterator);
QFETCH(bool, canEraseAtIterator);
QFETCH(bool, isSortable);
QVERIFY(metaSequence.canAddValue());
QCOMPARE(metaSequence.hasSize(), hasSize);
QCOMPARE(metaSequence.canGetValueAtIndex(), isIndexed);
QCOMPARE(metaSequence.canSetValueAtIndex(), isIndexed);
QCOMPARE(metaSequence.canRemoveValue(), canRemove);
QCOMPARE(metaSequence.hasBidirectionalIterator(), hasBidirectionalIterator);
QCOMPARE(metaSequence.hasRandomAccessIterator(), hasRandomAccessIterator);
QCOMPARE(metaSequence.canInsertValueAtIterator(), canInsertAtIterator);
QCOMPARE(metaSequence.canEraseValueAtIterator(), canEraseAtIterator);
QCOMPARE(metaSequence.isSortable(), isSortable);
QVariant var1(metaType);
QVariant var2(metaType);
QVariant var3(metaType);
if (hasSize) {
const qsizetype size = metaSequence.size(container);
// var1 is invalid, and our sets do not contain an invalid value so far.
metaSequence.addValue(container, var1.constData());
QCOMPARE(metaSequence.size(container), size + 1);
if (canRemove) {
metaSequence.removeValue(container);
QCOMPARE(metaSequence.size(container), size);
}
} else {
metaSequence.addValue(container, var1.constData());
if (canRemove)
metaSequence.removeValue(container);
}
if (isIndexed) {
QVERIFY(hasSize);
const qsizetype size = metaSequence.size(container);
for (qsizetype i = 0; i < size; ++i) {
metaSequence.valueAtIndex(container, i, var1.data());
metaSequence.valueAtIndex(container, size - i - 1, var2.data());
metaSequence.setValueAtIndex(container, i, var2.constData());
metaSequence.setValueAtIndex(container, size - i - 1, var1.constData());
metaSequence.valueAtIndex(container, i, var3.data());
QCOMPARE(var3, var2);
metaSequence.valueAtIndex(container, size - i - 1, var3.data());
QCOMPARE(var3, var1);
}
}
QVERIFY(metaSequence.hasIterator());
QVERIFY(metaSequence.hasConstIterator());
QVERIFY(metaSequence.canGetValueAtIterator());
QVERIFY(metaSequence.canGetValueAtConstIterator());
void *it = metaSequence.begin(container);
void *end = metaSequence.end(container);
QVERIFY(it);
QVERIFY(end);
void *constIt = metaSequence.constBegin(container);
void *constEnd = metaSequence.constEnd(container);
QVERIFY(constIt);
QVERIFY(constEnd);
const qsizetype size = metaSequence.diffIterator(end, it);
QCOMPARE(size, metaSequence.diffConstIterator(constEnd, constIt));
if (hasSize)
QCOMPARE(size, metaSequence.size(container));
qsizetype count = 0;
for (; !metaSequence.compareIterator(it, end);
metaSequence.advanceIterator(it, 1), metaSequence.advanceConstIterator(constIt, 1)) {
metaSequence.valueAtIterator(it, var1.data());
if (isIndexed) {
metaSequence.valueAtIndex(container, count, var2.data());
QCOMPARE(var1, var2);
}
metaSequence.valueAtConstIterator(constIt, var3.data());
QCOMPARE(var3, var1);
++count;
}
QCOMPARE(count, size);
QVERIFY(metaSequence.compareConstIterator(constIt, constEnd));
metaSequence.destroyIterator(it);
metaSequence.destroyIterator(end);
metaSequence.destroyConstIterator(constIt);
metaSequence.destroyConstIterator(constEnd);
if (metaSequence.canSetValueAtIterator()) {
void *it = metaSequence.begin(container);
void *end = metaSequence.end(container);
QVERIFY(it);
QVERIFY(end);
for (; !metaSequence.compareIterator(it, end); metaSequence.advanceIterator(it, 1)) {
metaSequence.valueAtIterator(it, var1.data());
metaSequence.setValueAtIterator(it, var2.constData());
metaSequence.valueAtIterator(it, var3.data());
QCOMPARE(var2, var3);
var2 = var1;
}
metaSequence.destroyIterator(it);
metaSequence.destroyIterator(end);
}
if (metaSequence.hasBidirectionalIterator()) {
void *it = metaSequence.end(container);
void *end = metaSequence.begin(container);
QVERIFY(it);
QVERIFY(end);
void *constIt = metaSequence.constEnd(container);
void *constEnd = metaSequence.constBegin(container);
QVERIFY(constIt);
QVERIFY(constEnd);
qsizetype size = 0;
if (metaSequence.hasRandomAccessIterator()) {
size = metaSequence.diffIterator(end, it);
QCOMPARE(size, metaSequence.diffConstIterator(constEnd, constIt));
} else {
size = -metaSequence.diffIterator(it, end);
}
if (hasSize)
QCOMPARE(size, -metaSequence.size(container));
qsizetype count = 0;
do {
metaSequence.advanceIterator(it, -1);
metaSequence.advanceConstIterator(constIt, -1);
--count;
metaSequence.valueAtIterator(it, var1.data());
if (isIndexed) {
metaSequence.valueAtIndex(container, count - size, var2.data());
QCOMPARE(var1, var2);
}
metaSequence.valueAtConstIterator(constIt, var3.data());
QCOMPARE(var3, var1);
} while (!metaSequence.compareIterator(it, end));
QCOMPARE(count, size);
QVERIFY(metaSequence.compareConstIterator(constIt, constEnd));
metaSequence.destroyIterator(it);
metaSequence.destroyIterator(end);
metaSequence.destroyConstIterator(constIt);
metaSequence.destroyConstIterator(constEnd);
}
if (canInsertAtIterator) {
void *it = metaSequence.begin(container);
void *end = metaSequence.end(container);
const qsizetype size = metaSequence.diffIterator(end, it);
metaSequence.destroyIterator(end);
metaSequence.insertValueAtIterator(container, it, var1.constData());
metaSequence.destroyIterator(it);
it = metaSequence.begin(container);
metaSequence.insertValueAtIterator(container, it, var2.constData());
metaSequence.destroyIterator(it);
it = metaSequence.begin(container);
metaSequence.insertValueAtIterator(container, it, var3.constData());
metaSequence.destroyIterator(it);
it = metaSequence.begin(container);
end = metaSequence.end(container);
const qsizetype newSize = metaSequence.diffIterator(end, it);
if (metaSequence.isSortable()) {
QCOMPARE(newSize, size + 3);
QVariant var4(metaType);
metaSequence.valueAtIterator(it, var4.data());
QCOMPARE(var4, var3);
metaSequence.advanceIterator(it, 1);
metaSequence.valueAtIterator(it, var4.data());
QCOMPARE(var4, var2);
metaSequence.advanceIterator(it, 1);
metaSequence.valueAtIterator(it, var4.data());
QCOMPARE(var4, var1);
} else {
QVERIFY(newSize >= size);
}
if (canEraseAtIterator) {
for (int i = 0; i < newSize; ++i) {
metaSequence.destroyIterator(it);
it = metaSequence.begin(container);
metaSequence.eraseValueAtIterator(container, it);
}
metaSequence.destroyIterator(it);
it = metaSequence.begin(container);
metaSequence.destroyIterator(end);
end = metaSequence.end(container);
QVERIFY(metaSequence.compareIterator(it, end));
metaSequence.addValue(container, var1.constData());
metaSequence.addValue(container, var2.constData());
metaSequence.addValue(container, var3.constData());
}
metaSequence.destroyIterator(end);
metaSequence.destroyIterator(it);
}
QVERIFY(metaSequence.canClear());
constIt = metaSequence.constBegin(container);
constEnd = metaSequence.constEnd(container);
QVERIFY(!metaSequence.compareConstIterator(constIt, constEnd));
metaSequence.destroyConstIterator(constIt);
metaSequence.destroyConstIterator(constEnd);
metaSequence.clear(container);
constIt = metaSequence.constBegin(container);
constEnd = metaSequence.constEnd(container);
QVERIFY(metaSequence.compareConstIterator(constIt, constEnd));
metaSequence.destroyConstIterator(constIt);
metaSequence.destroyConstIterator(constEnd);
}
void tst_QMetaContainer::testAssociation_data()
{
QTest::addColumn<void *>("container");
QTest::addColumn<QMetaAssociation>("metaAssociation");
QTest::addColumn<QMetaType>("keyType");
QTest::addColumn<QMetaType>("mappedType");
QTest::addColumn<bool>("hasSize");
QTest::addColumn<bool>("canRemove");
QTest::addColumn<bool>("canSetMapped");
QTest::addColumn<bool>("hasBidirectionalIterator");
QTest::addColumn<bool>("hasRandomAccessIterator");
QTest::addRow("QHash")
<< static_cast<void *>(&qhash)
<< QMetaAssociation::fromContainer<QHash<int, QMetaType>>()
<< QMetaType::fromType<int>()
<< QMetaType::fromType<QMetaType>()
<< true << true << true << false << false;
QTest::addRow("QMap")
<< static_cast<void *>(&qmap)
<< QMetaAssociation::fromContainer<QMap<QByteArray, bool>>()
<< QMetaType::fromType<QByteArray>()
<< QMetaType::fromType<bool>()
<< true << true << true << true << false;
QTest::addRow("std::map")
<< static_cast<void *>(&stdmap)
<< QMetaAssociation::fromContainer<std::map<QString, int>>()
<< QMetaType::fromType<QString>()
<< QMetaType::fromType<int>()
<< true << true << true << true << false;
QTest::addRow("std::unorderedmap")
<< static_cast<void *>(&stdunorderedmap)
<< QMetaAssociation::fromContainer<std::unordered_map<int, QMetaAssociation>>()
<< QMetaType::fromType<int>()
<< QMetaType::fromType<QMetaAssociation>()
<< true << true << true << false << false;
QTest::addRow("QSet")
<< static_cast<void *>(&qset)
<< QMetaAssociation::fromContainer<QSet<QByteArray>>()
<< QMetaType::fromType<QByteArray>()
<< QMetaType()
<< true << true << false << false << false;
QTest::addRow("std::set")
<< static_cast<void *>(&stdset)
<< QMetaAssociation::fromContainer<std::set<int>>()
<< QMetaType::fromType<int>()
<< QMetaType()
<< true << true << false << true << false;
}
void tst_QMetaContainer::testAssociation()
{
QFETCH(void *, container);
QFETCH(QMetaAssociation, metaAssociation);
QFETCH(QMetaType, keyType);
QFETCH(QMetaType, mappedType);
QFETCH(bool, hasSize);
QFETCH(bool, canRemove);
QFETCH(bool, canSetMapped);
QFETCH(bool, hasBidirectionalIterator);
QFETCH(bool, hasRandomAccessIterator);
QCOMPARE(metaAssociation.hasSize(), hasSize);
QCOMPARE(metaAssociation.canRemoveKey(), canRemove);
QCOMPARE(metaAssociation.canSetMappedAtKey(), canSetMapped);
QCOMPARE(metaAssociation.canSetMappedAtIterator(), canSetMapped);
// Apparently implementations can choose to provide "better" iterators than required by the std.
if (hasBidirectionalIterator)
QCOMPARE(metaAssociation.hasBidirectionalIterator(), hasBidirectionalIterator);
if (hasRandomAccessIterator)
QCOMPARE(metaAssociation.hasRandomAccessIterator(), hasRandomAccessIterator);
QVariant key1(keyType);
QVariant key2(keyType);
QVariant key3(keyType);
QVariant mapped1(mappedType);
QVariant mapped2(mappedType);
QVariant mapped3(mappedType);
if (hasSize) {
const qsizetype size = metaAssociation.size(container);
QVERIFY(metaAssociation.canInsertKey());
// var1 is invalid, and our containers do not contain an invalid key so far.
metaAssociation.insertKey(container, key1.constData());
QCOMPARE(metaAssociation.size(container), size + 1);
metaAssociation.removeKey(container, key1.constData());
QCOMPARE(metaAssociation.size(container), size);
} else {
metaAssociation.insertKey(container, key1.constData());
metaAssociation.removeKey(container, key1.constData());
}
QVERIFY(metaAssociation.hasIterator());
QVERIFY(metaAssociation.hasConstIterator());
QVERIFY(metaAssociation.canGetKeyAtIterator());
QVERIFY(metaAssociation.canGetKeyAtConstIterator());
void *it = metaAssociation.begin(container);
void *end = metaAssociation.end(container);
QVERIFY(it);
QVERIFY(end);
void *constIt = metaAssociation.constBegin(container);
void *constEnd = metaAssociation.constEnd(container);
QVERIFY(constIt);
QVERIFY(constEnd);
const qsizetype size = metaAssociation.diffIterator(end, it);
QCOMPARE(size, metaAssociation.diffConstIterator(constEnd, constIt));
if (hasSize)
QCOMPARE(size, metaAssociation.size(container));
qsizetype count = 0;
for (; !metaAssociation.compareIterator(it, end);
metaAssociation.advanceIterator(it, 1), metaAssociation.advanceConstIterator(constIt, 1)) {
metaAssociation.keyAtIterator(it, key1.data());
metaAssociation.keyAtConstIterator(constIt, key3.data());
QCOMPARE(key3, key1);
++count;
}
QCOMPARE(count, size);
QVERIFY(metaAssociation.compareConstIterator(constIt, constEnd));
metaAssociation.destroyIterator(it);
metaAssociation.destroyIterator(end);
metaAssociation.destroyConstIterator(constIt);
metaAssociation.destroyConstIterator(constEnd);
if (metaAssociation.canSetMappedAtIterator()) {
void *it = metaAssociation.begin(container);
void *end = metaAssociation.end(container);
QVERIFY(it);
QVERIFY(end);
for (; !metaAssociation.compareIterator(it, end); metaAssociation.advanceIterator(it, 1)) {
metaAssociation.mappedAtIterator(it, mapped1.data());
metaAssociation.setMappedAtIterator(it, mapped2.constData());
metaAssociation.mappedAtIterator(it, mapped3.data());
QCOMPARE(mapped2, mapped3);
mapped2 = mapped1;
}
metaAssociation.destroyIterator(it);
metaAssociation.destroyIterator(end);
it = metaAssociation.constBegin(container);
end = metaAssociation.constEnd(container);
QVERIFY(it);
QVERIFY(end);
for (; !metaAssociation.compareConstIterator(it, end); metaAssociation.advanceConstIterator(it, 1)) {
metaAssociation.mappedAtConstIterator(it, mapped1.data());
metaAssociation.keyAtConstIterator(it, key1.data());
metaAssociation.setMappedAtKey(container, key1.constData(), mapped2.constData());
metaAssociation.mappedAtConstIterator(it, mapped3.data());
QCOMPARE(mapped2, mapped3);
mapped2 = mapped1;
}
metaAssociation.destroyConstIterator(it);
metaAssociation.destroyConstIterator(end);
}
if (metaAssociation.hasBidirectionalIterator()) {
void *it = metaAssociation.end(container);
void *end = metaAssociation.begin(container);
QVERIFY(it);
QVERIFY(end);
void *constIt = metaAssociation.constEnd(container);
void *constEnd = metaAssociation.constBegin(container);
QVERIFY(constIt);
QVERIFY(constEnd);
qsizetype size = 0;
if (metaAssociation.hasRandomAccessIterator()) {
size = metaAssociation.diffIterator(end, it);
QCOMPARE(size, metaAssociation.diffConstIterator(constEnd, constIt));
} else {
size = -metaAssociation.diffIterator(it, end);
}
if (hasSize)
QCOMPARE(size, -metaAssociation.size(container));
qsizetype count = 0;
do {
metaAssociation.advanceIterator(it, -1);
metaAssociation.advanceConstIterator(constIt, -1);
--count;
metaAssociation.keyAtIterator(it, key1.data());
metaAssociation.keyAtConstIterator(constIt, key3.data());
QCOMPARE(key3, key1);
} while (!metaAssociation.compareIterator(it, end));
QCOMPARE(count, size);
QVERIFY(metaAssociation.compareConstIterator(constIt, constEnd));
metaAssociation.destroyIterator(it);
metaAssociation.destroyIterator(end);
metaAssociation.destroyConstIterator(constIt);
metaAssociation.destroyConstIterator(constEnd);
}
QVERIFY(metaAssociation.canClear());
constIt = metaAssociation.constBegin(container);
constEnd = metaAssociation.constEnd(container);
QVERIFY(!metaAssociation.compareConstIterator(constIt, constEnd));
metaAssociation.destroyConstIterator(constIt);
metaAssociation.destroyConstIterator(constEnd);
metaAssociation.clear(container);
constIt = metaAssociation.constBegin(container);
constEnd = metaAssociation.constEnd(container);
QVERIFY(metaAssociation.compareConstIterator(constIt, constEnd));
metaAssociation.destroyConstIterator(constIt);
metaAssociation.destroyConstIterator(constEnd);
}
QTEST_MAIN(tst_QMetaContainer)
#include "tst_qmetacontainer.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmetaenum Test:
#####################################################################
qt_internal_add_test(tst_qmetaenum
SOURCES
tst_qmetaenum.cpp
)

View File

@ -0,0 +1,96 @@
// Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QtCore/qobject.h>
#include <QtCore/qmetaobject.h>
class tst_QMetaEnum : public QObject
{
Q_OBJECT
public:
enum SuperEnum { SuperValue1 = 1 , SuperValue2 = 2 };
enum Flag { Flag1 = 1 , Flag2 = 2 };
Q_DECLARE_FLAGS(Flags, Flag)
Q_ENUM(SuperEnum)
Q_FLAG(Flags)
private slots:
void fromType();
void valuesToKeys_data();
void valuesToKeys();
void defaultConstructed();
};
void tst_QMetaEnum::fromType()
{
QMetaEnum meta = QMetaEnum::fromType<SuperEnum>();
QVERIFY(meta.isValid());
QVERIFY(!meta.isFlag());
QCOMPARE(meta.name(), "SuperEnum");
QCOMPARE(meta.enumName(), "SuperEnum");
QCOMPARE(meta.enclosingMetaObject(), &staticMetaObject);
QCOMPARE(meta.keyCount(), 2);
meta = QMetaEnum::fromType<Flags>();
QVERIFY(meta.isValid());
QVERIFY(meta.isFlag());
QCOMPARE(meta.name(), "Flags");
QCOMPARE(meta.enumName(), "Flag");
QCOMPARE(meta.enclosingMetaObject(), &staticMetaObject);
QCOMPARE(meta.keyCount(), 2);
}
Q_DECLARE_METATYPE(Qt::WindowFlags)
void tst_QMetaEnum::valuesToKeys_data()
{
QTest::addColumn<Qt::WindowFlags>("windowFlags");
QTest::addColumn<QByteArray>("expected");
QTest::newRow("Window")
<< Qt::WindowFlags(Qt::Window)
<< QByteArrayLiteral("Window");
// Verify that Qt::Dialog does not cause 'Window' to appear in the output.
QTest::newRow("Frameless_Dialog")
<< (Qt::Dialog | Qt::FramelessWindowHint)
<< QByteArrayLiteral("Dialog|FramelessWindowHint");
// Similarly, Qt::WindowMinMaxButtonsHint should not show up as
// WindowMinimizeButtonHint|WindowMaximizeButtonHint
QTest::newRow("Tool_MinMax_StaysOnTop")
<< (Qt::Tool | Qt::WindowMinMaxButtonsHint | Qt::WindowStaysOnTopHint)
<< QByteArrayLiteral("Tool|WindowMinMaxButtonsHint|WindowStaysOnTopHint");
}
void tst_QMetaEnum::valuesToKeys()
{
QFETCH(Qt::WindowFlags, windowFlags);
QFETCH(QByteArray, expected);
QMetaEnum me = QMetaEnum::fromType<Qt::WindowFlags>();
QCOMPARE(me.valueToKeys(windowFlags), expected);
}
void tst_QMetaEnum::defaultConstructed()
{
QMetaEnum e;
QVERIFY(!e.isValid());
QVERIFY(!e.isScoped());
QVERIFY(!e.isFlag());
QCOMPARE(e.name(), QByteArray());
}
static_assert(QtPrivate::IsQEnumHelper<tst_QMetaEnum::SuperEnum>::Value);
static_assert(QtPrivate::IsQEnumHelper<Qt::WindowFlags>::Value);
static_assert(QtPrivate::IsQEnumHelper<Qt::Orientation>::Value);
static_assert(!QtPrivate::IsQEnumHelper<int>::Value);
static_assert(!QtPrivate::IsQEnumHelper<QObject>::Value);
static_assert(!QtPrivate::IsQEnumHelper<QObject*>::Value);
static_assert(!QtPrivate::IsQEnumHelper<void>::Value);
QTEST_MAIN(tst_QMetaEnum)
#include "tst_qmetaenum.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmetamethod Test:
#####################################################################
qt_internal_add_test(tst_qmetamethod
SOURCES
tst_qmetamethod.cpp
)

View File

@ -0,0 +1,906 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2014 Olivier Goffart <ogoffart@woboq.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QTypeRevision>
#include <qobject.h>
#include <qmetaobject.h>
class tst_QMetaMethod : public QObject
{
Q_OBJECT
private slots:
void method_data();
void method();
void invalidMethod();
void comparisonOperators();
void fromSignal();
void gadget();
void revision();
void returnMetaType();
void parameterMetaType();
void parameterTypeName();
void isConst();
void methodIndexes_data();
void methodIndexes();
};
struct CustomType { };
Q_DECLARE_METATYPE(CustomType)
struct CustomUnregisteredType { };
Q_DECLARE_METATYPE(QMetaMethod::Access)
Q_DECLARE_METATYPE(QMetaMethod::MethodType)
class MethodTestObject : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE MethodTestObject();
Q_INVOKABLE MethodTestObject(int constructorIntArg);
Q_INVOKABLE MethodTestObject(qreal constructorQRealArg);
Q_INVOKABLE MethodTestObject(const QString &constructorQStringArg);
Q_INVOKABLE MethodTestObject(CustomType constructorCustomTypeArg);
Q_INVOKABLE MethodTestObject(CustomUnregisteredType constructorCustomUnregisteredTypeArg);
Q_INVOKABLE MethodTestObject(bool boolArg, int intArg, uint uintArg,
qlonglong longlongArg, qulonglong ulonglongArg,
double doubleArg, long longArg, short shortArg,
char charArg, ulong ulongArg, ushort ushortArg,
uchar ucharArg, float floatArg);
Q_INVOKABLE MethodTestObject(bool, int);
Q_INVOKABLE void voidInvokable() const;
Q_INVOKABLE void voidInvokableInt(int voidInvokableIntArg);
Q_INVOKABLE void voidInvokableQReal(qreal voidInvokableQRealArg);
Q_INVOKABLE void voidInvokableQString(const QString &voidInvokableQStringArg);
Q_INVOKABLE void voidInvokableCustomType(CustomType voidInvokableCustomTypeArg);
Q_INVOKABLE void voidInvokableCustomUnregisteredType(CustomUnregisteredType voidInvokableCustomUnregisteredTypeArg);
Q_INVOKABLE bool boolInvokable();
Q_INVOKABLE qreal qrealInvokable();
Q_INVOKABLE QString qstringInvokable();
Q_INVOKABLE CustomType customTypeInvokable();
Q_INVOKABLE CustomUnregisteredType customUnregisteredTypeInvokable();
Q_INVOKABLE QVariant qvariantInvokableBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat(
bool boolArg, int intArg, uint uintArg, qlonglong longlongArg, qulonglong ulonglongArg, double doubleArg,
long longArg, short shortArg, char charArg, ulong ulongArg, ushort ushortArg, uchar ucharArg, float floatArg);
Q_INVOKABLE void voidInvokableNoParameterNames(bool, int);
public slots:
void voidSlot();
void voidSlotInt(int voidSlotIntArg);
void voidSlotQReal(qreal voidSlotQRealArg);
void voidSlotQString(const QString &voidSlotQStringArg);
void voidSlotCustomType(CustomType voidSlotCustomTypeArg);
void voidSlotCustomUnregisteredType(CustomUnregisteredType voidSlotCustomUnregisteredTypeArg);
bool boolSlot();
qreal qrealSlot();
QString qstringSlot();
CustomType customTypeSlot();
CustomUnregisteredType customUnregisteredTypeSlot();
QVariant qvariantSlotBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat(
bool boolArg, int intArg, uint uintArg, qlonglong longlongArg, qulonglong ulonglongArg, double doubleArg,
long longArg, short shortArg, char charArg, ulong ulongArg, ushort ushortArg, uchar ucharArg, float floatArg);
void voidSlotNoParameterNames(bool, int);
signals:
void voidSignal();
void voidSignalVoid(void);
void voidSignalInt(int voidSignalIntArg);
void voidSignalQReal(qreal voidSignalQRealArg);
void voidSignalQString(const QString &voidSignalQStringArg);
void voidSignalCustomType(CustomType voidSignalCustomTypeArg);
void voidSignalCustomUnregisteredType(CustomUnregisteredType voidSignalCustomUnregisteredTypeArg);
bool boolSignal();
qreal qrealSignal();
QString qstringSignal();
CustomType customTypeSignal();
CustomUnregisteredType customUnregisteredTypeSignal();
QVariant qvariantSignalBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat(
bool boolArg, int intArg, uint uintArg, qlonglong longlongArg, qulonglong ulonglongArg, double doubleArg,
long longArg, short shortArg, char charArg, ulong ulongArg, ushort ushortArg, uchar ucharArg, float floatArg);
void voidSignalNoParameterNames(bool, int);
};
MethodTestObject::MethodTestObject() {}
MethodTestObject::MethodTestObject(int) {}
MethodTestObject::MethodTestObject(qreal) {}
MethodTestObject::MethodTestObject(const QString &) {}
MethodTestObject::MethodTestObject(CustomType) {}
MethodTestObject::MethodTestObject(CustomUnregisteredType) {}
MethodTestObject::MethodTestObject(bool, int, uint, qlonglong, qulonglong,
double, long, short, char, ulong, ushort,
uchar, float) {}
MethodTestObject::MethodTestObject(bool, int) {}
void MethodTestObject::voidInvokable() const {}
void MethodTestObject::voidInvokableInt(int) {}
void MethodTestObject::voidInvokableQReal(qreal) {}
void MethodTestObject::voidInvokableQString(const QString &) {}
void MethodTestObject::voidInvokableCustomType(CustomType) {}
void MethodTestObject::voidInvokableCustomUnregisteredType(CustomUnregisteredType) {}
bool MethodTestObject::boolInvokable() { return true; }
qreal MethodTestObject::qrealInvokable() { return 1.0; }
QString MethodTestObject::qstringInvokable() { return QString(); }
CustomType MethodTestObject::customTypeInvokable() { return CustomType(); }
CustomUnregisteredType MethodTestObject::customUnregisteredTypeInvokable()
{
return CustomUnregisteredType();
}
QVariant MethodTestObject::qvariantInvokableBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat(
bool, int, uint, qlonglong, qulonglong, double, long, short, char, ulong, ushort, uchar, float)
{
return QVariant();
}
void MethodTestObject::voidInvokableNoParameterNames(bool, int) {}
void MethodTestObject::voidSlot() {}
void MethodTestObject::voidSlotInt(int) {}
void MethodTestObject::voidSlotQReal(qreal) {}
void MethodTestObject::voidSlotQString(const QString &) {}
void MethodTestObject::voidSlotCustomType(CustomType) {}
void MethodTestObject::voidSlotCustomUnregisteredType(CustomUnregisteredType) {}
bool MethodTestObject::boolSlot() { return true; }
qreal MethodTestObject::qrealSlot() { return 1.0; }
QString MethodTestObject::qstringSlot() { return QString(); }
CustomType MethodTestObject::customTypeSlot() { return CustomType(); }
CustomUnregisteredType MethodTestObject::customUnregisteredTypeSlot()
{
return CustomUnregisteredType();
}
QVariant MethodTestObject::qvariantSlotBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat(
bool, int, uint, qlonglong, qulonglong, double, long, short, char, ulong, ushort, uchar, float)
{
return QVariant();
}
void MethodTestObject::voidSlotNoParameterNames(bool, int) {}
void tst_QMetaMethod::method_data()
{
QTest::addColumn<QByteArray>("signature");
QTest::addColumn<int>("returnType");
QTest::addColumn<QByteArray>("returnTypeName");
QTest::addColumn<QList<int> >("parameterTypes");
QTest::addColumn<QList<QByteArray> >("parameterTypeNames");
QTest::addColumn<QList<QByteArray> >("parameterNames");
QTest::addColumn<QMetaMethod::Access>("access");
QTest::addColumn<QMetaMethod::MethodType>("methodType");
QTest::newRow("voidSignal")
<< QByteArray("voidSignal()")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("voidInvokable")
<< QByteArray("voidInvokable()")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Method;
QTest::newRow("voidSlot")
<< QByteArray("voidSlot()")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Slot;
QTest::newRow("MethodTestObject()")
<< QByteArray("MethodTestObject()")
<< int(QMetaType::UnknownType) << QByteArray("")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Constructor;
QTest::newRow("voidSignalVoid")
<< QByteArray("voidSignalVoid()")
<< int(QMetaType::Void) << QByteArray("void")
<< QList<int>()
<< QList<QByteArray>()
<< QList<QByteArray>()
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("voidSignalInt")
<< QByteArray("voidSignalInt(int)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << int(QMetaType::Int))
<< (QList<QByteArray>() << QByteArray("int"))
<< (QList<QByteArray>() << QByteArray("voidSignalIntArg"))
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("voidInvokableInt")
<< QByteArray("voidInvokableInt(int)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << int(QMetaType::Int))
<< (QList<QByteArray>() << QByteArray("int"))
<< (QList<QByteArray>() << QByteArray("voidInvokableIntArg"))
<< QMetaMethod::Public
<< QMetaMethod::Method;
QTest::newRow("voidSlotInt")
<< QByteArray("voidSlotInt(int)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << int(QMetaType::Int))
<< (QList<QByteArray>() << QByteArray("int"))
<< (QList<QByteArray>() << QByteArray("voidSlotIntArg"))
<< QMetaMethod::Public
<< QMetaMethod::Slot;
QTest::newRow("MethodTestObject(int)")
<< QByteArray("MethodTestObject(int)")
<< int(QMetaType::UnknownType) << QByteArray("")
<< (QList<int>() << int(QMetaType::Int))
<< (QList<QByteArray>() << QByteArray("int"))
<< (QList<QByteArray>() << QByteArray("constructorIntArg"))
<< QMetaMethod::Public
<< QMetaMethod::Constructor;
QTest::newRow("voidSignalQReal")
<< QByteArray("voidSignalQReal(qreal)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << qMetaTypeId<qreal>())
<< (QList<QByteArray>() << QByteArray("qreal"))
<< (QList<QByteArray>() << QByteArray("voidSignalQRealArg"))
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("voidInvokableQReal")
<< QByteArray("voidInvokableQReal(qreal)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << qMetaTypeId<qreal>())
<< (QList<QByteArray>() << QByteArray("qreal"))
<< (QList<QByteArray>() << QByteArray("voidInvokableQRealArg"))
<< QMetaMethod::Public
<< QMetaMethod::Method;
QTest::newRow("voidSlotQReal")
<< QByteArray("voidSlotQReal(qreal)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << qMetaTypeId<qreal>())
<< (QList<QByteArray>() << QByteArray("qreal"))
<< (QList<QByteArray>() << QByteArray("voidSlotQRealArg"))
<< QMetaMethod::Public
<< QMetaMethod::Slot;
QTest::newRow("MethodTestObject(qreal)")
<< QByteArray("MethodTestObject(qreal)")
<< int(QMetaType::UnknownType) << QByteArray("")
<< (QList<int>() << qMetaTypeId<qreal>())
<< (QList<QByteArray>() << QByteArray("qreal"))
<< (QList<QByteArray>() << QByteArray("constructorQRealArg"))
<< QMetaMethod::Public
<< QMetaMethod::Constructor;
QTest::newRow("voidSignalQString")
<< QByteArray("voidSignalQString(QString)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << int(QMetaType::QString))
<< (QList<QByteArray>() << QByteArray("QString"))
<< (QList<QByteArray>() << QByteArray("voidSignalQStringArg"))
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("voidInvokableQString")
<< QByteArray("voidInvokableQString(QString)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << int(QMetaType::QString))
<< (QList<QByteArray>() << QByteArray("QString"))
<< (QList<QByteArray>() << QByteArray("voidInvokableQStringArg"))
<< QMetaMethod::Public
<< QMetaMethod::Method;
QTest::newRow("voidSlotQString")
<< QByteArray("voidSlotQString(QString)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << int(QMetaType::QString))
<< (QList<QByteArray>() << QByteArray("QString"))
<< (QList<QByteArray>() << QByteArray("voidSlotQStringArg"))
<< QMetaMethod::Public
<< QMetaMethod::Slot;
QTest::newRow("MethodTestObject(QString)")
<< QByteArray("MethodTestObject(QString)")
<< int(QMetaType::UnknownType) << QByteArray("")
<< (QList<int>() << int(QMetaType::QString))
<< (QList<QByteArray>() << QByteArray("QString"))
<< (QList<QByteArray>() << QByteArray("constructorQStringArg"))
<< QMetaMethod::Public
<< QMetaMethod::Constructor;
QTest::newRow("voidSignalCustomType")
<< QByteArray("voidSignalCustomType(CustomType)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << qMetaTypeId<CustomType>())
<< (QList<QByteArray>() << QByteArray("CustomType"))
<< (QList<QByteArray>() << QByteArray("voidSignalCustomTypeArg"))
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("voidInvokableCustomType")
<< QByteArray("voidInvokableCustomType(CustomType)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << qMetaTypeId<CustomType>())
<< (QList<QByteArray>() << QByteArray("CustomType"))
<< (QList<QByteArray>() << QByteArray("voidInvokableCustomTypeArg"))
<< QMetaMethod::Public
<< QMetaMethod::Method;
QTest::newRow("voidSlotCustomType")
<< QByteArray("voidSlotCustomType(CustomType)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << qMetaTypeId<CustomType>())
<< (QList<QByteArray>() << QByteArray("CustomType"))
<< (QList<QByteArray>() << QByteArray("voidSlotCustomTypeArg"))
<< QMetaMethod::Public
<< QMetaMethod::Slot;
QTest::newRow("MethodTestObject(CustomType)")
<< QByteArray("MethodTestObject(CustomType)")
<< int(QMetaType::UnknownType) << QByteArray("")
<< (QList<int>() << qMetaTypeId<CustomType>())
<< (QList<QByteArray>() << QByteArray("CustomType"))
<< (QList<QByteArray>() << QByteArray("constructorCustomTypeArg"))
<< QMetaMethod::Public
<< QMetaMethod::Constructor;
// since Qt 6.0, parameter types get automatically registered
QTest::newRow("voidSignalCustomUnregisteredType")
<< QByteArray("voidSignalCustomUnregisteredType(CustomUnregisteredType)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << QMetaType::fromType<CustomUnregisteredType>().id())
<< (QList<QByteArray>() << QByteArray("CustomUnregisteredType"))
<< (QList<QByteArray>() << QByteArray("voidSignalCustomUnregisteredTypeArg"))
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("voidInvokableCustomUnregisteredType")
<< QByteArray("voidInvokableCustomUnregisteredType(CustomUnregisteredType)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << QMetaType::fromType<CustomUnregisteredType>().id())
<< (QList<QByteArray>() << QByteArray("CustomUnregisteredType"))
<< (QList<QByteArray>() << QByteArray("voidInvokableCustomUnregisteredTypeArg"))
<< QMetaMethod::Public
<< QMetaMethod::Method;
QTest::newRow("voidSlotCustomUnregisteredType")
<< QByteArray("voidSlotCustomUnregisteredType(CustomUnregisteredType)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << QMetaType::fromType<CustomUnregisteredType>().id())
<< (QList<QByteArray>() << QByteArray("CustomUnregisteredType"))
<< (QList<QByteArray>() << QByteArray("voidSlotCustomUnregisteredTypeArg"))
<< QMetaMethod::Public
<< QMetaMethod::Slot;
QTest::newRow("MethodTestObject(CustomUnregisteredType)")
<< QByteArray("MethodTestObject(CustomUnregisteredType)")
<< int(QMetaType::UnknownType) << QByteArray("")
<< (QList<int>() << QMetaType::fromType<CustomUnregisteredType>().id())
<< (QList<QByteArray>() << QByteArray("CustomUnregisteredType"))
<< (QList<QByteArray>() << QByteArray("constructorCustomUnregisteredTypeArg"))
<< QMetaMethod::Public
<< QMetaMethod::Constructor;
QTest::newRow("boolSignal")
<< QByteArray("boolSignal()")
<< int(QMetaType::Bool) << QByteArray("bool")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("boolInvokable")
<< QByteArray("boolInvokable()")
<< int(QMetaType::Bool) << QByteArray("bool")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Method;
QTest::newRow("boolSlot")
<< QByteArray("boolSlot()")
<< int(QMetaType::Bool) << QByteArray("bool")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Slot;
QTest::newRow("qrealSignal")
<< QByteArray("qrealSignal()")
<< int(QMetaType::QReal) << QByteArray("qreal")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("qrealInvokable")
<< QByteArray("qrealInvokable()")
<< int(QMetaType::QReal) << QByteArray("qreal")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Method;
QTest::newRow("qrealSlot")
<< QByteArray("qrealSlot()")
<< int(QMetaType::QReal) << QByteArray("qreal")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Slot;
QTest::newRow("qstringSignal")
<< QByteArray("qstringSignal()")
<< int(QMetaType::QString) << QByteArray("QString")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("qstringInvokable")
<< QByteArray("qstringInvokable()")
<< int(QMetaType::QString) << QByteArray("QString")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Method;
QTest::newRow("qstringSlot")
<< QByteArray("qstringSlot()")
<< int(QMetaType::QString) << QByteArray("QString")
<< (QList<int>())
<< (QList<QByteArray>())
<< (QList<QByteArray>())
<< QMetaMethod::Public
<< QMetaMethod::Slot;
{
QList<int> parameterTypes = QList<int>()
<< int(QMetaType::Bool) << int(QMetaType::Int) << int(QMetaType::UInt)
<< int(QMetaType::LongLong) << int(QMetaType::ULongLong) << int(QMetaType::Double)
<< int(QMetaType::Long) << int(QMetaType::Short) << int(QMetaType::Char)
<< int(QMetaType::ULong) << int(QMetaType::UShort) << int(QMetaType::UChar)
<< int(QMetaType::Float);
QList<QByteArray> parameterTypeNames = QList<QByteArray>()
<< QByteArray("bool") << QByteArray("int") << QByteArray("uint")
<< QByteArray("qlonglong") << QByteArray("qulonglong") << QByteArray("double")
<< QByteArray("long") << QByteArray("short") << QByteArray("char") << QByteArray("ulong")
<< QByteArray("ushort") << QByteArray("uchar") << QByteArray("float");
QList<QByteArray> parameterNames = QList<QByteArray>()
<< QByteArray("boolArg") << QByteArray("intArg") << QByteArray("uintArg")
<< QByteArray("longlongArg") << QByteArray("ulonglongArg") << QByteArray("doubleArg")
<< QByteArray("longArg") << QByteArray("shortArg") << QByteArray("charArg")
<< QByteArray("ulongArg") << QByteArray("ushortArg") << QByteArray("ucharArg")
<< QByteArray("floatArg");
QTest::newRow("qvariantSignalBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat")
<< QByteArray("qvariantSignalBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat("
"bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)")
<< int(QMetaType::QVariant) << QByteArray("QVariant")
<< parameterTypes << parameterTypeNames << parameterNames
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("qvariantInvokableBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat")
<< QByteArray("qvariantInvokableBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat("
"bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)")
<< int(QMetaType::QVariant) << QByteArray("QVariant")
<< parameterTypes << parameterTypeNames << parameterNames
<< QMetaMethod::Public
<< QMetaMethod::Method;
QTest::newRow("qvariantSlotBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat")
<< QByteArray("qvariantSlotBoolIntUIntLonglongULonglongDoubleLongShortCharUlongUshortUcharFloat("
"bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)")
<< int(QMetaType::QVariant) << QByteArray("QVariant")
<< parameterTypes << parameterTypeNames << parameterNames
<< QMetaMethod::Public
<< QMetaMethod::Slot;
QTest::newRow("MethodTestObject(bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)")
<< QByteArray("MethodTestObject(bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)")
<< int(QMetaType::UnknownType) << QByteArray("")
<< parameterTypes << parameterTypeNames << parameterNames
<< QMetaMethod::Public
<< QMetaMethod::Constructor;
}
QTest::newRow("voidSignalNoParameterNames")
<< QByteArray("voidSignalNoParameterNames(bool,int)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << int(QMetaType::Bool) << int(QMetaType::Int))
<< (QList<QByteArray>() << QByteArray("bool") << QByteArray("int"))
<< (QList<QByteArray>() << QByteArray("") << QByteArray(""))
<< QMetaMethod::Public
<< QMetaMethod::Signal;
QTest::newRow("voidInvokableNoParameterNames")
<< QByteArray("voidInvokableNoParameterNames(bool,int)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << int(QMetaType::Bool) << int(QMetaType::Int))
<< (QList<QByteArray>() << QByteArray("bool") << QByteArray("int"))
<< (QList<QByteArray>() << QByteArray("") << QByteArray(""))
<< QMetaMethod::Public
<< QMetaMethod::Method;
QTest::newRow("voidSlotNoParameterNames")
<< QByteArray("voidSlotNoParameterNames(bool,int)")
<< int(QMetaType::Void) << QByteArray("void")
<< (QList<int>() << int(QMetaType::Bool) << int(QMetaType::Int))
<< (QList<QByteArray>() << QByteArray("bool") << QByteArray("int"))
<< (QList<QByteArray>() << QByteArray("") << QByteArray(""))
<< QMetaMethod::Public
<< QMetaMethod::Slot;
QTest::newRow("MethodTestObject(bool,int)")
<< QByteArray("MethodTestObject(bool,int)")
<< int(QMetaType::UnknownType) << QByteArray("")
<< (QList<int>() << int(QMetaType::Bool) << int(QMetaType::Int))
<< (QList<QByteArray>() << QByteArray("bool") << QByteArray("int"))
<< (QList<QByteArray>() << QByteArray("") << QByteArray(""))
<< QMetaMethod::Public
<< QMetaMethod::Constructor;
}
void tst_QMetaMethod::method()
{
QFETCH(QByteArray, signature);
QFETCH(int, returnType);
QFETCH(QByteArray, returnTypeName);
QFETCH(QList<int>, parameterTypes);
QFETCH(QList<QByteArray>, parameterTypeNames);
QFETCH(QList<QByteArray>, parameterNames);
QFETCH(QMetaMethod::MethodType, methodType);
QFETCH(QMetaMethod::Access, access);
QCOMPARE(parameterTypes.size(), parameterTypeNames.size());
QCOMPARE(parameterTypes.size(), parameterNames.size());
const QMetaObject *mo = &MethodTestObject::staticMetaObject;
int index = (methodType == QMetaMethod::Constructor)
? mo->indexOfConstructor(signature) : mo->indexOfMethod(signature);
QVERIFY(index != -1);
QMetaMethod method = (methodType == QMetaMethod::Constructor)
? mo->constructor(index) : mo->method(index);
QVERIFY(method.isValid());
QCOMPARE(method.methodType(), methodType);
QCOMPARE(method.access(), access);
QVERIFY(!method.methodSignature().isEmpty());
if (method.methodSignature() != signature) {
// QMetaMethod should always produce a semantically equivalent signature
int signatureIndex = (methodType == QMetaMethod::Constructor)
? mo->indexOfConstructor(method.methodSignature())
: mo->indexOfMethod(method.methodSignature());
QCOMPARE(signatureIndex, index);
}
QByteArray computedName = signature.left(signature.indexOf('('));
QCOMPARE(method.name(), computedName);
QCOMPARE(method.tag(), "");
QCOMPARE(method.returnType(), returnType);
QVERIFY(method.typeName() != 0);
if (QByteArray(method.typeName()) != returnTypeName) {
// QMetaMethod should always produce a semantically equivalent typename
QCOMPARE(QMetaType::fromName(method.typeName()), QMetaType::fromName(returnTypeName));
}
// check that parameterNames and parameterTypeName agree
const auto methodParmaterTypes = method.parameterTypes();
for (int i = 0; i< methodParmaterTypes.size(); ++i) {
QCOMPARE(methodParmaterTypes[i], method.parameterTypeName(i));
}
if (method.parameterTypes() != parameterTypeNames) {
// QMetaMethod should always produce semantically equivalent typenames
QList<QByteArray> actualTypeNames = method.parameterTypes();
QCOMPARE(actualTypeNames.size(), parameterTypeNames.size());
for (int i = 0; i < parameterTypeNames.size(); ++i) {
QCOMPARE(QMetaType::fromName(actualTypeNames.at(i)),
QMetaType::fromName(parameterTypeNames.at(i)));
}
}
QCOMPARE(method.parameterNames(), parameterNames);
QCOMPARE(method.parameterCount(), parameterTypes.size());
for (int i = 0; i < parameterTypes.size(); ++i)
QCOMPARE(method.parameterType(i), parameterTypes.at(i));
{
QList<int> actualParameterTypes(parameterTypes.size());
method.getParameterTypes(actualParameterTypes.data());
for (int i = 0; i < parameterTypes.size(); ++i)
QCOMPARE(actualParameterTypes.at(i), parameterTypes.at(i));
}
// Bogus indexes
QCOMPARE(method.parameterType(-1), 0);
QCOMPARE(method.parameterType(parameterTypes.size()), 0);
}
void tst_QMetaMethod::invalidMethod()
{
QMetaMethod method;
QVERIFY(!method.isValid());
QMetaMethod method2 = staticMetaObject.method(staticMetaObject.methodCount());
QVERIFY(!method2.isValid());
QMetaMethod method3 = staticMetaObject.method(-1);
QVERIFY(!method3.isValid());
}
void tst_QMetaMethod::comparisonOperators()
{
static const QMetaObject *mo = &MethodTestObject::staticMetaObject;
for (int x = 0; x < 2; ++x) {
int count = x ? mo->constructorCount() : mo->methodCount();
for (int i = 0; i < count; ++i) {
QMetaMethod method = x ? mo->constructor(i) : mo->method(i);
const QMetaObject *methodMo = method.enclosingMetaObject();
for (int j = 0; j < count; ++j) {
QMetaMethod other = x ? mo->constructor(j) : mo->method(j);
bool expectedEqual = ((methodMo == other.enclosingMetaObject())
&& (i == j));
QCOMPARE(method == other, expectedEqual);
QCOMPARE(method != other, !expectedEqual);
QCOMPARE(other == method, expectedEqual);
QCOMPARE(other != method, !expectedEqual);
}
QVERIFY(method != QMetaMethod());
QVERIFY(QMetaMethod() != method);
QVERIFY(!(method == QMetaMethod()));
QVERIFY(!(QMetaMethod() == method));
}
}
// Constructors and normal methods with identical index should not
// compare equal
for (int i = 0; i < qMin(mo->methodCount(), mo->constructorCount()); ++i) {
QMetaMethod method = mo->method(i);
QMetaMethod constructor = mo->constructor(i);
QVERIFY(method != constructor);
QVERIFY(!(method == constructor));
}
}
void tst_QMetaMethod::fromSignal()
{
#define FROMSIGNAL_HELPER(ObjectType, Name, Arguments) { \
const QMetaObject *signalMeta = &ObjectType::staticMetaObject; \
QCOMPARE(QMetaMethod::fromSignal(&ObjectType::Name), \
signalMeta->method(signalMeta->indexOfSignal(QMetaObject::normalizedSignature(#Name #Arguments)))); \
}
FROMSIGNAL_HELPER(MethodTestObject, voidSignal, ())
FROMSIGNAL_HELPER(MethodTestObject, voidSignalQString, (const QString&))
FROMSIGNAL_HELPER(QObject, destroyed, (QObject*))
FROMSIGNAL_HELPER(QObject, objectNameChanged, (const QString &))
// Inherited from QObject
FROMSIGNAL_HELPER(MethodTestObject, destroyed, (QObject*))
FROMSIGNAL_HELPER(MethodTestObject, objectNameChanged, (const QString &))
// Methods that are not signals; fromSignal should return invalid method
FROMSIGNAL_HELPER(MethodTestObject, voidSlot, ())
FROMSIGNAL_HELPER(QObject, deleteLater, ())
#undef FROMSIGNAL_HELPER
}
class MyGadget {
Q_GADGET
public:
QString m_value;
Q_INVOKABLE void setValue(const QString &value) { m_value = value; }
Q_INVOKABLE QString getValue() { return m_value; }
};
void tst_QMetaMethod::gadget()
{
int idx;
idx = MyGadget::staticMetaObject.indexOfMethod("setValue(QString)");
QVERIFY(idx >= 0);
QMetaMethod setValueMethod = MyGadget::staticMetaObject.method(idx);
QVERIFY(setValueMethod.isValid());
idx = MyGadget::staticMetaObject.indexOfMethod("getValue()");
QVERIFY(idx >= 0);
QMetaMethod getValueMethod = MyGadget::staticMetaObject.method(idx);
QVERIFY(getValueMethod.isValid());
{
MyGadget gadget;
QString string;
QVERIFY(getValueMethod.invokeOnGadget(&gadget, Q_RETURN_ARG(QString, string)));
QCOMPARE(string, gadget.m_value);
QVERIFY(setValueMethod.invokeOnGadget(&gadget, Q_ARG(QString, QLatin1String("hello"))));
QCOMPARE(gadget.m_value, QLatin1String("hello"));
QVERIFY(getValueMethod.invokeOnGadget(&gadget, Q_RETURN_ARG(QString, string)));
QCOMPARE(string, gadget.m_value);
}
{
// Call with null should not crash
MyGadget *gadget = nullptr;
QString string;
QVERIFY(!setValueMethod.invokeOnGadget(gadget, Q_ARG(QString, QLatin1String("hi"))));
QVERIFY(!getValueMethod.invokeOnGadget(gadget, Q_RETURN_ARG(QString, string)));
}
}
class MyTestClass : public QObject
{
Q_OBJECT
public:
MyTestClass() {};
public Q_SLOTS:
Q_REVISION(42) MyGadget doStuff(int, float, MyGadget) {return {};}
Q_SIGNALS:
QObject *mySignal();
};
void tst_QMetaMethod::revision()
{
auto mo = MyTestClass::staticMetaObject;
const auto normalized = QMetaObject::normalizedSignature("doStuff(int, float, MyGadget)");
const int idx = mo.indexOfSlot(normalized);
QMetaMethod mm = mo.method(idx);
QVERIFY(mm.isValid());
QCOMPARE(QTypeRevision::fromEncodedVersion(mm.revision()), QTypeRevision::fromMinorVersion(42));
}
void tst_QMetaMethod::returnMetaType()
{
{
QMetaMethod mm = QMetaMethod::fromSignal(&MyTestClass::mySignal);
QCOMPARE(mm.returnMetaType(), QMetaType::fromType<QObject*>());
}
auto mo = MyTestClass::staticMetaObject;
{
const auto normalized = QMetaObject::normalizedSignature("doStuff(int, float, MyGadget)");
const int idx = mo.indexOfSlot(normalized);
QMetaMethod mm = mo.method(idx);
QVERIFY(mm.isValid());
QCOMPARE(mm.returnMetaType(), QMetaType::fromType<MyGadget>());
}
{
// access of parent class meta methods works, too
const auto normalized = QMetaObject::normalizedSignature("deleteLater()");
const int idx = mo.indexOfSlot(normalized);
QMetaMethod mm = mo.method(idx);
QVERIFY(mm.isValid());
QCOMPARE(mm.returnMetaType(), QMetaType::fromType<void>());
}
}
void tst_QMetaMethod::parameterMetaType()
{
auto mo = MyTestClass::staticMetaObject;
const auto normalized = QMetaObject::normalizedSignature("doStuff(int, float, MyGadget)");
const int idx = mo.indexOfSlot(normalized);
QMetaMethod mm = mo.method(idx);
{
QVERIFY(!mm.parameterMetaType(-1).isValid());
QVERIFY(!mm.parameterMetaType(3).isValid());
}
{
QCOMPARE(mm.parameterMetaType(0), QMetaType::fromType<int>());
QCOMPARE(mm.parameterMetaType(1), QMetaType::fromType<float>());
QCOMPARE(mm.parameterMetaType(2), QMetaType::fromType<MyGadget>());
}
}
void tst_QMetaMethod::parameterTypeName()
{
auto mo = MyTestClass::staticMetaObject;
const auto normalized = QMetaObject::normalizedSignature("doStuff(int, float, MyGadget)");
const int idx = mo.indexOfSlot(normalized);
QMetaMethod mm = mo.method(idx);
{
// check invalid indices
QVERIFY(mm.parameterTypeName(-1).isEmpty());
QVERIFY(mm.parameterTypeName(3).isEmpty());
}
{
QCOMPARE(mm.parameterTypeName(0), QByteArray("int"));
QCOMPARE(mm.parameterTypeName(1), QByteArray("float"));
QCOMPARE(mm.parameterTypeName(2), QByteArray("MyGadget"));
}
}
void tst_QMetaMethod::isConst()
{
auto mo = MethodTestObject::staticMetaObject;
{
const auto normalized = QMetaObject::normalizedSignature("qrealInvokable()");
const int idx = mo.indexOfSlot(normalized);
QMetaMethod mm = mo.method(idx);
QVERIFY(mm.isValid());
QCOMPARE(mm.isConst(), false);
}
{
const auto normalized = QMetaObject::normalizedSignature("voidInvokable()");
const int idx = mo.indexOfSlot(normalized);
QMetaMethod mm = mo.method(idx);
QVERIFY(mm.isValid());
QCOMPARE(mm.isConst(), true);
}
}
void tst_QMetaMethod::methodIndexes_data()
{
QTest::addColumn<QByteArray>("signature");
QTest::addColumn<QMetaMethod::MethodType>("methodType");
QTest::newRow("constructor1") << QByteArray("MethodTestObject()") << QMetaMethod::Constructor;
QTest::newRow("constructor5") << QByteArray("MethodTestObject(CustomUnregisteredType)")
<< QMetaMethod::Constructor;
QTest::newRow("method0") << QByteArray("voidInvokable()") << QMetaMethod::Method;
QTest::newRow("method6") << QByteArray("boolInvokable()") << QMetaMethod::Method;
}
void tst_QMetaMethod::methodIndexes()
{
QFETCH(QByteArray, signature);
QFETCH(QMetaMethod::MethodType, methodType);
const bool isConstructor = methodType == QMetaMethod::Constructor;
// roundtrip: index = QMetaObject::indexOfConstructor/Method()
// <-> method = QMetaObject::constructor/method()
// <-> indexThatShouldBeEqualToAboveIndex = QMetaMethod::methodIndex()
const QMetaObject *mo = &MethodTestObject::staticMetaObject;
const int index =
isConstructor ? mo->indexOfConstructor(signature) : mo->indexOfMethod(signature);
QVERIFY(index != -1);
QMetaMethod methodFromMetaObject =
mo->method(index); // should work on all methods (constructors, signals, ...)
const int absoluteMethodIndex =
methodFromMetaObject
.methodIndex(); // should work on all methods (constructors, signals, ...)
QCOMPARE(absoluteMethodIndex, index);
}
QTEST_MAIN(tst_QMetaMethod)
#include "tst_qmetamethod.moc"

View File

@ -0,0 +1,30 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
set(tst_qmetaobject_SOURCES
tst_qmetaobject.cpp
forwarddeclared.h
forwarddeclared.cpp
)
#####################################################################
## tst_qmetaobject Test:
#####################################################################
qt_internal_add_test(tst_qmetaobject
SOURCES
${tst_qmetaobject_SOURCES}
LIBRARIES
Qt::CorePrivate
)
qt_internal_add_test(tst_qmetaobject_compat
SOURCES
${tst_qmetaobject_SOURCES}
DEFINES
USE_COMPAT_Q_ARG=1
LIBRARIES
Qt::CorePrivate
NO_BATCH
)

View File

@ -0,0 +1,17 @@
// Copyright (C) 2022 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "forwarddeclared.h"
struct MyForwardDeclaredType { };
static MyForwardDeclaredType t;
const MyForwardDeclaredType &getForwardDeclaredType() noexcept
{
return t;
}
MyForwardDeclaredType *getForwardDeclaredPointer() noexcept
{
return &t;
}

View File

@ -0,0 +1,12 @@
// Copyright (C) 2022 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef FORWARDDECLARED_H
#define FORWARDDECLARED_H
struct MyForwardDeclaredType; // and ONLY forward-declared
const MyForwardDeclaredType &getForwardDeclaredType() noexcept;
MyForwardDeclaredType *getForwardDeclaredPointer() noexcept;
#endif // FORWARDDECLARED_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmetaobjectbuilder Test:
#####################################################################
qt_internal_add_test(tst_qmetaobjectbuilder
SOURCES
tst_qmetaobjectbuilder.cpp
LIBRARIES
Qt::CorePrivate
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmetaproperty Test:
#####################################################################
qt_internal_add_test(tst_qmetaproperty
SOURCES
tst_qmetaproperty.cpp
)

View File

@ -0,0 +1,278 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <qobject.h>
#include <qmetaobject.h>
#include <QMap>
#include <QString>
struct CustomType
{
int padding;
QString str;
CustomType(const QString &str = QString()) : str(str) {}
operator QString() const { return str; }
friend bool operator!=(const CustomType &a, const CustomType &b)
{ return a.str != b.str; }
};
Q_DECLARE_METATYPE(CustomType)
class tst_QMetaProperty : public QObject
{
Q_OBJECT
Q_PROPERTY(EnumType value WRITE setValue READ getValue)
Q_PROPERTY(EnumType value2 WRITE set_value READ get_value)
Q_PROPERTY(QString value7 MEMBER value7 RESET resetValue7)
Q_PROPERTY(int value8 READ value8)
Q_PROPERTY(int value9 READ value9 CONSTANT)
Q_PROPERTY(int value10 READ value10 FINAL)
Q_PROPERTY(QMap<int, int> map MEMBER map)
Q_PROPERTY(CustomType custom MEMBER custom)
private slots:
void hasStdCppSet();
void isConstant();
void isFinal();
void gadget();
void readAndWriteWithLazyRegistration();
void mapProperty();
void conversion();
void enumsFlags();
public:
enum EnumType { EnumType1 };
void setValue(EnumType) {}
EnumType getValue() const { return EnumType1; }
void set_value(EnumType) {}
EnumType get_value() const { return EnumType1; }
void resetValue7() { value7 = QStringLiteral("reset"); }
int value8() const { return 1; }
int value9() const { return 1; }
int value10() const { return 1; }
QString value7;
QMap<int, int> map;
CustomType custom;
};
void tst_QMetaProperty::hasStdCppSet()
{
const QMetaObject *mo = metaObject();
QMetaProperty prop = mo->property(mo->indexOfProperty("value"));
QVERIFY(prop.isValid());
QVERIFY(prop.hasStdCppSet());
prop = mo->property(mo->indexOfProperty("value2"));
QVERIFY(prop.isValid());
QVERIFY(!prop.hasStdCppSet());
}
void tst_QMetaProperty::isConstant()
{
const QMetaObject *mo = metaObject();
QMetaProperty prop = mo->property(mo->indexOfProperty("value8"));
QVERIFY(prop.isValid());
QVERIFY(!prop.isConstant());
prop = mo->property(mo->indexOfProperty("value9"));
QVERIFY(prop.isValid());
QVERIFY(prop.isConstant());
}
void tst_QMetaProperty::isFinal()
{
const QMetaObject *mo = metaObject();
QMetaProperty prop = mo->property(mo->indexOfProperty("value10"));
QVERIFY(prop.isValid());
QVERIFY(prop.isFinal());
prop = mo->property(mo->indexOfProperty("value9"));
QVERIFY(prop.isValid());
QVERIFY(!prop.isFinal());
}
class MyGadget {
Q_GADGET
Q_PROPERTY(QString value READ getValue WRITE setValue RESET resetValue)
public:
QString m_value;
void setValue(const QString &value) { m_value = value; }
QString getValue() { return m_value; }
void resetValue() { m_value = QLatin1String("reset"); }
};
void tst_QMetaProperty::gadget()
{
const QMetaObject *mo = &MyGadget::staticMetaObject;
QMetaProperty valueProp = mo->property(mo->indexOfProperty("value"));
QVERIFY(valueProp.isValid());
QCOMPARE(valueProp.metaType(), QMetaType::fromType<QString>());
{
MyGadget g;
QString hello = QLatin1String("hello");
QVERIFY(valueProp.writeOnGadget(&g, hello));
QCOMPARE(g.m_value, QLatin1String("hello"));
QCOMPARE(valueProp.readOnGadget(&g), QVariant(hello));
QVERIFY(valueProp.resetOnGadget(&g));
QCOMPARE(valueProp.readOnGadget(&g), QVariant(QLatin1String("reset")));
}
}
struct CustomReadObject : QObject
{
Q_OBJECT
};
struct CustomWriteObject : QObject
{
Q_OBJECT
};
struct CustomWriteObjectChild : CustomWriteObject
{
Q_OBJECT
};
struct TypeLazyRegistration : QObject
{
Q_OBJECT
Q_PROPERTY(CustomReadObject *read MEMBER _read)
Q_PROPERTY(CustomWriteObject *write MEMBER _write)
CustomReadObject *_read;
CustomWriteObject *_write;
public:
TypeLazyRegistration()
: _read()
, _write()
{}
};
class EnumFlagsTester : public QObject
{
Q_OBJECT
Q_PROPERTY(TestEnum enumProperty READ enumProperty WRITE setEnumProperty)
Q_PROPERTY(TestFlags flagProperty READ flagProperty WRITE setFlagProperty)
public:
enum TestEnum { e1, e2 };
Q_ENUM(TestEnum)
enum TestFlag { flag1 = 0x1, flag2 = 0x2 };
Q_DECLARE_FLAGS(TestFlags, TestFlag)
using QObject::QObject;
TestEnum enumProperty() const { return m_enum; }
void setEnumProperty(TestEnum e) { m_enum = e; }
TestFlags flagProperty() const { return m_flags; }
void setFlagProperty(TestFlags f) { m_flags = f; }
private:
TestEnum m_enum = e1;
TestFlags m_flags;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(EnumFlagsTester::TestFlags)
void tst_QMetaProperty::readAndWriteWithLazyRegistration()
{
QVERIFY(!QMetaType::fromName("CustomReadObject*").isValid());
QVERIFY(!QMetaType::fromName("CustomWriteObject*").isValid());
TypeLazyRegistration o;
QVERIFY(o.property("read").isValid());
QVERIFY(QMetaType::fromName("CustomReadObject*").isValid());
QVERIFY(!QMetaType::fromName("CustomWriteObject*").isValid());
CustomWriteObjectChild data;
QVariant value = QVariant::fromValue(&data); // this register CustomWriteObjectChild
// check if base classes are not registered automatically, otherwise this test would be meaningless
QVERIFY(!QMetaType::fromName("CustomWriteObject*").isValid());
QVERIFY(o.setProperty("write", value));
QVERIFY(QMetaType::fromName("CustomWriteObject*").isValid());
QCOMPARE(o.property("write").value<CustomWriteObjectChild*>(), &data);
}
void tst_QMetaProperty::mapProperty()
{
map.insert(5, 9);
QVariant v1 = QVariant::fromValue(map);
QVariant v = property("map");
QVERIFY(v.isValid());
QCOMPARE(map, (v.value<QMap<int,int> >()));
}
void tst_QMetaProperty::conversion()
{
QMetaType::registerConverter<QString, CustomType>();
QMetaType::registerConverter<CustomType, QString>();
QString hello = QStringLiteral("Hello");
// Write to a QString property using a CustomType in a QVariant
QMetaProperty value7P = metaObject()->property(metaObject()->indexOfProperty("value7"));
QVERIFY(value7P.isValid());
QVERIFY(value7P.write(this, QVariant::fromValue(CustomType(hello))));
QCOMPARE(value7, hello);
// Write to a CustomType property using a QString in a QVariant
QMetaProperty customP = metaObject()->property(metaObject()->indexOfProperty("custom"));
QVERIFY(customP.isValid());
QVERIFY(customP.write(this, hello));
QCOMPARE(custom.str, hello);
// Something that cannot be converted should fail
QVERIFY(!customP.write(this, 45));
QVERIFY(!customP.write(this, QVariant::fromValue(this)));
QVERIFY(!value7P.write(this, QVariant::fromValue(this)));
QVERIFY(!value7P.write(this, QVariant::fromValue<QObject*>(this)));
// none of this should have changed the values
QCOMPARE(value7, hello);
QCOMPARE(custom.str, hello);
// Empty variant should be converted to default object
QVERIFY(customP.write(this, QVariant()));
QCOMPARE(custom.str, QString());
// or reset resetable
QVERIFY(value7P.write(this, QVariant()));
QCOMPARE(value7, QLatin1String("reset"));
}
void tst_QMetaProperty::enumsFlags()
{
// QTBUG-83689, verify that enumerations and flags can be assigned from int,
// which is important for Qt Designer.
EnumFlagsTester t;
auto mo = t.metaObject();
const int enumIndex = mo->indexOfProperty("enumProperty");
QVERIFY(enumIndex >= 0);
auto enumProperty = mo->property(enumIndex);
QVERIFY(enumProperty.metaType().flags().testFlag(QMetaType::IsEnumeration));
QVERIFY(enumProperty.write(&t, QVariant(int(EnumFlagsTester::e2))));
QCOMPARE(t.enumProperty(), EnumFlagsTester::e2);
const int flagsIndex = mo->indexOfProperty("flagProperty");
QVERIFY(flagsIndex >= 0);
auto flagsProperty = mo->property(flagsIndex);
QVERIFY(flagsProperty.metaType().flags().testFlag(QMetaType::IsEnumeration));
QVERIFY(flagsProperty.write(&t, QVariant(int(EnumFlagsTester::flag2))));
QCOMPARE(t.flagProperty(), EnumFlagsTester::flag2);
}
QTEST_MAIN(tst_QMetaProperty)
#include "tst_qmetaproperty.moc"

View File

@ -0,0 +1,57 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmetatype Test:
#####################################################################
# Collect test data
list(APPEND test_data "./typeFlags.bin")
qt_internal_add_cmake_library(qmetatype_lib1
INSTALL_DIRECTORY "${INSTALL_TESTSDIR}/tst_qmetatype"
SOURCES
lib1.cpp
PUBLIC_LIBRARIES
Qt::Core
)
qt_internal_add_cmake_library(qmetatype_lib2
INSTALL_DIRECTORY "${INSTALL_TESTSDIR}/tst_qmetatype"
SOURCES
lib2.cpp
PUBLIC_LIBRARIES
Qt::Core
)
set_target_properties(qmetatype_lib1 PROPERTIES
VERSION 1.0.0
SOVERSION 0
C_VISIBILITY_PRESET "hidden"
CXX_VISIBILITY_PRESET "hidden"
VISIBILITY_INLINES_HIDDEN ON
)
set_target_properties(qmetatype_lib2 PROPERTIES
VERSION 1.0.0
SOVERSION 0
C_VISIBILITY_PRESET "hidden"
CXX_VISIBILITY_PRESET "hidden"
VISIBILITY_INLINES_HIDDEN ON
)
qt_internal_add_test(tst_qmetatype
SOURCES
tst_qmetatype.h tst_qmetatype.cpp tst_qmetatype2.cpp
tst_qmetatype3.cpp
INCLUDE_DIRECTORIES
../../../other/qvariant_common
LIBRARIES
Qt::CorePrivate
Qt::Gui
qmetatype_lib1
qmetatype_lib2
TESTDATA ${test_data}
)
qt_internal_extend_target(tst_qmetatype CONDITION MSVC
COMPILE_OPTIONS
/bigobj
)

View File

@ -0,0 +1,5 @@
// Copyright (C) 2022 Intel Corporation
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#define LIB_NAMESPACE Lib1
#include "lib_common.cpp"

View File

@ -0,0 +1,5 @@
// Copyright (C) 2022 Intel Corporation
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#define LIB_NAMESPACE Lib2
#include "lib_common.cpp"

View File

@ -0,0 +1,13 @@
// Copyright (C) 2022 Intel Corporation
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qcollator.h>
#include "tst_qmetatype_libs.h"
#define DECLARE_FUNCTION(TYPE, ID) \
Q_DECL_EXPORT QMetaType metatype_ ## TYPE() \
{ return QMetaType::fromType<TYPE>(); }
namespace LIB_NAMESPACE {
FOR_EACH_METATYPE_LIBS(DECLARE_FUNCTION)
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,323 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include "tst_qmetatype_common.h"
#include "tst_qvariant_common.h"
struct Derived : QObject
{
Q_OBJECT
};
struct MessageHandlerCustom : public MessageHandler
{
MessageHandlerCustom(const int typeId)
: MessageHandler(typeId, handler)
{}
static void handler(QtMsgType, const QMessageLogContext &, const QString &msg)
{
QCOMPARE(msg.trimmed(), expectedMessage.trimmed());
}
inline static QString expectedMessage;
};
class tst_QMetaType: public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QVariant> prop READ prop WRITE setProp)
public:
struct GadgetPropertyType {
QByteArray type;
QByteArray name;
QVariant testData;
};
tst_QMetaType() { propList << 42 << "Hello"; }
QList<QVariant> prop() const { return propList; }
void setProp(const QList<QVariant> &list) { propList = list; }
private:
void registerGadget(const char * name, const QList<GadgetPropertyType> &gadgetProperties);
QList<QVariant> propList;
private slots:
void defined();
#if QT_CONFIG(thread)
void threadSafety();
#endif
void namespaces();
void id();
void qMetaTypeId();
void properties();
void normalizedTypes();
void typeName_data();
void typeName();
void type_data();
void type();
void type_fromSubString_data();
void type_fromSubString();
void create_data();
void create();
void createCopy_data();
void createCopy();
void sizeOf_data();
void sizeOf();
void sizeOfStaticLess_data();
void sizeOfStaticLess();
void alignOf_data();
void alignOf();
void flags_data();
void flags();
void flags2_data();
void flags2();
void flagsBinaryCompatibility6_0_data();
void flagsBinaryCompatibility6_0();
void construct_data();
void construct();
void defaultConstructTrivial_QTBUG_109594();
void typedConstruct();
void constructCopy_data();
void constructCopy();
void selfCompare_data();
void selfCompare();
void typedefs();
void registerType();
void isRegistered_data();
void isRegistered();
void isRegisteredStaticLess_data();
void isRegisteredStaticLess();
void isNotRegistered();
void isEnum();
void automaticTemplateRegistration_1();
void automaticTemplateRegistration_2(); // defined in tst_qmetatype3.cpp
void saveAndLoadBuiltin_data();
void saveAndLoadBuiltin();
void saveAndLoadCustom();
void metaObject_data();
void metaObject();
void constexprMetaTypeIds();
// tst_qmetatype2.cpp
void constRefs();
void convertCustomType_data();
void convertCustomType();
void convertConstNonConst();
void compareCustomEqualOnlyType();
void customDebugStream();
void unknownType();
void fromType();
void operatorEq_data();
void operatorEq();
void operatorEq2_data();
void operatorEq2();
void operatorEqAcrossLibs_data();
void operatorEqAcrossLibs();
void typesWithInaccessibleDTors();
void voidIsNotUnknown();
void typeNameNormalization();
// Tests for deprecated APIs
#if QT_DEPRECATED_SINCE(6, 0)
void testDeprecatedGetters_data() { type_data(); }
void testDeprecatedGetters();
void testDeprecatedLoadSave_data() { saveAndLoadBuiltin_data(); }
void testDeprecatedLoadSave();
#endif
};
template <typename T>
struct Whity { T t; Whity() {} };
Q_DECLARE_METATYPE(Whity<int>)
Q_DECLARE_METATYPE(Whity<double>)
#if !defined(Q_CC_CLANG) && defined(Q_CC_GNU) && Q_CC_GNU < 501
QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(Whity<double>, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
#endif
struct CustomConvertibleType
{
explicit CustomConvertibleType(const QVariant &foo = QVariant()) : m_foo(foo) {}
virtual ~CustomConvertibleType() {}
QString toString() const { return m_foo.toString(); }
operator QPoint() const { return QPoint(12, 34); }
template<typename To>
To convert() const { return s_value.value<To>();}
template<typename To>
To convertOk(bool *ok) const { *ok = s_ok; return s_value.value<To>();}
QVariant m_foo;
inline static QVariant s_value;
inline static bool s_ok = true;
friend bool operator<(const CustomConvertibleType &lhs, const CustomConvertibleType &rhs)
{ return lhs.m_foo.toString() < rhs.m_foo.toString(); }
friend bool operator==(const CustomConvertibleType &lhs, const CustomConvertibleType &rhs)
{ return lhs.m_foo == rhs.m_foo; }
friend bool operator!=(const CustomConvertibleType &lhs, const CustomConvertibleType &rhs)
{ return !operator==(lhs, rhs); }
};
struct CustomConvertibleType2
{
// implicit
CustomConvertibleType2(const CustomConvertibleType &t = CustomConvertibleType())
: m_foo(t.m_foo) {}
virtual ~CustomConvertibleType2() {}
QVariant m_foo;
friend bool operator==(const CustomConvertibleType2 &lhs, const CustomConvertibleType2 &rhs)
{ return lhs.m_foo == rhs.m_foo; }
friend bool operator!=(const CustomConvertibleType2 &lhs, const CustomConvertibleType2 &rhs)
{ return !operator==(lhs, rhs); }
};
struct CustomDebugStreamableType
{
QString toString() const { return "test"; }
friend QDebug operator<<(QDebug dbg, const CustomDebugStreamableType&)
{
return dbg << "string-content";
}
};
struct CustomDebugStreamableType2
{
QString toString() const { return "test"; }
};
struct CustomEqualsOnlyType
{
explicit CustomEqualsOnlyType(int value = 0) : val(value) {}
virtual ~CustomEqualsOnlyType() {}
int val;
friend bool operator==(const CustomEqualsOnlyType &lhs, const CustomEqualsOnlyType &rhs)
{ return lhs.val == rhs.val;}
friend bool operator!=(const CustomEqualsOnlyType &lhs, const CustomEqualsOnlyType &rhs)
{ return !operator==(lhs, rhs); }
};
static_assert(QTypeTraits::has_operator_equal_v<CustomEqualsOnlyType>);
static_assert(!QTypeTraits::has_operator_less_than_v<CustomEqualsOnlyType>);
struct BaseGadgetType
{
Q_GADGET
public:
explicit BaseGadgetType(QVariant foo = QVariant()) : m_foo(std::move(foo)) {}
QVariant m_foo;
};
struct DerivedGadgetType : public BaseGadgetType
{
Q_GADGET
public:
explicit DerivedGadgetType(QVariant foo = QVariant()) : BaseGadgetType(std::move(foo)) {}
int bar = 25;
};
Q_DECLARE_METATYPE(CustomConvertibleType);
Q_DECLARE_METATYPE(CustomConvertibleType2);
Q_DECLARE_METATYPE(CustomDebugStreamableType);
Q_DECLARE_METATYPE(CustomEqualsOnlyType);
struct CustomMovable {
CustomMovable() {}
friend bool operator==(const CustomMovable &, const CustomMovable &) { return true; }
// needed for QSet<CustomMovable>. We actually check that it makes sense.
friend qsizetype qHash(const CustomMovable &, qsizetype seed = 0) { return seed; }
};
#if !defined(Q_CC_CLANG) && defined(Q_CC_GNU) && Q_CC_GNU < 501
QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(CustomMovable, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
#endif
Q_DECLARE_METATYPE(CustomMovable);
#define FOR_EACH_STATIC_PRIMITIVE_TYPE(F) \
F(bool) \
F(int) \
F(qulonglong) \
F(double) \
F(short) \
F(char) \
F(ulong) \
F(uchar) \
F(float) \
F(QObject*) \
F(QString) \
F(CustomMovable)
#define FOR_EACH_STATIC_PRIMITIVE_TYPE2(F, SecondaryRealName) \
F(uint, SecondaryRealName) \
F(qlonglong, SecondaryRealName) \
F(char, SecondaryRealName) \
F(uchar, SecondaryRealName) \
F(QObject*, SecondaryRealName)
#define CREATE_AND_VERIFY_CONTAINER(CONTAINER, ...) \
{ \
CONTAINER< __VA_ARGS__ > t; \
const QVariant v = QVariant::fromValue(t); \
QByteArray tn = createTypeName(#CONTAINER "<", #__VA_ARGS__); \
const int expectedType = ::qMetaTypeId<CONTAINER< __VA_ARGS__ > >(); \
const int type = QMetaType::fromName(tn).id(); \
QCOMPARE(type, expectedType); \
QCOMPARE((QMetaType::fromType<CONTAINER< __VA_ARGS__ >>().id()), expectedType); \
}
#define FOR_EACH_1ARG_TEMPLATE_TYPE(F, TYPE) \
F(QList, TYPE) \
F(QQueue, TYPE) \
F(QStack, TYPE) \
F(QSet, TYPE)
#define PRINT_1ARG_TEMPLATE(RealName) \
FOR_EACH_1ARG_TEMPLATE_TYPE(CREATE_AND_VERIFY_CONTAINER, RealName)
#define FOR_EACH_2ARG_TEMPLATE_TYPE(F, RealName1, RealName2) \
F(QHash, RealName1, RealName2) \
F(QMap, RealName1, RealName2) \
F(std::pair, RealName1, RealName2)
#define PRINT_2ARG_TEMPLATE_INTERNAL(RealName1, RealName2) \
FOR_EACH_2ARG_TEMPLATE_TYPE(CREATE_AND_VERIFY_CONTAINER, RealName1, RealName2)
#define PRINT_2ARG_TEMPLATE(RealName) \
FOR_EACH_STATIC_PRIMITIVE_TYPE2(PRINT_2ARG_TEMPLATE_INTERNAL, RealName)
#define REGISTER_TYPEDEF(TYPE, ARG1, ARG2) \
qRegisterMetaType<TYPE <ARG1, ARG2>>(#TYPE "<" #ARG1 "," #ARG2 ">");
static inline QByteArray createTypeName(const char *begin, const char *va)
{
QByteArray tn(begin);
const QList<QByteArray> args = QByteArray(va).split(',');
tn += args.first().trimmed();
if (args.size() > 1) {
QList<QByteArray>::const_iterator it = args.constBegin() + 1;
const QList<QByteArray>::const_iterator end = args.constEnd();
for (; it != end; ++it) {
tn += ",";
tn += it->trimmed();
}
}
if (tn.endsWith('>'))
tn += ' ';
tn += '>';
return tn;
}
Q_DECLARE_METATYPE(const void*)

View File

@ -0,0 +1,718 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "tst_qmetatype.h"
#include "tst_qmetatype_libs.h"
#include <QtCore/private/qmetaobjectbuilder_p.h>
void tst_QMetaType::constRefs()
{
QCOMPARE(::qMetaTypeId<const int &>(), ::qMetaTypeId<int>());
QCOMPARE(::qMetaTypeId<const QString &>(), ::qMetaTypeId<QString>());
QCOMPARE(::qMetaTypeId<const CustomMovable &>(), ::qMetaTypeId<CustomMovable>());
QCOMPARE(::qMetaTypeId<const QList<CustomMovable> &>(), ::qMetaTypeId<QList<CustomMovable> >());
static_assert(::qMetaTypeId<const int &>() == ::qMetaTypeId<int>());
}
template<typename T, typename U>
U convert(const T &t)
{
return t;
}
template<typename From>
struct ConvertFunctor
{
CustomConvertibleType operator()(const From& f) const
{
return CustomConvertibleType(QVariant::fromValue(f));
}
};
template<typename T>
struct OptionalWrapper
{
std::optional<T> operator()(const T& t) const
{
if (!CustomConvertibleType::s_ok)
return std::nullopt;
return t;
}
};
template<typename From>
struct ConvertFunctorWithOptional
{
std::optional<CustomConvertibleType> operator()(const From& f) const
{
if (!CustomConvertibleType::s_ok)
return std::nullopt;
return CustomConvertibleType(QVariant::fromValue(f));
}
};
template<typename From, typename To>
bool hasRegisteredConverterFunction()
{
return QMetaType::hasRegisteredConverterFunction<From, To>();
}
template<typename From, typename To>
void testCustomTypeNotYetConvertible()
{
QVERIFY((!hasRegisteredConverterFunction<From, To>()));
QVERIFY((!QVariant::fromValue<From>(From()).template canConvert<To>()));
}
template<typename From, typename To>
void testCustomTypeConvertible()
{
QVERIFY((hasRegisteredConverterFunction<From, To>()));
QVERIFY((QVariant::fromValue<From>(From()).template canConvert<To>()));
}
void customTypeNotYetConvertible()
{
testCustomTypeNotYetConvertible<CustomConvertibleType, QString>();
testCustomTypeNotYetConvertible<CustomConvertibleType, bool>();
testCustomTypeNotYetConvertible<CustomConvertibleType, int>();
testCustomTypeNotYetConvertible<CustomConvertibleType, double>();
testCustomTypeNotYetConvertible<CustomConvertibleType, float>();
testCustomTypeNotYetConvertible<CustomConvertibleType, QRect>();
testCustomTypeNotYetConvertible<CustomConvertibleType, QRectF>();
testCustomTypeNotYetConvertible<CustomConvertibleType, QPoint>();
testCustomTypeNotYetConvertible<CustomConvertibleType, QPointF>();
testCustomTypeNotYetConvertible<CustomConvertibleType, QSize>();
testCustomTypeNotYetConvertible<CustomConvertibleType, QSizeF>();
testCustomTypeNotYetConvertible<CustomConvertibleType, QLine>();
testCustomTypeNotYetConvertible<CustomConvertibleType, QLineF>();
testCustomTypeNotYetConvertible<CustomConvertibleType, QChar>();
testCustomTypeNotYetConvertible<QString, CustomConvertibleType>();
testCustomTypeNotYetConvertible<bool, CustomConvertibleType>();
testCustomTypeNotYetConvertible<int, CustomConvertibleType>();
testCustomTypeNotYetConvertible<double, CustomConvertibleType>();
testCustomTypeNotYetConvertible<float, CustomConvertibleType>();
testCustomTypeNotYetConvertible<QRect, CustomConvertibleType>();
testCustomTypeNotYetConvertible<QRectF, CustomConvertibleType>();
testCustomTypeNotYetConvertible<QPoint, CustomConvertibleType>();
testCustomTypeNotYetConvertible<QPointF, CustomConvertibleType>();
testCustomTypeNotYetConvertible<QSize, CustomConvertibleType>();
testCustomTypeNotYetConvertible<QSizeF, CustomConvertibleType>();
testCustomTypeNotYetConvertible<QLine, CustomConvertibleType>();
testCustomTypeNotYetConvertible<QLineF, CustomConvertibleType>();
testCustomTypeNotYetConvertible<QChar, CustomConvertibleType>();
testCustomTypeNotYetConvertible<CustomConvertibleType, CustomConvertibleType2>();
testCustomTypeNotYetConvertible<CustomConvertibleType, std::optional<CustomConvertibleType>>();
}
void registerCustomTypeConversions()
{
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QString>(&CustomConvertibleType::convertOk<QString>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, bool>(&CustomConvertibleType::convert<bool>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, int>(&CustomConvertibleType::convertOk<int>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, double>(&CustomConvertibleType::convert<double>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, float>(&CustomConvertibleType::convertOk<float>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QRect>(&CustomConvertibleType::convert<QRect>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QRectF>(&CustomConvertibleType::convertOk<QRectF>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QPoint>(convert<CustomConvertibleType,QPoint>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QPointF>(&CustomConvertibleType::convertOk<QPointF>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QSize>(&CustomConvertibleType::convert<QSize>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QSizeF>(&CustomConvertibleType::convertOk<QSizeF>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLine>(&CustomConvertibleType::convert<QLine>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLineF>(&CustomConvertibleType::convertOk<QLineF>)));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QChar>(&CustomConvertibleType::convert<QChar>)));
QVERIFY((QMetaType::registerConverter<QString, CustomConvertibleType>(ConvertFunctorWithOptional<QString>())));
QVERIFY((QMetaType::registerConverter<bool, CustomConvertibleType>(ConvertFunctor<bool>())));
QVERIFY((QMetaType::registerConverter<int, CustomConvertibleType>(ConvertFunctorWithOptional<int>())));
QVERIFY((QMetaType::registerConverter<double, CustomConvertibleType>(ConvertFunctor<double>())));
QVERIFY((QMetaType::registerConverter<float, CustomConvertibleType>(ConvertFunctorWithOptional<float>())));
QVERIFY((QMetaType::registerConverter<QRect, CustomConvertibleType>(ConvertFunctor<QRect>())));
QVERIFY((QMetaType::registerConverter<QRectF, CustomConvertibleType>(ConvertFunctorWithOptional<QRectF>())));
QVERIFY((QMetaType::registerConverter<QPoint, CustomConvertibleType>(ConvertFunctor<QPoint>())));
QVERIFY((QMetaType::registerConverter<QPointF, CustomConvertibleType>(ConvertFunctorWithOptional<QPointF>())));
QVERIFY((QMetaType::registerConverter<QSize, CustomConvertibleType>(ConvertFunctor<QSize>())));
QVERIFY((QMetaType::registerConverter<QSizeF, CustomConvertibleType>(ConvertFunctorWithOptional<QSizeF>())));
QVERIFY((QMetaType::registerConverter<QLine, CustomConvertibleType>(ConvertFunctor<QLine>())));
QVERIFY((QMetaType::registerConverter<QLineF, CustomConvertibleType>(ConvertFunctorWithOptional<QLineF>())));
QVERIFY((QMetaType::registerConverter<QChar, CustomConvertibleType>(ConvertFunctor<QChar>())));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, std::optional<CustomConvertibleType>>(OptionalWrapper<CustomConvertibleType>())));
QVERIFY((QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>()));
QTest::ignoreMessage(QtWarningMsg, "Type conversion already registered from type CustomConvertibleType to type CustomConvertibleType2");
QVERIFY((!QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>()));
}
void tst_QMetaType::convertCustomType_data()
{
customTypeNotYetConvertible();
registerCustomTypeConversions();
QTest::addColumn<bool>("ok");
QTest::addColumn<QString>("testQString");
QTest::addColumn<bool>("testBool");
QTest::addColumn<int>("testInt");
QTest::addColumn<double>("testDouble");
QTest::addColumn<float>("testFloat");
QTest::addColumn<QRect>("testQRect");
QTest::addColumn<QRectF>("testQRectF");
QTest::addColumn<QPoint>("testQPoint");
QTest::addColumn<QPointF>("testQPointF");
QTest::addColumn<QSize>("testQSize");
QTest::addColumn<QSizeF>("testQSizeF");
QTest::addColumn<QLine>("testQLine");
QTest::addColumn<QLineF>("testQLineF");
QTest::addColumn<QChar>("testQChar");
QTest::addColumn<CustomConvertibleType>("testCustom");
QTest::addColumn<DerivedGadgetType>("testDerived");
QTest::newRow("default") << true
<< QString::fromLatin1("string") << true << 15
<< double(3.14) << float(3.6) << QRect(1, 2, 3, 4)
<< QRectF(1.4, 1.9, 10.9, 40.2) << QPoint(12, 34)
<< QPointF(9.2, 2.7) << QSize(4, 9) << QSizeF(3.3, 9.8)
<< QLine(3, 9, 29, 4) << QLineF(38.9, 28.9, 102.3, 0.0)
<< QChar('Q') << CustomConvertibleType(QString::fromLatin1("test"))
<< DerivedGadgetType(QString::fromLatin1("test"));
QTest::newRow("not ok") << false
<< QString::fromLatin1("string") << true << 15
<< double(3.14) << float(3.6) << QRect(1, 2, 3, 4)
<< QRectF(1.4, 1.9, 10.9, 40.2) << QPoint(12, 34)
<< QPointF(9.2, 2.7) << QSize(4, 9) << QSizeF(3.3, 9.8)
<< QLine(3, 9, 29, 4) << QLineF()
<< QChar('Q') << CustomConvertibleType(42)
<< DerivedGadgetType(42);
}
void tst_QMetaType::convertCustomType()
{
QFETCH(bool, ok);
CustomConvertibleType::s_ok = ok;
CustomConvertibleType t;
QVariant v = QVariant::fromValue(t);
QFETCH(QString, testQString);
CustomConvertibleType::s_value = testQString;
QCOMPARE(v.toString(), ok ? testQString : QString());
QCOMPARE(v.value<QString>(), ok ? testQString : QString());
QVERIFY(CustomConvertibleType::s_value.canConvert<CustomConvertibleType>());
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toString()), ok ? testQString : QString());
QFETCH(bool, testBool);
CustomConvertibleType::s_value = testBool;
QCOMPARE(v.toBool(), testBool);
QCOMPARE(v.value<bool>(), testBool);
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toBool()), testBool);
QFETCH(int, testInt);
CustomConvertibleType::s_value = testInt;
QCOMPARE(v.toInt(), ok ? testInt : 0);
QCOMPARE(v.value<int>(), ok ? testInt : 0);
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toInt()), ok ? testInt : 0);
QFETCH(double, testDouble);
CustomConvertibleType::s_value = testDouble;
QCOMPARE(v.toDouble(), testDouble);
QCOMPARE(v.value<double>(), testDouble);
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toDouble()), testDouble);
QFETCH(float, testFloat);
CustomConvertibleType::s_value = testFloat;
QCOMPARE(v.toFloat(), ok ? testFloat : 0.0);
QCOMPARE(v.value<float>(), ok ? testFloat : 0.0);
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toFloat()), ok ? testFloat : 0);
QFETCH(QRect, testQRect);
CustomConvertibleType::s_value = testQRect;
QCOMPARE(v.toRect(), testQRect);
QCOMPARE(v.value<QRect>(), testQRect);
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toRect()), testQRect);
QFETCH(QRectF, testQRectF);
CustomConvertibleType::s_value = testQRectF;
QCOMPARE(v.toRectF(), ok ? testQRectF : QRectF());
QCOMPARE(v.value<QRectF>(), ok ? testQRectF : QRectF());
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toRectF()), ok ? testQRectF : QRectF());
QFETCH(QPoint, testQPoint);
CustomConvertibleType::s_value = testQPoint;
QCOMPARE(v.toPoint(), testQPoint);
QCOMPARE(v.value<QPoint>(), testQPoint);
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toPoint()), testQPoint);
QFETCH(QPointF, testQPointF);
CustomConvertibleType::s_value = testQPointF;
QCOMPARE(v.toPointF(), ok ? testQPointF : QPointF());
QCOMPARE(v.value<QPointF>(), ok ? testQPointF : QPointF());
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toPointF()), ok ? testQPointF : QPointF());
QFETCH(QSize, testQSize);
CustomConvertibleType::s_value = testQSize;
QCOMPARE(v.toSize(), testQSize);
QCOMPARE(v.value<QSize>(), testQSize);
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toSize()), testQSize);
QFETCH(QSizeF, testQSizeF);
CustomConvertibleType::s_value = testQSizeF;
QCOMPARE(v.toSizeF(), ok ? testQSizeF : QSizeF());
QCOMPARE(v.value<QSizeF>(), ok ? testQSizeF : QSizeF());
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toSizeF()), ok ? testQSizeF : QSizeF());
QFETCH(QLine, testQLine);
CustomConvertibleType::s_value = testQLine;
QCOMPARE(v.toLine(), testQLine);
QCOMPARE(v.value<QLine>(), testQLine);
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toLine()), testQLine);
QFETCH(QLineF, testQLineF);
CustomConvertibleType::s_value = testQLineF;
QCOMPARE(v.toLineF(), ok ? testQLineF : QLineF());
QCOMPARE(v.value<QLineF>(), ok ? testQLineF : QLineF());
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toLineF()), ok ? testQLineF : QLineF());
QFETCH(QChar, testQChar);
CustomConvertibleType::s_value = testQChar;
QCOMPARE(v.toChar(), testQChar);
QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toChar()), testQChar);
QFETCH(CustomConvertibleType, testCustom);
v = QVariant::fromValue(testCustom);
QVERIFY(v.canConvert(QMetaType(::qMetaTypeId<CustomConvertibleType2>())));
QCOMPARE(v.value<CustomConvertibleType2>().m_foo, testCustom.m_foo);
// Check that converters that actually convert to std::optional<T> are not
// taken to indicate success or failure of the conversion. In these cases,
// the conversion must always succeed, even if the converter has returned a
// nullopt.
v = QVariant::fromValue(testCustom);
QVERIFY(v.canConvert(QMetaType::fromType<std::optional<CustomConvertibleType>>()));
QVERIFY(v.convert(QMetaType::fromType<std::optional<CustomConvertibleType>>()));
QCOMPARE(v.value<std::optional<CustomConvertibleType>>().has_value(), ok);
if (ok) {
QCOMPARE(v.value<std::optional<CustomConvertibleType>>().value().m_foo, testCustom.m_foo);
}
QFETCH(DerivedGadgetType, testDerived);
v = QVariant::fromValue(testDerived);
QCOMPARE(v.metaType(), QMetaType::fromType<DerivedGadgetType>());
QCOMPARE(v.value<DerivedGadgetType>().m_foo, testDerived.m_foo);
QVERIFY(v.canConvert(QMetaType::fromType<BaseGadgetType>()));
QVERIFY(v.convert(QMetaType::fromType<BaseGadgetType>()));
QCOMPARE(v.metaType(), QMetaType::fromType<BaseGadgetType>());
QCOMPARE(v.value<BaseGadgetType>().m_foo, testDerived.m_foo);
}
void tst_QMetaType::convertConstNonConst()
{
auto mtConstObj = QMetaType::fromType<QObject const*>();
auto mtObj = QMetaType::fromType<QObject *>();
auto mtConstDerived = QMetaType::fromType<Derived const*>();
auto mtDerived = QMetaType::fromType<Derived *>();
QVERIFY(QMetaType::canConvert(mtConstObj, mtObj));
QVERIFY(QMetaType::canConvert(mtObj, mtConstObj)); // casting const away is allowed (but can lead to UB)
QVERIFY(QMetaType::canConvert(mtConstDerived, mtObj));
QVERIFY(QMetaType::canConvert(mtDerived, mtConstObj));
QVERIFY(QMetaType::canConvert(mtObj, mtConstDerived));
}
void tst_QMetaType::compareCustomEqualOnlyType()
{
QMetaType type = QMetaType::fromType<CustomEqualsOnlyType>();
CustomEqualsOnlyType val50(50);
CustomEqualsOnlyType val100(100);
CustomEqualsOnlyType val100x(100);
QVariant variant50 = QVariant::fromValue(val50);
QVariant variant100 = QVariant::fromValue(val100);
QVariant variant100x = QVariant::fromValue(val100x);
QVERIFY(variant50 != variant100);
QVERIFY(variant50 != variant100x);
QVERIFY(variant100 != variant50);
QVERIFY(variant100x != variant50);
QCOMPARE(variant100, variant100x);
QCOMPARE(variant100, variant100);
// check QMetaType::compare works/doesn't crash for equals only comparators
auto cmp = type.compare(variant50.constData(), variant50.constData());
QCOMPARE(cmp, QPartialOrdering::Unordered);
bool equals = type.equals(variant50.constData(), variant50.constData());
QVERIFY(equals);
cmp = type.compare(variant100.constData(), variant100x.constData());
QCOMPARE(cmp, QPartialOrdering::Unordered);
equals = type.equals(variant100.constData(), variant100x.constData());
QVERIFY(equals);
cmp = type.compare(variant50.constData(), variant100.constData());
QCOMPARE(cmp, QPartialOrdering::Unordered);
equals = type.equals(variant50.constData(), variant100.constData());
QVERIFY(!equals);
//check QMetaType::equals for type w/o equals comparator being registered
CustomMovable movable1;
CustomMovable movable2;
type = QMetaType::fromType<CustomMovable>();
equals = type.equals(&movable1, &movable2);
}
void tst_QMetaType::customDebugStream()
{
MessageHandlerCustom handler(::qMetaTypeId<CustomDebugStreamableType>());
QVariant v1 = QVariant::fromValue(CustomDebugStreamableType());
handler.expectedMessage = "QVariant(CustomDebugStreamableType, string-content)";
qDebug() << v1;
MessageHandlerCustom handler2(::qMetaTypeId<CustomDebugStreamableType2>());
QMetaType::registerConverter<CustomDebugStreamableType2, QString>(&CustomDebugStreamableType2::toString);
handler2.expectedMessage = "QVariant(CustomDebugStreamableType2, \"test\")";
QVariant v2 = QVariant::fromValue(CustomDebugStreamableType2());
qDebug() << v2;
}
void tst_QMetaType::unknownType()
{
QMetaType invalid(QMetaType::UnknownType);
QVERIFY(!invalid.create());
QVERIFY(!invalid.sizeOf());
QVERIFY(!invalid.metaObject());
int buffer = 0xBAD;
invalid.construct(&buffer);
QCOMPARE(buffer, 0xBAD);
}
void tst_QMetaType::fromType()
{
#define FROMTYPE_CHECK(MetaTypeName, MetaTypeId, RealType) \
QCOMPARE(QMetaType::fromType<RealType>(), QMetaType(MetaTypeId)); \
QVERIFY(QMetaType::fromType<RealType>() == QMetaType(MetaTypeId)); \
QVERIFY(!(QMetaType::fromType<RealType>() != QMetaType(MetaTypeId))); \
if (MetaTypeId != QMetaType::Void) \
QCOMPARE(QMetaType::fromType<RealType>().id(), MetaTypeId);
FOR_EACH_CORE_METATYPE(FROMTYPE_CHECK)
QVERIFY(QMetaType::fromType<QString>() != QMetaType());
QCOMPARE(QMetaType(), QMetaType());
QCOMPARE(QMetaType(QMetaType::UnknownType), QMetaType());
FROMTYPE_CHECK(_, ::qMetaTypeId<Whity<int>>(), Whity<int>)
#undef FROMTYPE_CHECK
}
template<char X, typename T = void>
struct CharTemplate
{
struct
{
int a;
} x;
union
{
int a;
} y;
};
void tst_QMetaType::operatorEq_data()
{
QTest::addColumn<QMetaType>("typeA");
QTest::addColumn<QMetaType>("typeB");
QTest::addColumn<bool>("eq");
QTest::newRow("String") << QMetaType(QMetaType::QString)
<< QMetaType::fromType<const QString &>() << true;
QTest::newRow("void1") << QMetaType(QMetaType::UnknownType) << QMetaType::fromType<void>()
<< false;
QTest::newRow("void2") << QMetaType::fromType<const void>() << QMetaType::fromType<void>()
<< true;
QTest::newRow("list1") << QMetaType::fromType<QList<const int *>>()
<< QMetaType::fromType<QList<const int *>>() << true;
QTest::newRow("list2") << QMetaType::fromType<QList<const int *>>()
<< QMetaType::fromType<QList<int *>>() << false;
QTest::newRow("char1") << QMetaType::fromType<CharTemplate<'>'>>()
<< QMetaType::fromType<CharTemplate<'>', void>>() << true;
QTest::newRow("annon1") << QMetaType::fromType<decltype(CharTemplate<'>'>::x)>()
<< QMetaType::fromType<decltype(CharTemplate<'>'>::x)>() << true;
QTest::newRow("annon2") << QMetaType::fromType<decltype(CharTemplate<'>'>::x)>()
<< QMetaType::fromType<decltype(CharTemplate<'<'>::x)>() << false;
}
void tst_QMetaType::operatorEq()
{
QFETCH(QMetaType, typeA);
QFETCH(QMetaType, typeB);
QFETCH(bool, eq);
QCOMPARE(typeA == typeB, eq);
QCOMPARE(typeB == typeA, eq);
QCOMPARE(typeA != typeB, !eq);
QCOMPARE(typeB != typeA, !eq);
#if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY)
// for built-in types or locally-defined types, this must also hold true
if (eq)
QCOMPARE(typeA.iface(), typeB.iface());
#endif
}
void tst_QMetaType::operatorEq2_data()
{
create_data();
}
void tst_QMetaType::operatorEq2()
{
QFETCH(int, type);
QMetaType fromType1, fromType2;
QMetaType fromId1(type), fromId2(type);
switch (type) {
case QMetaType::UnknownType:
break;
#define GET_METATYPE_FROM_TYPE(MetaTypeName, MetaTypeId, RealType) \
case QMetaType::MetaTypeName: \
fromType1 = QMetaType::fromType<RealType>(); \
fromType2 = QMetaType::fromType<RealType>(); \
break;
FOR_EACH_CORE_METATYPE(GET_METATYPE_FROM_TYPE)
#undef GET_METATYPE_FROM_TYPE
}
// sanity check
QCOMPARE(fromId1.id(), type);
QCOMPARE(fromId2.id(), type);
// confirm that they're all equal
QCOMPARE(fromId1, fromId2);
QCOMPARE(fromType1, fromType2);
QCOMPARE(fromType1, fromId1);
QCOMPARE(fromType2, fromId2);
#if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY)
// for built-in types (other than void), this must be true
if (type != QMetaType::Void) {
QCOMPARE(fromType1.iface(), fromId1.iface());
QCOMPARE(fromType2.iface(), fromId1.iface());
}
#endif
}
#define DECLARE_LIB_FUNCTION(TYPE, ID) \
Q_DECL_IMPORT QMetaType metatype_ ## TYPE();
namespace Lib1 { FOR_EACH_METATYPE_LIBS(DECLARE_LIB_FUNCTION) }
namespace Lib2 { FOR_EACH_METATYPE_LIBS(DECLARE_LIB_FUNCTION) }
#undef DECLARE_LIB_FUNCTION
using LibMetatypeFunction = QMetaType (*)();
void tst_QMetaType::operatorEqAcrossLibs_data()
{
QTest::addColumn<int>("builtinTypeId");
QTest::addColumn<QMetaType>("localType");
QTest::addColumn<LibMetatypeFunction>("lib1Function");
QTest::addColumn<LibMetatypeFunction>("lib2Function");
#define ADD_ROW(TYPE, ID) \
QTest::addRow(QT_STRINGIFY(TYPE)) << int(ID) \
<< QMetaType::fromType<TYPE>() \
<< &Lib1::metatype_ ## TYPE \
<< &Lib2::metatype_ ## TYPE;
FOR_EACH_METATYPE_LIBS(ADD_ROW)
#undef ADD_ROW
}
void tst_QMetaType::operatorEqAcrossLibs()
{
QFETCH(int, builtinTypeId);
QFETCH(QMetaType, localType);
QFETCH(LibMetatypeFunction, lib1Function);
QFETCH(LibMetatypeFunction, lib2Function);
QMetaType lib1Type = lib1Function();
QMetaType lib2Type = lib2Function();
const QtPrivate::QMetaTypeInterface *localIface = localType.iface();
const QtPrivate::QMetaTypeInterface *lib1Iface = lib1Type.iface();
const QtPrivate::QMetaTypeInterface *lib2Iface = lib2Type.iface();
// DO THIS FIRST:
// if this isn't a built-in type, then the QMetaTypeInterface::typeId is
// initially set to 0
QCOMPARE(lib1Type, lib2Type);
int actualTypeId = localType.id();
bool builtinTypeExpected = builtinTypeId != QMetaType::UnknownType;
bool builtinTypeActually = actualTypeId < QMetaType::User;
qDebug() << "QMetaType for type" << QByteArray(localType.name())
<< "(type ID" << (actualTypeId >= 0x1000 ? Qt::hex : Qt::dec) << actualTypeId << ')'
<< (builtinTypeActually ? "IS" : "is NOT") << "a built-in type;"
<< "local interface:" << static_cast<const void *>(localIface)
<< "lib1 interface:" << static_cast<const void *>(lib1Iface)
<< "lib2 interface:" << static_cast<const void *>(lib2Iface);
QCOMPARE(builtinTypeActually, builtinTypeExpected);
QCOMPARE(lib1Type.id(), actualTypeId);
QCOMPARE(lib2Type.id(), actualTypeId);
QCOMPARE(QByteArray(lib1Type.name()), QByteArray(localType.name()));
QCOMPARE(QByteArray(lib2Type.name()), QByteArray(localType.name()));
QCOMPARE(lib1Type, localType);
QCOMPARE(lib2Type, localType);
#if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY)
if (actualTypeId < QMetaType::FirstGuiType && actualTypeId != QMetaType::Void) {
// for built-in QtCore types, we expect the interfaces to be the same too
QCOMPARE(lib1Iface, localIface);
QCOMPARE(lib2Iface, localIface);
}
#endif
}
class WithPrivateDTor {
~WithPrivateDTor(){};
};
struct WithDeletedDtor {
~WithDeletedDtor() = delete;
};
void tst_QMetaType::typesWithInaccessibleDTors()
{
// should compile
Q_UNUSED(QMetaType::fromType<WithPrivateDTor>());
Q_UNUSED(QMetaType::fromType<WithDeletedDtor>());
}
void tst_QMetaType::voidIsNotUnknown()
{
QMetaType voidType = QMetaType::fromType<void>();
QMetaType voidType2 = QMetaType(QMetaType::Void);
QCOMPARE(voidType, voidType2);
QVERIFY(voidType != QMetaType(QMetaType::UnknownType));
}
void tst_QMetaType::typeNameNormalization()
{
// check the we normalize types the right way
#define CHECK_TYPE_NORMALIZATION(Normalized, ...) \
do { \
/*QCOMPARE(QtPrivate::typenameHelper<Type>(), Normalized);*/ \
QByteArray typeName = QMetaObject::normalizedType(#__VA_ARGS__); \
QCOMPARE(typeName, Normalized); \
typeName = QMetaType::fromType<__VA_ARGS__>().name(); \
QCOMPARE(typeName, Normalized); \
} while (0)
CHECK_TYPE_NORMALIZATION("QList<QString*const>", QList<QString * const>);
CHECK_TYPE_NORMALIZATION("QList<const QString*>", QList<const QString * >);
CHECK_TYPE_NORMALIZATION("QList<const QString*const>", QList<const QString * const>);
CHECK_TYPE_NORMALIZATION("QList<const QString*>", QList<QString const *>);
CHECK_TYPE_NORMALIZATION("QList<signed char>", QList<signed char>);
CHECK_TYPE_NORMALIZATION("QList<uint>", QList<unsigned>);
CHECK_TYPE_NORMALIZATION("uint", uint);
CHECK_TYPE_NORMALIZATION("QList<QHash<uint,QString*>>", QList<QHash<unsigned, QString *>>);
CHECK_TYPE_NORMALIZATION("QList<qlonglong>", QList<qlonglong>);
CHECK_TYPE_NORMALIZATION("QList<qulonglong>", QList<qulonglong>);
CHECK_TYPE_NORMALIZATION("QList<qlonglong>", QList<long long>);
CHECK_TYPE_NORMALIZATION("QList<qulonglong>", QList<unsigned long long>);
CHECK_TYPE_NORMALIZATION("QList<qulonglong*>", QList<unsigned long long *>);
CHECK_TYPE_NORMALIZATION("QList<ulong>", QList<long unsigned >);
#ifdef Q_CC_MSVC
CHECK_TYPE_NORMALIZATION("qulonglong", __int64 unsigned);
#endif
CHECK_TYPE_NORMALIZATION("std::pair<const QString&&,short>", QPair<const QString &&, signed short>);
// The string based normalization doesn't handle aliases, QMetaType::fromType() does
// CHECK_TYPE_NORMALIZATION("qulonglong", quint64);
QCOMPARE(QMetaType::fromType<quint64>().name(), "qulonglong");
// noramlizedType and metatype name agree
{
auto type = QMetaType::fromType<decltype(CharTemplate<'<'>::x)>();
QCOMPARE(type.name(), QMetaObject::normalizedType(type.name()));
}
{
auto type = QMetaType::fromType<decltype(CharTemplate<'<'>::y)>();
QCOMPARE(type.name(), QMetaObject::normalizedType(type.name()));
}
}
#if QT_DEPRECATED_SINCE(6, 0)
void tst_QMetaType::testDeprecatedGetters()
{
QFETCH(int, aType);
QFETCH(QByteArray, aTypeName);
if (aType >= QMetaType::FirstWidgetsType)
QSKIP("The test doesn't link against QtWidgets.");
// QMetaType::type("name") -> QMetaType::fromName("name").id()
QT_IGNORE_DEPRECATIONS(QCOMPARE(QMetaType::type(aTypeName),
QMetaType::fromName(aTypeName).id());)
// QMetaType::typeName(int) -> QMetaType(int).name()
QT_IGNORE_DEPRECATIONS(QCOMPARE(QLatin1String(QMetaType::typeName(aType)),
QLatin1String(QMetaType(aType).name()));)
// QMetaType::typeFlags(int) -> QMetaType(int).flags()
QT_IGNORE_DEPRECATIONS(QCOMPARE(QMetaType::typeFlags(aType),
QMetaType(aType).flags());)
// QMetaType::metaObjectForType(int) -> QMetaType(int).metaObject()
QT_IGNORE_DEPRECATIONS(QCOMPARE(QMetaType::metaObjectForType(aType),
QMetaType(aType).metaObject());)
}
void tst_QMetaType::testDeprecatedLoadSave()
{
QFETCH(int, type);
QFETCH(bool, isStreamable);
if (!isStreamable)
return;
QMetaType metaType(type);
void *value = metaType.create();
auto cleanup = qScopeGuard([&metaType, value]() {
metaType.destroy(value);
});
QByteArray ba;
QDataStream stream(&ba, QIODevice::ReadWrite);
// Write using deprecated API
QT_IGNORE_DEPRECATIONS(QVERIFY(QMetaType::save(stream, type, value));)
QCOMPARE(stream.status(), QDataStream::Ok);
// Read using non-deprecated API
stream.device()->seek(0);
QVERIFY(metaType.load(stream, value));
QCOMPARE(stream.status(), QDataStream::Ok);
// Write using non-deprecated API
stream.device()->seek(0);
ba.clear();
QVERIFY(metaType.save(stream, value));
QCOMPARE(stream.status(), QDataStream::Ok);
// Read using deprecated API
stream.device()->seek(0);
QT_IGNORE_DEPRECATIONS(QVERIFY(QMetaType::load(stream, type, value));)
QCOMPARE(stream.status(), QDataStream::Ok);
}
#endif // QT_DEPRECATED_SINCE(6, 0)
// Compile-time test, it should be possible to register function pointer types
class Undefined;
typedef Undefined (*UndefinedFunction0)();
typedef Undefined (*UndefinedFunction1)(Undefined);
typedef Undefined (*UndefinedFunction2)(Undefined, Undefined);
typedef Undefined (*UndefinedFunction3)(Undefined, Undefined, Undefined);
typedef Undefined (*UndefinedFunction4)(Undefined, Undefined, Undefined, Undefined, Undefined, Undefined, Undefined, Undefined);
Q_DECLARE_METATYPE(UndefinedFunction0);
Q_DECLARE_METATYPE(UndefinedFunction1);
Q_DECLARE_METATYPE(UndefinedFunction2);
Q_DECLARE_METATYPE(UndefinedFunction3);
Q_DECLARE_METATYPE(UndefinedFunction4);

View File

@ -0,0 +1,14 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "tst_qmetatype.h"
#include <QtCore/private/qmetaobjectbuilder_p.h>
void tst_QMetaType::automaticTemplateRegistration_2()
{
FOR_EACH_STATIC_PRIMITIVE_TYPE(
PRINT_2ARG_TEMPLATE
)
}

View File

@ -0,0 +1,269 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
// Used by both tst_qmetatype and tst_qsettings
#ifndef TST_QMETATYPE_H
#define TST_QMETATYPE_H
#include <QtCore>
#include <float.h>
#define FOR_EACH_PRIMITIVE_METATYPE(F) \
QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F) \
QT_FOR_EACH_STATIC_CORE_POINTER(F) \
#define FOR_EACH_COMPLEX_CORE_METATYPE(F) \
QT_FOR_EACH_STATIC_CORE_CLASS(F) \
QT_FOR_EACH_STATIC_CORE_TEMPLATE(F)
#define FOR_EACH_CORE_METATYPE(F) \
FOR_EACH_PRIMITIVE_METATYPE(F) \
FOR_EACH_COMPLEX_CORE_METATYPE(F) \
template <int ID>
struct MetaEnumToType {};
#define DEFINE_META_ENUM_TO_TYPE(MetaTypeName, MetaTypeId, RealType) \
template<> \
struct MetaEnumToType<QMetaType::MetaTypeName> { \
typedef RealType Type; \
};
FOR_EACH_CORE_METATYPE(DEFINE_META_ENUM_TO_TYPE)
#undef DEFINE_META_ENUM_TO_TYPE
template <int ID>
struct DefaultValueFactory
{
typedef typename MetaEnumToType<ID>::Type Type;
static Type *create() { return new Type(); }
};
template <>
struct DefaultValueFactory<QMetaType::Void>
{
typedef MetaEnumToType<QMetaType::Void>::Type Type;
static Type *create() { return nullptr; }
};
template <int ID>
struct DefaultValueTraits
{
// By default we assume that a default-constructed value (new T) is
// initialized; e.g. QCOMPARE(*(new T), *(new T)) should succeed
enum { IsInitialized = true };
};
template <int ID>
struct TestValueFactory {};
template<> struct TestValueFactory<QMetaType::Void> {
static void *create() { return 0; }
};
template<> struct TestValueFactory<QMetaType::QString> {
static QString *create() { return new QString(QString::fromLatin1("QString")); }
};
template<> struct TestValueFactory<QMetaType::Int> {
static int *create() { return new int(INT_MIN); }
};
template<> struct TestValueFactory<QMetaType::UInt> {
static uint *create() { return new uint(UINT_MAX); }
};
template<> struct TestValueFactory<QMetaType::Bool> {
static bool *create() { return new bool(true); }
};
template<> struct TestValueFactory<QMetaType::Double> {
static double *create() { return new double(DBL_MIN); }
};
template<> struct TestValueFactory<QMetaType::QByteArray> {
static QByteArray *create() { return new QByteArray(QByteArray("QByteArray")); }
};
template<> struct TestValueFactory<QMetaType::QByteArrayList> {
static QByteArrayList *create() { return new QByteArrayList(QByteArrayList() << "Q" << "Byte" << "Array" << "List"); }
};
template<> struct TestValueFactory<QMetaType::QVariantMap> {
static QVariantMap *create() { return new QVariantMap(); }
};
template<> struct TestValueFactory<QMetaType::QVariantHash> {
static QVariantHash *create() { return new QVariantHash(); }
};
template<> struct TestValueFactory<QMetaType::QVariantPair> {
static QVariantPair *create() { return new QVariantPair(); }
};
template<> struct TestValueFactory<QMetaType::QVariantList> {
static QVariantList *create() { return new QVariantList(QVariantList() << 123 << "Q" << "Variant" << "List"); }
};
template<> struct TestValueFactory<QMetaType::QChar> {
static QChar *create() { return new QChar(QChar('q')); }
};
template<> struct TestValueFactory<QMetaType::Long> {
static long *create() { return new long(LONG_MIN); }
};
template<> struct TestValueFactory<QMetaType::Short> {
static short *create() { return new short(SHRT_MIN); }
};
template<> struct TestValueFactory<QMetaType::Char> {
static char *create() { return new char('c'); }
};
template<> struct TestValueFactory<QMetaType::Char16> {
static char16_t *create() { return new char16_t('c'); }
};
template<> struct TestValueFactory<QMetaType::Char32> {
static char32_t *create() { return new char32_t('c'); }
};
template<> struct TestValueFactory<QMetaType::ULong> {
static ulong *create() { return new ulong(ULONG_MAX); }
};
template<> struct TestValueFactory<QMetaType::UShort> {
static ushort *create() { return new ushort(USHRT_MAX); }
};
template<> struct TestValueFactory<QMetaType::SChar> {
static signed char *create() { return new signed char(CHAR_MIN); }
};
template<> struct TestValueFactory<QMetaType::UChar> {
static uchar *create() { return new uchar(UCHAR_MAX); }
};
template<> struct TestValueFactory<QMetaType::Float> {
static float *create() { return new float(FLT_MIN); }
};
template<> struct TestValueFactory<QMetaType::Float16> {
static auto create() { return new qfloat16(std::numeric_limits<qfloat16>::min()); }
};
template<> struct TestValueFactory<QMetaType::QObjectStar> {
static QObject * *create() { return new QObject *(0); }
};
template<> struct TestValueFactory<QMetaType::VoidStar> {
static void * *create() { return new void *(0); }
};
template<> struct TestValueFactory<QMetaType::LongLong> {
static qlonglong *create() { return new qlonglong(LLONG_MIN); }
};
template<> struct TestValueFactory<QMetaType::ULongLong> {
static qulonglong *create() { return new qulonglong(ULLONG_MAX); }
};
template<> struct TestValueFactory<QMetaType::QStringList> {
static QStringList *create() { return new QStringList(QStringList() << "Q" << "t"); }
};
template<> struct TestValueFactory<QMetaType::QBitArray> {
static QBitArray *create() { return new QBitArray(QBitArray(256, true)); }
};
template<> struct TestValueFactory<QMetaType::QDate> {
static QDate *create() { return new QDate(QDate::currentDate()); }
};
template<> struct TestValueFactory<QMetaType::QTime> {
static QTime *create() { return new QTime(QTime::currentTime()); }
};
template<> struct TestValueFactory<QMetaType::QDateTime> {
static QDateTime *create() { return new QDateTime(QDateTime::currentDateTime()); }
};
template<> struct TestValueFactory<QMetaType::QUrl> {
static QUrl *create() { return new QUrl("http://www.example.org"); }
};
template<> struct TestValueFactory<QMetaType::QLocale> {
static QLocale *create() { return new QLocale(QLocale::c()); }
};
template<> struct TestValueFactory<QMetaType::QRect> {
static QRect *create() { return new QRect(10, 20, 30, 40); }
};
template<> struct TestValueFactory<QMetaType::QRectF> {
static QRectF *create() { return new QRectF(10, 20, 30, 40); }
};
template<> struct TestValueFactory<QMetaType::QSize> {
static QSize *create() { return new QSize(10, 20); }
};
template<> struct TestValueFactory<QMetaType::QSizeF> {
static QSizeF *create() { return new QSizeF(10, 20); }
};
template<> struct TestValueFactory<QMetaType::QLine> {
static QLine *create() { return new QLine(10, 20, 30, 40); }
};
template<> struct TestValueFactory<QMetaType::QLineF> {
static QLineF *create() { return new QLineF(10, 20, 30, 40); }
};
template<> struct TestValueFactory<QMetaType::QPoint> {
static QPoint *create() { return new QPoint(10, 20); }
};
template<> struct TestValueFactory<QMetaType::QPointF> {
static QPointF *create() { return new QPointF(10, 20); }
};
template<> struct TestValueFactory<QMetaType::QEasingCurve> {
static QEasingCurve *create() { return new QEasingCurve(QEasingCurve::InOutElastic); }
};
template<> struct TestValueFactory<QMetaType::QUuid> {
static QUuid *create() { return new QUuid(); }
};
template<> struct TestValueFactory<QMetaType::QModelIndex> {
static QModelIndex *create() { return new QModelIndex(); }
};
template<> struct TestValueFactory<QMetaType::QPersistentModelIndex> {
static QPersistentModelIndex *create() { return new QPersistentModelIndex(); }
};
template<> struct TestValueFactory<QMetaType::Nullptr> {
static std::nullptr_t *create() { return new std::nullptr_t; }
};
template<> struct TestValueFactory<QMetaType::QRegularExpression> {
static QRegularExpression *create()
{
#if QT_CONFIG(regularexpression)
return new QRegularExpression("abc.*def");
#else
return 0;
#endif
}
};
template<> struct TestValueFactory<QMetaType::QJsonValue> {
static QJsonValue *create() { return new QJsonValue(123.); }
};
template<> struct TestValueFactory<QMetaType::QJsonObject> {
static QJsonObject *create() {
QJsonObject *o = new QJsonObject();
o->insert("a", 123.);
o->insert("b", true);
o->insert("c", QJsonValue::Null);
o->insert("d", QLatin1String("ciao"));
return o;
}
};
template<> struct TestValueFactory<QMetaType::QJsonArray> {
static QJsonArray *create() {
QJsonArray *a = new QJsonArray();
a->append(123.);
a->append(true);
a->append(QJsonValue::Null);
a->append(QLatin1String("ciao"));
return a;
}
};
template<> struct TestValueFactory<QMetaType::QJsonDocument> {
static QJsonDocument *create() {
return new QJsonDocument(
QJsonDocument::fromJson("{ 'foo': 123, 'bar': [true, null, 'ciao'] }")
);
}
};
template<> struct TestValueFactory<QMetaType::QCborSimpleType> {
static QCborSimpleType *create() { return new QCborSimpleType(QCborSimpleType::True); }
};
template<> struct TestValueFactory<QMetaType::QCborValue> {
static QCborValue *create() { return new QCborValue(123.); }
};
template<> struct TestValueFactory<QMetaType::QCborMap> {
static QCborMap *create() {
return new QCborMap{{0, 0}, {"Hello", 1}, {1, nullptr}};
}
};
template<> struct TestValueFactory<QMetaType::QCborArray> {
static QCborArray *create() {
return new QCborArray{0, 1, -2, 2.5, false, nullptr, "Hello", QByteArray("World") };
}
};
template<> struct TestValueFactory<QMetaType::QVariant> {
static QVariant *create() { return new QVariant(QStringList(QStringList() << "Q" << "t")); }
};
#endif // TST_QMETATYPE_H

View File

@ -0,0 +1,24 @@
// Copyright (C) 2022 Intel Corporation
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef TST_QMETATYPE_LIBS_H
#define TST_QMETATYPE_LIBS_H
#include <qmetatype.h>
#include <stdlib.h> // for div_t
// void: builtin metatype, special
// int: builtin metatype, primitive type
// QString: builtin metatype, class
// QCollator: not builtin, class, Q_CORE_EXPORT
// div_t: not builtin, class, no export
#define FOR_EACH_METATYPE_LIBS(F) \
F(void, QMetaType::Void) \
F(int, QMetaType::Int) \
F(QString, QMetaType::QString) \
F(QCollator, QMetaType::UnknownType) \
F(div_t, QMetaType::UnknownType) \
/**/
#endif // TST_QMETATYPE_LIBS_H

Binary file not shown.

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmimedata Test:
#####################################################################
qt_internal_add_test(tst_qmimedata
SOURCES
tst_qmimedata.cpp
LIBRARIES
Qt::Gui
)

View File

@ -0,0 +1,327 @@
// 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>
#include <QMimeData>
class tst_QMimeData : public QObject
{
Q_OBJECT
private slots:
void clear() const;
void colorData() const;
void data() const;
void formats() const;
void hasColor() const;
void hasFormat() const;
void hasHtml() const;
void hasImage() const;
// hasText() covered by setText()
// hasUrls() covered by setUrls()
// html() covered by setHtml()
void imageData() const;
void removeFormat() const;
// setColorData() covered by hasColor()
// setData() covered in a few different tests
void setHtml() const;
// setImageData() covered in a few tests
void setText() const;
void setUrls() const;
// text() covered in setText()
// urls() covered by setUrls()
};
void tst_QMimeData::clear() const
{
QMimeData mimeData;
// set, clear, verify empty
mimeData.setData("text/plain", "pirates");
QVERIFY(mimeData.hasText());
mimeData.clear();
QVERIFY(!mimeData.hasText());
// repopulate, verify not empty
mimeData.setData("text/plain", "pirates");
QVERIFY(mimeData.hasText());
}
void tst_QMimeData::colorData() const
{
QMimeData mimeData;
QColor red = Qt::red;
QColor blue = Qt::blue;
// set, verify
mimeData.setColorData(red);
QVERIFY(mimeData.hasColor());
QCOMPARE(qvariant_cast<QColor>(mimeData.colorData()), red);
// change, verify
mimeData.setColorData(QColor(Qt::blue));
QVERIFY(mimeData.hasColor());
QCOMPARE(qvariant_cast<QColor>(mimeData.colorData()), blue);
}
void tst_QMimeData::data() const
{
QMimeData mimeData;
// set text, verify
mimeData.setData("text/plain", "pirates");
QCOMPARE(mimeData.data("text/plain"), QByteArray("pirates"));
QCOMPARE(mimeData.data("text/html").size(), 0);
QCOMPARE(mimeData.data("text/markdown").size(), 0);
// html time
mimeData.setData("text/html", "ninjas");
QCOMPARE(mimeData.data("text/html"), QByteArray("ninjas"));
QCOMPARE(mimeData.data("text/plain"), QByteArray("pirates")); // make sure text not damaged
QCOMPARE(mimeData.data("text/html"), mimeData.html().toLatin1());
// markdown time
mimeData.setData("text/markdown", "vikings");
QCOMPARE(mimeData.data("text/markdown"), QByteArray("vikings"));
QCOMPARE(mimeData.data("text/html"), QByteArray("ninjas"));
QCOMPARE(mimeData.data("text/plain"), QByteArray("pirates"));
}
void tst_QMimeData::formats() const
{
QMimeData mimeData;
// set text, verify
mimeData.setData("text/plain", "pirates");
QCOMPARE(mimeData.formats(), QStringList() << "text/plain");
// set html, verify
mimeData.setData("text/html", "ninjas");
QCOMPARE(mimeData.formats(), QStringList() << "text/plain" << "text/html");
// set markdown, verify
mimeData.setData("text/markdown", "vikings");
QCOMPARE(mimeData.formats(), QStringList() << "text/plain" << "text/html" << "text/markdown");
// clear, verify
mimeData.clear();
QCOMPARE(mimeData.formats(), QStringList());
// set an odd format, verify
mimeData.setData("foo/bar", "somevalue");
QCOMPARE(mimeData.formats(), QStringList() << "foo/bar");
}
void tst_QMimeData::hasColor() const
{
QMimeData mimeData;
// initial state
QVERIFY(!mimeData.hasColor());
// set, verify
mimeData.setColorData(QColor(Qt::red));
QVERIFY(mimeData.hasColor());
// clear, verify
mimeData.clear();
QVERIFY(!mimeData.hasColor());
// set something else, verify
mimeData.setData("text/plain", "pirates");
QVERIFY(!mimeData.hasColor());
}
void tst_QMimeData::hasFormat() const
{
QMimeData mimeData;
// initial state
QVERIFY(!mimeData.hasFormat("text/plain"));
// add, verify
mimeData.setData("text/plain", "pirates");
QVERIFY(mimeData.hasFormat("text/plain"));
QVERIFY(!mimeData.hasFormat("text/html"));
// clear, verify
mimeData.clear();
QVERIFY(!mimeData.hasFormat("text/plain"));
QVERIFY(!mimeData.hasFormat("text/html"));
}
void tst_QMimeData::hasHtml() const
{
QMimeData mimeData;
// initial state
QVERIFY(!mimeData.hasHtml());
// add plain, verify false
mimeData.setData("text/plain", "pirates");
QVERIFY(!mimeData.hasHtml());
// add html, verify
mimeData.setData("text/html", "ninjas");
QVERIFY(mimeData.hasHtml());
// clear, verify
mimeData.clear();
QVERIFY(!mimeData.hasHtml());
// readd, verify
mimeData.setData("text/html", "ninjas");
QVERIFY(mimeData.hasHtml());
}
void tst_QMimeData::hasImage() const
{
QMimeData mimeData;
// initial state
QVERIFY(!mimeData.hasImage());
// add text, verify false
mimeData.setData("text/plain", "pirates");
QVERIFY(!mimeData.hasImage());
// add image
mimeData.setImageData(QImage());
QVERIFY(mimeData.hasImage());
// clear, verify
mimeData.clear();
QVERIFY(!mimeData.hasImage());
}
void tst_QMimeData::imageData() const
{
QMimeData mimeData;
// initial state
QCOMPARE(mimeData.imageData(), QVariant());
// set, test
mimeData.setImageData(QImage());
QVERIFY(mimeData.hasImage());
QCOMPARE(mimeData.imageData(), QVariant(QImage()));
// clear, verify
mimeData.clear();
QCOMPARE(mimeData.imageData(), QVariant());
}
void tst_QMimeData::removeFormat() const
{
QMimeData mimeData;
// add, verify
mimeData.setData("text/plain", "pirates");
QVERIFY(mimeData.hasFormat("text/plain"));
// add another, verify
mimeData.setData("text/html", "ninjas");
QVERIFY(mimeData.hasFormat("text/html"));
// remove, verify
mimeData.removeFormat("text/plain");
QVERIFY(!mimeData.hasFormat("text/plain"));
QVERIFY(mimeData.hasFormat("text/html"));
// remove, verify
mimeData.removeFormat("text/html");
QVERIFY(!mimeData.hasFormat("text/plain"));
QVERIFY(!mimeData.hasFormat("text/html"));
}
void tst_QMimeData::setHtml() const
{
QMimeData mimeData;
// initial state
QVERIFY(!mimeData.hasHtml());
// add html, verify
mimeData.setHtml("ninjas");
QVERIFY(mimeData.hasHtml());
QCOMPARE(mimeData.html(), QLatin1String("ninjas"));
// reset html
mimeData.setHtml("pirates");
QVERIFY(mimeData.hasHtml());
QCOMPARE(mimeData.html(), QLatin1String("pirates"));
}
void tst_QMimeData::setText() const
{
QMimeData mimeData;
// verify initial state
QCOMPARE(mimeData.text(), QLatin1String(""));
QVERIFY(!mimeData.hasText());
// set, verify
mimeData.setText("pirates");
QVERIFY(mimeData.hasText());
QCOMPARE(mimeData.text(), QLatin1String("pirates"));
QCOMPARE(mimeData.text().toLatin1(), mimeData.data("text/plain"));
// reset, verify
mimeData.setText("ninjas");
QVERIFY(mimeData.hasText());
QCOMPARE(mimeData.text(), QLatin1String("ninjas"));
QCOMPARE(mimeData.text().toLatin1(), mimeData.data("text/plain"));
// clear, verify
mimeData.clear();
QCOMPARE(mimeData.text(), QLatin1String(""));
QVERIFY(!mimeData.hasText());
}
// Publish retrieveData for verifying content validity
class TstMetaData : public QMimeData
{
public:
using QMimeData::retrieveData;
};
void tst_QMimeData::setUrls() const
{
TstMetaData mimeData;
QList<QUrl> shortUrlList;
QList<QUrl> longUrlList;
// set up
shortUrlList += QUrl("http://qt-project.org");
longUrlList = shortUrlList;
longUrlList += QUrl("http://www.google.com");
// verify initial state
QCOMPARE(mimeData.hasUrls(), false);
// set a few, verify
mimeData.setUrls(shortUrlList);
QCOMPARE(mimeData.urls(), shortUrlList);
QCOMPARE(mimeData.text(), QString("http://qt-project.org"));
// change them, verify
mimeData.setUrls(longUrlList);
QCOMPARE(mimeData.urls(), longUrlList);
QCOMPARE(mimeData.text(), QString("http://qt-project.org\nhttp://www.google.com\n"));
// test and verify that setData doesn't corrupt url content
foreach (const QString &format, mimeData.formats()) {
QVariant before = mimeData.retrieveData(format, QMetaType(QMetaType::QByteArray));
mimeData.setData(format, mimeData.data(format));
QVariant after = mimeData.retrieveData(format, QMetaType(QMetaType::QByteArray));
QCOMPARE(after, before);
}
// clear, verify
mimeData.clear();
QCOMPARE(mimeData.hasUrls(), false);
QCOMPARE(mimeData.hasText(), false);
}
QTEST_APPLESS_MAIN(tst_QMimeData)
#include "tst_qmimedata.moc"

View File

@ -0,0 +1,20 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qobject Test:
#####################################################################
qt_internal_add_test(tst_qobject
SOURCES
tst_qobject.cpp
LIBRARIES
Qt::CorePrivate
Qt::Network
Qt::TestPrivate
)
## Scopes:
#####################################################################
add_subdirectory(signalbug)
add_dependencies(tst_qobject signalbug_helper)

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## signalbug_helper Binary:
#####################################################################
qt_internal_add_test_helper(signalbug_helper
SOURCES
signalbug.cpp signalbug.h
)

View File

@ -0,0 +1,109 @@
// 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 "signalbug.h"
#include <qcoreapplication.h>
#include <qstring.h>
#include <stdio.h>
static int Step = 0;
Sender RandomSender (0, 0);
void TRACE (int step, const char *name)
{
for (int t = 0; t < step - 1; t++)
fprintf (stderr, "\t");
fprintf (stderr, "Step %d: %s\n", step, name);
return;
}
Receiver::Receiver ()
: QObject ()
{
}
void Receiver::received ()
{
::Step++;
const int stepCopy = ::Step;
TRACE (stepCopy, "Receiver::received()");
if (::Step != 2 && ::Step != 4)
qFatal("%s: Incorrect Step: %d (should be 2 or 4)", Q_FUNC_INFO, ::Step);
if (::Step == 2)
s->fire ();
fprintf (stderr, "Receiver<%s>::received() sender=%s\n",
(const char *) objectName ().toLatin1 (), sender ()->metaObject()->className());
TRACE (stepCopy, "ends Receiver::received()");
}
Disconnector::Disconnector ()
: QObject ()
{
}
void Disconnector::received ()
{
::Step++;
const int stepCopy = ::Step;
TRACE (stepCopy, "Disconnector::received()");
if (::Step != 5 && ::Step != 6)
qFatal("%s: Incorrect Step: %d (should be 5 or 6)", Q_FUNC_INFO, ::Step);
fprintf (stderr, "Disconnector<%s>::received() sender=%s\n",
(const char *) objectName ().toLatin1 (), sender ()->metaObject()->className());
if (sender () == 0)
fprintf (stderr, "WE SHOULD NOT BE RECEIVING THIS SIGNAL\n");
if (::Step == 5)
{
disconnect (s, SIGNAL(fired()), s->d, SLOT(received()));
connect (&RandomSender, SIGNAL(fired()), s->d, SLOT(received()));
}
TRACE (stepCopy, "ends Disconnector::received()");
}
Sender::Sender (Receiver *r, Disconnector *d)
: QObject ()
{
this->r = r; this->d = d;
if (r)
connect (this, SIGNAL(fired()), r, SLOT(received()));
if (d)
connect (this, SIGNAL(fired()), d, SLOT(received()));
};
void Sender::fire ()
{
::Step++;
const int stepCopy = ::Step;
TRACE (stepCopy, "Sender::fire()");
if (::Step != 1 && ::Step != 3)
qFatal("%s: Incorrect Step: %d (should be 1 or 3)", Q_FUNC_INFO, ::Step);
emit fired ();
TRACE (stepCopy, "ends Sender::fire()");
}
int main (int argc, char *argv [])
{
QCoreApplication app (argc, argv);
Receiver r;
Disconnector d;
Sender s (&r, &d);
r.s = &s;
d.s = &s;
::Step = 0;
s.fire ();
return 0;
}

View File

@ -0,0 +1,62 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef SIGNAL_BUG_H
#define SIGNAL_BUG_H
#include <QObject>
class Sender;
class Receiver : public QObject
{
Q_OBJECT
public:
Receiver ();
virtual ~Receiver () {}
protected slots:
void received ();
public:
Sender *s;
};
class Disconnector : public QObject
{
Q_OBJECT
public:
Disconnector ();
virtual ~Disconnector () {}
protected slots:
void received ();
public:
Sender *s;
};
class Sender : public QObject
{
Q_OBJECT
public:
Sender (Receiver *r, Disconnector *d);
virtual ~Sender () {}
void fire ();
signals:
void fired ();
public:
Receiver *r;
Disconnector *d;
};
#endif // SIGNAL_BUG_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if (NOT QT_FEATURE_permissions)
return()
endif()
qt_internal_add_test(tst_qpermission
SOURCES
tst_qpermission.cpp
LIBRARIES
Qt::Core
)

View File

@ -0,0 +1,237 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QPermission>
#include <QTest>
struct DummyPermission // a minimal QPermission-compatible type
{
using QtPermissionHelper = void;
int state = 0;
};
Q_DECLARE_METATYPE(DummyPermission)
class tst_QPermission : public QObject
{
Q_OBJECT
private Q_SLOTS:
void converting_Dummy() const { return converting_impl<DummyPermission>(); }
void converting_Location() const { return converting_impl<QLocationPermission>(); }
void converting_Calendar() const { return converting_impl<QCalendarPermission>(); }
void converting_Contacts() const { return converting_impl<QContactsPermission>(); }
void converting_Camera() const { return converting_impl<QCameraPermission>(); }
void converting_Microphone() const { return converting_impl<QMicrophonePermission>(); }
void converting_Bluetooth() const { return converting_impl<QBluetoothPermission>(); }
void conversionMaintainsState() const;
void functorWithContextInThread();
void receiverInThread();
void destroyedContextObject();
private:
template <typename T>
void converting_impl() const;
};
template <typename T>
void tst_QPermission::converting_impl() const
{
T concrete;
const T cconcrete = concrete;
const auto metaType = QMetaType::fromType<T>();
// construction is implicit:
// from rvalue:
{
QPermission p = T();
QCOMPARE_EQ(p.type(), metaType);
}
// from mutable lvalue:
{
QPermission p = concrete;
QCOMPARE_EQ(p.type(), metaType);
}
// from const lvalue:
{
QPermission p = cconcrete;
QCOMPARE_EQ(p.type(), metaType);
}
// value<>() compiles:
{
const QPermission p = concrete;
auto v = p.value<T>();
static_assert(std::is_same_v<decltype(v), std::optional<T>>);
QCOMPARE_NE(v, std::nullopt);
}
}
void tst_QPermission::conversionMaintainsState() const
{
DummyPermission dummy{42}, dummy_default;
QCOMPARE_NE(dummy.state, dummy_default.state);
QLocationPermission loc, loc_default;
QCOMPARE_EQ(loc_default.accuracy(), QLocationPermission::Accuracy::Approximate);
QCOMPARE_EQ(loc_default.availability(), QLocationPermission::Availability::WhenInUse);
loc.setAccuracy(QLocationPermission::Accuracy::Precise);
loc.setAvailability(QLocationPermission::Availability::Always);
QCOMPARE_EQ(loc.accuracy(), QLocationPermission::Accuracy::Precise);
QCOMPARE_EQ(loc.availability(), QLocationPermission::Availability::Always);
QCalendarPermission cal, cal_default;
QCOMPARE_EQ(cal_default.accessMode(), QCalendarPermission::AccessMode::ReadOnly);
cal.setAccessMode(QCalendarPermission::AccessMode::ReadWrite);
QCOMPARE_EQ(cal.accessMode(), QCalendarPermission::AccessMode::ReadWrite);
QContactsPermission con, con_default;
QCOMPARE_EQ(con_default.accessMode(), QContactsPermission::AccessMode::ReadOnly);
con.setAccessMode(QContactsPermission::AccessMode::ReadWrite);
QCOMPARE_EQ(con.accessMode(), QContactsPermission::AccessMode::ReadWrite);
//
// QCameraPermission, QMicrophonePermission, QBluetoothPermission don't have
// state at the time of writing
//
QPermission p; // maintain state between the blocks below to test reset behavior
{
p = dummy;
auto v = p.value<DummyPermission>();
QCOMPARE_NE(v, std::nullopt);
auto &r = *v;
QCOMPARE_EQ(r.state, dummy.state);
// check mismatched returns nullopt:
QCOMPARE_EQ(p.value<QCalendarPermission>(), std::nullopt);
}
{
p = loc;
auto v = p.value<QLocationPermission>();
QCOMPARE_NE(v, std::nullopt);
auto &r = *v;
QCOMPARE_EQ(r.accuracy(), loc.accuracy());
QCOMPARE_EQ(r.availability(), loc.availability());
// check mismatched returns nullopt:
QCOMPARE_EQ(p.value<DummyPermission>(), std::nullopt);
}
{
p = con;
auto v = p.value<QContactsPermission>();
QCOMPARE_NE(v, std::nullopt);
auto &r = *v;
QCOMPARE_EQ(r.accessMode(), con.accessMode());
// check mismatched returns nullopt:
QCOMPARE_EQ(p.value<QLocationPermission>(), std::nullopt);
}
{
p = cal;
auto v = p.value<QCalendarPermission>();
QCOMPARE_NE(v, std::nullopt);
auto &r = *v;
QCOMPARE_EQ(r.accessMode(), cal.accessMode());
// check mismatched returns nullopt:
QCOMPARE_EQ(p.value<QContactsPermission>(), std::nullopt);
}
}
void tst_QPermission::functorWithContextInThread()
{
int argc = 0;
char *argv = nullptr;
QCoreApplication app(argc, &argv);
QThread::currentThread()->setObjectName("main thread");
QThread receiverThread;
receiverThread.setObjectName("receiverThread");
QObject receiver;
receiver.moveToThread(&receiverThread);
receiverThread.start();
auto guard = qScopeGuard([&receiverThread]{
receiverThread.quit();
QVERIFY(receiverThread.wait(1000));
});
DummyPermission dummy;
#ifdef Q_OS_DARWIN
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Could not find permission plugin for DummyPermission.*"));
#endif
QThread *permissionReceiverThread = nullptr;
qApp->requestPermission(dummy, &receiver, [&](const QPermission &permission){
auto dummy = permission.value<DummyPermission>();
QVERIFY(dummy);
permissionReceiverThread = QThread::currentThread();
});
QTRY_COMPARE(permissionReceiverThread, &receiverThread);
}
void tst_QPermission::receiverInThread()
{
int argc = 0;
char *argv = nullptr;
QCoreApplication app(argc, &argv);
QThread::currentThread()->setObjectName("main thread");
QThread receiverThread;
receiverThread.setObjectName("receiverThread");
class Receiver : public QObject
{
public:
using QObject::QObject;
void handlePermission(const QPermission &permission)
{
auto dummy = permission.value<DummyPermission>();
QVERIFY(dummy);
permissionReceiverThread = QThread::currentThread();
}
QThread *permissionReceiverThread = nullptr;
} receiver;
receiver.moveToThread(&receiverThread);
receiverThread.start();
auto guard = qScopeGuard([&receiverThread]{
receiverThread.quit();
QVERIFY(receiverThread.wait(1000));
});
DummyPermission dummy;
#ifdef Q_OS_DARWIN
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Could not find permission plugin for DummyPermission.*"));
#endif
qApp->requestPermission(dummy, &receiver, &Receiver::handlePermission);
QTRY_COMPARE(receiver.permissionReceiverThread, &receiverThread);
}
void tst_QPermission::destroyedContextObject()
{
int argc = 0;
char *argv = nullptr;
QCoreApplication app(argc, &argv);
QObject *context = new QObject;
DummyPermission dummy;
#ifdef Q_OS_DARWIN
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Could not find permission plugin for DummyPermission.*"));
#endif
bool permissionReceived = false;
qApp->requestPermission(dummy, context, [&]{
permissionReceived = true;
});
QVERIFY2(!permissionReceived, "Permission received synchronously");
delete context;
QTest::qWait(100);
QVERIFY(!permissionReceived);
}
QTEST_APPLESS_MAIN(tst_QPermission)
#include "tst_qpermission.moc"

View File

@ -0,0 +1,25 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qpointer Test:
#####################################################################
if (NOT QT_FEATURE_thread)
return()
endif()
qt_internal_add_test(tst_qpointer
SOURCES
tst_qpointer.cpp
LIBRARIES
Qt::Gui
)
## Scopes:
#####################################################################
qt_internal_extend_target(tst_qpointer CONDITION TARGET Qt::Widgets
LIBRARIES
Qt::Widgets
)

View File

@ -0,0 +1,400 @@
// 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>
#include <QRunnable>
#include <QThreadPool>
#include <QPointer>
#ifndef QT_NO_WIDGETS
#include <QWidget>
#endif
class tst_QPointer : public QObject
{
Q_OBJECT
public:
inline tst_QPointer *me() const
{ return const_cast<tst_QPointer *>(this); }
private slots:
void constructors();
void destructor();
void assignment_operators();
void equality_operators();
void swap();
void isNull();
void dereference_operators();
void disconnect();
void castDuringDestruction();
void threadSafety();
void qvariantCast();
void constPointer();
void constQPointer();
};
void tst_QPointer::constructors()
{
QPointer<QObject> p1;
QPointer<QObject> p2(this);
QPointer<QObject> p3(p2);
QCOMPARE(p1, QPointer<QObject>(0));
QCOMPARE(p2, QPointer<QObject>(this));
QCOMPARE(p3, QPointer<QObject>(this));
}
void tst_QPointer::destructor()
{
// Make two QPointer's to the same object
QObject *object = new QObject;
QPointer<QObject> p1 = object;
QPointer<QObject> p2 = object;
// Check that they point to the correct object
QCOMPARE(p1, QPointer<QObject>(object));
QCOMPARE(p2, QPointer<QObject>(object));
QCOMPARE(p1, p2);
// Destroy the guarded object
delete object;
// Check that both pointers were zeroed
QCOMPARE(p1, QPointer<QObject>(0));
QCOMPARE(p2, QPointer<QObject>(0));
QCOMPARE(p1, p2);
}
void tst_QPointer::assignment_operators()
{
QPointer<QObject> p1;
QPointer<QObject> p2;
// Test assignment with a QObject-derived object pointer
p1 = this;
p2 = p1;
QCOMPARE(p1, QPointer<QObject>(this));
QCOMPARE(p2, QPointer<QObject>(this));
QCOMPARE(p1, QPointer<QObject>(p2));
// Test assignment with a null pointer
p1 = nullptr;
p2 = p1;
QCOMPARE(p1, QPointer<QObject>(0));
QCOMPARE(p2, QPointer<QObject>(0));
QCOMPARE(p1, QPointer<QObject>(p2));
// Test assignment with a real QObject pointer
QObject *object = new QObject;
p1 = object;
p2 = p1;
QCOMPARE(p1, QPointer<QObject>(object));
QCOMPARE(p2, QPointer<QObject>(object));
QCOMPARE(p1, QPointer<QObject>(p2));
// Test assignment with the same pointer that's already guarded
p1 = object;
p2 = p1;
QCOMPARE(p1, QPointer<QObject>(object));
QCOMPARE(p2, QPointer<QObject>(object));
QCOMPARE(p1, QPointer<QObject>(p2));
// Cleanup
delete object;
}
void tst_QPointer::equality_operators()
{
QPointer<QObject> p1;
QPointer<QObject> p2;
QVERIFY(p1 == p2);
QObject *object = nullptr;
#ifndef QT_NO_WIDGETS
QWidget *widget = nullptr;
#endif
p1 = object;
QVERIFY(p1 == p2);
QVERIFY(p1 == object);
p2 = object;
QVERIFY(p2 == p1);
QVERIFY(p2 == object);
p1 = this;
QVERIFY(p1 != p2);
p2 = p1;
QVERIFY(p1 == p2);
// compare to zero
p1 = nullptr;
QVERIFY(p1 == 0);
QVERIFY(0 == p1);
QVERIFY(p2 != 0);
QVERIFY(0 != p2);
QVERIFY(p1 == nullptr);
QVERIFY(nullptr == p1);
QVERIFY(p2 != nullptr);
QVERIFY(nullptr != p2);
QVERIFY(p1 == object);
QVERIFY(object == p1);
QVERIFY(p2 != object);
QVERIFY(object != p2);
#ifndef QT_NO_WIDGETS
QVERIFY(p1 == widget);
QVERIFY(widget == p1);
QVERIFY(p2 != widget);
QVERIFY(widget != p2);
#endif
}
void tst_QPointer::swap()
{
QPointer<QObject> c1, c2;
{
QObject o;
c1 = &o;
QVERIFY(c2.isNull());
QCOMPARE(c1.data(), &o);
c1.swap(c2);
QVERIFY(c1.isNull());
QCOMPARE(c2.data(), &o);
}
QVERIFY(c1.isNull());
QVERIFY(c2.isNull());
}
void tst_QPointer::isNull()
{
QPointer<QObject> p1;
QVERIFY(p1.isNull());
p1 = this;
QVERIFY(!p1.isNull());
p1 = nullptr;
QVERIFY(p1.isNull());
}
void tst_QPointer::dereference_operators()
{
QPointer<tst_QPointer> p1 = this;
QPointer<tst_QPointer> p2;
// operator->() -- only makes sense if not null
QObject *object = p1->me();
QCOMPARE(object, this);
// operator*() -- only makes sense if not null
QObject &ref = *p1;
QCOMPARE(&ref, this);
// operator T*()
QCOMPARE(static_cast<QObject *>(p1), this);
QCOMPARE(static_cast<QObject *>(p2), static_cast<QObject *>(0));
// data()
QCOMPARE(p1.data(), this);
QCOMPARE(p2.data(), static_cast<QObject *>(0));
}
void tst_QPointer::disconnect()
{
// Verify that pointer remains guarded when all signals are disconencted.
QPointer<QObject> p1 = new QObject;
QVERIFY(!p1.isNull());
p1->disconnect();
QVERIFY(!p1.isNull());
delete static_cast<QObject *>(p1);
QVERIFY(p1.isNull());
}
class ChildObject : public QObject
{
QPointer<QObject> guardedPointer;
public:
ChildObject(QObject *parent)
: QObject(parent), guardedPointer(parent)
{ }
~ChildObject();
};
ChildObject::~ChildObject()
{
QCOMPARE(static_cast<QObject *>(guardedPointer), static_cast<QObject *>(0));
QCOMPARE(qobject_cast<QObject *>(guardedPointer), static_cast<QObject *>(0));
}
#ifndef QT_NO_WIDGETS
class ChildWidget : public QWidget
{
QPointer<QWidget> guardedPointer;
public:
ChildWidget(QWidget *parent)
: QWidget(parent), guardedPointer(parent)
{ }
~ChildWidget();
};
ChildWidget::~ChildWidget()
{
QCOMPARE(static_cast<QWidget *>(guardedPointer), parentWidget());
QCOMPARE(qobject_cast<QWidget *>(guardedPointer), parentWidget());
}
#endif
class DerivedChild;
class DerivedParent : public QObject
{
Q_OBJECT
DerivedChild *derivedChild;
public:
DerivedParent();
~DerivedParent();
};
class DerivedChild : public QObject
{
Q_OBJECT
DerivedParent *parentPointer;
QPointer<DerivedParent> guardedParentPointer;
public:
DerivedChild(DerivedParent *parent)
: QObject(parent), parentPointer(parent), guardedParentPointer(parent)
{ }
~DerivedChild();
};
DerivedParent::DerivedParent()
: QObject()
{
derivedChild = new DerivedChild(this);
}
DerivedParent::~DerivedParent()
{
delete derivedChild;
}
DerivedChild::~DerivedChild()
{
QCOMPARE(static_cast<DerivedParent *>(guardedParentPointer), parentPointer);
QCOMPARE(qobject_cast<DerivedParent *>(guardedParentPointer), parentPointer);
}
void tst_QPointer::castDuringDestruction()
{
{
QObject *parentObject = new QObject();
(void) new ChildObject(parentObject);
delete parentObject;
}
#ifndef QT_NO_WIDGETS
{
QWidget *parentWidget = new QWidget();
(void) new ChildWidget(parentWidget);
delete parentWidget;
}
#endif
{
delete new DerivedParent();
}
}
class TestRunnable : public QObject, public QRunnable {
void run() override
{
QPointer<QObject> obj1 = new QObject;
QPointer<QObject> obj2 = new QObject;
obj1->moveToThread(thread()); // this is the owner thread
obj1->deleteLater(); // the delete will happen in the owner thread
obj2->moveToThread(thread()); // this is the owner thread
obj2->deleteLater(); // the delete will happen in the owner thread
}
};
void tst_QPointer::threadSafety()
{
QThread owner;
owner.start();
QThreadPool pool;
for (int i = 0; i < 300; i++) {
QPointer<TestRunnable> task = new TestRunnable;
task->setAutoDelete(true);
task->moveToThread(&owner);
pool.start(task);
}
pool.waitForDone();
owner.quit();
owner.wait();
}
void tst_QPointer::qvariantCast()
{
QFile file;
QPointer<QFile> tracking = &file;
tracking->setObjectName("A test name");
QVariant v = QVariant::fromValue(tracking);
{
QPointer<QObject> other = qPointerFromVariant<QObject>(v);
QCOMPARE(other->objectName(), QString::fromLatin1("A test name"));
}
{
QPointer<QIODevice> other = qPointerFromVariant<QIODevice>(v);
QCOMPARE(other->objectName(), QString::fromLatin1("A test name"));
}
{
QPointer<QFile> other = qPointerFromVariant<QFile>(v);
QCOMPARE(other->objectName(), QString::fromLatin1("A test name"));
}
{
QPointer<QThread> other = qPointerFromVariant<QThread>(v);
QVERIFY(!other);
}
{
QPointer<QFile> toBeDeleted = new QFile;
QVariant deletedVariant = QVariant::fromValue(toBeDeleted);
delete toBeDeleted;
QPointer<QObject> deleted = qPointerFromVariant<QObject>(deletedVariant);
QVERIFY(!deleted);
}
// Intentionally does not compile.
// QPointer<int> sop = qPointerFromVariant<int>(v);
}
void tst_QPointer::constPointer()
{
// Compile-time test that QPointer<const T> works.
QPointer<const QFile> fp = new QFile;
delete fp.data();
}
void tst_QPointer::constQPointer()
{
// Check that const QPointers work. It's a bit weird to mark a pointer
// const if its value can change, but the shallow-const principle in C/C++
// allows this, and people use it, so document it with a test.
//
// It's unlikely that this test will fail in and out of itself, but it
// presents the use-case to static and dynamic checkers that can raise
// a warning (hopefully) should this become an issue.
QObject *o = new QObject(this);
const QPointer<QObject> p = o;
delete o;
QVERIFY(!p);
}
QTEST_MAIN(tst_QPointer)
#include "tst_qpointer.moc"

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qproperty Test:
#####################################################################
qt_internal_add_test(tst_qproperty
SOURCES
tst_qproperty.cpp
LIBRARIES
Qt::CorePrivate
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
# 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()

View File

@ -0,0 +1,13 @@
# 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
)

View File

@ -0,0 +1,173 @@
// 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;
}

View File

@ -0,0 +1,807 @@
// 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"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qsignalblocker Test:
#####################################################################
qt_internal_add_test(tst_qsignalblocker
SOURCES
tst_qsignalblocker.cpp
)

View File

@ -0,0 +1,137 @@
// Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@woboq.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include "qobject.h"
class tst_QSignalBlocker : public QObject
{
Q_OBJECT
private slots:
void signalBlocking();
void moveAssignment();
};
void tst_QSignalBlocker::signalBlocking()
{
QObject o;
QVERIFY(!o.signalsBlocked());
{
QSignalBlocker blocker(&o);
QVERIFY(o.signalsBlocked());
o.blockSignals(false);
QVERIFY(!o.signalsBlocked());
o.blockSignals(true);
QVERIFY(o.signalsBlocked());
blocker.unblock();
QVERIFY(!o.signalsBlocked());
blocker.reblock();
QVERIFY(o.signalsBlocked());
}
QVERIFY(!o.signalsBlocked());
}
void tst_QSignalBlocker::moveAssignment()
{
QObject o1, o2;
// move-assignment: both block other objects
{
QSignalBlocker b(&o1);
QVERIFY(o1.signalsBlocked());
QVERIFY(!o2.signalsBlocked());
b = QSignalBlocker(&o2);
QVERIFY(!o1.signalsBlocked());
QVERIFY(o2.signalsBlocked());
}
QVERIFY(!o1.signalsBlocked());
QVERIFY(!o2.signalsBlocked());
// move-assignment: from inert other
{
QSignalBlocker b(&o1);
QVERIFY(o1.signalsBlocked());
b = QSignalBlocker(0);
}
QVERIFY(!o1.signalsBlocked());
QVERIFY(!o2.signalsBlocked());
// move-assignment: to inert *this
{
QSignalBlocker b(0);
QVERIFY(!o1.signalsBlocked());
{
QSignalBlocker inner(&o1);
QVERIFY(o1.signalsBlocked());
b = std::move(inner);
}
QVERIFY(o1.signalsBlocked());
}
QVERIFY(!o1.signalsBlocked());
QVERIFY(!o2.signalsBlocked());
// move-assignment: both block the same object, neither is unblocked
{
QSignalBlocker b(&o1);
QVERIFY(o1.signalsBlocked());
{
b.unblock(); // make sure inner.m_blocked = false
QVERIFY(!o1.signalsBlocked());
QSignalBlocker inner(&o1);
QVERIFY(o1.signalsBlocked());
b.reblock();
QVERIFY(o1.signalsBlocked());
b = std::move(inner);
}
QVERIFY(o1.signalsBlocked());
}
QVERIFY(!o1.signalsBlocked());
QVERIFY(!o2.signalsBlocked());
// move-assignment: both block the same object, but *this is unblocked
{
QSignalBlocker b(&o1);
QVERIFY(o1.signalsBlocked());
b.unblock();
QVERIFY(!o1.signalsBlocked());
b = QSignalBlocker(&o1);
QVERIFY(o1.signalsBlocked());
}
QVERIFY(!o1.signalsBlocked());
QVERIFY(!o2.signalsBlocked());
// move-assignment: both block the same object, but other is unblocked
{
QSignalBlocker b(&o1);
{
QVERIFY(o1.signalsBlocked());
QSignalBlocker inner(&o1);
QVERIFY(o1.signalsBlocked());
inner.unblock();
QVERIFY(o1.signalsBlocked());
b = std::move(inner);
QVERIFY(!o1.signalsBlocked());
}
QVERIFY(!o1.signalsBlocked());
}
QVERIFY(!o1.signalsBlocked());
QVERIFY(!o2.signalsBlocked());
}
QTEST_MAIN(tst_QSignalBlocker)
#include "tst_qsignalblocker.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qsignalmapper Test:
#####################################################################
qt_internal_add_test(tst_qsignalmapper
SOURCES
tst_qsignalmapper.cpp
)

View File

@ -0,0 +1,83 @@
// 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>
#include <qsignalmapper.h>
class tst_QSignalMapper : public QObject
{
Q_OBJECT
private slots:
void mapped();
};
class QtTestObject : public QObject
{
Q_OBJECT
public slots:
void myIntSlot(int id);
void myStringSlot(const QString &str);
signals:
void mysignal(int);
public:
void emit_mysignal(int);
int id;
QString str;
};
void QtTestObject::myIntSlot(int id)
{
this->id = id;
}
void QtTestObject::myStringSlot(const QString &str)
{
this->str = str;
}
void QtTestObject::emit_mysignal(int value)
{
emit mysignal(value);
}
void tst_QSignalMapper::mapped()
{
QSignalMapper mapper;
QtTestObject target;
QtTestObject src1;
QtTestObject src2;
QtTestObject src3;
connect(&src1, SIGNAL(mysignal(int)), &mapper, SLOT(map()));
connect(&src2, SIGNAL(mysignal(int)), &mapper, SLOT(map()));
connect(&src3, SIGNAL(mysignal(int)), &mapper, SLOT(map()));
mapper.setMapping(&src1, 7);
mapper.setMapping(&src1, 1);
mapper.setMapping(&src2, 2);
mapper.setMapping(&src2, "two");
mapper.setMapping(&src3, "three");
connect(&mapper, &QSignalMapper::mappedInt, &target, &QtTestObject::myIntSlot);
connect(&mapper, &QSignalMapper::mappedString, &target, &QtTestObject::myStringSlot);
src1.emit_mysignal(20);
QCOMPARE(target.id, 1);
QVERIFY(target.str.isEmpty());
src2.emit_mysignal(20);
QCOMPARE(target.id, 2);
QCOMPARE(target.str, QLatin1String("two"));
src3.emit_mysignal(20);
QCOMPARE(target.id, 2);
QCOMPARE(target.str, QLatin1String("three"));
}
QTEST_MAIN(tst_QSignalMapper)
#include "tst_qsignalmapper.moc"

View File

@ -0,0 +1,3 @@
[unexpectedDisconnection]
windows ci

View File

@ -0,0 +1,27 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(NOT QT_FEATURE_private_tests)
return()
endif()
#####################################################################
## tst_qsocketnotifier Test:
#####################################################################
qt_internal_add_test(tst_qsocketnotifier
SOURCES
tst_qsocketnotifier.cpp
LIBRARIES
Qt::CorePrivate
Qt::Network
Qt::NetworkPrivate
)
## Scopes:
#####################################################################
qt_internal_extend_target(tst_qsocketnotifier CONDITION WIN32
LIBRARIES
ws2_32
)

View File

@ -0,0 +1,453 @@
// 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 <QtTest/QTest>
#include <QtTest/QSignalSpy>
#include <QtTest/QTestEventLoop>
#include <QtCore/QCoreApplication>
#include <QtCore/QTimer>
#include <QtCore/QSocketNotifier>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QUdpSocket>
#include <private/qnativesocketengine_p.h>
#define NATIVESOCKETENGINE QNativeSocketEngine
#ifdef Q_OS_UNIX
#include <private/qnet_unix_p.h>
#include <sys/select.h>
#endif
#include <limits>
#if defined (Q_CC_MSVC) && defined(max)
# undef max
# undef min
#endif // Q_CC_MSVC
class tst_QSocketNotifier : public QObject
{
Q_OBJECT
private slots:
void constructing();
void unexpectedDisconnection();
void mixingWithTimers();
#ifdef Q_OS_UNIX
void posixSockets();
#endif
void asyncMultipleDatagram();
void activationReason_data();
void activationReason();
void legacyConnect();
protected slots:
void async_readDatagramSlot();
void async_writeDatagramSlot();
private:
QUdpSocket *m_asyncSender;
QUdpSocket *m_asyncReceiver;
};
static QHostAddress makeNonAny(const QHostAddress &address,
QHostAddress::SpecialAddress preferForAny = QHostAddress::LocalHost)
{
if (address == QHostAddress::Any)
return preferForAny;
if (address == QHostAddress::AnyIPv4)
return QHostAddress::LocalHost;
if (address == QHostAddress::AnyIPv6)
return QHostAddress::LocalHostIPv6;
return address;
}
void tst_QSocketNotifier::constructing()
{
const qintptr fd = 15;
// Test constructing with no descriptor assigned.
{
QSocketNotifier notifier(QSocketNotifier::Read);
QVERIFY(!notifier.isValid());
QCOMPARE(notifier.socket(), Q_INT64_C(-1));
QCOMPARE(notifier.type(), QSocketNotifier::Read);
QVERIFY(!notifier.isEnabled());
notifier.setEnabled(true);
QVERIFY(!notifier.isEnabled());
notifier.setSocket(fd);
QVERIFY(notifier.isValid());
QCOMPARE(notifier.socket(), fd);
QVERIFY(!notifier.isEnabled());
notifier.setEnabled(true);
QVERIFY(notifier.isEnabled());
}
// Test constructing with the notifications enabled by default.
{
QSocketNotifier notifier(fd, QSocketNotifier::Write);
QVERIFY(notifier.isValid());
QCOMPARE(notifier.socket(), fd);
QCOMPARE(notifier.type(), QSocketNotifier::Write);
QVERIFY(notifier.isEnabled());
notifier.setSocket(fd);
QVERIFY(!notifier.isEnabled());
notifier.setEnabled(true);
QVERIFY(notifier.isEnabled());
notifier.setSocket(-1);
QVERIFY(!notifier.isValid());
QCOMPARE(notifier.socket(), Q_INT64_C(-1));
QVERIFY(!notifier.isEnabled());
}
}
class UnexpectedDisconnectTester : public QObject
{
Q_OBJECT
public:
NATIVESOCKETENGINE *readEnd1, *readEnd2;
int sequence;
UnexpectedDisconnectTester(NATIVESOCKETENGINE *s1, NATIVESOCKETENGINE *s2)
: readEnd1(s1), readEnd2(s2), sequence(0)
{
QSocketNotifier *notifier1 =
new QSocketNotifier(readEnd1->socketDescriptor(), QSocketNotifier::Read, this);
connect(notifier1, SIGNAL(activated(QSocketDescriptor)), SLOT(handleActivated()));
QSocketNotifier *notifier2 =
new QSocketNotifier(readEnd2->socketDescriptor(), QSocketNotifier::Read, this);
connect(notifier2, SIGNAL(activated(QSocketDescriptor)), SLOT(handleActivated()));
}
public slots:
void handleActivated()
{
char data1[1], data2[1];
++sequence;
if (sequence == 1) {
// read from both ends
(void) readEnd1->read(data1, sizeof(data1));
(void) readEnd2->read(data2, sizeof(data2));
emit finished();
} else if (sequence == 2) {
// we should never get here
QCOMPARE(readEnd2->read(data2, sizeof(data2)), qint64(-2));
QVERIFY(readEnd2->isValid());
}
}
signals:
void finished();
};
void tst_QSocketNotifier::unexpectedDisconnection()
{
/*
Given two sockets and two QSocketNotifiers registered on each
their socket. If both sockets receive data, and the first slot
invoked by one of the socket notifiers empties both sockets, the
other notifier will also emit activated(). This results in
unexpected disconnection in QAbstractSocket.
The use case is that somebody calls one of the
waitFor... functions in a QSocketNotifier activated slot, and
the waitFor... functions do local selects that can empty both
stdin and stderr while waiting for fex bytes to be written.
*/
QTcpServer server;
QVERIFY(server.listen(QHostAddress::LocalHost, 0));
NATIVESOCKETENGINE readEnd1;
readEnd1.initialize(QAbstractSocket::TcpSocket);
readEnd1.connectToHost(server.serverAddress(), server.serverPort());
QVERIFY(readEnd1.waitForWrite());
QCOMPARE(readEnd1.state(), QAbstractSocket::ConnectedState);
QVERIFY(server.waitForNewConnection(5000));
QTcpSocket *writeEnd1 = server.nextPendingConnection();
QVERIFY(writeEnd1 != 0);
NATIVESOCKETENGINE readEnd2;
readEnd2.initialize(QAbstractSocket::TcpSocket);
readEnd2.connectToHost(server.serverAddress(), server.serverPort());
QVERIFY(readEnd2.waitForWrite());
QCOMPARE(readEnd2.state(), QAbstractSocket::ConnectedState);
QVERIFY(server.waitForNewConnection(5000));
QTcpSocket *writeEnd2 = server.nextPendingConnection();
QVERIFY(writeEnd2 != 0);
writeEnd1->write("1", 1);
writeEnd2->write("2", 1);
writeEnd1->waitForBytesWritten();
writeEnd2->waitForBytesWritten();
writeEnd1->flush();
writeEnd2->flush();
UnexpectedDisconnectTester tester(&readEnd1, &readEnd2);
QTimer timer;
timer.setSingleShot(true);
timer.start(30000);
do {
// we have to wait until sequence value changes
// as any event can make us jump out processing
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
QVERIFY(timer.isActive()); //escape if test would hang
} while(tester.sequence <= 0);
QCOMPARE(readEnd1.state(), QAbstractSocket::ConnectedState);
QCOMPARE(readEnd2.state(), QAbstractSocket::ConnectedState);
QCOMPARE(tester.sequence, 2);
readEnd1.close();
readEnd2.close();
writeEnd1->close();
writeEnd2->close();
server.close();
}
class MixingWithTimersHelper : public QObject
{
Q_OBJECT
public:
MixingWithTimersHelper(QTimer *timer, QTcpServer *server);
bool timerActivated;
bool socketActivated;
private slots:
void timerFired();
void socketFired();
};
MixingWithTimersHelper::MixingWithTimersHelper(QTimer *timer, QTcpServer *server)
{
timerActivated = false;
socketActivated = false;
connect(timer, SIGNAL(timeout()), SLOT(timerFired()));
connect(server, SIGNAL(newConnection()), SLOT(socketFired()));
}
void MixingWithTimersHelper::timerFired()
{
timerActivated = true;
}
void MixingWithTimersHelper::socketFired()
{
socketActivated = true;
}
void tst_QSocketNotifier::mixingWithTimers()
{
QTimer timer;
timer.setInterval(0);
timer.start();
QTcpServer server;
QVERIFY(server.listen(QHostAddress::LocalHost, 0));
MixingWithTimersHelper helper(&timer, &server);
QCoreApplication::processEvents();
QCOMPARE(helper.timerActivated, true);
QCOMPARE(helper.socketActivated, false);
helper.timerActivated = false;
helper.socketActivated = false;
QTcpSocket socket;
socket.connectToHost(server.serverAddress(), server.serverPort());
QCoreApplication::processEvents();
QCOMPARE(helper.timerActivated, true);
QTRY_COMPARE(helper.socketActivated, true);
}
#ifdef Q_OS_UNIX
// test only for posix
void tst_QSocketNotifier::posixSockets()
{
QTcpServer server;
QVERIFY(server.listen(QHostAddress::LocalHost, 0));
int posixSocket = qt_safe_socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in addr;
addr.sin_addr.s_addr = htonl(0x7f000001);
addr.sin_family = AF_INET;
addr.sin_port = htons(server.serverPort());
qt_safe_connect(posixSocket, (const struct sockaddr*)&addr, sizeof(sockaddr_in));
QVERIFY(server.waitForNewConnection(5000));
QScopedPointer<QTcpSocket> passive(server.nextPendingConnection());
::fcntl(posixSocket, F_SETFL, ::fcntl(posixSocket, F_GETFL) | O_NONBLOCK);
{
QSocketNotifier rn(posixSocket, QSocketNotifier::Read);
connect(&rn, SIGNAL(activated(QSocketDescriptor)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QSignalSpy readSpy(&rn, &QSocketNotifier::activated);
QVERIFY(readSpy.isValid());
// No write notifier, some systems trigger write notification on socket creation, but not all
QSocketNotifier en(posixSocket, QSocketNotifier::Exception);
connect(&en, SIGNAL(activated(QSocketDescriptor)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QSignalSpy errorSpy(&en, &QSocketNotifier::activated);
QVERIFY(errorSpy.isValid());
passive->write("hello",6);
passive->waitForBytesWritten(5000);
QTestEventLoop::instance().enterLoop(3);
QCOMPARE(readSpy.size(), 1);
QCOMPARE(errorSpy.size(), 0);
char buffer[100];
int r = qt_safe_read(posixSocket, buffer, 100);
QCOMPARE(r, 6);
QCOMPARE(buffer, "hello");
QSocketNotifier wn(posixSocket, QSocketNotifier::Write);
connect(&wn, SIGNAL(activated(QSocketDescriptor)), &QTestEventLoop::instance(), SLOT(exitLoop()));
QSignalSpy writeSpy(&wn, &QSocketNotifier::activated);
QVERIFY(writeSpy.isValid());
qt_safe_write(posixSocket, "goodbye", 8);
QTestEventLoop::instance().enterLoop(3);
QCOMPARE(readSpy.size(), 1);
QCOMPARE(writeSpy.size(), 1);
QCOMPARE(errorSpy.size(), 0);
// Write notifier may have fired before the read notifier inside
// QTcpSocket, give QTcpSocket a chance to see the incoming data
passive->waitForReadyRead(100);
QCOMPARE(passive->readAll(), QByteArray("goodbye",8));
}
qt_safe_close(posixSocket);
}
#endif
void tst_QSocketNotifier::async_readDatagramSlot()
{
char buf[1];
QVERIFY(m_asyncReceiver->hasPendingDatagrams());
do {
QCOMPARE(m_asyncReceiver->pendingDatagramSize(), qint64(1));
QCOMPARE(m_asyncReceiver->readDatagram(buf, sizeof(buf)), qint64(1));
if (buf[0] == '1') {
// wait for the second datagram message.
QTest::qSleep(100);
}
} while (m_asyncReceiver->hasPendingDatagrams());
if (buf[0] == '3')
QTestEventLoop::instance().exitLoop();
}
void tst_QSocketNotifier::async_writeDatagramSlot()
{
m_asyncSender->writeDatagram("3", makeNonAny(m_asyncReceiver->localAddress()),
m_asyncReceiver->localPort());
}
void tst_QSocketNotifier::asyncMultipleDatagram()
{
m_asyncSender = new QUdpSocket;
m_asyncReceiver = new QUdpSocket;
QVERIFY(m_asyncReceiver->bind(QHostAddress(QHostAddress::AnyIPv4), 0));
quint16 port = m_asyncReceiver->localPort();
QVERIFY(port != 0);
QSignalSpy spy(m_asyncReceiver, &QIODevice::readyRead);
connect(m_asyncReceiver, &QIODevice::readyRead, this,
&tst_QSocketNotifier::async_readDatagramSlot);
// activate socket notifiers
QTestEventLoop::instance().enterLoopMSecs(100);
m_asyncSender->writeDatagram("1", makeNonAny(m_asyncReceiver->localAddress()), port);
m_asyncSender->writeDatagram("2", makeNonAny(m_asyncReceiver->localAddress()), port);
// wait a little to ensure that the datagrams we've just sent
// will be delivered on receiver side.
QTest::qSleep(100);
QVERIFY(m_asyncReceiver->hasPendingDatagrams());
QTimer::singleShot(500, this, &tst_QSocketNotifier::async_writeDatagramSlot);
QTestEventLoop::instance().enterLoop(1);
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(spy.size(), 2);
delete m_asyncSender;
delete m_asyncReceiver;
}
void tst_QSocketNotifier::activationReason_data()
{
QTest::addColumn<QSocketNotifier::Type>("type");
QTest::addRow("read") << QSocketNotifier::Read;
QTest::addRow("write") << QSocketNotifier::Write;
QTest::addRow("exception") << QSocketNotifier::Exception;
}
void tst_QSocketNotifier::activationReason()
{
QSocketDescriptor fd = 15;
QFETCH(QSocketNotifier::Type, type);
QSocketNotifier notifier(fd, type);
auto activation = new QEvent(QEvent::SockAct);
QCoreApplication::postEvent(&notifier, activation);
QSocketNotifier::Type notifierType;
connect(&notifier, &QSocketNotifier::activated, this,
[&notifierType, fd](QSocketDescriptor sockfd, QSocketNotifier::Type sntype) {
if (sockfd == fd)
notifierType = sntype;
else
qWarning() << "Got an unexpected socket file descriptor:" << qintptr(sockfd);
});
QCoreApplication::processEvents();
QCOMPARE(notifierType, type);
}
// This test ensures that we can connect QSocketNotifier::activated to a slot taking an integer
// or qintptr.
void tst_QSocketNotifier::legacyConnect()
{
qintptr fd = 15;
QSocketNotifier notifier(fd, QSocketNotifier::Read);
auto activation = new QEvent(QEvent::SockAct);
QCoreApplication::postEvent(&notifier, activation);
bool receivedQIntPtr = false;
connect(&notifier, &QSocketNotifier::activated, this, [&receivedQIntPtr, fd](qintptr q){
if (q == fd)
receivedQIntPtr = true;
});
bool receivedInt = false;
connect(&notifier, &QSocketNotifier::activated, this, [&receivedInt, fd](int q){
if (q == fd)
receivedInt = true;
});
QCoreApplication::processEvents();
QVERIFY(receivedQIntPtr);
QVERIFY(receivedInt);
}
QTEST_MAIN(tst_QSocketNotifier)
#include <tst_qsocketnotifier.moc>

View File

@ -0,0 +1,16 @@
# 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()

View File

@ -0,0 +1,13 @@
# 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
)

View File

@ -0,0 +1,76 @@
// 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;
}

View File

@ -0,0 +1,271 @@
// 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"

View File

@ -0,0 +1,20 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qtimer Test:
#####################################################################
if (NOT QT_FEATURE_thread)
return()
endif()
qt_internal_add_test(tst_qtimer
SOURCES
tst_qtimer.cpp
LIBRARIES
Qt::CorePrivate
)
## Scopes:
#####################################################################

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qtranslator Test:
#####################################################################
if (NOT QT_CONFIG_thread)
return()
endif()
qt_internal_add_test(tst_qtranslator
SOURCES
tst_qtranslator.cpp
)
# Resources:
set(qtranslator_resource_files
"dependencies_la.qm"
"hellotr_empty.qm"
"hellotr_la.qm"
"msgfmt_from_po.qm"
)
qt_internal_add_resource(tst_qtranslator "qtranslator"
PREFIX
"/tst_qtranslator"
FILES
${qtranslator_resource_files}
)
## Scopes:
#####################################################################
if(ANDROID)
# Resources:
set(android_testdata_resource_files
"dependencies_la.qm"
"hellotr_empty.qm"
"hellotr_la.qm"
"msgfmt_from_po.qm"
)
qt_internal_add_resource(tst_qtranslator "android_testdata"
PREFIX
"/android_testdata"
FILES
${android_testdata_resource_files}
)
endif()

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0" language="de">
<dependencies>
<dependency catalog="hellotr_la"/>
</dependencies>
<context>
<name>QPushButton</name>
<message>
<source>It's a small world</source>
<translation>Es ist eine kleine Welt</translation>
</message>
</context>
</TS>

View File

@ -0,0 +1 @@
<<3C>d<18><EFBFBD>!<1C>`<60><><EFBFBD>

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1"/>

Binary file not shown.

View File

@ -0,0 +1,16 @@
<!DOCTYPE TS><TS version="1.1" language="de">
<context>
<name>QPushButton</name>
<message>
<source>Hello world!</source>
<translation>Hallo Welt!</translation>
</message>
<message numerus="yes">
<source>Hello %n world(s)!</source>
<translation>
<numerusform>Hallo %n Welt!</numerusform>
<numerusform>Hallo %n Welten!</numerusform>
</translation>
</message>
</context>
</TS>

View File

@ -0,0 +1,397 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QWaitCondition>
#include <QMutex>
#include <QStandardPaths>
#include <qtranslator.h>
#include <qfile.h>
#include <qtemporarydir.h>
#ifdef Q_OS_ANDROID
#include <QDirIterator>
#endif
class tst_QTranslator : public QObject
{
Q_OBJECT
public:
tst_QTranslator();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private slots:
void initTestCase();
void init();
void load_data();
void load();
void loadLocale();
void threadLoad();
void testLanguageChange();
void plural();
void translate_qm_file_generated_with_msgfmt();
void loadDirectory();
void dependencies();
void translationInThreadWhileInstallingTranslator();
private:
int languageChangeEventCounter;
QSharedPointer<QTemporaryDir> dataDir;
};
tst_QTranslator::tst_QTranslator()
: languageChangeEventCounter(0)
{
qApp->installEventFilter(this);
}
void tst_QTranslator::initTestCase()
{
dataDir = QEXTRACTTESTDATA(QStringLiteral("/tst_qtranslator"));
QVERIFY2(!dataDir.isNull(), qPrintable("Could not extract test data"));
}
void tst_QTranslator::init()
{
QVERIFY2(QDir::setCurrent(dataDir->path()),
qPrintable("Could not chdir to " + dataDir->path()));
}
bool tst_QTranslator::eventFilter(QObject *, QEvent *event)
{
if (event->type() == QEvent::LanguageChange)
++languageChangeEventCounter;
return false;
}
void tst_QTranslator::load_data()
{
QTest::addColumn<QString>("filepath");
QTest::addColumn<bool>("isEmpty");
QTest::addColumn<QString>("translation");
QTest::addColumn<QString>("language");
QTest::newRow("hellotr_la") << "hellotr_la.qm" << false << "Hallo Welt!" << "de";
QTest::newRow("hellotr_empty") << "hellotr_empty.qm" << true << "" << "";
}
void tst_QTranslator::load()
{
QFETCH(QString, filepath);
QFETCH(bool, isEmpty);
QFETCH(QString, translation);
QFETCH(QString, language);
{
QTranslator tor;
QVERIFY(tor.load(QFileInfo(filepath).baseName()));
QCOMPARE(tor.isEmpty(), isEmpty);
QCOMPARE(tor.translate("QPushButton", "Hello world!"), translation);
QCOMPARE(tor.filePath(), filepath);
QCOMPARE(tor.language(), language);
}
{
QFile file(filepath);
file.open(QFile::ReadOnly);
QByteArray data = file.readAll();
QTranslator tor;
QVERIFY(tor.load((const uchar *)data.constData(), data.length()));
QCOMPARE(tor.isEmpty(), isEmpty);
QCOMPARE(tor.translate("QPushButton", "Hello world!"), translation);
QCOMPARE(tor.filePath(), "");
QCOMPARE(tor.language(), language);
}
{
QTranslator tor;
QString path = QString(":/tst_qtranslator/%1").arg(filepath);
QVERIFY(tor.load(path));
QCOMPARE(tor.isEmpty(), isEmpty);
QCOMPARE(tor.translate("QPushButton", "Hello world!"), translation);
QCOMPARE(tor.filePath(), path);
QCOMPARE(tor.language(), language);
}
}
void tst_QTranslator::loadLocale()
{
QLocale locale;
auto localeName = locale.uiLanguages().value(0).replace('-', '_');
if (localeName.isEmpty())
QSKIP("This test requires at least one available UI language.");
QByteArray ba;
{
QFile file(":/tst_qtranslator/hellotr_la.qm");
QVERIFY2(file.open(QFile::ReadOnly), qPrintable(file.errorString()));
ba = file.readAll();
QVERIFY(!ba.isEmpty());
}
QTemporaryDir dir;
QVERIFY(dir.isValid());
auto path = dir.path();
QFile file(path + "/dummy");
QVERIFY2(file.open(QFile::WriteOnly), qPrintable(file.errorString()));
QCOMPARE(file.write(ba), ba.size());
file.close();
/*
Test the following order:
/tmp/tmpDir/foo-en_US.qm
/tmp/tmpDir/foo-en_US
/tmp/tmpDir/foo-en.qm
/tmp/tmpDir/foo-en
/tmp/tmpDir/foo.qm
/tmp/tmpDir/foo-
/tmp/tmpDir/foo
*/
QStringList files;
while (true) {
files.append(path + "/foo-" + localeName + ".qm");
QVERIFY2(file.copy(files.last()), qPrintable(file.errorString()));
files.append(path + "/foo-" + localeName);
QVERIFY2(file.copy(files.last()), qPrintable(file.errorString()));
int rightmost = localeName.lastIndexOf(QLatin1Char('_'));
if (rightmost <= 0)
break;
localeName.truncate(rightmost);
}
files.append(path + "/foo.qm");
QVERIFY2(file.copy(files.last()), qPrintable(file.errorString()));
files.append(path + "/foo-");
QVERIFY2(file.copy(files.last()), qPrintable(file.errorString()));
files.append(path + "/foo");
QVERIFY2(file.rename(files.last()), qPrintable(file.errorString()));
QTranslator tor;
for (const auto &filePath : files) {
QVERIFY(tor.load(locale, "foo", "-", path, ".qm"));
QCOMPARE(tor.filePath(), filePath);
QVERIFY2(file.remove(filePath), qPrintable(file.errorString()));
}
}
class TranslatorThread : public QThread
{
void run() override {
QTranslator tor( 0 );
(void)tor.load("hellotr_la");
if (tor.isEmpty())
qFatal("Could not load translation");
if (tor.translate("QPushButton", "Hello world!") != QLatin1String("Hallo Welt!"))
qFatal("Test string was not translated correctlys");
}
};
void tst_QTranslator::threadLoad()
{
TranslatorThread thread;
thread.start();
QVERIFY(thread.wait(10 * 1000));
}
void tst_QTranslator::testLanguageChange()
{
languageChangeEventCounter = 0;
QTranslator *tor = new QTranslator;
QVERIFY(tor->load("hellotr_la.qm"));
qApp->sendPostedEvents();
qApp->sendPostedEvents();
QCOMPARE(languageChangeEventCounter, 0);
QVERIFY(!tor->load("doesn't exist, same as clearing"));
qApp->sendPostedEvents();
qApp->sendPostedEvents();
QCOMPARE(languageChangeEventCounter, 0);
QVERIFY(tor->load("hellotr_la.qm"));
qApp->sendPostedEvents();
qApp->sendPostedEvents();
QCOMPARE(languageChangeEventCounter, 0);
qApp->installTranslator(tor);
qApp->sendPostedEvents();
qApp->sendPostedEvents();
QCOMPARE(languageChangeEventCounter, 1);
QVERIFY(!tor->load("doesn't exist, same as clearing"));
qApp->sendPostedEvents();
qApp->sendPostedEvents();
QCOMPARE(languageChangeEventCounter, 2);
QVERIFY(tor->load("hellotr_la.qm"));
qApp->sendPostedEvents();
qApp->sendPostedEvents();
QCOMPARE(languageChangeEventCounter, 3);
qApp->removeTranslator(tor);
qApp->sendPostedEvents();
qApp->sendPostedEvents();
QCOMPARE(languageChangeEventCounter, 4);
QVERIFY(!tor->load("doesn't exist, same as clearing"));
qApp->sendPostedEvents();
qApp->sendPostedEvents();
QCOMPARE(languageChangeEventCounter, 4);
qApp->installTranslator(tor);
qApp->sendPostedEvents();
qApp->sendPostedEvents();
QCOMPARE(languageChangeEventCounter, 4);
QVERIFY(tor->load("hellotr_la.qm"));
qApp->sendPostedEvents();
qApp->sendPostedEvents();
QCOMPARE(languageChangeEventCounter, 5);
delete tor;
tor = 0;
qApp->sendPostedEvents();
qApp->sendPostedEvents();
QCOMPARE(languageChangeEventCounter, 6);
}
void tst_QTranslator::plural()
{
QTranslator tor( 0 );
QVERIFY(tor.load("hellotr_la"));
QVERIFY(!tor.isEmpty());
QCoreApplication::installTranslator(&tor);
QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 0), QLatin1String("Hallo 0 Welten!"));
QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 1), QLatin1String("Hallo 1 Welt!"));
QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 2), QLatin1String("Hallo 2 Welten!"));
}
void tst_QTranslator::translate_qm_file_generated_with_msgfmt()
{
QTranslator translator;
QVERIFY(translator.load("msgfmt_from_po"));
qApp->installTranslator(&translator);
QCOMPARE(QCoreApplication::translate("", "Intro"), QLatin1String("Einleitung"));
// The file is converted from a po file, thus it does not have any context info.
// The following should then not be translated
QCOMPARE(QCoreApplication::translate("contekst", "Intro"), QLatin1String("Intro"));
QCOMPARE(QCoreApplication::translate("contekst", "Intro\0\0"), QLatin1String("Intro"));
QCOMPARE(QCoreApplication::translate("contekst", "Intro\0x"), QLatin1String("Intro"));
QCOMPARE(QCoreApplication::translate("", "Intro\0\0"), QLatin1String("Einleitung"));
QCOMPARE(QCoreApplication::translate("", "Intro\0x"), QLatin1String("Einleitung"));
qApp->removeTranslator(&translator);
}
void tst_QTranslator::loadDirectory()
{
QString current_base = QDir::current().dirName();
QVERIFY(QFileInfo("../" + current_base).isDir());
QTranslator tor;
QVERIFY(!tor.load(current_base, ".."));
QVERIFY(tor.isEmpty());
}
void tst_QTranslator::dependencies()
{
{
// load
QTranslator tor;
QVERIFY(tor.load("dependencies_la"));
QVERIFY(!tor.isEmpty());
QCOMPARE(tor.translate("QPushButton", "Hello world!"), QLatin1String("Hallo Welt!"));
// plural
QCoreApplication::installTranslator(&tor);
QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 0), QLatin1String("Hallo 0 Welten!"));
QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 1), QLatin1String("Hallo 1 Welt!"));
QCOMPARE(QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 2), QLatin1String("Hallo 2 Welten!"));
// pick up translation from the file with dependencies
QCOMPARE(tor.translate("QPushButton", "It's a small world"), QLatin1String("Es ist eine kleine Welt"));
}
{
QTranslator tor( 0 );
QFile file("dependencies_la.qm");
file.open(QFile::ReadOnly);
QByteArray data = file.readAll();
QVERIFY(tor.load((const uchar *)data.constData(), data.length()));
QVERIFY(!tor.isEmpty());
QCOMPARE(tor.translate("QPushButton", "Hello world!"), QLatin1String("Hallo Welt!"));
}
{
// Test resolution of paths relative to main file
const QString absoluteFile = QFileInfo("dependencies_la").absoluteFilePath();
QDir::setCurrent(QDir::tempPath());
QTranslator tor;
QVERIFY(tor.load(absoluteFile));
QVERIFY(!tor.isEmpty());
}
}
struct TranslateThread : public QThread
{
bool ok = false;
QAtomicInt terminate;
QMutex startupLock;
QWaitCondition runningCondition;
void run() override {
bool startSignalled = false;
while (terminate.loadRelaxed() == 0) {
const QString result = QCoreApplication::translate("QPushButton", "Hello %n world(s)!", 0, 0);
if (!startSignalled) {
QMutexLocker startupLocker(&startupLock);
runningCondition.wakeAll();
startSignalled = true;
}
ok = (result == QLatin1String("Hallo 0 Welten!"))
|| (result == QLatin1String("Hello 0 world(s)!"));
if (!ok)
break;
}
}
};
void tst_QTranslator::translationInThreadWhileInstallingTranslator()
{
TranslateThread thread;
QMutexLocker startupLocker(&thread.startupLock);
thread.start();
thread.runningCondition.wait(&thread.startupLock);
QTranslator tor;
QVERIFY(tor.load("hellotr_la"));
QVERIFY(QCoreApplication::installTranslator(&tor));
++thread.terminate;
QVERIFY(thread.wait());
QVERIFY(thread.ok);
}
QTEST_MAIN(tst_QTranslator)
#include "tst_qtranslator.moc"

View File

@ -0,0 +1,37 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qvariant Test:
#####################################################################
# Resources:
set(qvariant_resource_files
"stream/qt4.9/"
"stream/qt5.0/"
)
qt_internal_add_test(tst_qvariant
SOURCES
tst_qvariant.cpp
INCLUDE_DIRECTORIES
../../../other/qvariant_common
LIBRARIES
Qt::CorePrivate
Qt::Gui
TESTDATA ${qvariant_resource_files}
BUILTIN_TESTDATA
)
## Scopes:
#####################################################################
qt_internal_extend_target(tst_qvariant CONDITION MSVC
COMPILE_OPTIONS
/bigobj
)
qt_internal_extend_target(tst_qvariant CONDITION NOT QT_FEATURE_doubleconversion AND NOT QT_FEATURE_system_doubleconversion
DEFINES
QT_NO_DOUBLECONVERSION
)

Some files were not shown because too many files have changed in this diff Show More