summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/kernel')
-rw-r--r--tests/auto/corelib/kernel/CMakeLists.txt6
-rw-r--r--tests/auto/corelib/kernel/qapplicationstatic/CMakeLists.txt6
-rw-r--r--tests/auto/corelib/kernel/qapplicationstatic/tst_qapplicationstatic.cpp2
-rw-r--r--tests/auto/corelib/kernel/qchronotimer/.gitignore1
-rw-r--r--tests/auto/corelib/kernel/qchronotimer/CMakeLists.txt33
-rw-r--r--tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp1266
-rw-r--r--tests/auto/corelib/kernel/qcoreapplication/CMakeLists.txt26
-rw-r--r--tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp122
-rw-r--r--tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h7
-rw-r--r--tests/auto/corelib/kernel/qdeadlinetimer/CMakeLists.txt10
-rw-r--r--tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp442
-rw-r--r--tests/auto/corelib/kernel/qelapsedtimer/CMakeLists.txt10
-rw-r--r--tests/auto/corelib/kernel/qelapsedtimer/tst_qelapsedtimer.cpp46
-rw-r--r--tests/auto/corelib/kernel/qeventdispatcher/CMakeLists.txt32
-rw-r--r--tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp134
-rw-r--r--tests/auto/corelib/kernel/qeventloop/BLACKLIST2
-rw-r--r--tests/auto/corelib/kernel/qeventloop/CMakeLists.txt8
-rw-r--r--tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp26
-rw-r--r--tests/auto/corelib/kernel/qjniarray/CMakeLists.txt13
-rw-r--r--tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp165
-rw-r--r--tests/auto/corelib/kernel/qjnienvironment/CMakeLists.txt6
-rw-r--r--tests/auto/corelib/kernel/qjnienvironment/testdata/src/org/qtproject/qt/android/testdata/QtJniEnvironmentTestClass.java2
-rw-r--r--tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp19
-rw-r--r--tests/auto/corelib/kernel/qjniobject/CMakeLists.txt6
-rw-r--r--tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java175
-rw-r--r--tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp664
-rw-r--r--tests/auto/corelib/kernel/qjnitypes/CMakeLists.txt6
-rw-r--r--tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp229
-rw-r--r--tests/auto/corelib/kernel/qmath/CMakeLists.txt8
-rw-r--r--tests/auto/corelib/kernel/qmath/tst_qmath.cpp2
-rw-r--r--tests/auto/corelib/kernel/qmetacontainer/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/kernel/qmetacontainer/tst_qmetacontainer.cpp23
-rw-r--r--tests/auto/corelib/kernel/qmetaenum/CMakeLists.txt8
-rw-r--r--tests/auto/corelib/kernel/qmetaenum/tst_qmetaenum.cpp5
-rw-r--r--tests/auto/corelib/kernel/qmetamethod/CMakeLists.txt10
-rw-r--r--tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp36
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/CMakeLists.txt7
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/forwarddeclared.cpp2
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/forwarddeclared.h2
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp234
-rw-r--r--tests/auto/corelib/kernel/qmetaobjectbuilder/CMakeLists.txt8
-rw-r--r--tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp24
-rw-r--r--tests/auto/corelib/kernel/qmetaproperty/CMakeLists.txt8
-rw-r--r--tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp152
-rw-r--r--tests/auto/corelib/kernel/qmetatype/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/kernel/qmetatype/lib1.cpp2
-rw-r--r--tests/auto/corelib/kernel/qmetatype/lib2.cpp2
-rw-r--r--tests/auto/corelib/kernel/qmetatype/lib_common.cpp2
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp78
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h6
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp42
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype3.cpp2
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype_common.h2
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype_libs.h2
-rw-r--r--tests/auto/corelib/kernel/qmimedata/CMakeLists.txt8
-rw-r--r--tests/auto/corelib/kernel/qmimedata/tst_qmimedata.cpp13
-rw-r--r--tests/auto/corelib/kernel/qobject/CMakeLists.txt10
-rw-r--r--tests/auto/corelib/kernel/qobject/signalbug/CMakeLists.txt2
-rw-r--r--tests/auto/corelib/kernel/qobject/signalbug/signalbug.cpp2
-rw-r--r--tests/auto/corelib/kernel/qobject/signalbug/signalbug.h2
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp535
-rw-r--r--tests/auto/corelib/kernel/qpermission/CMakeLists.txt6
-rw-r--r--tests/auto/corelib/kernel/qpermission/tst_qpermission.cpp157
-rw-r--r--tests/auto/corelib/kernel/qpointer/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp148
-rw-r--r--tests/auto/corelib/kernel/qproperty/CMakeLists.txt8
-rw-r--r--tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp375
-rw-r--r--tests/auto/corelib/kernel/qsignalblocker/CMakeLists.txt8
-rw-r--r--tests/auto/corelib/kernel/qsignalblocker/tst_qsignalblocker.cpp13
-rw-r--r--tests/auto/corelib/kernel/qsignalmapper/CMakeLists.txt8
-rw-r--r--tests/auto/corelib/kernel/qsignalmapper/tst_qsignalmapper.cpp2
-rw-r--r--tests/auto/corelib/kernel/qsocketnotifier/BLACKLIST3
-rw-r--r--tests/auto/corelib/kernel/qsocketnotifier/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp18
-rw-r--r--tests/auto/corelib/kernel/qtimer/BLACKLIST0
-rw-r--r--tests/auto/corelib/kernel/qtimer/CMakeLists.txt38
-rw-r--r--tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp335
-rw-r--r--tests/auto/corelib/kernel/qtranslator/CMakeLists.txt14
-rw-r--r--tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp8
-rw-r--r--tests/auto/corelib/kernel/qvariant/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp808
-rw-r--r--tests/auto/corelib/kernel/qwineventnotifier/CMakeLists.txt8
-rw-r--r--tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp2
-rw-r--r--tests/auto/corelib/kernel/qwinregistrykey/CMakeLists.txt6
-rw-r--r--tests/auto/corelib/kernel/qwinregistrykey/tst_qwinregistrykey.cpp2
85 files changed, 6075 insertions, 640 deletions
diff --git a/tests/auto/corelib/kernel/CMakeLists.txt b/tests/auto/corelib/kernel/CMakeLists.txt
index 46a4e7d72e..c2feb38641 100644
--- a/tests/auto/corelib/kernel/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/CMakeLists.txt
@@ -1,9 +1,8 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from kernel.pro.
-
add_subdirectory(qapplicationstatic)
+add_subdirectory(qchronotimer)
add_subdirectory(qcoreapplication)
add_subdirectory(qdeadlinetimer)
add_subdirectory(qelapsedtimer)
@@ -18,7 +17,7 @@ add_subdirectory(qsignalblocker)
add_subdirectory(qsignalmapper)
add_subdirectory(qtimer)
add_subdirectory(qtranslator)
-# QTBUG-88135 # special case
+# QTBUG-88135
if(NOT ANDROID)
add_subdirectory(qeventdispatcher)
endif()
@@ -51,4 +50,5 @@ if(ANDROID)
add_subdirectory(qjnienvironment)
add_subdirectory(qjniobject)
add_subdirectory(qjnitypes)
+ add_subdirectory(qjniarray)
endif()
diff --git a/tests/auto/corelib/kernel/qapplicationstatic/CMakeLists.txt b/tests/auto/corelib/kernel/qapplicationstatic/CMakeLists.txt
index ad8d5d169a..9332b6ecd0 100644
--- a/tests/auto/corelib/kernel/qapplicationstatic/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qapplicationstatic/CMakeLists.txt
@@ -5,6 +5,12 @@
## tst_qapplicationstatic Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qapplicationstatic LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qapplicationstatic
SOURCES
tst_qapplicationstatic.cpp
diff --git a/tests/auto/corelib/kernel/qapplicationstatic/tst_qapplicationstatic.cpp b/tests/auto/corelib/kernel/qapplicationstatic/tst_qapplicationstatic.cpp
index 987a20a589..dd9a415a52 100644
--- a/tests/auto/corelib/kernel/qapplicationstatic/tst_qapplicationstatic.cpp
+++ b/tests/auto/corelib/kernel/qapplicationstatic/tst_qapplicationstatic.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QPointer>
diff --git a/tests/auto/corelib/kernel/qchronotimer/.gitignore b/tests/auto/corelib/kernel/qchronotimer/.gitignore
new file mode 100644
index 0000000000..254f7a0281
--- /dev/null
+++ b/tests/auto/corelib/kernel/qchronotimer/.gitignore
@@ -0,0 +1 @@
+tst_qchronotimer
diff --git a/tests/auto/corelib/kernel/qchronotimer/CMakeLists.txt b/tests/auto/corelib/kernel/qchronotimer/CMakeLists.txt
new file mode 100644
index 0000000000..43164858c5
--- /dev/null
+++ b/tests/auto/corelib/kernel/qchronotimer/CMakeLists.txt
@@ -0,0 +1,33 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qchronotimer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+if (NOT QT_FEATURE_thread)
+ return()
+endif()
+
+function(addChronoTimerTest test)
+ qt_internal_add_test(${test}
+ SOURCES
+ tst_qchronotimer.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::TestPrivate
+ )
+endfunction()
+
+addChronoTimerTest(tst_qchronotimer)
+
+if(QT_FEATURE_glib AND UNIX)
+ addChronoTimerTest(tst_qchronotimer_no_glib)
+ qt_internal_extend_target(tst_qchronotimer_no_glib
+ DEFINES
+ DISABLE_GLIB
+ tst_QChronoTimer=tst_QChronoTimer_no_glib # Class name in the unittest
+ )
+endif()
diff --git a/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
new file mode 100644
index 0000000000..62c402ae24
--- /dev/null
+++ b/tests/auto/corelib/kernel/qchronotimer/tst_qchronotimer.cpp
@@ -0,0 +1,1266 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifdef QT_GUI_LIB
+# include <QtGui/QGuiApplication>
+#else
+# include <QtCore/QCoreApplication>
+#endif
+
+#include <QtCore/private/qglobal_p.h>
+#include <QTest>
+#include <QSignalSpy>
+#include <QtTest/private/qpropertytesthelper_p.h>
+
+#include <qbasictimer.h>
+#include <qchronotimer.h>
+#include <qthread.h>
+#include <qtimer.h>
+#include <qelapsedtimer.h>
+#include <qproperty.h>
+
+#if defined Q_OS_UNIX
+#include <unistd.h>
+#endif
+
+using namespace std::chrono_literals;
+
+#ifdef DISABLE_GLIB
+static bool glibDisabled = []() {
+ qputenv("QT_NO_GLIB", "1");
+ return true;
+}();
+#endif
+
+class tst_QChronoTimer : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void zeroTimer();
+ void singleShotTimeout(); // Non-static singleShot()
+ void timeout();
+ void sequentialTimers_data();
+ void sequentialTimers();
+ void remainingTime();
+ void remainingTimeInitial_data();
+ void remainingTimeInitial();
+ void remainingTimeDuringActivation_data();
+ void remainingTimeDuringActivation();
+ void basic_chrono();
+ void livelock_data();
+ void livelock();
+ void timerInfiniteRecursion_data();
+ void timerInfiniteRecursion();
+ void recurringTimer_data();
+ void recurringTimer();
+ void deleteLaterOnQChronoTimer(); // long name, don't want to shadow QObject::deleteLater()
+ void moveToThread();
+ void restartedTimerFiresTooSoon();
+ void timerFiresOnlyOncePerProcessEvents_data();
+ void timerFiresOnlyOncePerProcessEvents();
+ void timerIdPersistsAfterThreadExit();
+ void cancelLongTimer();
+ void recurseOnTimeoutAndStopTimer();
+ void timerOrder();
+ void timerOrder_data();
+ void timerOrderBackgroundThread();
+ void timerOrderBackgroundThread_data() { timerOrder_data(); }
+ void timerPrecision();
+
+ void dontBlockEvents();
+ void postedEventsShouldNotStarveTimers();
+ void callOnTimeout();
+
+ void bindToTimer();
+ void bindTimer();
+ void automatedBindingTests();
+
+ void negativeInterval();
+};
+
+void tst_QChronoTimer::zeroTimer()
+{
+ QChronoTimer timer;
+ QVERIFY(!timer.isSingleShot());
+ timer.setInterval(0ns);
+ timer.setSingleShot(true);
+ QVERIFY(timer.isSingleShot());
+
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ timer.start();
+
+ // Pass timeout to work round glib issue, see QTBUG-84291.
+ QCoreApplication::processEvents(QEventLoop::AllEvents, INT_MAX);
+
+ QCOMPARE(timeoutSpy.size(), 1);
+}
+
+void tst_QChronoTimer::singleShotTimeout()
+{
+ QChronoTimer timer;
+ QVERIFY(!timer.isSingleShot());
+ timer.setSingleShot(true);
+ QVERIFY(timer.isSingleShot());
+
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ timer.setInterval(100ms);
+ timer.start();
+
+ QVERIFY(timeoutSpy.wait(500ms));
+ QCOMPARE(timeoutSpy.size(), 1);
+ QTest::qWait(500ms);
+ QCOMPARE(timeoutSpy.size(), 1);
+}
+
+static constexpr auto Timeout_Interval = 200ms;
+
+void tst_QChronoTimer::timeout()
+{
+ QChronoTimer timer{100ms};
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ timer.start();
+
+ QCOMPARE(timeoutSpy.size(), 0);
+
+ QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > 0, Timeout_Interval);
+ const qsizetype oldCount = timeoutSpy.size();
+
+ QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > oldCount, Timeout_Interval);
+}
+
+void tst_QChronoTimer::sequentialTimers_data()
+{
+#ifdef Q_OS_WIN
+ QSKIP("The API used by QEventDispatcherWin32 doesn't respect the order");
+#endif
+ QTest::addColumn<QList<std::chrono::milliseconds>>("timeouts");
+ auto addRow = [](const QList<std::chrono::milliseconds> &l) {
+ Q_ASSERT_X(std::is_sorted(l.begin(), l.end()),
+ "tst_QChronoTimer", "input list must be sorted");
+ QByteArray name;
+ for (auto msec : l)
+ name += QByteArray::number(msec.count()) + ',';
+ name.chop(1);
+ QTest::addRow("%s", name.constData()) << l;
+ };
+ // PreciseTimers
+ addRow({0ms, 0ms, 0ms, 0ms, 0ms, 0ms});
+ addRow({0ms, 1ms, 2ms});
+ addRow({1ms, 1ms, 1ms, 2ms, 2ms, 2ms, 2ms});
+ addRow({1ms, 2ms, 3ms});
+ addRow({19ms, 19ms, 19ms});
+ // CoarseTimer for setinterval
+ addRow({20ms, 20ms, 20ms, 20ms, 20ms});
+ addRow({25ms, 25ms, 25ms, 25ms, 25ms, 25ms, 50ms});
+}
+
+void tst_QChronoTimer::sequentialTimers()
+{
+ QFETCH(const QList<std::chrono::milliseconds>, timeouts);
+ QByteArray result, expected;
+ std::vector<std::unique_ptr<QChronoTimer>> timers;
+ expected.resize(timeouts.size());
+ result.reserve(timeouts.size());
+ timers.reserve(timeouts.size());
+ for (int i = 0; i < timeouts.size(); ++i) {
+ auto timer = std::make_unique<QChronoTimer>(timeouts[i]);
+ timer->setSingleShot(true);
+
+ char c = 'A' + i;
+ expected[i] = c;
+ QObject::connect(timer.get(), &QChronoTimer::timeout, this, [&result, c = c]() {
+ result.append(c);
+ });
+ timers.push_back(std::move(timer));
+ }
+
+ // start the timers
+ for (auto &timer : timers)
+ timer->start();
+
+ QTestEventLoop::instance().enterLoop(timeouts.last() * 2 + 10ms);
+
+ QCOMPARE(result, expected);
+}
+
+void tst_QChronoTimer::remainingTime()
+{
+ QChronoTimer tested;
+ tested.setTimerType(Qt::PreciseTimer);
+
+ QChronoTimer tester;
+ tester.setTimerType(Qt::PreciseTimer);
+ tester.setSingleShot(true);
+
+ constexpr auto tested_interval = 200ms;
+ constexpr auto tester_interval = 50ms;
+ constexpr auto expectedRemainingTime = tested_interval - tester_interval;
+
+ int testIteration = 0;
+ const int desiredTestCount = 2;
+
+ // We let tested (which isn't a single-shot) run repeatedly, to verify
+ // it *does* repeat, and check that the single-shot tester, starting
+ // at the same time, does finish first each time, by about the right duration.
+ auto connection = QObject::connect(&tested, &QChronoTimer::timeout,
+ &tester, &QChronoTimer::start);
+
+ QObject::connect(&tester, &QChronoTimer::timeout, this, [&]() {
+ const std::chrono::nanoseconds remainingTime = tested.remainingTime();
+ // We expect that remainingTime is at most 150 and not overdue.
+ const bool remainingTimeInRange = remainingTime > 0ns
+ && remainingTime <= expectedRemainingTime;
+ if (remainingTimeInRange)
+ ++testIteration;
+ else
+ testIteration = desiredTestCount; // We are going to fail on QVERIFY2()
+ // below, so we don't want to iterate
+ // anymore and quickly exit the QTRY_...()
+ // with this failure.
+ if (testIteration == desiredTestCount)
+ QObject::disconnect(connection); // Last iteration, don't start tester again.
+ QVERIFY2(remainingTimeInRange, qPrintable("Remaining time "
+ + QByteArray::number(remainingTime.count()) + "ms outside expected range (0ns, "
+ + QByteArray::number(expectedRemainingTime.count()) + "ms]"));
+ });
+
+ tested.setInterval(tested_interval);
+ tested.start();
+ tester.setInterval(tester_interval);
+ tester.start(); // Start tester for the 1st time.
+
+ // Test it desiredTestCount times, give it reasonable amount of time
+ // (twice as much as needed).
+ const auto tryTimeout = tested_interval * desiredTestCount * 2;
+ QTRY_COMPARE_WITH_TIMEOUT(testIteration, desiredTestCount, tryTimeout);
+}
+
+void tst_QChronoTimer::remainingTimeInitial_data()
+{
+ using namespace std::chrono;
+
+ QTest::addColumn<nanoseconds>("startTimeNs");
+ QTest::addColumn<Qt::TimerType>("timerType");
+
+ QTest::addRow("precisetiemr-0ns") << 0ns << Qt::PreciseTimer;
+ QTest::addRow("precisetimer-1ms") << nanoseconds{1ms} << Qt::PreciseTimer;
+ QTest::addRow("precisetimer-10ms") <<nanoseconds{10ms} << Qt::PreciseTimer;
+
+ QTest::addRow("coarsetimer-0ns") << 0ns << Qt::CoarseTimer;
+ QTest::addRow("coarsetimer-1ms") << nanoseconds{1ms} << Qt::CoarseTimer;
+ QTest::addRow("coarsetimer-10ms") << nanoseconds{10ms} << Qt::CoarseTimer;
+}
+
+void tst_QChronoTimer::remainingTimeInitial()
+{
+ QFETCH(std::chrono::nanoseconds, startTimeNs);
+ QFETCH(Qt::TimerType, timerType);
+
+ QChronoTimer timer;
+ QCOMPARE(timer.timerType(), Qt::CoarseTimer);
+ timer.setTimerType(timerType);
+ QCOMPARE(timer.timerType(), timerType);
+ timer.setInterval(startTimeNs);
+ timer.start();
+
+ const std::chrono::nanoseconds rt = timer.remainingTime();
+ QCOMPARE_GE(rt, 0ns);
+ QCOMPARE_LE(rt, startTimeNs);
+}
+
+void tst_QChronoTimer::remainingTimeDuringActivation_data()
+{
+ QTest::addColumn<bool>("singleShot");
+ QTest::newRow("repeating") << false;
+ QTest::newRow("single-shot") << true;
+}
+
+void tst_QChronoTimer::remainingTimeDuringActivation()
+{
+ QFETCH(bool, singleShot);
+
+ QChronoTimer timer;
+ timer.setSingleShot(singleShot);
+
+ auto remainingTime = 0ns; // not the expected value in either case
+ connect(&timer, &QChronoTimer::timeout, this, [&]() { remainingTime = timer.remainingTime(); });
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ // 20 ms is short enough and should not round down to 0 in any timer mode
+ constexpr auto timeout = 20ms;
+ timer.setInterval(timeout);
+ timer.start();
+
+ QVERIFY(timeoutSpy.wait());
+ if (singleShot)
+ QCOMPARE_LT(remainingTime, 0ns); // timer not running
+ else {
+ QCOMPARE_LE(remainingTime, timeout);
+ QCOMPARE_GT(remainingTime, 0ns);
+ }
+
+ if (!singleShot) {
+ // do it again - see QTBUG-46940
+ remainingTime = std::chrono::milliseconds::min();
+ QVERIFY(timeoutSpy.wait());
+ QCOMPARE_LE(remainingTime, timeout);
+ QCOMPARE_GT(remainingTime, 0ns);
+ }
+}
+
+namespace {
+ template <typename T>
+ auto to_ms(T t)
+ {
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(t);
+ }
+} // unnamed namespace
+
+void tst_QChronoTimer::basic_chrono()
+{
+ // duplicates zeroTimer, singleShotTimeout, interval and remainingTime
+ using namespace std::chrono;
+ QChronoTimer timer;
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ timer.start();
+ QCOMPARE(timer.interval(), 0ns);
+ QCOMPARE(timer.remainingTime(), 0ns);
+
+ QCoreApplication::processEvents();
+
+ QCOMPARE(timeoutSpy.size(), 1);
+
+ timeoutSpy.clear();
+ timer.setInterval(100ms);
+ timer.start();
+ QCOMPARE(timeoutSpy.size(), 0);
+
+ QVERIFY(timeoutSpy.wait(Timeout_Interval));
+ QVERIFY(timeoutSpy.size() > 0);
+ const qsizetype oldCount = timeoutSpy.size();
+
+ QVERIFY(timeoutSpy.wait(Timeout_Interval));
+ QVERIFY(timeoutSpy.size() > oldCount);
+
+ timeoutSpy.clear();
+ timer.setInterval(200ms);
+ timer.start();
+ QCOMPARE(timer.interval(), 200ms);
+ QTest::qWait(50ms);
+ QCOMPARE(timeoutSpy.size(), 0);
+
+ nanoseconds rt = timer.remainingTime();
+ QCOMPARE_GE(rt, 50ms);
+ QCOMPARE_LE(rt, 200ms);
+
+ timeoutSpy.clear();
+ timer.setSingleShot(true);
+ timer.setInterval(100ms);
+ timer.start();
+ QVERIFY(timeoutSpy.wait(Timeout_Interval));
+ QCOMPARE(timeoutSpy.size(), 1);
+ QTest::qWait(500ms);
+ QCOMPARE(timeoutSpy.size(), 1);
+}
+
+void tst_QChronoTimer::livelock_data()
+{
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
+ QTest::newRow("zero-timer") << 0ns;
+ QTest::newRow("non-zero-timer") << std::chrono::nanoseconds{1ms};
+ QTest::newRow("longer-than-sleep") << std::chrono::nanoseconds{20ms};
+}
+
+/*!
+ *
+ * DO NOT "FIX" THIS TEST! it is written like this for a reason, do
+ * not *change it without first dicussing it with its maintainers.
+ *
+*/
+class LiveLockTester : public QObject
+{
+ static constexpr QEvent::Type PostEventType = static_cast<QEvent::Type>(4002);
+public:
+ LiveLockTester(std::chrono::nanoseconds i)
+ : interval(i)
+ {
+ firstTimerId = startTimer(interval);
+ extraTimerId = startTimer(interval + 80ms);
+ secondTimerId = -1; // started later
+ }
+
+ bool event(QEvent *e) override
+ {
+ if (e->type() == PostEventType) {
+ // got the posted event
+ if (timeoutsForFirst == 1 && timeoutsForSecond == 0)
+ postEventAtRightTime = true;
+ return true;
+ }
+ return QObject::event(e);
+ }
+
+ void timerEvent(QTimerEvent *te) override
+ {
+ if (te->timerId() == firstTimerId) {
+ if (++timeoutsForFirst == 1) {
+ killTimer(extraTimerId);
+ extraTimerId = -1;
+ QCoreApplication::postEvent(this, new QEvent(PostEventType));
+ secondTimerId = startTimer(interval);
+ }
+ } else if (te->timerId() == secondTimerId) {
+ ++timeoutsForSecond;
+ } else if (te->timerId() == extraTimerId) {
+ ++timeoutsForExtra;
+ }
+
+ // sleep for 2ms
+ QTest::qSleep(2);
+ killTimer(te->timerId());
+ }
+
+ const std::chrono::nanoseconds interval;
+ int firstTimerId = -1;
+ int secondTimerId = -1;
+ int extraTimerId = -1;
+ int timeoutsForFirst = 0;
+ int timeoutsForExtra = 0;
+ int timeoutsForSecond = 0;
+ bool postEventAtRightTime = false;
+};
+
+void tst_QChronoTimer::livelock()
+{
+ /*
+ New timers created in timer event handlers should not be sent
+ until the next iteration of the eventloop. Note: this test
+ depends on the fact that we send posted events before timer
+ events (since new posted events are not sent until the next
+ iteration of the eventloop either).
+ */
+ QFETCH(std::chrono::nanoseconds, interval);
+ LiveLockTester tester(interval);
+ QTest::qWait(180ms); // we have to use wait here, since we're testing timers with a non-zero timeout
+ QTRY_COMPARE(tester.timeoutsForFirst, 1);
+ QCOMPARE(tester.timeoutsForExtra, 0);
+ QTRY_COMPARE(tester.timeoutsForSecond, 1);
+ QVERIFY(tester.postEventAtRightTime);
+}
+
+class TimerInfiniteRecursionObject : public QObject
+{
+public:
+ bool inTimerEvent = false;
+ bool timerEventRecursed = false;
+ std::chrono::nanoseconds interval;
+
+ TimerInfiniteRecursionObject(std::chrono::nanoseconds interval)
+ : interval(interval)
+ { }
+
+ void timerEvent(QTimerEvent *timerEvent) override
+ {
+ timerEventRecursed = inTimerEvent;
+ if (timerEventRecursed) {
+ // bug detected!
+ return;
+ }
+
+ inTimerEvent = true;
+
+ QEventLoop eventLoop;
+ QChronoTimer::singleShot(std::max<std::chrono::nanoseconds>(100ms, interval * 2),
+ &eventLoop, &QEventLoop::quit);
+ eventLoop.exec();
+
+ inTimerEvent = false;
+
+ killTimer(timerEvent->timerId());
+ }
+};
+
+void tst_QChronoTimer::timerInfiniteRecursion_data()
+{
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
+ QTest::newRow("zero timer") << 0ns;
+ QTest::newRow("non-zero timer") << std::chrono::nanoseconds{1ms};
+ QTest::newRow("10ms timer") << std::chrono::nanoseconds{10ms};
+ QTest::newRow("11ms timer") << std::chrono::nanoseconds{11ms};
+ QTest::newRow("100ms timer") << std::chrono::nanoseconds{100ms};
+ QTest::newRow("1s timer") << std::chrono::nanoseconds{1000ms};
+}
+
+
+void tst_QChronoTimer::timerInfiniteRecursion()
+{
+ QFETCH(std::chrono::nanoseconds, interval);
+ TimerInfiniteRecursionObject object(interval);
+ (void) object.startTimer(interval);
+
+ QEventLoop eventLoop;
+ QChronoTimer::singleShot(std::max<std::chrono::nanoseconds>(100ms, interval * 2), &eventLoop,
+ &QEventLoop::quit);
+ eventLoop.exec();
+
+ QVERIFY(!object.timerEventRecursed);
+}
+
+class RecurringTimerObject : public QObject
+{
+Q_OBJECT
+public:
+ int times;
+ int target;
+ bool recurse;
+
+ RecurringTimerObject(int target)
+ : times(0), target(target), recurse(false)
+ { }
+
+ void timerEvent(QTimerEvent *timerEvent) override
+ {
+ if (++times == target) {
+ killTimer(timerEvent->timerId());
+ Q_EMIT done();
+ } if (recurse) {
+ QEventLoop eventLoop;
+ QChronoTimer::singleShot(100ms, &eventLoop, &QEventLoop::quit);
+ eventLoop.exec();
+ }
+ }
+
+signals:
+ void done();
+};
+
+void tst_QChronoTimer::recurringTimer_data()
+{
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
+ QTest::addColumn<bool>("recurse");
+ // make sure that eventloop recursion doesn't affect timer recurrence
+ QTest::newRow("zero timer, don't recurse") << 0ns << false;
+ QTest::newRow("zero timer, recurse") << 0ns << true;
+ QTest::newRow("non-zero timer, don't recurse") << std::chrono::nanoseconds{1ms} << false;
+ QTest::newRow("non-zero timer, recurse") << std::chrono::nanoseconds{1ms} << true;
+}
+
+void tst_QChronoTimer::recurringTimer()
+{
+ const int target = 5;
+ QFETCH(std::chrono::nanoseconds, interval);
+ QFETCH(bool, recurse);
+
+ RecurringTimerObject object(target);
+ object.recurse = recurse;
+ QSignalSpy doneSpy(&object, &RecurringTimerObject::done);
+
+ (void) object.startTimer(interval);
+ QVERIFY(doneSpy.wait());
+
+ QCOMPARE(object.times, target);
+}
+
+void tst_QChronoTimer::deleteLaterOnQChronoTimer()
+{
+ QChronoTimer *timer = new QChronoTimer;
+ connect(timer, &QChronoTimer::timeout, timer, &QObject::deleteLater);
+ QSignalSpy destroyedSpy(timer, &QObject::destroyed);
+ timer->setInterval(1ms);
+ timer->setSingleShot(true);
+ timer->start();
+ QPointer<QChronoTimer> pointer = timer;
+ QVERIFY(destroyedSpy.wait());
+ QVERIFY(pointer.isNull());
+}
+
+namespace {
+int operator&(Qt::TimerId id, int i) { return qToUnderlying(id) & i; }
+}
+
+static constexpr auto MoveToThread_Timeout = 200ms;
+static constexpr auto MoveToThread_Wait = 300ms;
+
+void tst_QChronoTimer::moveToThread()
+{
+#if defined(Q_OS_WIN32)
+ QSKIP("Does not work reliably on Windows :(");
+#elif defined(Q_OS_MACOS)
+ QSKIP("Does not work reliably on macOS 10.12+ (QTBUG-59679)");
+#endif
+ QChronoTimer timer1{MoveToThread_Timeout};
+ QChronoTimer timer2{MoveToThread_Timeout};
+ timer1.setSingleShot(true);
+ timer1.start();
+ timer2.start();
+ QVERIFY((timer1.id() & 0xffffff) != (timer2.id() & 0xffffff));
+ QThread tr;
+ timer1.moveToThread(&tr);
+ connect(&timer1, &QChronoTimer::timeout, &tr, &QThread::quit);
+ tr.start();
+ QChronoTimer ti3{MoveToThread_Timeout};
+ ti3.start();
+ QVERIFY((ti3.id() & 0xffffff) != (timer2.id() & 0xffffff));
+ QVERIFY((ti3.id() & 0xffffff) != (timer1.id() & 0xffffff));
+ QTest::qWait(MoveToThread_Wait);
+ QVERIFY(tr.wait());
+ timer2.stop();
+ QChronoTimer ti4{MoveToThread_Timeout};
+ ti4.start();
+ ti3.stop();
+ timer2.setInterval(MoveToThread_Timeout);
+ timer2.start();
+ ti3.setInterval(MoveToThread_Timeout);
+ ti3.start();
+ QVERIFY((ti4.id() & 0xffffff) != (timer2.id() & 0xffffff));
+ QVERIFY((ti3.id() & 0xffffff) != (timer2.id() & 0xffffff));
+ QVERIFY((ti3.id() & 0xffffff) != (timer1.id() & 0xffffff));
+}
+
+class RestartedTimerFiresTooSoonObject : public QObject
+{
+ Q_OBJECT
+
+public:
+ QBasicTimer m_timer;
+
+ std::chrono::milliseconds m_interval = 0ms;
+ QElapsedTimer m_elapsedTimer;
+ QEventLoop eventLoop;
+
+ RestartedTimerFiresTooSoonObject() = default;
+
+ void timerFired()
+ {
+ static std::chrono::milliseconds interval = 1s;
+
+ m_interval = interval;
+ m_elapsedTimer.start();
+ m_timer.start(interval, this);
+
+ // alternate between single-shot and 1 sec
+ interval = interval > 0ms ? 0ms : 1s;
+ }
+
+ void timerEvent(QTimerEvent* ev) override
+ {
+ if (ev->timerId() != m_timer.timerId())
+ return;
+
+ m_timer.stop();
+
+ std::chrono::nanoseconds elapsed = m_elapsedTimer.durationElapsed();
+
+ if (elapsed < m_interval / 2) {
+ // severely too early!
+ m_timer.stop();
+ eventLoop.exit(-1);
+ return;
+ }
+
+ timerFired();
+
+ // don't do this forever
+ static int count = 0;
+ if (count++ > 20) {
+ m_timer.stop();
+ eventLoop.quit();
+ return;
+ }
+ }
+};
+
+void tst_QChronoTimer::restartedTimerFiresTooSoon()
+{
+ RestartedTimerFiresTooSoonObject object;
+ object.timerFired();
+ QCOMPARE(object.eventLoop.exec(), 0);
+}
+
+class LongLastingSlotClass : public QObject
+{
+ Q_OBJECT
+
+public:
+ LongLastingSlotClass(QChronoTimer *timer) : timer(timer) { }
+
+public slots:
+ void longLastingSlot()
+ {
+ // Don't use QChronoTimer for this, because we are testing it.
+ QElapsedTimer control;
+ control.start();
+ while (control.durationElapsed() < 200ms) {
+ for (int c = 0; c < 100'000; c++) {} // Mindless looping.
+ }
+ if (++count >= 2) {
+ timer->stop();
+ }
+ }
+
+public:
+ int count = 0;
+ QChronoTimer *timer;
+};
+
+void tst_QChronoTimer::timerFiresOnlyOncePerProcessEvents_data()
+{
+ QTest::addColumn<std::chrono::nanoseconds>("interval");
+ QTest::newRow("zero-timer") << 0ns;
+ QTest::newRow("non-zero-timer") << std::chrono::nanoseconds{10ms};
+}
+
+void tst_QChronoTimer::timerFiresOnlyOncePerProcessEvents()
+{
+ QFETCH(std::chrono::nanoseconds, interval);
+
+ QChronoTimer t{interval};
+ LongLastingSlotClass longSlot(&t);
+ t.start();
+ connect(&t, &QChronoTimer::timeout, &longSlot, &LongLastingSlotClass::longLastingSlot);
+ // Loop because there may be other events pending.
+ while (longSlot.count == 0)
+ QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
+
+ QCOMPARE(longSlot.count, 1);
+}
+
+class TimerIdPersistsAfterThreadExitThread : public QThread
+{
+public:
+ std::unique_ptr<QChronoTimer> timer;
+ Qt::TimerId timerId = Qt::TimerId::Invalid;
+ int returnValue = -1;
+
+ void run() override
+ {
+ QEventLoop eventLoop;
+ timer = std::make_unique<QChronoTimer>();
+ connect(timer.get(), &QChronoTimer::timeout, &eventLoop, &QEventLoop::quit);
+ timer->setInterval(100ms);
+ timer->start();
+ timerId = timer->id();
+ returnValue = eventLoop.exec();
+ }
+};
+
+void tst_QChronoTimer::timerIdPersistsAfterThreadExit()
+{
+ TimerIdPersistsAfterThreadExitThread thread;
+ thread.start();
+ QVERIFY(thread.wait(30s));
+ QCOMPARE(thread.returnValue, 0);
+
+ // even though the thread has exited, and the event dispatcher destroyed, the timer is still
+ // "active", meaning the timer id should NOT be reused (i.e. the event dispatcher should not
+ // have unregistered it)
+ int timerId = thread.startTimer(100ms);
+ QVERIFY((timerId & 0xffffff) != (thread.timerId & 0xffffff));
+}
+
+void tst_QChronoTimer::cancelLongTimer()
+{
+ QChronoTimer timer{1h};
+ timer.setSingleShot(true);
+ timer.start();
+ QCoreApplication::processEvents();
+ // If the timer completes immediately with an error, then this will fail
+ QVERIFY(timer.isActive());
+ timer.stop();
+ QVERIFY(!timer.isActive());
+}
+
+class TimeoutCounter : public QObject
+{
+ Q_OBJECT
+public slots:
+ void timeout() { ++count; };
+public:
+ int count = 0;
+};
+
+class RecursOnTimeoutAndStopTimerTimer : public QObject
+{
+ Q_OBJECT
+
+public:
+ QChronoTimer *one;
+ QChronoTimer *two;
+
+public slots:
+ void onetrigger()
+ {
+ QCoreApplication::processEvents();
+ }
+
+ void twotrigger()
+ {
+ one->stop();
+ }
+};
+
+void tst_QChronoTimer::recurseOnTimeoutAndStopTimer()
+{
+ QEventLoop eventLoop;
+ QChronoTimer::singleShot(1s, &eventLoop, &QEventLoop::quit);
+
+ RecursOnTimeoutAndStopTimerTimer t;
+ t.one = new QChronoTimer(&t);
+ t.two = new QChronoTimer(&t);
+
+ QObject::connect(t.one, SIGNAL(timeout()), &t, SLOT(onetrigger()));
+ QObject::connect(t.two, SIGNAL(timeout()), &t, SLOT(twotrigger()));
+
+ t.two->setSingleShot(true);
+
+ t.one->start();
+ t.two->start();
+
+ (void) eventLoop.exec();
+
+ QVERIFY(!t.one->isActive());
+ QVERIFY(!t.two->isActive());
+}
+
+struct CountedStruct
+{
+ CountedStruct(int *count, QThread *t = nullptr) : count(count), thread(t) { }
+ ~CountedStruct() { }
+ void operator()() const { ++(*count); if (thread) QCOMPARE(QThread::currentThread(), thread); }
+
+ int *count;
+ QThread *thread;
+};
+
+static QScopedPointer<QEventLoop> _e;
+static QThread *_t = nullptr;
+
+class StaticEventLoop
+{
+public:
+ static void quitEventLoop()
+ {
+ quitEventLoop_noexcept();
+ }
+
+ static void quitEventLoop_noexcept() noexcept
+ {
+ QVERIFY(!_e.isNull());
+ _e->quit();
+ if (_t)
+ QCOMPARE(QThread::currentThread(), _t);
+ }
+};
+
+class DontBlockEvents : public QObject
+{
+ Q_OBJECT
+public:
+ DontBlockEvents();
+ void timerEvent(QTimerEvent*) override;
+
+ int count;
+ int total;
+ QBasicTimer m_timer;
+
+public slots:
+ void paintEvent();
+
+};
+
+DontBlockEvents::DontBlockEvents()
+{
+ count = 0;
+ total = 0;
+
+ const std::chrono::milliseconds intervals[] = {2s, 2500ms, 3s, 5s, 1s, 2s};
+ // need a few unrelated timers running to reproduce the bug.
+ for (auto dur : intervals) {
+ auto *t = new QChronoTimer(dur, this);
+ t->start();
+ }
+
+ m_timer.start(1ms, this);
+}
+
+void DontBlockEvents::timerEvent(QTimerEvent* event)
+{
+ if (event->timerId() == m_timer.timerId()) {
+ QMetaObject::invokeMethod(this, &DontBlockEvents::paintEvent, Qt::QueuedConnection);
+ m_timer.start(0ms, this);
+ count++;
+ QCOMPARE(count, 1);
+ total++;
+ }
+}
+
+void DontBlockEvents::paintEvent()
+{
+ count--;
+ QCOMPARE(count, 0);
+}
+
+// This is a regression test for QTBUG-13633, where a timer with a zero
+// timeout that was restarted by the event handler could starve other timers.
+void tst_QChronoTimer::dontBlockEvents()
+{
+ DontBlockEvents t;
+ QTest::qWait(60ms);
+ QTRY_VERIFY(t.total > 2);
+}
+
+class SlotRepeater : public QObject {
+ Q_OBJECT
+public:
+ SlotRepeater() {}
+
+public slots:
+ void repeatThisSlot()
+ {
+ QMetaObject::invokeMethod(this, &SlotRepeater::repeatThisSlot, Qt::QueuedConnection);
+ }
+};
+
+void tst_QChronoTimer::postedEventsShouldNotStarveTimers()
+{
+ QChronoTimer timer;
+ timer.setInterval(0ns);
+ timer.setSingleShot(false);
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ timer.start();
+ SlotRepeater slotRepeater;
+ slotRepeater.repeatThisSlot();
+ QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > 5, 100);
+}
+
+struct DummyFunctor {
+ static QThread *callThread;
+ void operator()() {
+ callThread = QThread::currentThread();
+ callThread->quit();
+ }
+};
+QThread *DummyFunctor::callThread = nullptr;
+
+void tst_QChronoTimer::callOnTimeout()
+{
+ QChronoTimer timer;
+ QSignalSpy timeoutSpy(&timer, &QChronoTimer::timeout);
+ timer.start();
+
+ auto context = std::make_unique<QObject>();
+
+ int count = 0;
+ timer.callOnTimeout([&count] { count++; });
+ QMetaObject::Connection connection = timer.callOnTimeout(context.get(), [&count] { count++; });
+ timer.callOnTimeout(&timer, &QChronoTimer::stop);
+
+
+ QTest::qWait(100ms);
+ QCOMPARE(count, 2);
+ QCOMPARE(timeoutSpy.size(), 1);
+
+ // Test that connection is bound to context lifetime
+ QVERIFY(connection);
+ context.reset();
+ QVERIFY(!connection);
+}
+
+void tst_QChronoTimer::bindToTimer()
+{
+ QChronoTimer timer;
+
+ // singleShot property
+ QProperty<bool> singleShot;
+ singleShot.setBinding(timer.bindableSingleShot().makeBinding());
+ QCOMPARE(timer.isSingleShot(), singleShot);
+
+ timer.setSingleShot(true);
+ QVERIFY(singleShot);
+ timer.setSingleShot(false);
+ QVERIFY(!singleShot);
+
+ // interval property
+ QProperty<std::chrono::nanoseconds> interval;
+ interval.setBinding([&](){ return timer.interval(); });
+ QCOMPARE(timer.interval(), interval.value());
+
+ timer.setInterval(10ms);
+ QCOMPARE(interval.value(), 10ms);
+ timer.setInterval(100ms);
+ QCOMPARE(interval.value(), 100ms);
+
+ // timerType property
+ QProperty<Qt::TimerType> timerType;
+ timerType.setBinding(timer.bindableTimerType().makeBinding());
+ QCOMPARE(timer.timerType(), timerType);
+
+ timer.setTimerType(Qt::PreciseTimer);
+ QCOMPARE(timerType, Qt::PreciseTimer);
+
+ timer.setTimerType(Qt::VeryCoarseTimer);
+ QCOMPARE(timerType, Qt::VeryCoarseTimer);
+
+ // active property
+ QProperty<bool> active;
+ active.setBinding([&](){ return timer.isActive(); });
+ QCOMPARE(active, timer.isActive());
+
+ timer.setInterval(1s);
+ timer.start();
+ QVERIFY(active);
+
+ timer.stop();
+ QVERIFY(!active);
+
+ // Also test that using negative interval updates the binding correctly
+ timer.setInterval(100ms);
+ timer.start();
+ QVERIFY(active);
+
+ auto ignoreMsg = [] {
+ QTest::ignoreMessage(QtWarningMsg,
+ "QObject::startTimer: Timers cannot have negative intervals");
+ };
+
+ ignoreMsg();
+ timer.setInterval(-100ms);
+ ignoreMsg();
+ timer.start();
+ QVERIFY(!active);
+
+ timer.setInterval(100ms);
+ timer.start();
+ QVERIFY(active);
+
+ ignoreMsg();
+ timer.setInterval(-100ms);
+ ignoreMsg();
+ timer.start();
+ QVERIFY(!active);
+}
+
+void tst_QChronoTimer::bindTimer()
+{
+ QChronoTimer timer;
+
+ // singleShot property
+ QVERIFY(!timer.isSingleShot());
+
+ QProperty<bool> singleShot;
+ timer.bindableSingleShot().setBinding(Qt::makePropertyBinding(singleShot));
+
+ singleShot = true;
+ QVERIFY(timer.isSingleShot());
+ singleShot = false;
+ QVERIFY(!timer.isSingleShot());
+
+ // interval property
+ QCOMPARE(timer.interval(), 0ns);
+
+ QProperty<std::chrono::nanoseconds> interval;
+ timer.bindableInterval().setBinding(Qt::makePropertyBinding(interval));
+
+ interval = 10ms;
+ QCOMPARE(timer.interval(), 10ms);
+ interval = 100ms;
+ QCOMPARE(timer.interval(), 100ms);
+ timer.setInterval(50ms);
+ QCOMPARE(timer.interval(), 50ms);
+ interval = 30ms;
+ QCOMPARE(timer.interval(), 50ms);
+
+ // timerType property
+ QCOMPARE(timer.timerType(), Qt::CoarseTimer);
+
+ QProperty<Qt::TimerType> timerType;
+ timer.bindableTimerType().setBinding(Qt::makePropertyBinding(timerType));
+
+ timerType = Qt::PreciseTimer;
+ QCOMPARE(timer.timerType(), Qt::PreciseTimer);
+ timerType = Qt::VeryCoarseTimer;
+ QCOMPARE(timer.timerType(), Qt::VeryCoarseTimer);
+}
+
+void tst_QChronoTimer::automatedBindingTests()
+{
+ QChronoTimer timer;
+
+ QVERIFY(!timer.isSingleShot());
+ QTestPrivate::testReadWritePropertyBasics(timer, true, false, "singleShot");
+ if (QTest::currentTestFailed()) {
+ qDebug("Failed property test for QChronoTimer::singleShot");
+ return;
+ }
+
+ QCOMPARE_NE(timer.interval(), 10ms);
+ using NSec = std::chrono::nanoseconds;
+ QTestPrivate::testReadWritePropertyBasics(timer, NSec{10ms}, NSec{20ms}, "interval");
+ if (QTest::currentTestFailed()) {
+ qDebug("Failed property test for QChronoTimer::interval");
+ return;
+ }
+
+ QCOMPARE_NE(timer.timerType(), Qt::PreciseTimer);
+ QTestPrivate::testReadWritePropertyBasics(timer, Qt::PreciseTimer, Qt::CoarseTimer,
+ "timerType");
+ if (QTest::currentTestFailed()) {
+ qDebug("Failed property test for QChronoTimer::timerType");
+ return;
+ }
+
+ timer.setInterval(1s);
+ timer.start();
+ QVERIFY(timer.isActive());
+ QTestPrivate::testReadOnlyPropertyBasics(timer, true, false, "active",
+ [&timer]() { timer.stop(); });
+ if (QTest::currentTestFailed()) {
+ qDebug("Failed property test for QChronoTimer::active");
+ return;
+ }
+}
+
+void tst_QChronoTimer::negativeInterval()
+{
+ QChronoTimer timer;
+
+ auto ignoreMsg = [] {
+ QTest::ignoreMessage(QtWarningMsg,
+ "QObject::startTimer: Timers cannot have negative intervals");
+ };
+
+ ignoreMsg();
+ // Setting a negative interval does not change the active state.
+ timer.setInterval(-100ms);
+ ignoreMsg();
+ timer.start();
+ QVERIFY(!timer.isActive());
+
+ // Starting a timer that has a positive interval, the active state is changed
+ timer.setInterval(100ms);
+ timer.start();
+ QVERIFY(timer.isActive());
+
+ ignoreMsg();
+ // Setting a negative interval on an already running timer...
+ timer.setInterval(-100ms);
+ // ... the timer is stopped and the active state is changed
+ QVERIFY(!timer.isActive());
+
+ // Calling start on a timer that has a negative interval, does not change the active state
+ timer.start();
+ QVERIFY(!timer.isActive());
+}
+
+class OrderHelper : public QObject
+{
+ Q_OBJECT
+public:
+ enum CallType
+ {
+ String,
+ PMF,
+ Functor,
+ FunctorNoCtx
+ };
+ Q_ENUM(CallType)
+ QList<CallType> calls;
+
+ void triggerCall(CallType callType)
+ {
+ switch (callType)
+ {
+ case String:
+ QChronoTimer::singleShot(0ns, this, SLOT(stringSlot()));
+ break;
+ case PMF:
+ QChronoTimer::singleShot(0ns, this, &OrderHelper::pmfSlot);
+ break;
+ case Functor:
+ QChronoTimer::singleShot(0ns, this, [this]() { functorSlot(); });
+ break;
+ case FunctorNoCtx:
+ QChronoTimer::singleShot(0ns, [this]() { functorNoCtxSlot(); });
+ break;
+ }
+ }
+
+public slots:
+ void stringSlot() { calls << String; }
+ void pmfSlot() { calls << PMF; }
+ void functorSlot() { calls << Functor; }
+ void functorNoCtxSlot() { calls << FunctorNoCtx; }
+};
+
+Q_DECLARE_METATYPE(OrderHelper::CallType)
+
+void tst_QChronoTimer::timerOrder()
+{
+ QFETCH(QList<OrderHelper::CallType>, calls);
+
+ OrderHelper helper;
+
+ for (const auto call : calls)
+ helper.triggerCall(call);
+
+ QTRY_COMPARE(helper.calls, calls);
+}
+
+void tst_QChronoTimer::timerOrder_data()
+{
+ QTest::addColumn<QList<OrderHelper::CallType>>("calls");
+
+ QList<OrderHelper::CallType> calls = {
+ OrderHelper::String, OrderHelper::PMF,
+ OrderHelper::Functor, OrderHelper::FunctorNoCtx
+ };
+ std::sort(calls.begin(), calls.end());
+
+ int permutation = 0;
+ do {
+ QTest::addRow("permutation=%d", permutation) << calls;
+ ++permutation;
+ } while (std::next_permutation(calls.begin(), calls.end()));
+}
+
+void tst_QChronoTimer::timerOrderBackgroundThread()
+{
+ auto *thread = QThread::create([this]() { timerOrder(); });
+ thread->start();
+ QVERIFY(thread->wait());
+ delete thread;
+}
+
+void tst_QChronoTimer::timerPrecision()
+{
+ using namespace std::chrono;
+ steady_clock::time_point t1{};
+ steady_clock::time_point t2{};
+
+ QEventLoop loop;
+
+ QChronoTimer zeroTimer{0ns};
+ zeroTimer.setTimerType(Qt::PreciseTimer);
+ zeroTimer.setSingleShot(true);
+ connect(&zeroTimer, &QChronoTimer::timeout, this, [&t1] { t1 = steady_clock::now(); });
+
+ QChronoTimer oneNSecTimer{1ns};
+ oneNSecTimer.setTimerType(Qt::PreciseTimer);
+ oneNSecTimer.setSingleShot(true);
+ connect(&oneNSecTimer, &QChronoTimer::timeout, this, [&t2, &loop] {
+ t2 = steady_clock::now();
+ loop.quit();
+ });
+
+ zeroTimer.start();
+ oneNSecTimer.start();
+ loop.exec();
+ QCOMPARE_GT(t2, t1);
+ // qDebug() << "t2 - t1" << duration<double, std::chrono::milliseconds::period>{t2 - t1};
+}
+
+QTEST_MAIN(tst_QChronoTimer)
+
+#include "tst_qchronotimer.moc"
diff --git a/tests/auto/corelib/kernel/qcoreapplication/CMakeLists.txt b/tests/auto/corelib/kernel/qcoreapplication/CMakeLists.txt
index d51f857a4a..8f9783088c 100644
--- a/tests/auto/corelib/kernel/qcoreapplication/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qcoreapplication/CMakeLists.txt
@@ -1,7 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qcoreapplication.pro.
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qcoreapplication LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
if(NOT QT_FEATURE_private_tests)
return()
@@ -11,43 +15,25 @@ endif()
## tst_qcoreapplication Test:
#####################################################################
-# special case begin
if (WIN32)
set(target_version "1.2.3.4")
else()
set(target_version "1.2.3")
endif()
-# special case end
qt_internal_add_test(tst_qcoreapplication
- VERSION ${target_version} # special case
+ VERSION ${target_version}
SOURCES
tst_qcoreapplication.cpp tst_qcoreapplication.h
LIBRARIES
Qt::CorePrivate
)
-# special case begin
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()
-# special case end
if (ANDROID)
set_property(TARGET tst_qcoreapplication PROPERTY QT_ANDROID_VERSION_NAME ${target_version})
endif()
-
-
-#### Keys ignored in scope 1:.:.:qcoreapplication.pro:<TRUE>:
-# QMAKE_INFO_PLIST = "$$PWD/Info.plist"
-# _REQUIREMENTS = "qtConfig(private_tests)"
-
-## Scopes:
-#####################################################################
-
-#### Keys ignored in scope 2:.:.:qcoreapplication.pro:WIN32:
-# VERSION = "1.2.3.4"
-
-#### Keys ignored in scope 3:.:.:qcoreapplication.pro:else:
-# VERSION = "1.2.3"
diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp
index 46d52c2340..8f8ab33e64 100644
--- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp
+++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp
@@ -1,6 +1,6 @@
// 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qcoreapplication.h"
@@ -9,6 +9,7 @@
#include <private/qabstracteventdispatcher_p.h> // for qGlobalPostedEventsCount()
#include <private/qcoreapplication_p.h>
+#include <private/qcoreevent_p.h>
#include <private/qeventloop_p.h>
#include <private/qthread_p.h>
@@ -24,9 +25,12 @@ class EventSpy : public QObject
public:
QList<int> recordedEvents;
- bool eventFilter(QObject *, QEvent *event) override
+ std::function<void(QObject *, QEvent *)> eventCallback;
+ bool eventFilter(QObject *target, QEvent *event) override
{
recordedEvents.append(event->type());
+ if (eventCallback)
+ eventCallback(target, event);
return false;
}
};
@@ -1034,19 +1038,127 @@ void tst_QCoreApplication::addRemoveLibPaths()
}
#endif
+static bool theMainThreadIsSet()
+{
+ // QCoreApplicationPrivate::mainThread() has a Q_ASSERT we'd trigger
+ return QCoreApplicationPrivate::theMainThreadId.loadRelaxed() != nullptr;
+}
+
+static bool theMainThreadWasUnset = !theMainThreadIsSet(); // global static
+void tst_QCoreApplication::theMainThread()
+{
+ QVERIFY2(theMainThreadWasUnset, "Something set the theMainThread before main()");
+ QVERIFY(theMainThreadIsSet()); // we have at LEAST one QObject alive: tst_QCoreApplication
+
+ int argc = 1;
+ char *argv[] = { const_cast<char*>(QTest::currentAppName()) };
+ TestApplication app(argc, argv);
+ QVERIFY(QCoreApplicationPrivate::theMainThreadId.loadRelaxed());
+ QVERIFY(QThread::isMainThread());
+ QCOMPARE(app.thread(), thread());
+ QCOMPARE(app.thread(), QThread::currentThread());
+}
+
static void createQObjectOnDestruction()
{
- // Make sure that we can create a QObject after the last QObject has been
- // destroyed (especially after QCoreApplication has).
- //
+ // Make sure that we can create a QObject (and thus have an associated
+ // QThread) after the last QObject has been destroyed (especially after
+ // QCoreApplication has).
+
+#if !defined(QT_QGUIAPPLICATIONTEST) && !defined(Q_OS_WIN)
+ // QCoreApplicationData's global static destructor has run and cleaned up
+ // the QAdoptedThread.
+ if (theMainThreadIsSet())
+ qFatal("theMainThreadIsSet() returned true; some QObject must have leaked");
+#endif
+
// Before the fixes, this would cause a dangling pointer dereference. If
// the problem comes back, it's possible that the following causes no
// effect.
QObject obj;
obj.thread()->setProperty("testing", 1);
+ if (!theMainThreadIsSet())
+ qFatal("theMainThreadIsSet() returned false");
+
+ // because we created a QObject after QCoreApplicationData was destroyed,
+ // the QAdoptedThread won't get cleaned up
}
Q_DESTRUCTOR_FUNCTION(createQObjectOnDestruction)
+void tst_QCoreApplication::testDeleteLaterFromBeforeOutermostEventLoop()
+{
+ int argc = 0;
+ QCoreApplication app(argc, nullptr);
+
+ EventSpy *spy = new EventSpy();
+ QPointer<QObject> spyPointer = spy;
+
+ app.installEventFilter(spy);
+ spy->eventCallback = [spy](QObject *, QEvent *event) {
+ if (event->type() == QEvent::User + 1)
+ spy->deleteLater();
+ };
+
+ QCoreApplication::postEvent(&app, new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+
+ QEventLoop loop;
+ QTimer::singleShot(0, &loop, &QEventLoop::quit);
+ loop.exec();
+ QVERIFY(!spyPointer);
+}
+
+void tst_QCoreApplication::setIndividualAttributes_data()
+{
+ QTest::addColumn<Qt::ApplicationAttribute>("attribute");
+
+ const QMetaEnum &metaEnum = Qt::staticMetaObject.enumerator(Qt::staticMetaObject.indexOfEnumerator("ApplicationAttribute"));
+ // - 1 to avoid AA_AttributeCount.
+ for (int i = 0; i < metaEnum.keyCount(); ++i) {
+ const auto attribute = static_cast<Qt::ApplicationAttribute>(metaEnum.value(i));
+ if (attribute == Qt::AA_AttributeCount)
+ continue;
+
+ QTest::addRow("%s", metaEnum.key(i)) << attribute;
+ }
+}
+
+void tst_QCoreApplication::setIndividualAttributes()
+{
+ QFETCH(Qt::ApplicationAttribute, attribute);
+
+ const auto originalValue = QCoreApplication::testAttribute(attribute);
+ auto cleanup = qScopeGuard([=]() {
+ QCoreApplication::setAttribute(attribute, originalValue);
+ });
+
+ QCoreApplication::setAttribute(attribute, true);
+ QVERIFY(QCoreApplication::testAttribute(attribute));
+
+ QCoreApplication::setAttribute(attribute, false);
+ QVERIFY(!QCoreApplication::testAttribute(attribute));
+}
+
+void tst_QCoreApplication::setMultipleAttributes()
+{
+ const auto originalDontUseNativeMenuWindowsValue = QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuWindows);
+ const auto originalDisableSessionManagerValue = QCoreApplication::testAttribute(Qt::AA_DisableSessionManager);
+ auto cleanup = qScopeGuard([=]() {
+ QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, originalDontUseNativeMenuWindowsValue);
+ QCoreApplication::setAttribute(Qt::AA_DisableSessionManager, originalDisableSessionManagerValue);
+ });
+
+ QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, true);
+ QCoreApplication::setAttribute(Qt::AA_DisableSessionManager, true);
+ QVERIFY(QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuWindows));
+ QVERIFY(QCoreApplication::testAttribute(Qt::AA_DisableSessionManager));
+
+ QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
+ QCoreApplication::setAttribute(Qt::AA_DisableSessionManager, false);
+ QVERIFY(!QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuWindows));
+ QVERIFY(!QCoreApplication::testAttribute(Qt::AA_DisableSessionManager));
+}
+
#ifndef QT_QGUIAPPLICATIONTEST
QTEST_APPLESS_MAIN(tst_QCoreApplication)
#endif
diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h
index 0894b64f33..1c25f63534 100644
--- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h
+++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h
@@ -1,6 +1,6 @@
// 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QCOREAPPLICATION_H
#define TST_QCOREAPPLICATION_H
@@ -44,6 +44,11 @@ private slots:
#if QT_CONFIG(library)
void addRemoveLibPaths();
#endif
+ void theMainThread();
+ void testDeleteLaterFromBeforeOutermostEventLoop();
+ void setIndividualAttributes_data();
+ void setIndividualAttributes();
+ void setMultipleAttributes();
};
#endif // TST_QCOREAPPLICATION_H
diff --git a/tests/auto/corelib/kernel/qdeadlinetimer/CMakeLists.txt b/tests/auto/corelib/kernel/qdeadlinetimer/CMakeLists.txt
index 30c9d3f9a4..2031cd9d48 100644
--- a/tests/auto/corelib/kernel/qdeadlinetimer/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qdeadlinetimer/CMakeLists.txt
@@ -1,13 +1,19 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qdeadlinetimer.pro.
-
#####################################################################
## tst_qdeadlinetimer Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qdeadlinetimer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qdeadlinetimer
SOURCES
tst_qdeadlinetimer.cpp
+ LIBRARIES
+ Qt::TestPrivate
)
diff --git a/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp b/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp
index ddb765cf82..79416faaf9 100644
--- a/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp
+++ b/tests/auto/corelib/kernel/qdeadlinetimer/tst_qdeadlinetimer.cpp
@@ -1,25 +1,42 @@
// Copyright (C) 2016 Intel Corporation.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QString>
#include <QtCore/QTime>
#include <QtCore/QDeadlineTimer>
#include <QtCore/QElapsedTimer>
#include <QTest>
+#include <QtTest/private/qcomparisontesthelper_p.h>
#include <QTimer>
#include <chrono>
+#include <inttypes.h>
static const int minResolution = 400; // the minimum resolution for the tests
-Q_DECLARE_METATYPE(Qt::TimerType)
+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;
+}
+}
+QT_END_NAMESPACE
class tst_QDeadlineTimer : public QObject
{
Q_OBJECT
private Q_SLOTS:
- void initTestCase_data();
+ void compareCompiles();
void basics();
void foreverness();
void current();
@@ -30,12 +47,11 @@ private Q_SLOTS:
void stdchrono();
};
-void tst_QDeadlineTimer::initTestCase_data()
+static constexpr auto timerType = Qt::PreciseTimer;
+
+void tst_QDeadlineTimer::compareCompiles()
{
- qRegisterMetaType<Qt::TimerType>();
- QTest::addColumn<Qt::TimerType>("timerType");
- QTest::newRow("precise") << Qt::PreciseTimer;
- QTest::newRow("coarse") << Qt::CoarseTimer;
+ QTestPrivate::testAllComparisonOperatorsCompile<QDeadlineTimer>();
}
void tst_QDeadlineTimer::basics()
@@ -43,20 +59,22 @@ 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()));
- QVERIFY(deadline <= QDeadlineTimer());
- QVERIFY(deadline >= QDeadlineTimer());
+ QCOMPARE_LE(deadline, QDeadlineTimer());
+ QCOMPARE_GE(deadline, QDeadlineTimer());
QVERIFY(!(deadline > QDeadlineTimer()));
QVERIFY(!(deadline < deadline));
- QVERIFY(deadline <= deadline);
- QVERIFY(deadline >= deadline);
+ QCOMPARE_LE(deadline, deadline);
+ QCOMPARE_GE(deadline, deadline);
QVERIFY(!(deadline > deadline));
+ QT_TEST_ALL_COMPARISON_OPS(deadline, QDeadlineTimer(timerType), Qt::strong_ordering::equal);
+ QT_TEST_ALL_COMPARISON_OPS(deadline, QDeadlineTimer(), Qt::strong_ordering::equal);
+ QT_TEST_ALL_COMPARISON_OPS(QDeadlineTimer(), QDeadlineTimer(), Qt::strong_ordering::equal);
// should have expired, but we may be running too early after boot
QTRY_VERIFY_WITH_TIMEOUT(deadline.hasExpired(), 100);
@@ -69,18 +87,18 @@ void tst_QDeadlineTimer::basics()
deadline.setRemainingTime(0, timerType);
QCOMPARE(deadline.remainingTime(), qint64(0));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(0));
- QVERIFY(deadline.deadline() != 0);
- QVERIFY(deadline.deadline() != std::numeric_limits<qint64>::max());
- QVERIFY(deadline.deadlineNSecs() != 0);
- QVERIFY(deadline.deadlineNSecs() != std::numeric_limits<qint64>::max());
+ 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));
- QVERIFY(deadline.deadline() != 0);
- QVERIFY(deadline.deadline() != std::numeric_limits<qint64>::max());
- QVERIFY(deadline.deadlineNSecs() != 0);
- QVERIFY(deadline.deadlineNSecs() != std::numeric_limits<qint64>::max());
+ 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));
@@ -97,9 +115,6 @@ void tst_QDeadlineTimer::basics()
void tst_QDeadlineTimer::foreverness()
{
- QFETCH_GLOBAL(Qt::TimerType, timerType);
- // we don't check whether timerType() is our type since it's possible it detects it's forever
-
QDeadlineTimer deadline = QDeadlineTimer::Forever;
QCOMPARE(deadline.timerType(), Qt::CoarseTimer);
QVERIFY(deadline.isForever());
@@ -159,9 +174,10 @@ void tst_QDeadlineTimer::foreverness()
QCOMPARE(deadline, deadline);
QVERIFY(!(deadline < deadline));
- QVERIFY(deadline <= deadline);
- QVERIFY(deadline >= deadline);
+ QCOMPARE_LE(deadline, deadline);
+ QCOMPARE_GE(deadline, deadline);
QVERIFY(!(deadline > deadline));
+ QT_TEST_ALL_COMPARISON_OPS(deadline, deadline, Qt::strong_ordering::equal);
// adding to forever must still be forever
QDeadlineTimer deadline2 = deadline + 1;
@@ -176,9 +192,10 @@ void tst_QDeadlineTimer::foreverness()
QCOMPARE(deadline2 - deadline, qint64(0));
QCOMPARE(deadline2, deadline);
QVERIFY(!(deadline2 < deadline));
- QVERIFY(deadline2 <= deadline);
- QVERIFY(deadline2 >= deadline);
+ QCOMPARE_LE(deadline2, deadline);
+ QCOMPARE_GE(deadline2, deadline);
QVERIFY(!(deadline2 > deadline));
+ QT_TEST_ALL_COMPARISON_OPS(deadline2, deadline, Qt::strong_ordering::equal);
// subtracting from forever is *also* forever
deadline2 = deadline - 1;
@@ -193,33 +210,34 @@ void tst_QDeadlineTimer::foreverness()
QCOMPARE(deadline2 - deadline, qint64(0));
QCOMPARE(deadline2, deadline);
QVERIFY(!(deadline2 < deadline));
- QVERIFY(deadline2 <= deadline);
- QVERIFY(deadline2 >= deadline);
+ QCOMPARE_LE(deadline2, deadline);
+ QCOMPARE_GE(deadline2, deadline);
QVERIFY(!(deadline2 > deadline));
+ QT_TEST_ALL_COMPARISON_OPS(deadline2, deadline, Qt::strong_ordering::equal);
// compare and order against a default-constructed object
QDeadlineTimer expired;
QVERIFY(!(deadline == expired));
- QVERIFY(deadline != expired);
+ QCOMPARE_NE(deadline, expired);
QVERIFY(!(deadline < expired));
QVERIFY(!(deadline <= expired));
- QVERIFY(deadline >= expired);
- QVERIFY(deadline > expired);
+ QCOMPARE_GE(deadline, expired);
+ QCOMPARE_GT(deadline, expired);
+ QT_TEST_EQUALITY_OPS(deadline, expired, false);
}
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));
- QVERIFY(deadline.deadline() != 0);
- QVERIFY(deadline.deadline() != std::numeric_limits<qint64>::max());
- QVERIFY(deadline.deadlineNSecs() != 0);
- QVERIFY(deadline.deadlineNSecs() != std::numeric_limits<qint64>::max());
+ 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;
@@ -228,122 +246,123 @@ void tst_QDeadlineTimer::current()
QCOMPARE(earlierDeadline.timerType(), timerType);
QCOMPARE(earlierDeadline.remainingTime(), qint64(0));
QCOMPARE(earlierDeadline.remainingTimeNSecs(), qint64(0));
- QVERIFY(earlierDeadline.deadline() != 0);
- QVERIFY(earlierDeadline.deadline() != std::numeric_limits<qint64>::max());
- QVERIFY(earlierDeadline.deadlineNSecs() != 0);
- QVERIFY(earlierDeadline.deadlineNSecs() != std::numeric_limits<qint64>::max());
+ 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));
- QVERIFY(earlierDeadline != deadline);
- QVERIFY(earlierDeadline < deadline);
- QVERIFY(earlierDeadline <= deadline);
+ QCOMPARE_NE(earlierDeadline, deadline);
+ QCOMPARE_LT(earlierDeadline, deadline);
+ QCOMPARE_LE(earlierDeadline, deadline);
QVERIFY(!(earlierDeadline >= deadline));
QVERIFY(!(earlierDeadline > deadline));
+ QT_TEST_ALL_COMPARISON_OPS(earlierDeadline, deadline, Qt::strong_ordering::less);
}
void tst_QDeadlineTimer::deadlines()
{
- QFETCH_GLOBAL(Qt::TimerType, timerType);
-
QDeadlineTimer deadline(4 * minResolution, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
- QVERIFY(deadline.remainingTime() > (3 * minResolution));
- QVERIFY(deadline.remainingTime() <= (4 * minResolution));
- QVERIFY(deadline.remainingTimeNSecs() > (3000000 * minResolution));
- QVERIFY(deadline.remainingTimeNSecs() <= (4000000 * minResolution));
- QVERIFY(deadline.deadline() != 0);
- QVERIFY(deadline.deadline() != std::numeric_limits<qint64>::max());
- QVERIFY(deadline.deadlineNSecs() != 0);
- QVERIFY(deadline.deadlineNSecs() != std::numeric_limits<qint64>::max());
+ 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);
- QVERIFY(deadline.remainingTime() > (3 * minResolution));
- QVERIFY(deadline.remainingTime() <= (4 * minResolution));
- QVERIFY(deadline.remainingTimeNSecs() > (3000000 * minResolution));
- QVERIFY(deadline.remainingTimeNSecs() <= (4000000 * minResolution));
- QVERIFY(deadline.deadline() != 0);
- QVERIFY(deadline.deadline() != std::numeric_limits<qint64>::max());
- QVERIFY(deadline.deadlineNSecs() != 0);
- QVERIFY(deadline.deadlineNSecs() != std::numeric_limits<qint64>::max());
+ 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);
- QVERIFY(deadline.remainingTime() > (3 * minResolution));
- QVERIFY(deadline.remainingTime() <= (4 * minResolution));
- QVERIFY(deadline.remainingTimeNSecs() > (3000000 * minResolution));
- QVERIFY(deadline.remainingTimeNSecs() <= (4000000 * minResolution));
- QVERIFY(deadline.deadline() != 0);
- QVERIFY(deadline.deadline() != std::numeric_limits<qint64>::max());
- QVERIFY(deadline.deadlineNSecs() != 0);
- QVERIFY(deadline.deadlineNSecs() != std::numeric_limits<qint64>::max());
+ 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);
- QVERIFY(deadline.remainingTime() > (1000 - minResolution));
- QVERIFY(deadline.remainingTime() <= 1000);
- QVERIFY(deadline.remainingTimeNSecs() > (1000 - minResolution)*1000*1000);
- QVERIFY(deadline.remainingTimeNSecs() <= (1000*1000*1000));
- QVERIFY(deadline.deadline() != 0);
- QVERIFY(deadline.deadline() != std::numeric_limits<qint64>::max());
- QVERIFY(deadline.deadlineNSecs() != 0);
- QVERIFY(deadline.deadlineNSecs() != std::numeric_limits<qint64>::max());
+ 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);
- QVERIFY(laterDeadline.remainingTime() > (1000 - minResolution));
- QVERIFY(laterDeadline.remainingTime() <= 1001);
- QVERIFY(laterDeadline.remainingTimeNSecs() > (1001 - minResolution)*1000*1000);
- QVERIFY(laterDeadline.remainingTimeNSecs() <= (1001*1000*1000));
- QVERIFY(laterDeadline.deadline() != 0);
- QVERIFY(laterDeadline.deadline() != std::numeric_limits<qint64>::max());
- QVERIFY(laterDeadline.deadlineNSecs() != 0);
- QVERIFY(laterDeadline.deadlineNSecs() != std::numeric_limits<qint64>::max());
+ 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));
- QVERIFY(laterDeadline != deadline);
+ QCOMPARE_NE(laterDeadline, deadline);
QVERIFY(!(laterDeadline < deadline));
QVERIFY(!(laterDeadline <= deadline));
- QVERIFY(laterDeadline >= deadline);
- QVERIFY(laterDeadline > deadline);
+ QCOMPARE_GE(laterDeadline, deadline);
+ QCOMPARE_GT(laterDeadline, deadline);
+ QT_TEST_ALL_COMPARISON_OPS(laterDeadline, deadline, Qt::strong_ordering::greater);
// compare and order against a default-constructed object
QDeadlineTimer expired;
QVERIFY(!(deadline == expired));
- QVERIFY(deadline != expired);
+ QCOMPARE_NE(deadline, expired);
QVERIFY(!(deadline < expired));
QVERIFY(!(deadline <= expired));
- QVERIFY(deadline >= expired);
- QVERIFY(deadline > expired);
+ QCOMPARE_GE(deadline, expired);
+ QCOMPARE_GT(deadline, expired);
+ QT_TEST_EQUALITY_OPS(deadline, expired, false);
// compare and order against a forever deadline
QDeadlineTimer forever_(QDeadlineTimer::Forever);
+ QT_TEST_EQUALITY_OPS(deadline, forever_, false);
QVERIFY(!(deadline == forever_));
- QVERIFY(deadline != forever_);
- QVERIFY(deadline < 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;
@@ -371,10 +390,10 @@ void tst_QDeadlineTimer::setDeadline()
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
- QVERIFY(deadline.remainingTime() > (3 * minResolution));
- QVERIFY(deadline.remainingTime() <= (4 * minResolution));
- QVERIFY(deadline.remainingTimeNSecs() > (3000000 * minResolution));
- QVERIFY(deadline.remainingTimeNSecs() <= (4000000 * minResolution));
+ 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!
@@ -385,17 +404,16 @@ void tst_QDeadlineTimer::setDeadline()
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
- QVERIFY(deadline.remainingTime() > (3 * minResolution));
- QVERIFY(deadline.remainingTime() <= (4 * minResolution));
- QVERIFY(deadline.remainingTimeNSecs() > (3000000 * minResolution));
- QVERIFY(deadline.remainingTimeNSecs() <= (4000000 * minResolution));
+ 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());
@@ -453,27 +471,48 @@ void tst_QDeadlineTimer::overflow()
// 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;
- QVERIFY(difference >= 0); // Should always be true, but just in case
- QVERIFY(difference <= callTimer.nsecsElapsed()); // Ideally difference should be 0 exactly
+ 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);
+ QVERIFY(deadline.isForever()); // The above could underflow, so make sure we did set to Forever
+ QCOMPARE(deadline.remainingTimeNSecs(), -1);
+ QCOMPARE(deadline.remainingTime(), -1);
// If the timer is saturated we don't want to get a valid number of milliseconds
- QVERIFY(deadline.deadline() == std::numeric_limits<qint64>::min());
+ QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
// Check that the conversion to milliseconds and nanoseconds underflows gracefully
deadline.setPreciseDeadline(std::numeric_limits<qint64>::min() / 10, 0, timerType);
- QVERIFY(!deadline.isForever()); // On Win/macOS the above underflows, make sure we don't saturate to Forever
+ QVERIFY(!deadline.isForever()); // The above underflows, make sure we don't saturate to Forever
QVERIFY(deadline.deadline() == std::numeric_limits<qint64>::min());
QVERIFY(deadline.deadlineNSecs() == std::numeric_limits<qint64>::min());
+
+ // Check that subtracting max() twice doesn't make it become positive
+ deadline.setPreciseDeadline(0);
+ deadline -= std::numeric_limits<qint64>::max();
+ deadline -= std::numeric_limits<qint64>::max();
+ QVERIFY(!deadline.isForever());
+ QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::min());
+ QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::min());
+
+ // Ditto for adding max()
+ deadline.setPreciseDeadline(0);
+ deadline += std::numeric_limits<qint64>::max();
+ deadline += std::numeric_limits<qint64>::max();
+ QVERIFY(deadline.isForever()); // it's so far in the future it's effectively forever
+ QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
+ QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
+
+ // But we don't un-become forever after saturation
+ deadline -= std::numeric_limits<qint64>::max();
+ QVERIFY(deadline.isForever());
+ QCOMPARE(deadline.deadline(), std::numeric_limits<qint64>::max());
+ QCOMPARE(deadline.deadlineNSecs(), std::numeric_limits<qint64>::max());
}
void tst_QDeadlineTimer::expire()
{
- QFETCH_GLOBAL(Qt::TimerType, timerType);
-
QDeadlineTimer deadline(minResolution, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
@@ -484,17 +523,17 @@ void tst_QDeadlineTimer::expire()
QCOMPARE(deadline.remainingTime(), qint64(0));
QCOMPARE(deadline.remainingTimeNSecs(), qint64(0));
- QVERIFY(deadline.deadline() != 0);
- QVERIFY(deadline.deadline() != std::numeric_limits<qint64>::max());
- QVERIFY(deadline.deadlineNSecs() != 0);
- QVERIFY(deadline.deadlineNSecs() != std::numeric_limits<qint64>::max());
+ 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);
+ using namespace std::chrono_literals;
// create some forevers
QDeadlineTimer deadline = milliseconds::max();
@@ -567,131 +606,134 @@ void tst_QDeadlineTimer::stdchrono()
QTRY_VERIFY2_WITH_TIMEOUT(timersExecuted,
"Looks like timers didn't fire on time.", 4 * minResolution);
-#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || (defined(Q_CC_MSVC) && Q_CC_MSVC >= 1900)
{
- // We know for these OS/compilers that the std::chrono::steady_clock uses the same
- // reference time as QDeadlineTimer
qint64 before = duration_cast<nanoseconds>(steady_before.time_since_epoch()).count();
qint64 after = duration_cast<nanoseconds>(steady_after.time_since_epoch()).count();
- QVERIFY2(now.deadlineNSecs() > before, QByteArray::number(now.deadlineNSecs()) +
- " > " + QByteArray::number(before));
- QVERIFY2(now.deadlineNSecs() < after, QByteArray::number(now.deadlineNSecs()) +
- " < " + QByteArray::number(after));
+ QCOMPARE_GT(now.deadlineNSecs(), before);
+ QCOMPARE_LT(now.deadlineNSecs(), after);
}
-#endif
+
{
auto diff = duration_cast<milliseconds>(steady_after - steady_deadline);
- QVERIFY2(diff.count() > minResolution / 2, QByteArray::number(qint64(diff.count())));
- QVERIFY2(diff.count() < 3 * minResolution / 2, QByteArray::number(qint64(diff.count())));
+ QCOMPARE_GT(diff.count(), minResolution / 2);
+ QCOMPARE_LT(diff.count(), 3 * minResolution / 2);
QDeadlineTimer dt_after(steady_after, timerType);
- QVERIFY2(now < dt_after,
- ("now = " + QLocale().toString(now.deadlineNSecs()) +
- "; after = " + QLocale().toString(dt_after.deadlineNSecs())).toLatin1());
+ QCOMPARE_LT(now, dt_after);
+ QT_TEST_ALL_COMPARISON_OPS(now, dt_after, Qt::strong_ordering::less);
diff = duration_cast<milliseconds>(steady_deadline - steady_before);
- QVERIFY2(diff.count() > minResolution / 2, QByteArray::number(qint64(diff.count())));
- QVERIFY2(diff.count() < 3 * minResolution / 2, QByteArray::number(qint64(diff.count())));
+ QCOMPARE_GT(diff.count(), minResolution / 2);
+ QCOMPARE_LT(diff.count(), 3 * minResolution / 2);
QDeadlineTimer dt_before(steady_before, timerType);
- QVERIFY2(now > dt_before,
- ("now = " + QLocale().toString(now.deadlineNSecs()) +
- "; before = " + QLocale().toString(dt_before.deadlineNSecs())).toLatin1());
+ QCOMPARE_GT(now, dt_before);
+ QT_TEST_ALL_COMPARISON_OPS(now, dt_before, Qt::strong_ordering::greater);
}
{
auto diff = duration_cast<milliseconds>(system_after - system_deadline);
- QVERIFY2(diff.count() > minResolution / 2, QByteArray::number(qint64(diff.count())));
- QVERIFY2(diff.count() < 3 * minResolution / 2, QByteArray::number(qint64(diff.count())));
+ QCOMPARE_GT(diff.count(), minResolution / 2);
+ QCOMPARE_LT(diff.count(), 3 * minResolution / 2);
QDeadlineTimer dt_after(system_after, timerType);
- QVERIFY2(now < dt_after,
- ("now = " + QLocale().toString(now.deadlineNSecs()) +
- "; after = " + QLocale().toString(dt_after.deadlineNSecs())).toLatin1());
+ QCOMPARE_LT(now, dt_after);
+ QT_TEST_ALL_COMPARISON_OPS(now, dt_after, Qt::strong_ordering::less);
diff = duration_cast<milliseconds>(system_deadline - system_before);
- QVERIFY2(diff.count() > minResolution / 2, QByteArray::number(qint64(diff.count())));
- QVERIFY2(diff.count() < 3 * minResolution / 2, QByteArray::number(qint64(diff.count())));
+ QCOMPARE_GT(diff.count(), minResolution / 2);
+ QCOMPARE_LT(diff.count(), 3 * minResolution / 2);
QDeadlineTimer dt_before(system_before, timerType);
- QVERIFY2(now > dt_before,
- ("now = " + QLocale().toString(now.deadlineNSecs()) +
- "; before = " + QLocale().toString(dt_before.deadlineNSecs())).toLatin1());
+ QCOMPARE_GT(now, dt_before);
+ QT_TEST_ALL_COMPARISON_OPS(now, dt_before, Qt::strong_ordering::greater);
}
// make it regular
now = QDeadlineTimer::current(timerType);
- deadline.setRemainingTime(milliseconds(4 * minResolution), timerType);
+ deadline.setRemainingTime(4ms * minResolution, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
- QVERIFY(deadline.remainingTimeAsDuration() > milliseconds(3 * minResolution));
- QVERIFY(deadline.remainingTimeAsDuration() < milliseconds(5 * minResolution));
- QVERIFY(deadline.remainingTimeAsDuration() > nanoseconds(3000000 * minResolution));
- QVERIFY(deadline.remainingTimeAsDuration() < nanoseconds(5000000 * minResolution));
- QVERIFY(deadline.deadline<steady_clock>() > (steady_clock::now() + milliseconds(3 * minResolution)));
- QVERIFY(deadline.deadline<steady_clock>() < (steady_clock::now() + milliseconds(5 * minResolution)));
- QVERIFY(deadline.deadline<system_clock>() > (system_clock::now() + milliseconds(3 * minResolution)));
- QVERIFY(deadline.deadline<system_clock>() < (system_clock::now() + milliseconds(5 * minResolution)));
- if (timerType == Qt::CoarseTimer) {
- QVERIFY(deadline > (now + milliseconds(3 * minResolution)));
- QVERIFY(deadline < (now + milliseconds(5 * minResolution)));
- QVERIFY(deadline > (now + nanoseconds(3000000 * minResolution)));
- QVERIFY(deadline < (now + nanoseconds(5000000 * minResolution)));
- QVERIFY(deadline > milliseconds(3 * minResolution));
- QVERIFY(deadline < milliseconds(5 * minResolution));
- QVERIFY(deadline > nanoseconds(3000000 * minResolution));
- QVERIFY(deadline < nanoseconds(5000000 * minResolution));
- QVERIFY(deadline >= steady_clock::now());
- QVERIFY(deadline >= system_clock::now());
- }
+ QCOMPARE_GT(deadline.remainingTimeAsDuration(), 3ms * minResolution);
+ QCOMPARE_LT(deadline.remainingTimeAsDuration(), 5ms * minResolution);
+ QCOMPARE_GT(deadline.remainingTimeAsDuration(), 3'000'000ns * minResolution);
+ QCOMPARE_LT(deadline.remainingTimeAsDuration(), 5'000'000ns * minResolution);
+ QCOMPARE_GT(deadline.deadline<steady_clock>(), (steady_clock::now() + 3ms * minResolution));
+ QCOMPARE_LT(deadline.deadline<steady_clock>(), (steady_clock::now() + 5ms * minResolution));
+ QCOMPARE_GT(deadline.deadline<system_clock>(), (system_clock::now() + 3ms * minResolution));
+ QCOMPARE_LT(deadline.deadline<system_clock>(), (system_clock::now() + 5ms * minResolution));
+ QCOMPARE_GT((deadline.deadline<steady_clock, milliseconds>()),
+ steady_clock::now() + 3ms * minResolution);
+ QCOMPARE_LT((deadline.deadline<steady_clock, milliseconds>()),
+ steady_clock::now() + 5ms * minResolution);
+ QCOMPARE_GT((deadline.deadline<system_clock, milliseconds>()),
+ system_clock::now() + 3ms * minResolution);
+ QCOMPARE_LT((deadline.deadline<system_clock, milliseconds>()),
+ system_clock::now() + 5ms * minResolution);
+ QCOMPARE_GT(deadline, now + 3ms * minResolution);
+ QCOMPARE_LT(deadline, now + 5ms * minResolution);
+ QCOMPARE_GT(deadline, now + 3000000ns * minResolution);
+ QCOMPARE_LT(deadline, now + 5000000ns * minResolution);
+ QCOMPARE_GT(deadline, 3ms * minResolution);
+ QCOMPARE_LT(deadline, 5ms * minResolution);
+ QCOMPARE_GT(deadline, 3000000ns * minResolution);
+ QCOMPARE_LT(deadline, 5000000ns * minResolution);
+ QCOMPARE_GE(deadline, steady_clock::now());
+ QCOMPARE_GE(deadline, system_clock::now());
+ QT_TEST_ALL_COMPARISON_OPS(deadline, now + 3ms * minResolution, Qt::strong_ordering::greater);
+ QT_TEST_ALL_COMPARISON_OPS(deadline, now + 5ms * minResolution, Qt::strong_ordering::less);
+ QT_TEST_ALL_COMPARISON_OPS(deadline, now + 3000000ns * minResolution, Qt::strong_ordering::greater);
+ QT_TEST_ALL_COMPARISON_OPS(deadline, now + 5000000ns * minResolution, Qt::strong_ordering::less);
+ QT_TEST_ALL_COMPARISON_OPS(deadline, 3ms * minResolution, Qt::strong_ordering::greater);
+ QT_TEST_ALL_COMPARISON_OPS(deadline, 5ms * minResolution, Qt::strong_ordering::less);
+ QT_TEST_ALL_COMPARISON_OPS(deadline, steady_clock::now(), Qt::strong_ordering::greater);
+ QT_TEST_ALL_COMPARISON_OPS(deadline, system_clock::now(), Qt::strong_ordering::greater);
now = QDeadlineTimer::current(timerType);
- deadline = QDeadlineTimer(seconds(1), timerType);
+ deadline = QDeadlineTimer(1s, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
- QVERIFY(deadline.remainingTimeAsDuration() > (seconds(1) - milliseconds(minResolution)));
- QVERIFY(deadline.remainingTimeAsDuration() <= seconds(1));
- QVERIFY(deadline.deadline<steady_clock>() > (steady_clock::now() + seconds(1) - milliseconds(minResolution)));
- QVERIFY(deadline.deadline<steady_clock>() <= (steady_clock::now() + seconds(1) + milliseconds(minResolution)));
- QVERIFY(deadline.deadline<system_clock>() > (system_clock::now() + seconds(1) - milliseconds(minResolution)));
- QVERIFY(deadline.deadline<system_clock>() <= (system_clock::now() + seconds(1) + milliseconds(minResolution)));
- if (timerType == Qt::CoarseTimer) {
- QVERIFY(deadline > (seconds(1) - milliseconds(minResolution)));
- QVERIFY(deadline <= seconds(1));
- }
+ QCOMPARE_GT(deadline.remainingTimeAsDuration(), 1s - 1ms * minResolution);
+ QCOMPARE_LE(deadline.remainingTimeAsDuration(), 1s);
+ QCOMPARE_GT(deadline.deadline<steady_clock>(), steady_clock::now() + 1s - 1ms * minResolution);
+ QCOMPARE_LE(deadline.deadline<steady_clock>(), steady_clock::now() + 1s + 1ms * minResolution);
+ QCOMPARE_GT(deadline.deadline<system_clock>(), system_clock::now() + 1s - 1ms * minResolution);
+ QCOMPARE_LE(deadline.deadline<system_clock>(), system_clock::now() + 1s + 1ms * minResolution);
+ QCOMPARE_GT(deadline, 1s - 1ms * minResolution);
+ QCOMPARE_LE(deadline, 1s);
now = QDeadlineTimer::current(timerType);
- deadline.setRemainingTime(hours(1), timerType);
+ deadline.setRemainingTime(1h, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
- QVERIFY(deadline.remainingTimeAsDuration() > (hours(1) - milliseconds(minResolution)));
- QVERIFY(deadline.remainingTimeAsDuration() <= hours(1));
- QVERIFY(deadline.deadline<steady_clock>() > (steady_clock::now() + hours(1) - milliseconds(minResolution)));
- QVERIFY(deadline.deadline<steady_clock>() <= (steady_clock::now() + hours(1) + milliseconds(minResolution)));
- QVERIFY(deadline.deadline<system_clock>() > (system_clock::now() + hours(1) - milliseconds(minResolution)));
- QVERIFY(deadline.deadline<system_clock>() <= (system_clock::now() + hours(1) + milliseconds(minResolution)));
+ QCOMPARE_GT(deadline.remainingTimeAsDuration(), 1h - 1ms * minResolution);
+ QCOMPARE_LE(deadline.remainingTimeAsDuration(), 1h);
+ QCOMPARE_GT(deadline.deadline<steady_clock>(), steady_clock::now() + 1h - 1ms * minResolution);
+ QCOMPARE_LE(deadline.deadline<steady_clock>(), steady_clock::now() + 1h + 1ms * minResolution);
+ QCOMPARE_GT(deadline.deadline<system_clock>(), system_clock::now() + 1h - 1ms * minResolution);
+ QCOMPARE_LE(deadline.deadline<system_clock>(), system_clock::now() + 1h + 1ms * minResolution);
now = QDeadlineTimer::current(timerType);
- deadline.setDeadline(system_clock::now() + seconds(1), timerType);
+ deadline.setDeadline(system_clock::now() + 1s, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
- QVERIFY(deadline.remainingTimeAsDuration() > (seconds(1) - milliseconds(minResolution)));
- QVERIFY(deadline.remainingTimeAsDuration() <= seconds(1));
- QVERIFY(deadline.deadline<steady_clock>() > (steady_clock::now() + seconds(1) - milliseconds(minResolution)));
- QVERIFY(deadline.deadline<steady_clock>() <= (steady_clock::now() + seconds(1) + milliseconds(minResolution)));
- QVERIFY(deadline.deadline<system_clock>() > (system_clock::now() + seconds(1) - milliseconds(minResolution)));
- QVERIFY(deadline.deadline<system_clock>() <= (system_clock::now() + seconds(1) + milliseconds(minResolution)));
+ QCOMPARE_GT(deadline.remainingTimeAsDuration(), 1s - 1ms * minResolution);
+ QCOMPARE_LE(deadline.remainingTimeAsDuration(), 1s);
+ QCOMPARE_GT(deadline.deadline<steady_clock>(), steady_clock::now() + 1s - 1ms * minResolution);
+ QCOMPARE_LE(deadline.deadline<steady_clock>(), steady_clock::now() + 1s + 1ms * minResolution);
+ QCOMPARE_GT(deadline.deadline<system_clock>(), system_clock::now() + 1s - 1ms * minResolution);
+ QCOMPARE_LE(deadline.deadline<system_clock>(), system_clock::now() + 1s + 1ms * minResolution);
now = QDeadlineTimer::current(timerType);
- deadline.setDeadline(steady_clock::now() + seconds(1), timerType);
+ deadline.setDeadline(steady_clock::now() + 1s, timerType);
QVERIFY(!deadline.hasExpired());
QVERIFY(!deadline.isForever());
QCOMPARE(deadline.timerType(), timerType);
- QVERIFY(deadline.remainingTimeAsDuration() > (seconds(1) - milliseconds(minResolution)));
- QVERIFY(deadline.remainingTimeAsDuration() <= seconds(1));
- QVERIFY(deadline.deadline<steady_clock>() > (steady_clock::now() + seconds(1) - milliseconds(minResolution)));
- QVERIFY(deadline.deadline<steady_clock>() <= (steady_clock::now() + seconds(1) + milliseconds(minResolution)));
- QVERIFY(deadline.deadline<system_clock>() > (system_clock::now() + seconds(1) - milliseconds(minResolution)));
- QVERIFY(deadline.deadline<system_clock>() <= (system_clock::now() + seconds(1) + milliseconds(minResolution)));
+ QCOMPARE_GT(deadline.remainingTimeAsDuration(), 1s - 1ms * minResolution);
+ QCOMPARE_LE(deadline.remainingTimeAsDuration(), 1s);
+ QCOMPARE_GT(deadline.deadline<steady_clock>(), steady_clock::now() + 1s - 1ms * minResolution);
+ QCOMPARE_LE(deadline.deadline<steady_clock>(), steady_clock::now() + 1s + 1ms * minResolution);
+ QCOMPARE_GT(deadline.deadline<system_clock>(), system_clock::now() + 1s - 1ms * minResolution);
+ QCOMPARE_LE(deadline.deadline<system_clock>(), system_clock::now() + 1s + 1ms * minResolution);
}
QTEST_MAIN(tst_QDeadlineTimer)
diff --git a/tests/auto/corelib/kernel/qelapsedtimer/CMakeLists.txt b/tests/auto/corelib/kernel/qelapsedtimer/CMakeLists.txt
index 1e86634290..eccde38df2 100644
--- a/tests/auto/corelib/kernel/qelapsedtimer/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qelapsedtimer/CMakeLists.txt
@@ -1,13 +1,19 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qelapsedtimer.pro.
-
#####################################################################
## tst_qelapsedtimer Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qelapsedtimer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qelapsedtimer
SOURCES
tst_qelapsedtimer.cpp
+ LIBRARIES
+ Qt::TestPrivate
)
diff --git a/tests/auto/corelib/kernel/qelapsedtimer/tst_qelapsedtimer.cpp b/tests/auto/corelib/kernel/qelapsedtimer/tst_qelapsedtimer.cpp
index f2f42bef4b..7a2b12b2eb 100644
--- a/tests/auto/corelib/kernel/qelapsedtimer/tst_qelapsedtimer.cpp
+++ b/tests/auto/corelib/kernel/qelapsedtimer/tst_qelapsedtimer.cpp
@@ -1,10 +1,11 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include <QtCore/QDateTime>
#include <QtCore/QString>
-#include <QtCore/QTime>
#include <QtCore/QElapsedTimer>
#include <QTest>
+#include <QtTest/private/qcomparisontesthelper_p.h>
#include <QTimer>
static const int minResolution = 100; // the minimum resolution for the tests
@@ -22,6 +23,7 @@ class tst_QElapsedTimer : public QObject
Q_OBJECT
private Q_SLOTS:
+ void compareCompiles();
void statics();
void validity();
void basics();
@@ -29,13 +31,32 @@ private Q_SLOTS:
void msecsTo();
};
+void tst_QElapsedTimer::compareCompiles()
+{
+ QTestPrivate::testAllComparisonOperatorsCompile<QElapsedTimer>();
+}
+
void tst_QElapsedTimer::statics()
{
- qDebug() << "Clock type is" << QElapsedTimer::clockType();
- qDebug() << "Said clock is" << (QElapsedTimer::isMonotonic() ? "monotonic" : "not monotonic");
+ // these have been required since Qt 6.6
+ QCOMPARE(QElapsedTimer::clockType(), QElapsedTimer::MonotonicClock);
+ QVERIFY(QElapsedTimer::isMonotonic());
+
QElapsedTimer t;
t.start();
- qDebug() << "Current time is" << t.msecsSinceReference();
+ qint64 system_now = QDateTime::currentMSecsSinceEpoch();
+
+ auto setprecision = +[](QTextStream &s) -> QTextStream & {
+ s.setRealNumberNotation(QTextStream::FixedNotation);
+ s.setRealNumberPrecision(3);
+ return s;
+ };
+ qDebug() << setprecision
+ << "Current monotonic time is" << (t.msecsSinceReference() / 1000.)
+ << "s and current system time is" << (system_now / 1000.) << 's';
+ if (qAbs(system_now - t.msecsSinceReference()) < 5 * minResolution)
+ qWarning() << "The monotonic clock is awfully close to the system clock"
+ " (it may not be monotonic at all!)";
}
void tst_QElapsedTimer::validity()
@@ -63,6 +84,7 @@ void tst_QElapsedTimer::basics()
QVERIFY(!(t1 < t1));
QCOMPARE(t1.msecsTo(t1), qint64(0));
QCOMPARE(t1.secsTo(t1), qint64(0));
+ QT_TEST_ALL_COMPARISON_OPS(t1, t1, Qt::strong_ordering::equal);
quint64 value1 = t1.msecsSinceReference();
qDebug() << "value1:" << value1 << "t1:" << t1;
@@ -127,10 +149,16 @@ void tst_QElapsedTimer::msecsTo()
QTest::qSleep(minResolution);
QElapsedTimer t2;
t2.start();
-
- QVERIFY(t1 != t2);
- QVERIFY(!(t1 == t2));
- QVERIFY(t1 < t2);
+ QTest::qSleep(minResolution);
+ QElapsedTimer t3;
+ t3.start();
+
+ QT_TEST_EQUALITY_OPS(t1, t2, false);
+ QT_TEST_EQUALITY_OPS(QElapsedTimer(), QElapsedTimer(), true);
+ QT_TEST_EQUALITY_OPS(QElapsedTimer(), t2, false);
+ QT_TEST_ALL_COMPARISON_OPS(t1, t2, Qt::strong_ordering::less);
+ QT_TEST_ALL_COMPARISON_OPS(t3, t2, Qt::strong_ordering::greater);
+ QT_TEST_ALL_COMPARISON_OPS(t3, QElapsedTimer(), Qt::strong_ordering::greater);
auto diff = t1.msecsTo(t2);
QVERIFY2(diff > 0, QString("difference t1 and t2 is %1").arg(diff).toLatin1());
diff --git a/tests/auto/corelib/kernel/qeventdispatcher/CMakeLists.txt b/tests/auto/corelib/kernel/qeventdispatcher/CMakeLists.txt
index 5ef51b4906..1f9cfb9449 100644
--- a/tests/auto/corelib/kernel/qeventdispatcher/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qeventdispatcher/CMakeLists.txt
@@ -1,13 +1,33 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qeventdispatcher.pro.
-
#####################################################################
## tst_qeventdispatcher Test:
#####################################################################
-qt_internal_add_test(tst_qeventdispatcher
- SOURCES
- tst_qeventdispatcher.cpp
-)
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qeventdispatcher LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+set(test_names "tst_qeventdispatcher")
+if(QT_FEATURE_glib AND UNIX)
+ list(APPEND test_names "tst_qeventdispatcher_no_glib")
+endif()
+
+foreach(test ${test_names})
+ qt_internal_add_test(${test}
+ NO_BATCH
+ SOURCES
+ tst_qeventdispatcher.cpp
+ )
+endforeach()
+
+if (TARGET tst_qeventdispatcher_no_glib)
+ qt_internal_extend_target(tst_qeventdispatcher_no_glib
+ DEFINES
+ DISABLE_GLIB
+ tst_QEventDispatcher=tst_QEventDispatcher_no_glib
+ )
+endif()
diff --git a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp
index 99563f187a..285d080960 100644
--- a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp
+++ b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp
@@ -1,9 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifdef QT_GUI_LIB
# include <QtGui/QGuiApplication>
-# define tst_QEventDispatcher tst_QGuiEventDispatcher
#else
# include <QtCore/QCoreApplication>
#endif
@@ -12,11 +11,39 @@
#include <QTimer>
#include <QThreadPool>
-enum {
- PreciseTimerInterval = 10,
- CoarseTimerInterval = 200,
- VeryCoarseTimerInterval = 1000
-};
+#ifdef DISABLE_GLIB
+static bool glibDisabled = []() {
+ qputenv("QT_NO_GLIB", "1");
+ return true;
+}();
+#endif
+
+#include <chrono>
+
+using namespace std::chrono_literals;
+
+static constexpr auto PreciseTimerInterval = 10ms;
+static constexpr auto CoarseTimerInterval = 200ms;
+static constexpr auto VeryCoarseTimerInterval = 1s;
+
+static constexpr
+std::chrono::nanoseconds fudgeInterval(std::chrono::nanoseconds interval, Qt::TimerType timerType)
+{
+ // Make the intervals have have fractions of milliseconds so we can check
+ // that they have been rounded & stored properly (where applicable).
+ switch (timerType) {
+ case Qt::VeryCoarseTimer:
+ // rounds down (floor) to seconds
+ return interval + 1010us;
+ case Qt::CoarseTimer:
+ // rounds up (ceil) to milliseconds
+ return interval - 10us;
+ case Qt::PreciseTimer:
+ // not rounded using QAbstractEventDispatcherV2; rounded up (ceil) on V1
+ return interval - 10us;
+ }
+ Q_UNREACHABLE_RETURN(std::chrono::nanoseconds::min());
+}
class tst_QEventDispatcher : public QObject
{
@@ -81,11 +108,9 @@ bool tst_QEventDispatcher::event(QEvent *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)) {
- ;
- }
+ QDeadlineTimer deadline(CoarseTimerInterval);
+ while (!deadline.hasExpired() && eventDispatcher->processEvents(QEventLoop::AllEvents))
+ ;
}
// consume pending posted events to avoid impact on the next test function
@@ -112,35 +137,36 @@ public:
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; }
+ int preciseTimerId() const { return int(m_preciseTimerId); }
+ int coarseTimerId() const { return int(m_coarseTimerId); }
+ int veryCoarseTimerId() const { return int(m_veryCoarseTimerId); }
- bool foundPrecise() const { return m_preciseTimerId > 0; }
- bool foundCoarse() const { return m_coarseTimerId > 0; }
- bool foundVeryCoarse() const { return m_veryCoarseTimerId > 0; }
+ bool foundPrecise() const { return preciseTimerId() > 0; }
+ bool foundCoarse() const { return coarseTimerId() > 0; }
+ bool foundVeryCoarse() const { return veryCoarseTimerId() > 0; }
- QList<QAbstractEventDispatcher::TimerInfo> registeredTimers() const
+ QList<QAbstractEventDispatcher::TimerInfoV2> registeredTimers() const
{
- return m_eventDispatcher->registeredTimers(m_parent);
+ return m_eventDispatcher->timersForObject(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);
+ auto registerTimer = [&](std::chrono::milliseconds interval, Qt::TimerType timerType) {
+ return m_eventDispatcher->registerTimer(fudgeInterval(interval, timerType), timerType,
+ m_parent);
+ };
+ m_preciseTimerId = registerTimer(PreciseTimerInterval, Qt::PreciseTimer);
+ m_coarseTimerId = registerTimer(CoarseTimerInterval, Qt::CoarseTimer);
+ m_veryCoarseTimerId = registerTimer(VeryCoarseTimerInterval, Qt::VeryCoarseTimer);
+ QVERIFY(foundPrecise());
+ QVERIFY(foundCoarse());
+ QVERIFY(foundVeryCoarse());
findTimers();
}
- void unregister(int timerId)
+ void unregister(Qt::TimerId timerId)
{
m_eventDispatcher->unregisterTimer(timerId);
findTimers();
@@ -158,36 +184,43 @@ private:
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);
+ const QList<QAbstractEventDispatcher::TimerInfoV2> timers = registeredTimers();
+ for (const QAbstractEventDispatcher::TimerInfoV2 &timerInfo : timers) {
if (timerInfo.timerId == m_preciseTimerId) {
- QCOMPARE(timerInfo.interval, int(PreciseTimerInterval));
+ // For precise timers, we expect the fudge factor to be present
+ QAbstractEventDispatcher::Duration interval =
+ fudgeInterval(PreciseTimerInterval, Qt::PreciseTimer);
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ if (!qobject_cast<QAbstractEventDispatcherV2 *>(m_eventDispatcher))
+ interval = PreciseTimerInterval;
+#endif
+ QCOMPARE(timerInfo.interval, interval);
QCOMPARE(timerInfo.timerType, Qt::PreciseTimer);
foundPrecise = true;
} else if (timerInfo.timerId == m_coarseTimerId) {
- QCOMPARE(timerInfo.interval, int(CoarseTimerInterval));
+ // For other timers, the fudge factors ought to have been rounded away
+ QCOMPARE(timerInfo.interval, CoarseTimerInterval);
QCOMPARE(timerInfo.timerType, Qt::CoarseTimer);
foundCoarse = true;
} else if (timerInfo.timerId == m_veryCoarseTimerId) {
- QCOMPARE(timerInfo.interval, int(VeryCoarseTimerInterval));
+ QCOMPARE(timerInfo.interval, VeryCoarseTimerInterval);
QCOMPARE(timerInfo.timerType, Qt::VeryCoarseTimer);
foundVeryCoarse = true;
}
}
if (!foundPrecise)
- m_preciseTimerId = -1;
+ m_preciseTimerId = Qt::TimerId::Invalid;
if (!foundCoarse)
- m_coarseTimerId = -1;
+ m_coarseTimerId = Qt::TimerId::Invalid;
if (!foundVeryCoarse)
- m_veryCoarseTimerId = -1;
+ m_veryCoarseTimerId = Qt::TimerId::Invalid;
}
QAbstractEventDispatcher *m_eventDispatcher = nullptr;
- int m_preciseTimerId = -1;
- int m_coarseTimerId = -1;
- int m_veryCoarseTimerId = -1;
+ Qt::TimerId m_preciseTimerId = Qt::TimerId::Invalid;
+ Qt::TimerId m_coarseTimerId = Qt::TimerId::Invalid;
+ Qt::TimerId m_veryCoarseTimerId = Qt::TimerId::Invalid;
QObject *m_parent = nullptr;
};
@@ -227,13 +260,13 @@ void tst_QEventDispatcher::registerTimer()
if (doubleTimer)
QSKIP("Double timer during a single timeout - aborting test as flaky on macOS");
if (timerIdFromEvent != timers.preciseTimerId()
- && elapsedTimer.elapsed() > PreciseTimerInterval * 3)
+ && elapsedTimer.durationElapsed() > 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());
+ timers.unregister(Qt::TimerId(timers.preciseTimerId()));
if (QTest::currentTestFailed())
return;
QCOMPARE(timers.registeredTimers().size(), 2);
@@ -251,13 +284,13 @@ void tst_QEventDispatcher::registerTimer()
if (doubleTimer)
QSKIP("Double timer during a single timeout - aborting test as flaky on macOS");
if (timerIdFromEvent != timers.coarseTimerId()
- && elapsedTimer.elapsed() > CoarseTimerInterval * 3)
+ && elapsedTimer.durationElapsed() > 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());
+ timers.unregister(Qt::TimerId(timers.coarseTimerId()));
if (QTest::currentTestFailed())
return;
QCOMPARE(timers.registeredTimers().size(), 1);
@@ -286,9 +319,8 @@ void tst_QEventDispatcher::sendPostedEvents()
QFETCH(int, processEventsFlagsInt);
QEventLoop::ProcessEventsFlags processEventsFlags = QEventLoop::ProcessEventsFlags(processEventsFlagsInt);
- QElapsedTimer elapsedTimer;
- elapsedTimer.start();
- while (!elapsedTimer.hasExpired(200)) {
+ QDeadlineTimer deadline(200ms);
+ while (!deadline.hasExpired()) {
receivedEventType = -1;
QCoreApplication::postEvent(this, new QEvent(QEvent::User));
@@ -355,7 +387,7 @@ void tst_QEventDispatcher::postEventFromThread()
threadPool->start([&]{
int loop = 1000 / 10; // give it a second
while (!done && --loop)
- QThread::msleep(10);
+ QThread::sleep(std::chrono::milliseconds{10});
if (done)
return;
hadToQuit = true;
@@ -393,7 +425,7 @@ void tst_QEventDispatcher::postEventFromEventHandler()
threadPool->start([&]{
int loop = 250 / 10; // give it 250ms
while (!done && --loop)
- QThread::msleep(10);
+ QThread::sleep(std::chrono::milliseconds{10});
if (done)
return;
hadToQuit = true;
diff --git a/tests/auto/corelib/kernel/qeventloop/BLACKLIST b/tests/auto/corelib/kernel/qeventloop/BLACKLIST
new file mode 100644
index 0000000000..772ec3312c
--- /dev/null
+++ b/tests/auto/corelib/kernel/qeventloop/BLACKLIST
@@ -0,0 +1,2 @@
+[processEvents]
+qnx # QTBUG-119359
diff --git a/tests/auto/corelib/kernel/qeventloop/CMakeLists.txt b/tests/auto/corelib/kernel/qeventloop/CMakeLists.txt
index 177b349a45..5d90f97b21 100644
--- a/tests/auto/corelib/kernel/qeventloop/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qeventloop/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qeventloop.pro.
-
#####################################################################
## tst_qeventloop Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qeventloop LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qeventloop
SOURCES
tst_qeventloop.cpp
diff --git a/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp
index 16437d39f9..06175e6f2f 100644
--- a/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp
+++ b/tests/auto/corelib/kernel/qeventloop/tst_qeventloop.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -9,7 +9,9 @@
#include <qeventloop.h>
#include <private/qeventloop_p.h>
#if defined(Q_OS_UNIX)
- #include <private/qeventdispatcher_unix_p.h>
+ #if !defined(Q_OS_WASM)
+ #include <private/qeventdispatcher_unix_p.h>
+ #endif
#include <QtCore/private/qcore_unix_p.h>
#if defined(HAVE_GLIB)
#include <private/qeventdispatcher_glib_p.h>
@@ -138,6 +140,7 @@ class tst_QEventLoop : public QObject
Q_OBJECT
private slots:
// This test *must* run first. See the definition for why.
+ void processEvents_data();
void processEvents();
void exec();
void reexec();
@@ -159,8 +162,21 @@ protected:
void customEvent(QEvent *e) override;
};
+void tst_QEventLoop::processEvents_data()
+{
+ QTest::addColumn<QString>("mode");
+
+#ifdef QT_GUI_LIB
+ QTest::addRow("gui") << "gui";
+#else
+ QTest::addRow("core") << "core";
+#endif
+}
+
void tst_QEventLoop::processEvents()
{
+ QFETCH(QString, mode);
+
QSignalSpy aboutToBlockSpy(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::aboutToBlock);
QSignalSpy awakeSpy(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::awake);
@@ -400,8 +416,8 @@ public slots:
dataSent = serverSocket->waitForBytesWritten(-1);
if (dataSent) {
- pollfd pfd = qt_make_pollfd(socket->socketDescriptor(), POLLIN);
- dataReadable = (1 == qt_safe_poll(&pfd, 1, nullptr));
+ pollfd pfd = qt_make_pollfd(int(socket->socketDescriptor()), POLLIN);
+ dataReadable = (1 == qt_safe_poll(&pfd, 1, QDeadlineTimer::Forever));
}
if (!dataReadable) {
@@ -486,7 +502,7 @@ void tst_QEventLoop::processEventsExcludeTimers()
// but not if we exclude timers
eventLoop.processEvents(QEventLoop::X11ExcludeTimers);
-#if defined(Q_OS_UNIX)
+#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM)
QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher();
if (!qobject_cast<QEventDispatcherUNIX *>(eventDispatcher)
#if defined(HAVE_GLIB)
diff --git a/tests/auto/corelib/kernel/qjniarray/CMakeLists.txt b/tests/auto/corelib/kernel/qjniarray/CMakeLists.txt
new file mode 100644
index 0000000000..308b2c6427
--- /dev/null
+++ b/tests/auto/corelib/kernel/qjniarray/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qjnitypes LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qjniarray
+ SOURCES
+ tst_qjniarray.cpp
+)
diff --git a/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp
new file mode 100644
index 0000000000..b7b1f95b39
--- /dev/null
+++ b/tests/auto/corelib/kernel/qjniarray/tst_qjniarray.cpp
@@ -0,0 +1,165 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest>
+
+#include <QtCore/qjnitypes.h>
+#include <QtCore/qjniarray.h>
+
+using namespace Qt::StringLiterals;
+
+class tst_QJniArray : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QJniArray() = default;
+
+private slots:
+ void construct();
+ void size();
+ void operators();
+};
+
+using namespace QtJniTypes;
+
+// fake type so that we can compile-time test and assert correct type mappings
+Q_DECLARE_JNI_CLASS(List, "qt/test/List");
+
+// verify that we get the return type we expect for the specified return type
+#define VERIFY_RETURN_FOR_TYPE(In, Out) \
+static_assert(std::is_same_v<decltype(List::callStaticMethod<In>("toByteArray")), Out>)
+
+VERIFY_RETURN_FOR_TYPE(jobjectArray, QJniObject);
+VERIFY_RETURN_FOR_TYPE(jobject[], QJniArray<jobject>);
+//VERIFY_RETURN_FOR_TYPE(QList<QJniObject>, QList<QJniObject>);
+VERIFY_RETURN_FOR_TYPE(QList<jobject>, QList<jobject>);
+
+VERIFY_RETURN_FOR_TYPE(jbyteArray, QJniObject);
+VERIFY_RETURN_FOR_TYPE(jbyte[], QJniArray<jbyte>);
+VERIFY_RETURN_FOR_TYPE(QByteArray, QByteArray);
+
+VERIFY_RETURN_FOR_TYPE(jbooleanArray, QJniObject);
+VERIFY_RETURN_FOR_TYPE(jboolean[], QJniArray<jboolean>);
+VERIFY_RETURN_FOR_TYPE(QList<jboolean>, QList<jboolean>);
+VERIFY_RETURN_FOR_TYPE(QList<bool>, QList<jboolean>);
+
+VERIFY_RETURN_FOR_TYPE(jcharArray, QJniObject);
+VERIFY_RETURN_FOR_TYPE(jchar[], QJniArray<jchar>);
+VERIFY_RETURN_FOR_TYPE(QList<jchar>, QList<jchar>);
+
+VERIFY_RETURN_FOR_TYPE(jshortArray, QJniObject);
+VERIFY_RETURN_FOR_TYPE(jshort[], QJniArray<jshort>);
+VERIFY_RETURN_FOR_TYPE(QList<jshort>, QList<jshort>);
+VERIFY_RETURN_FOR_TYPE(QList<short>, QList<short>);
+
+VERIFY_RETURN_FOR_TYPE(jintArray, QJniObject);
+VERIFY_RETURN_FOR_TYPE(jint[], QJniArray<jint>);
+VERIFY_RETURN_FOR_TYPE(QList<jint>, QList<jint>);
+VERIFY_RETURN_FOR_TYPE(QList<int>, QList<int>);
+
+VERIFY_RETURN_FOR_TYPE(jlongArray, QJniObject);
+VERIFY_RETURN_FOR_TYPE(jlong[], QJniArray<jlong>);
+VERIFY_RETURN_FOR_TYPE(QList<jlong>, QList<jlong>);
+// VERIFY_RETURN_FOR_TYPE(QList<long>, QList<long>); // assumes long is 64bit
+
+VERIFY_RETURN_FOR_TYPE(jfloatArray, QJniObject);
+VERIFY_RETURN_FOR_TYPE(jfloat[], QJniArray<jfloat>);
+VERIFY_RETURN_FOR_TYPE(QList<jfloat>, QList<jfloat>);
+VERIFY_RETURN_FOR_TYPE(QList<float>, QList<float>);
+
+VERIFY_RETURN_FOR_TYPE(jdoubleArray, QJniObject);
+VERIFY_RETURN_FOR_TYPE(jdouble[], QJniArray<jdouble>);
+VERIFY_RETURN_FOR_TYPE(QList<jdouble>, QList<jdouble>);
+VERIFY_RETURN_FOR_TYPE(QList<double>, QList<double>);
+
+VERIFY_RETURN_FOR_TYPE(QString, QString);
+
+VERIFY_RETURN_FOR_TYPE(List, List);
+VERIFY_RETURN_FOR_TYPE(List[], QJniArray<List>);
+VERIFY_RETURN_FOR_TYPE(QJniArray<List>, QJniArray<List>);
+
+void tst_QJniArray::construct()
+{
+ {
+ QStringList strings;
+ for (int i = 0; i < 10000; ++i)
+ strings << QString::number(i);
+ QJniArray<QString> list(strings);
+ QCOMPARE(list.size(), 10000);
+ }
+ {
+ QJniArray<jint> list{1, 2, 3};
+ QCOMPARE(list.size(), 3);
+ }
+ {
+ QJniArray<jint> list(QList<int>{1, 2, 3});
+ QCOMPARE(list.size(), 3);
+ }
+ {
+ QJniArray<jint> list{QList<int>{1, 2, 3}};
+ QCOMPARE(list.size(), 3);
+ }
+}
+
+void tst_QJniArray::size()
+{
+ QJniArray<jint> array;
+ QVERIFY(!array.isValid());
+ QCOMPARE(array.size(), 0);
+
+ QList<int> intList;
+ intList.resize(10);
+ auto intArray = QJniArrayBase::fromContainer(intList);
+ QCOMPARE(intArray.size(), 10);
+}
+
+void tst_QJniArray::operators()
+{
+ QByteArray bytes("abcde");
+ QJniArray<jbyte> array(bytes);
+ QVERIFY(array.isValid());
+
+ {
+ auto it = array.begin();
+ QCOMPARE(*it, 'a');
+ QCOMPARE(*++it, 'b');
+ QCOMPARE(*it++, 'b');
+ QCOMPARE(*it, 'c');
+ ++it;
+ it++;
+ QCOMPARE(*it, 'e');
+ QCOMPARE(++it, array.end());
+ }
+ {
+ auto it = array.rbegin();
+ QCOMPARE(*it, 'e');
+ QCOMPARE(*++it, 'd');
+ QCOMPARE(*it++, 'd');
+ QCOMPARE(*it, 'c');
+ ++it;
+ it++;
+ QCOMPARE(*it, 'a');
+ QCOMPARE(++it, array.rend());
+ }
+ {
+ QJniArray<jbyte>::const_iterator it = {};
+ QCOMPARE(it, QJniArray<jbyte>::const_iterator{});
+ QCOMPARE_NE(array.begin(), array.end());
+
+ it = array.begin();
+ QCOMPARE(it, array.begin());
+ }
+
+ QCOMPARE(std::distance(array.begin(), array.end()), array.size());
+
+ qsizetype index = 0;
+ for (const auto &value : array) {
+ QCOMPARE(value, bytes.at(index));
+ ++index;
+ }
+}
+
+QTEST_MAIN(tst_QJniArray)
+
+#include "tst_qjniarray.moc"
diff --git a/tests/auto/corelib/kernel/qjnienvironment/CMakeLists.txt b/tests/auto/corelib/kernel/qjnienvironment/CMakeLists.txt
index 549e11c9ae..f405438314 100644
--- a/tests/auto/corelib/kernel/qjnienvironment/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qjnienvironment/CMakeLists.txt
@@ -5,6 +5,12 @@
## tst_qjnienvironment Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qjnienvironment LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qjnienvironment
SOURCES
tst_qjnienvironment.cpp
diff --git a/tests/auto/corelib/kernel/qjnienvironment/testdata/src/org/qtproject/qt/android/testdata/QtJniEnvironmentTestClass.java b/tests/auto/corelib/kernel/qjnienvironment/testdata/src/org/qtproject/qt/android/testdata/QtJniEnvironmentTestClass.java
index 984307d57d..4f307b3bc7 100644
--- a/tests/auto/corelib/kernel/qjnienvironment/testdata/src/org/qtproject/qt/android/testdata/QtJniEnvironmentTestClass.java
+++ b/tests/auto/corelib/kernel/qjnienvironment/testdata/src/org/qtproject/qt/android/testdata/QtJniEnvironmentTestClass.java
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
package org.qtproject.qt.android.testdatapackage;
diff --git a/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp b/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp
index da72b6d32b..95748f46f7 100644
--- a/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp
+++ b/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <jni.h>
@@ -20,6 +20,7 @@ class tst_QJniEnvironment : public QObject
Q_OBJECT
private slots:
+ void init();
void jniEnv();
void javaVM();
void registerNativeMethods();
@@ -30,6 +31,14 @@ private slots:
void findStaticField();
};
+void tst_QJniEnvironment::init()
+{
+ // Unless explicitly ignored to test error handling, warning messages
+ // in this test about a failure to look up a field, method, or class
+ // make the test fail.
+ QTest::failOnWarning(QRegularExpression("java.lang.NoSuch.*Error"));
+}
+
void tst_QJniEnvironment::jniEnv()
{
QJniEnvironment env;
@@ -59,6 +68,7 @@ void tst_QJniEnvironment::jniEnv()
QVERIFY(!QJniEnvironment::checkAndClearExceptions(env.jniEnv()));
// try to find a nonexistent class
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.ClassNotFoundException: .*"));
QVERIFY(!env->FindClass("this/doesnt/Exist"));
QVERIFY(QJniEnvironment::checkAndClearExceptions(env.jniEnv()));
@@ -68,9 +78,11 @@ void tst_QJniEnvironment::jniEnv()
QVERIFY(env.findClass<jstring>());
// try to find a nonexistent class
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.ClassNotFoundException: .*"));
QVERIFY(!env.findClass("this/doesnt/Exist"));
// clear exception with member function
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.ClassNotFoundException: .*"));
QVERIFY(!env->FindClass("this/doesnt/Exist"));
QVERIFY(env.checkAndClearExceptions());
}
@@ -273,6 +285,7 @@ void tst_QJniEnvironment::findMethod()
QVERIFY(methodId != nullptr);
// invalid signature
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.NoSuchMethodError: .*"));
jmethodID invalid = env.findMethod(clazz, "unknown", "()I");
QVERIFY(invalid == nullptr);
// check that all exceptions are already cleared
@@ -299,8 +312,10 @@ void tst_QJniEnvironment::findStaticMethod()
QCOMPARE(result, 123);
// invalid method
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.NoSuchMethodError: .*"));
jmethodID invalid = env.findStaticMethod(clazz, "unknown", "()I");
QVERIFY(invalid == nullptr);
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.NoSuchMethodError: .*"));
invalid = env.findStaticMethod<jint>(clazz, "unknown");
QVERIFY(invalid == nullptr);
// check that all exceptions are already cleared
@@ -328,6 +343,7 @@ void tst_QJniEnvironment::findField()
QVERIFY(value == 123);
// invalid signature
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.NoSuchFieldError: .*"));
jfieldID invalidId = env.findField(clazz, "unknown", "I");
QVERIFY(invalidId == nullptr);
// check that all exceptions are already cleared
@@ -351,6 +367,7 @@ void tst_QJniEnvironment::findStaticField()
QVERIFY(size == 321);
// invalid signature
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.NoSuchFieldError: .*"));
jfieldID invalidId = env.findStaticField(clazz, "unknown", "I");
QVERIFY(invalidId == nullptr);
// check that all exceptions are already cleared
diff --git a/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt b/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt
index d35aa2428f..98509dc0e5 100644
--- a/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt
@@ -5,6 +5,12 @@
## tst_qjniobject Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qjniobject LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qjniobject
SOURCES
tst_qjniobject.cpp
diff --git a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java
index dc55ad0f89..07a94d1cac 100644
--- a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java
+++ b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
package org.qtproject.qt.android.testdatapackage;
@@ -20,6 +20,9 @@ public class QtJniObjectTestClass
// --------------------------------------------------------------------------------------------
+ final int INT_FIELD = 123;
+ final boolean BOOL_FIELD = true;
+
byte BYTE_VAR;
short SHORT_VAR;
int INT_VAR;
@@ -125,6 +128,9 @@ public class QtJniObjectTestClass
public String stringMethod() { return staticStringMethod(); }
// --------------------------------------------------------------------------------------------
+ public static String staticEchoMethod(String value) { return value; }
+
+ // --------------------------------------------------------------------------------------------
public static Throwable staticThrowableMethod() { return A_THROWABLE_OBJECT; }
public Throwable throwableMethod() { return staticThrowableMethod(); }
@@ -132,43 +138,208 @@ public class QtJniObjectTestClass
public static Object[] staticObjectArrayMethod()
{ Object[] array = { new Object(), new Object(), new Object() }; return array; }
public Object[] objectArrayMethod() { return staticObjectArrayMethod(); }
+ public static Object[] staticReverseObjectArray(Object[] array)
+ {
+ for (int i = 0; i < array.length / 2; ++i) {
+ Object old = array[array.length - i - 1];
+ array[array.length - i - 1] = array[i];
+ array[i] = old;
+ }
+ return array;
+ }
+ public Object[] reverseObjectArray(Object[] array)
+ { return staticReverseObjectArray(array); }
// --------------------------------------------------------------------------------------------
public static boolean[] staticBooleanArrayMethod()
{ boolean[] array = { true, true, true }; return array; }
public boolean[] booleanArrayMethod() { return staticBooleanArrayMethod(); }
+ public static boolean[] staticReverseBooleanArray(boolean[] array)
+ {
+ for (int i = 0; i < array.length / 2; ++i) {
+ boolean old = array[array.length - i - 1];
+ array[array.length - i - 1] = array[i];
+ array[i] = old;
+ }
+ return array;
+ }
+ public boolean[] reverseBooleanArray(boolean[] array)
+ { return staticReverseBooleanArray(array); }
// --------------------------------------------------------------------------------------------
public static byte[] staticByteArrayMethod()
{ byte[] array = { 'a', 'b', 'c' }; return array; }
public byte[] byteArrayMethod() { return staticByteArrayMethod(); }
+ public static byte[] staticReverseByteArray(byte[] array)
+ {
+ for (int i = 0; i < array.length / 2; ++i) {
+ byte old = array[array.length - i - 1];
+ array[array.length - i - 1] = array[i];
+ array[i] = old;
+ }
+ return array;
+ }
+ public byte[] reverseByteArray(byte[] array)
+ { return staticReverseByteArray(array); }
// --------------------------------------------------------------------------------------------
public static char[] staticCharArrayMethod()
{ char[] array = { 'a', 'b', 'c' }; return array; }
public char[] charArrayMethod() { return staticCharArrayMethod(); }
+ public static char[] staticReverseCharArray(char[] array)
+ {
+ for (int i = 0; i < array.length / 2; ++i) {
+ char old = array[array.length - i - 1];
+ array[array.length - i - 1] = array[i];
+ array[i] = old;
+ }
+ return array;
+ }
+ public char[] reverseCharArray(char[] array)
+ { return staticReverseCharArray(array); }
// --------------------------------------------------------------------------------------------
public static short[] staticShortArrayMethod() { short[] array = { 3, 2, 1 }; return array; }
public short[] shortArrayMethod() { return staticShortArrayMethod(); }
+ public static short[] staticReverseShortArray(short[] array)
+ {
+ for (int i = 0; i < array.length / 2; ++i) {
+ short old = array[array.length - i - 1];
+ array[array.length - i - 1] = array[i];
+ array[i] = old;
+ }
+ return array;
+ }
+ public short[] reverseShortArray(short[] array)
+ { return staticReverseShortArray(array); }
// --------------------------------------------------------------------------------------------
public static int[] staticIntArrayMethod() { int[] array = { 3, 2, 1 }; return array; }
public int[] intArrayMethod() { return staticIntArrayMethod(); }
+ public static int[] staticReverseIntArray(int[] array)
+ {
+ for (int i = 0; i < array.length / 2; ++i) {
+ int old = array[array.length - i - 1];
+ array[array.length - i - 1] = array[i];
+ array[i] = old;
+ }
+ return array;
+ }
+ public int[] reverseIntArray(int[] array)
+ { return staticReverseIntArray(array); }
// --------------------------------------------------------------------------------------------
public static long[] staticLongArrayMethod()
{ long[] array = { 3, 2, 1 }; return array; }
public long[] longArrayMethod() { return staticLongArrayMethod(); }
+ public static long[] staticReverseLongArray(long[] array)
+ {
+ for (int i = 0; i < array.length / 2; ++i) {
+ long old = array[array.length - i - 1];
+ array[array.length - i - 1] = array[i];
+ array[i] = old;
+ }
+ return array;
+ }
+ public long[] reverseLongArray(long[] array)
+ { return staticReverseLongArray(array); }
// --------------------------------------------------------------------------------------------
public static float[] staticFloatArrayMethod()
{ float[] array = { 1.0f, 2.0f, 3.0f }; return array; }
public float[] floatArrayMethod() { return staticFloatArrayMethod(); }
+ public static float[] staticReverseFloatArray(float[] array)
+ {
+ for (int i = 0; i < array.length / 2; ++i) {
+ float old = array[array.length - i - 1];
+ array[array.length - i - 1] = array[i];
+ array[i] = old;
+ }
+ return array;
+ }
+ public float[] reverseFloatArray(float[] array)
+ { return staticReverseFloatArray(array); }
// --------------------------------------------------------------------------------------------
public static double[] staticDoubleArrayMethod()
{ double[] array = { 3.0, 2.0, 1.0 }; return array; }
public double[] doubleArrayMethod() { return staticDoubleArrayMethod(); }
-}
+ public static double[] staticReverseDoubleArray(double[] array)
+ {
+ for (int i = 0; i < array.length / 2; ++i) {
+ double old = array[array.length - i - 1];
+ array[array.length - i - 1] = array[i];
+ array[i] = old;
+ }
+ return array;
+ }
+ public double[] reverseDoubleArray(double[] array)
+ { return staticReverseDoubleArray(array); }
+ // --------------------------------------------------------------------------------------------
+ native public int callbackWithObject(QtJniObjectTestClass that);
+ native public int callbackWithObjectRef(QtJniObjectTestClass that);
+ native public int callbackWithString(String string);
+ native public int callbackWithByte(byte value);
+ native public int callbackWithBoolean(boolean value);
+ native public int callbackWithInt(int value);
+ native public int callbackWithDouble(double value);
+ native public int callbackWithJniArray(double[] value);
+ native public int callbackWithQList(double[] value);
+ native public int callbackWithStringList(String[] value);
+
+ public int callMeBackWithObject(QtJniObjectTestClass that)
+ {
+ return callbackWithObject(that);
+ }
+
+ public int callMeBackWithObjectRef(QtJniObjectTestClass that)
+ {
+ return callbackWithObjectRef(that);
+ }
+
+ public int callMeBackWithString(String string)
+ {
+ return callbackWithString(string);
+ }
+
+ public int callMeBackWithByte(byte value)
+ {
+ return callbackWithByte(value);
+ }
+
+ public int callMeBackWithBoolean(boolean value)
+ {
+ return callbackWithBoolean(value);
+ }
+
+ public int callMeBackWithInt(int value)
+ {
+ return callbackWithInt(value);
+ }
+
+ public int callMeBackWithDouble(double value)
+ {
+ return callbackWithDouble(value);
+ }
+ public int callMeBackWithJniArray(double[] value)
+ {
+ return callbackWithJniArray(value);
+ }
+ public int callMeBackWithQList(double[] value)
+ {
+ return callbackWithQList(value);
+ }
+ public int callMeBackWithStringList(String[] value)
+ {
+ return callbackWithStringList(value);
+ }
+
+ public Object callMethodThrowsException() throws Exception {
+ throw new Exception();
+ }
+
+ public static Object callStaticMethodThrowsException() throws Exception {
+ throw new Exception();
+ }
+}
diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp
index 7ea6b1e121..64b464e002 100644
--- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp
+++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <jni.h>
@@ -8,8 +8,11 @@
#include <QtCore/QJniObject>
#include <QtTest>
-static const char testClassName[] = "org/qtproject/qt/android/testdatapackage/QtJniObjectTestClass";
+using namespace Qt::StringLiterals;
+
+static constexpr const char testClassName[] = "org/qtproject/qt/android/testdatapackage/QtJniObjectTestClass";
Q_DECLARE_JNI_CLASS(QtJniObjectTestClass, testClassName)
+using TestClass = QtJniTypes::QtJniObjectTestClass;
static const jbyte A_BYTE_VALUE = 127;
static const jshort A_SHORT_VALUE = 32767;
@@ -33,12 +36,16 @@ public:
private slots:
void initTestCase();
+ void init();
void ctor();
void callMethodTest();
+ void callMethodThrowsException();
void callObjectMethodTest();
void stringConvertionTest();
void compareOperatorTests();
+ void className();
+ void callStaticMethodThrowsException();
void callStaticObjectMethodClassName();
void callStaticObjectMethod();
void callStaticObjectMethodById();
@@ -72,6 +79,7 @@ private slots:
void getStaticIntField();
void getStaticByteFieldClassName();
void getStaticByteField();
+ void getStaticBooleanField();
void getStaticLongFieldClassName();
void getStaticLongField();
void getStaticDoubleFieldClassName();
@@ -107,6 +115,11 @@ private slots:
void templateApiCheck();
void isClassAvailable();
void fromLocalRef();
+ void largeObjectArray();
+
+ void callback_data();
+ void callback();
+ void callStaticOverloadResolution();
void cleanupTestCase();
};
@@ -119,6 +132,14 @@ void tst_QJniObject::initTestCase()
{
}
+void tst_QJniObject::init()
+{
+ // Unless explicitly ignored to test error handling, warning messages
+ // in this test about a failure to look up a field, method, or class
+ // make the test fail.
+ QTest::failOnWarning(QRegularExpression("java.lang.NoSuch.*Error"));
+}
+
void tst_QJniObject::cleanupTestCase()
{
}
@@ -141,6 +162,16 @@ void tst_QJniObject::ctor()
}
{
+ // from Qt 6.7 on we can construct declared classes through the helper type
+ QJniObject object = TestClass::construct();
+ QVERIFY(object.isValid());
+
+ // or even directly
+ TestClass testObject;
+ QVERIFY(testObject.isValid());
+ }
+
+ {
QJniObject string = QJniObject::fromString(QLatin1String("Hello, Java"));
QJniObject object("java/lang/String", "(Ljava/lang/String;)V", string.object<jstring>());
QVERIFY(object.isValid());
@@ -185,8 +216,10 @@ void tst_QJniObject::ctor()
void tst_QJniObject::callMethodTest()
{
{
- QJniObject jString1 = QJniObject::fromString(QLatin1String("Hello, Java"));
- QJniObject jString2 = QJniObject::fromString(QLatin1String("hELLO, jAVA"));
+ const QString qString1 = u"Hello, Java"_s;
+ const QString qString2 = u"hELLO, jAVA"_s;
+ QJniObject jString1 = QJniObject::fromString(qString1);
+ QJniObject jString2 = QJniObject::fromString(qString2);
QVERIFY(jString1 != jString2);
const jboolean isEmpty = jString1.callMethod<jboolean>("isEmpty");
@@ -199,6 +232,10 @@ void tst_QJniObject::callMethodTest()
ret = jString1.callMethod<jint>("compareToIgnoreCase", jString2.object<jstring>());
QVERIFY(0 == ret);
+
+ // as of Qt 6.7, we can pass QString directly
+ ret = jString1.callMethod<jint>("compareToIgnoreCase", qString2);
+ QVERIFY(0 == ret);
}
{
@@ -217,9 +254,25 @@ void tst_QJniObject::callMethodTest()
QJniObject subString = jString.callMethod<jstring>("substring", 0, 4);
QCOMPARE(subString.toString(), qString.mid(0, 4));
+
+ // and as of Qt 6.7, we can return and take QString directly
+ QCOMPARE(jString.callMethod<QString>("substring", 0, 4), qString.mid(0, 4));
+
+ QCOMPARE(jString.callMethod<jstring>("substring", 0, 7)
+ .callMethod<jstring>("toUpperCase")
+ .callMethod<QString>("concat", u"C++"_s), u"HELLO, C++"_s);
}
}
+void tst_QJniObject::callMethodThrowsException()
+{
+ QtJniTypes::QtJniObjectTestClass instance;
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.Exception"));
+ auto res = instance.callMethod<jobject>("callMethodThrowsException");
+ QVERIFY(!res.isValid());
+ QVERIFY(!QJniEnvironment().checkAndClearExceptions());
+}
+
void tst_QJniObject::callObjectMethodTest()
{
const QString qString = QLatin1String("Hello, Java");
@@ -267,7 +320,7 @@ void tst_QJniObject::compareOperatorTests()
QJniObject stringObject2 = QJniObject::fromString(str);
QVERIFY(stringObject != stringObject2);
- jstring jstrobj = 0;
+ jstring jstrobj = nullptr;
QJniObject invalidStringObject;
QVERIFY(invalidStringObject == jstrobj);
@@ -276,6 +329,36 @@ void tst_QJniObject::compareOperatorTests()
QVERIFY(!invalidStringObject.isValid());
}
+void tst_QJniObject::className()
+{
+ const QString str("Hello!");
+ QJniObject jString = QJniObject::fromString(str);
+ {
+ QCOMPARE(jString.className(), "java/lang/String");
+ QCOMPARE(jString.toString(), str);
+ }
+
+ {
+ QJniObject strObject = QJniObject("java/lang/String", str);
+ QCOMPARE(strObject.className(), "java/lang/String");
+ QCOMPARE(strObject.toString(), str);
+ }
+
+ {
+ TestClass test;
+ QCOMPARE(test.className(), testClassName);
+ }
+}
+
+void tst_QJniObject::callStaticMethodThrowsException()
+{
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.Exception"));
+ auto res = QtJniTypes::QtJniObjectTestClass::callStaticMethod<jobject>(
+ "callStaticMethodThrowsException");
+ QVERIFY(!res.isValid());
+ QVERIFY(!QJniEnvironment().checkAndClearExceptions());
+}
+
void tst_QJniObject::callStaticObjectMethodClassName()
{
QJniObject formatString = QJniObject::fromString(QLatin1String("test format"));
@@ -310,7 +393,8 @@ void tst_QJniObject::callStaticObjectMethod()
jclass cls = env->FindClass("java/lang/String");
QVERIFY(cls != 0);
- QJniObject formatString = QJniObject::fromString(QLatin1String("test format"));
+ const QString string = u"test format"_s;
+ QJniObject formatString = QJniObject::fromString(string);
QVERIFY(formatString.isValid());
QJniObject returnValue = QJniObject::callStaticObjectMethod(cls,
@@ -319,20 +403,14 @@ void tst_QJniObject::callStaticObjectMethod()
formatString.object<jstring>(),
jobjectArray(0));
QVERIFY(returnValue.isValid());
-
- QString returnedString = returnValue.toString();
-
- QCOMPARE(returnedString, QString::fromLatin1("test format"));
+ QCOMPARE(returnValue.toString(), string);
returnValue = QJniObject::callStaticObjectMethod<jstring>(cls,
"format",
formatString.object<jstring>(),
jobjectArray(0));
QVERIFY(returnValue.isValid());
-
- returnedString = returnValue.toString();
-
- QCOMPARE(returnedString, QString::fromLatin1("test format"));
+ QCOMPARE(returnValue.toString(), string);
// from 6.4 on we can use callStaticMethod
returnValue = QJniObject::callStaticMethod<jstring>(cls,
@@ -340,10 +418,20 @@ void tst_QJniObject::callStaticObjectMethod()
formatString.object<jstring>(),
jobjectArray(0));
QVERIFY(returnValue.isValid());
+ QCOMPARE(returnValue.toString(), string);
- returnedString = returnValue.toString();
+ // from 6.7 we can use callStaticMethod without specifying the class string
+ returnValue = QJniObject::callStaticMethod<jstring, jstring>("format",
+ formatString.object<jstring>(),
+ jobjectArray(0));
+ QVERIFY(returnValue.isValid());
+ QCOMPARE(returnValue.toString(), string);
- QCOMPARE(returnedString, QString::fromLatin1("test format"));
+ // from 6.7 we can pass QString directly, both as parameters and return type
+ QString result = QJniObject::callStaticMethod<jstring, QString>("format",
+ string,
+ jobjectArray(0));
+ QCOMPARE(result, string);
}
void tst_QJniObject::callStaticObjectMethodById()
@@ -845,6 +933,10 @@ void tst_QJniObject::getStaticIntField()
jint i = QJniObject::getStaticField<jint>(cls, "SIZE");
QCOMPARE(i, 64);
+
+ enum class Enum { SIZE = 64 };
+ Enum e = QJniObject::getStaticField<Enum>(cls, "SIZE");
+ QCOMPARE(e, Enum::SIZE);
}
void tst_QJniObject::getStaticByteFieldClassName()
@@ -861,6 +953,16 @@ void tst_QJniObject::getStaticByteField()
jbyte i = QJniObject::getStaticField<jbyte>(cls, "MAX_VALUE");
QCOMPARE(i, jbyte(127));
+
+ enum class Enum : jbyte { MAX_VALUE = 127 };
+ Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE");
+ QCOMPARE(e, Enum::MAX_VALUE);
+}
+
+void tst_QJniObject::getStaticBooleanField()
+{
+ QCOMPARE(TestClass::getStaticField<jboolean>("S_BOOLEAN_VAR"),
+ TestClass::getStaticField<bool>("S_BOOLEAN_VAR"));
}
void tst_QJniObject::getStaticLongFieldClassName()
@@ -877,6 +979,10 @@ void tst_QJniObject::getStaticLongField()
jlong i = QJniObject::getStaticField<jlong>(cls, "MAX_VALUE");
QCOMPARE(i, jlong(9223372036854775807L));
+
+ enum class Enum : jlong { MAX_VALUE = 9223372036854775807L };
+ Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE");
+ QCOMPARE(e, Enum::MAX_VALUE);
}
void tst_QJniObject::getStaticDoubleFieldClassName()
@@ -929,6 +1035,9 @@ void tst_QJniObject::getStaticShortField()
jshort i = QJniObject::getStaticField<jshort>(cls, "MAX_VALUE");
QCOMPARE(i, jshort(32767));
+ enum class Enum : jshort { MAX_VALUE = 32767 };
+ Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE");
+ QCOMPARE(e, Enum::MAX_VALUE);
}
void tst_QJniObject::getStaticCharFieldClassName()
@@ -945,24 +1054,29 @@ void tst_QJniObject::getStaticCharField()
jchar i = QJniObject::getStaticField<jchar>(cls, "MAX_VALUE");
QCOMPARE(i, jchar(0xffff));
+
+ enum class Enum : jchar { MAX_VALUE = 0xffff };
+ Enum e = QJniObject::getStaticField<Enum>(cls, "MAX_VALUE");
+ QCOMPARE(e, Enum::MAX_VALUE);
}
void tst_QJniObject::getBooleanField()
{
- QJniObject obj("org/qtproject/qt/android/QtActivityDelegate");
+ QJniObject obj(testClassName);
QVERIFY(obj.isValid());
- QVERIFY(!obj.getField<jboolean>("m_fullScreen"));
+ QVERIFY(obj.getField<jboolean>("BOOL_FIELD"));
+ QVERIFY(obj.getField<bool>("BOOL_FIELD"));
}
void tst_QJniObject::getIntField()
{
- QJniObject obj("org/qtproject/qt/android/QtActivityDelegate");
+ QJniObject obj(testClassName);
QVERIFY(obj.isValid());
- jint res = obj.getField<jint>("m_currentRotation");
- QCOMPARE(res, -1);
+ jint res = obj.getField<jint>("INT_FIELD");
+ QCOMPARE(res, 123);
}
template <typename T>
@@ -980,16 +1094,22 @@ void setField(const char *fieldName, T testValue)
void tst_QJniObject::setIntField()
{
setField("INT_VAR", 555);
+ enum class Enum : jint { VALUE = 555 };
+ setField("INT_VAR", Enum::VALUE);
}
void tst_QJniObject::setByteField()
{
- setField("BYTE_VAR", jbyte(555));
+ setField("BYTE_VAR", jbyte(123));
+ enum class Enum : jbyte { VALUE = 123 };
+ setField("BYTE_VAR", Enum::VALUE);
}
void tst_QJniObject::setLongField()
{
setField("LONG_VAR", jlong(9223372036847758232L));
+ enum class Enum : jlong { VALUE = 9223372036847758232L };
+ setField("LONG_VAR", Enum::VALUE);
}
void tst_QJniObject::setDoubleField()
@@ -1004,17 +1124,22 @@ void tst_QJniObject::setFloatField()
void tst_QJniObject::setShortField()
{
- setField("SHORT_VAR", jshort(123));
+ setField("SHORT_VAR", jshort(555));
+ enum class Enum : jshort { VALUE = 555 };
+ setField("SHORT_VAR", Enum::VALUE);
}
void tst_QJniObject::setCharField()
{
setField("CHAR_VAR", jchar('A'));
+ enum class Enum : jchar { VALUE = 'A' };
+ setField("CHAR_VAR", Enum::VALUE);
}
void tst_QJniObject::setBooleanField()
{
setField("BOOLEAN_VAR", jboolean(true));
+ setField("BOOLEAN_VAR", true);
}
void tst_QJniObject::setObjectField()
@@ -1022,11 +1147,16 @@ void tst_QJniObject::setObjectField()
QJniObject obj(testClassName);
QVERIFY(obj.isValid());
- QJniObject testValue = QJniObject::fromString(QStringLiteral("Hello"));
+ const QString qString = u"Hello"_s;
+ QJniObject testValue = QJniObject::fromString(qString);
obj.setField("STRING_OBJECT_VAR", testValue.object<jstring>());
QJniObject res = obj.getObjectField<jstring>("STRING_OBJECT_VAR");
QCOMPARE(res.toString(), testValue.toString());
+
+ // as of Qt 6.7, we can set and get strings directly
+ obj.setField("STRING_OBJECT_VAR", qString);
+ QCOMPARE(obj.getField<QString>("STRING_OBJECT_VAR"), qString);
}
template <typename T>
@@ -1039,24 +1169,30 @@ void setStaticField(const char *fieldName, T testValue)
// use template overload to reset to default
T defaultValue = {};
- QJniObject::setStaticField<QtJniTypes::QtJniObjectTestClass, T>(fieldName, defaultValue);
- res = QJniObject::getStaticField<QtJniTypes::QtJniObjectTestClass, T>(fieldName);
+ TestClass::setStaticField(fieldName, defaultValue);
+ res = TestClass::getStaticField<T>(fieldName);
QCOMPARE(res, defaultValue);
}
void tst_QJniObject::setStaticIntField()
{
setStaticField("S_INT_VAR", 555);
+ enum class Enum : jint { VALUE = 555 };
+ setStaticField("S_INT_VAR", Enum::VALUE);
}
void tst_QJniObject::setStaticByteField()
{
- setStaticField("S_BYTE_VAR", jbyte(555));
+ setStaticField("S_BYTE_VAR", jbyte(123));
+ enum class Enum : jbyte { VALUE = 123 };
+ setStaticField("S_BYTE_VAR", Enum::VALUE);
}
void tst_QJniObject::setStaticLongField()
{
setStaticField("S_LONG_VAR", jlong(9223372036847758232L));
+ enum class Enum : jlong { VALUE = 9223372036847758232L };
+ setStaticField("S_LONG_VAR", Enum::VALUE);
}
void tst_QJniObject::setStaticDoubleField()
@@ -1071,26 +1207,37 @@ void tst_QJniObject::setStaticFloatField()
void tst_QJniObject::setStaticShortField()
{
- setStaticField("S_SHORT_VAR", jshort(123));
+ setStaticField("S_SHORT_VAR", jshort(555));
+ enum class Enum : jshort { VALUE = 555 };
+ setStaticField("S_SHORT_VAR", Enum::VALUE);
}
void tst_QJniObject::setStaticCharField()
{
setStaticField("S_CHAR_VAR", jchar('A'));
+ enum class Enum : jchar { VALUE = 'A' };
+ setStaticField("S_CHAR_VAR", Enum::VALUE);
}
void tst_QJniObject::setStaticBooleanField()
{
setStaticField("S_BOOLEAN_VAR", jboolean(true));
+ setStaticField("S_BOOLEAN_VAR", true);
}
void tst_QJniObject::setStaticObjectField()
{
- QJniObject testValue = QJniObject::fromString(QStringLiteral("Hello"));
+ const QString qString = u"Hello"_s;
+ QJniObject testValue = QJniObject::fromString(qString);
QJniObject::setStaticField(testClassName, "S_STRING_OBJECT_VAR", testValue.object<jstring>());
QJniObject res = QJniObject::getStaticObjectField<jstring>(testClassName, "S_STRING_OBJECT_VAR");
QCOMPARE(res.toString(), testValue.toString());
+
+ // as of Qt 6.7, we can set and get strings directly
+ using namespace QtJniTypes;
+ QtJniObjectTestClass::setStaticField("S_STRING_OBJECT_VAR", qString);
+ QCOMPARE(QtJniObjectTestClass::getStaticField<QString>("S_STRING_OBJECT_VAR"), qString);
}
void tst_QJniObject::templateApiCheck()
@@ -1377,11 +1524,42 @@ void tst_QJniObject::templateApiCheck()
QJniObject res = QJniObject::callStaticObjectMethod<jobjectArray>(testClassName,
"staticObjectArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = TestClass::callStaticMethod<jobject[]>("staticObjectArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+
+ QJniArray<jobject> newArray(QList<QJniObject>{QJniObject::fromString(u"one"_s),
+ QJniObject::fromString(u"two"_s),
+ QJniObject::fromString(u"three"_s)});
+ QVERIFY(newArray.isValid());
+ const auto reverse = TestClass::callStaticMethod<jobject[]>("staticReverseObjectArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.size(), 3);
+ QCOMPARE(QJniObject(reverse.at(0)).toString(), u"three"_s);
+ QCOMPARE(QJniObject(reverse.at(1)).toString(), u"two"_s);
+ QCOMPARE(QJniObject(reverse.at(2)).toString(), u"one"_s);
}
{
QJniObject res = testClass.callObjectMethod<jobjectArray>("objectArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = testClass.callMethod<jobject[]>("objectArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+
+ QJniArray<jobject> newArray(QList<QJniObject>{QJniObject::fromString(u"one"_s),
+ QJniObject::fromString(u"two"_s),
+ QJniObject::fromString(u"three"_s)});
+ QVERIFY(newArray.isValid());
+ const auto reverse = testClass.callMethod<jobject[]>("reverseObjectArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.size(), 3);
+ // QJniArray::at returns a jobject that's a local reference; make sure we don't free it twice
+ QCOMPARE(QJniObject::fromLocalRef(reverse.at(0)).toString(), u"three"_s);
+ QCOMPARE(QJniObject::fromLocalRef(reverse.at(1)).toString(), u"two"_s);
+ QCOMPARE(QJniObject::fromLocalRef(reverse.at(2)).toString(), u"one"_s);
}
// jbooleanArray ------------------------------------------------------------------------------
@@ -1389,11 +1567,33 @@ void tst_QJniObject::templateApiCheck()
QJniObject res = QJniObject::callStaticObjectMethod<jbooleanArray>(testClassName,
"staticBooleanArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = TestClass::callStaticMethod<jboolean[]>("staticBooleanArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jboolean>{true, true, true}));
+
+ QJniArray<jboolean> newArray(QList<jboolean>{true, false, false});
+ QVERIFY(newArray.isValid());
+ const auto reverse = TestClass::callStaticMethod<jboolean[]>("staticReverseBooleanArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jboolean>{false, false, true}));
}
{
QJniObject res = testClass.callObjectMethod<jbooleanArray>("booleanArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = testClass.callMethod<jboolean[]>("booleanArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jboolean>{true, true, true}));
+
+ QJniArray<jboolean> newArray(QList<jboolean>{true, false, false});
+ QVERIFY(newArray.isValid());
+ const auto reverse = testClass.callMethod<jboolean[]>("reverseBooleanArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jboolean>{false, false, true}));
}
// jbyteArray ---------------------------------------------------------------------------------
@@ -1401,11 +1601,37 @@ void tst_QJniObject::templateApiCheck()
QJniObject res = QJniObject::callStaticObjectMethod<jbyteArray>(testClassName,
"staticByteArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = TestClass::callStaticMethod<jbyte[]>("staticByteArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), "abc");
+
+ QJniArray<jbyte> newArray(QByteArray{"cba"});
+ QVERIFY(newArray.isValid());
+ const auto reverse = TestClass::callStaticMethod<jbyte[]>("staticReverseByteArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), "abc");
+
+ const QByteArray reverse2 = TestClass::callStaticMethod<QByteArray>("staticReverseByteArray",
+ QByteArray("abc"));
+ QCOMPARE(reverse2, "cba");
}
{
QJniObject res = testClass.callObjectMethod<jbyteArray>("byteArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = testClass.callMethod<jbyte[]>("byteArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), "abc");
+
+ QJniArray<jbyte> newArray = QJniArrayBase::fromContainer(QByteArray{"cba"});
+ QVERIFY(newArray.isValid());
+ const auto reverse = testClass.callMethod<jbyte[]>("reverseByteArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), "abc");
}
// jcharArray ---------------------------------------------------------------------------------
@@ -1413,11 +1639,37 @@ void tst_QJniObject::templateApiCheck()
QJniObject res = QJniObject::callStaticObjectMethod<jcharArray>(testClassName,
"staticCharArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = TestClass::callStaticMethod<jchar[]>("staticCharArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jchar>{u'a', u'b', u'c'}));
+
+ QJniArray<jchar> newArray = {u'c', u'b', u'a'};
+ QVERIFY(newArray.isValid());
+ const auto reverse = TestClass::callStaticMethod<jchar[]>("staticReverseCharArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jchar>{u'a', u'b', u'c'}));
+
+ const QList<jchar> reverse2 = TestClass::callStaticMethod<QList<jchar>>("staticReverseCharArray",
+ (QList<jchar>{u'c', u'b', u'a'}));
+ QCOMPARE(reverse2, (QList<jchar>{u'a', u'b', u'c'}));
}
{
QJniObject res = testClass.callObjectMethod<jcharArray>("charArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = testClass.callMethod<jchar[]>("charArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jchar>{u'a', u'b', u'c'}));
+
+ QJniArray<jchar> newArray = {u'c', u'b', u'a'};
+ QVERIFY(newArray.isValid());
+ const auto reverse = testClass.callMethod<jchar[]>("reverseCharArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jchar>{u'a', u'b', u'c'}));
}
// jshortArray --------------------------------------------------------------------------------
@@ -1425,11 +1677,38 @@ void tst_QJniObject::templateApiCheck()
QJniObject res = QJniObject::callStaticObjectMethod<jshortArray>(testClassName,
"staticShortArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = TestClass::callStaticMethod<jshort[]>("staticShortArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jshort>{3, 2, 1}));
+
+ QJniArray<jshort> newArray = {3, 2, 1};
+ QVERIFY(newArray.isValid());
+ const auto reverse = TestClass::callStaticMethod<jshort[]>("staticReverseShortArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jshort>{1, 2, 3}));
+
+ const QList<jshort> reverse2 = TestClass::callStaticMethod<QList<jshort>>("staticReverseShortArray",
+ (QList<jshort>{1, 2, 3}));
+ QCOMPARE(reverse2, (QList<jshort>{3, 2, 1}));
}
{
QJniObject res = testClass.callObjectMethod<jshortArray>("shortArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = testClass.callMethod<jshort[]>("shortArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jshort>{3, 2, 1}));
+
+ QJniArray<jshort> newArray = {3, 2, 1};
+ static_assert(std::is_same_v<decltype(newArray)::Type, jshort>);
+ QVERIFY(newArray.isValid());
+ const auto reverse = testClass.callMethod<jshort[]>("reverseShortArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jshort>{1, 2, 3}));
}
// jintArray ----------------------------------------------------------------------------------
@@ -1437,11 +1716,33 @@ void tst_QJniObject::templateApiCheck()
QJniObject res = QJniObject::callStaticObjectMethod<jintArray>(testClassName,
"staticIntArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = TestClass::callStaticMethod<jint[]>("staticIntArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jint>{3, 2, 1}));
+
+ QJniArray<jint> newArray = {3, 2, 1};
+ QVERIFY(newArray.isValid());
+ const auto reverse = TestClass::callStaticMethod<jint[]>("staticReverseIntArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jint>{1, 2, 3}));
}
{
QJniObject res = testClass.callObjectMethod<jintArray>("intArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = testClass.callMethod<jint[]>("intArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jint>{3, 2, 1}));
+
+ QJniArray<jint> newArray = {3, 2, 1};
+ QVERIFY(newArray.isValid());
+ const auto reverse = testClass.callMethod<jint[]>("reverseIntArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jint>{1, 2, 3}));
}
// jlongArray ---------------------------------------------------------------------------------
@@ -1449,11 +1750,33 @@ void tst_QJniObject::templateApiCheck()
QJniObject res = QJniObject::callStaticObjectMethod<jlongArray>(testClassName,
"staticLongArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = TestClass::callStaticMethod<jlong[]>("staticLongArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jlong>{3, 2, 1}));
+
+ QJniArray<jlong> newArray = {3, 2, 1};
+ QVERIFY(newArray.isValid());
+ const auto reverse = TestClass::callStaticMethod<jlong[]>("staticReverseLongArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jlong>{1, 2, 3}));
}
{
QJniObject res = testClass.callObjectMethod<jlongArray>("longArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = testClass.callMethod<jlong[]>("longArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jlong>{3, 2, 1}));
+
+ QJniArray<jlong> newArray = {3, 2, 1};
+ QVERIFY(newArray.isValid());
+ const auto reverse = testClass.callMethod<jlong[]>("reverseLongArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jlong>{1, 2, 3}));
}
// jfloatArray --------------------------------------------------------------------------------
@@ -1461,11 +1784,33 @@ void tst_QJniObject::templateApiCheck()
QJniObject res = QJniObject::callStaticObjectMethod<jfloatArray>(testClassName,
"staticFloatArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = TestClass::callStaticMethod<jfloat[]>("staticFloatArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jfloat>{1.0f, 2.0f, 3.0f}));
+
+ QJniArray<jfloat> newArray = {3.0f, 2.0f, 1.0f};
+ QVERIFY(newArray.isValid());
+ const auto reverse = TestClass::callStaticMethod<jfloat[]>("staticReverseFloatArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jfloat>{1.0f, 2.0f, 3.0f}));
}
{
QJniObject res = testClass.callObjectMethod<jfloatArray>("floatArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = testClass.callMethod<jfloat[]>("floatArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jfloat>{1.0f, 2.0f, 3.0f}));
+
+ QJniArray<jfloat> newArray = {3.0f, 2.0f, 1.0f};
+ QVERIFY(newArray.isValid());
+ const auto reverse = testClass.callMethod<jfloat[]>("reverseFloatArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jfloat>{1.0f, 2.0f, 3.0f}));
}
// jdoubleArray -------------------------------------------------------------------------------
@@ -1473,11 +1818,33 @@ void tst_QJniObject::templateApiCheck()
QJniObject res = QJniObject::callStaticObjectMethod<jdoubleArray>(testClassName,
"staticDoubleArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = TestClass::callStaticMethod<jdouble[]>("staticDoubleArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jdouble>{3.0, 2.0, 1.0}));
+
+ QJniArray<jdouble> newArray = {3.0, 2.0, 1.0};
+ QVERIFY(newArray.isValid());
+ const auto reverse = TestClass::callStaticMethod<jdouble[]>("staticReverseDoubleArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jdouble>{1.0, 2.0, 3.0}));
}
{
QJniObject res = testClass.callObjectMethod<jdoubleArray>("doubleArrayMethod");
QVERIFY(res.isValid());
+
+ const auto array = testClass.callMethod<jdouble[]>("doubleArrayMethod");
+ QVERIFY(array.isValid());
+ QCOMPARE(array.size(), 3);
+ QCOMPARE(array.toContainer(), (QList<jdouble>{3.0, 2.0, 1.0}));
+
+ QJniArray<jdouble> newArray = {3.0, 2.0, 1.0};
+ QVERIFY(newArray.isValid());
+ const auto reverse = testClass.callMethod<jdouble[]>("reverseDoubleArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.toContainer(), (QList<jdouble>{1.0, 2.0, 3.0}));
}
}
@@ -1485,6 +1852,7 @@ void tst_QJniObject::templateApiCheck()
void tst_QJniObject::isClassAvailable()
{
QVERIFY(QJniObject::isClassAvailable("java/lang/String"));
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("java.lang.ClassNotFoundException"));
QVERIFY(!QJniObject::isClassAvailable("class/not/Available"));
QVERIFY(QJniObject::isClassAvailable("org/qtproject/qt/android/QtActivityDelegate"));
}
@@ -1497,6 +1865,244 @@ void tst_QJniObject::fromLocalRef()
QJniObject o = QJniObject::fromLocalRef(env->FindClass("java/lang/String"));
}
+void tst_QJniObject::largeObjectArray()
+{
+ QJniArray<jobject> newArray(QList<QJniObject>{QJniObject::fromString(u"one"_s),
+ QJniObject::fromString(u"two"_s),
+ QJniObject::fromString(u"three"_s)});
+ QVERIFY(newArray.isValid());
+ const QJniArray<QJniObject> reverse = TestClass::callStaticMethod<jobject[]>(
+ "staticReverseObjectArray", newArray);
+ QVERIFY(reverse.isValid());
+ QCOMPARE(reverse.size(), 3);
+
+ // make sure we don't leak local references
+ for (int i = 0; i < 10000; ++i) {
+ QVERIFY(reverse.at(0).isValid());
+ QVERIFY(reverse.at(1).isValid());
+ QVERIFY(reverse.at(2).isValid());
+ }
+}
+
+enum class CallbackParameterType
+{
+ Object,
+ ObjectRef,
+ String,
+ Byte,
+ Boolean,
+ Int,
+ Double,
+ JniArray,
+ QList,
+ QStringList,
+};
+
+static std::optional<TestClass> calledWithObject;
+static int callbackWithObject(JNIEnv *, jobject, TestClass that)
+{
+ calledWithObject.emplace(that);
+ return int(CallbackParameterType::Object);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(callbackWithObject)
+static int callbackWithObjectRef(JNIEnv *, jobject, const TestClass &that)
+{
+ calledWithObject.emplace(that);
+ return int(CallbackParameterType::ObjectRef);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(callbackWithObjectRef)
+
+static std::optional<QString> calledWithString;
+static int callbackWithString(JNIEnv *, jobject, const QString &string)
+{
+ calledWithString.emplace(string);
+ return int(CallbackParameterType::String);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(callbackWithString)
+
+static std::optional<jbyte> calledWithByte;
+static int callbackWithByte(JNIEnv *, jobject, jbyte value)
+{
+ calledWithByte.emplace(value);
+ return int(CallbackParameterType::Byte);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(callbackWithByte)
+
+static std::optional<jbyte> calledWithBoolean;
+static int callbackWithBoolean(JNIEnv *, jobject, bool value)
+{
+ calledWithBoolean.emplace(value);
+ return int(CallbackParameterType::Boolean);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(callbackWithBoolean)
+
+static std::optional<int> calledWithInt;
+static int callbackWithInt(JNIEnv *, jobject, int value)
+{
+ calledWithInt.emplace(value);
+ return int(CallbackParameterType::Int);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(callbackWithInt)
+
+static std::optional<double> calledWithDouble;
+static int callbackWithDouble(JNIEnv *, jobject, double value)
+{
+ calledWithDouble.emplace(value);
+ return int(CallbackParameterType::Double);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(callbackWithDouble)
+
+static std::optional<QJniArray<jdouble>> calledWithJniArray;
+static int callbackWithJniArray(JNIEnv *, jobject, const QJniArray<jdouble> &value)
+{
+ calledWithJniArray.emplace(value);
+ return int(CallbackParameterType::JniArray);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(callbackWithJniArray)
+
+static std::optional<QList<double>> calledWithQList;
+static int callbackWithQList(JNIEnv *, jobject, const QList<double> &value)
+{
+ calledWithQList.emplace(value);
+ return int(CallbackParameterType::QList);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(callbackWithQList)
+
+static std::optional<QStringList> calledWithStringList;
+static int callbackWithStringList(JNIEnv *, jobject, const QStringList &value)
+{
+ calledWithStringList.emplace(value);
+ return int(CallbackParameterType::QStringList);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(callbackWithStringList)
+
+void tst_QJniObject::callback_data()
+{
+ QTest::addColumn<CallbackParameterType>("parameterType");
+
+ QTest::addRow("Object") << CallbackParameterType::Object;
+ QTest::addRow("ObjectRef") << CallbackParameterType::ObjectRef;
+ QTest::addRow("String") << CallbackParameterType::String;
+ QTest::addRow("Byte") << CallbackParameterType::Byte;
+ QTest::addRow("Boolean") << CallbackParameterType::Boolean;
+ QTest::addRow("Int") << CallbackParameterType::Int;
+ QTest::addRow("Double") << CallbackParameterType::Double;
+ QTest::addRow("JniArray") << CallbackParameterType::JniArray;
+ QTest::addRow("QList") << CallbackParameterType::QList;
+ QTest::addRow("QStringList") << CallbackParameterType::QStringList;
+}
+
+void tst_QJniObject::callback()
+{
+ QFETCH(const CallbackParameterType, parameterType);
+
+ TestClass testObject;
+ int result = -1;
+
+ switch (parameterType) {
+ case CallbackParameterType::Object:
+ QVERIFY(TestClass::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(callbackWithObject)
+ }));
+ result = testObject.callMethod<int>("callMeBackWithObject", testObject);
+ QVERIFY(calledWithObject);
+ QCOMPARE(calledWithObject.value(), testObject);
+ break;
+ case CallbackParameterType::ObjectRef:
+ QVERIFY(TestClass::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(callbackWithObjectRef)
+ }));
+ result = testObject.callMethod<int>("callMeBackWithObjectRef", testObject);
+ QVERIFY(calledWithObject);
+ QCOMPARE(calledWithObject.value(), testObject);
+ break;
+ case CallbackParameterType::String:
+ QVERIFY(TestClass::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(callbackWithString)
+ }));
+ result = testObject.callMethod<int>("callMeBackWithString", QString::number(123));
+ QVERIFY(calledWithString);
+ QCOMPARE(calledWithString.value(), "123");
+ break;
+ case CallbackParameterType::Byte:
+ QVERIFY(TestClass::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(callbackWithByte)
+ }));
+ result = testObject.callMethod<int>("callMeBackWithByte", jbyte(123));
+ QVERIFY(calledWithByte);
+ QCOMPARE(calledWithByte.value(), 123);
+ break;
+ case CallbackParameterType::Boolean:
+ QVERIFY(TestClass::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(callbackWithBoolean)
+ }));
+ result = testObject.callMethod<int>("callMeBackWithBoolean", true);
+ QVERIFY(calledWithBoolean);
+ QCOMPARE(calledWithBoolean.value(), true);
+ break;
+ case CallbackParameterType::Int:
+ QVERIFY(TestClass::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(callbackWithInt)
+ }));
+ result = testObject.callMethod<int>("callMeBackWithInt", 12345);
+ QVERIFY(calledWithInt);
+ QCOMPARE(calledWithInt.value(), 12345);
+ break;
+ case CallbackParameterType::Double:
+ QVERIFY(TestClass::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(callbackWithDouble)
+ }));
+ result = testObject.callMethod<int>("callMeBackWithDouble", 1.2345);
+ QVERIFY(calledWithDouble);
+ QCOMPARE(calledWithDouble.value(), 1.2345);
+ break;
+ case CallbackParameterType::JniArray: {
+ QVERIFY(TestClass::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(callbackWithJniArray)
+ }));
+ const QJniArray<double> doubles = { 1.2, 3.4, 5.6 };
+ result = testObject.callMethod<int>("callMeBackWithJniArray", doubles);
+ QVERIFY(calledWithJniArray);
+ QCOMPARE(calledWithJniArray, doubles);
+ break;
+ }
+ case CallbackParameterType::QList: {
+ QVERIFY(TestClass::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(callbackWithQList)
+ }));
+ const QList<double> doubles = { 1.2, 3.4, 5.6 };
+ result = testObject.callMethod<int>("callMeBackWithQList", doubles);
+ QVERIFY(calledWithQList);
+ QCOMPARE(calledWithQList.value(), doubles);
+ break;
+ }
+ case CallbackParameterType::QStringList: {
+ QVERIFY(TestClass::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(callbackWithStringList)
+ }));
+ const QStringList strings = { "one", "two" };
+ result = testObject.callMethod<int>("callMeBackWithStringList", strings);
+ QVERIFY(calledWithStringList);
+ QCOMPARE(calledWithStringList.value(), strings);
+ break;
+ }
+ }
+ QCOMPARE(result, int(parameterType));
+}
+
+// Make sure the new callStaticMethod overload taking a class, return type,
+// and argument as template parameters, doesn't break overload resolution
+// and that the class name doesn't get interpreted as the function name.
+void tst_QJniObject::callStaticOverloadResolution()
+{
+ const QString value = u"Hello World"_s;
+ QJniObject str = QJniObject::fromString(value);
+ const auto result = QJniObject::callStaticMethod<jstring, jstring>(
+ QtJniTypes::Traits<TestClass>::className(),
+ "staticEchoMethod", str.object<jstring>()).toString();
+ QCOMPARE(result, value);
+}
+
QTEST_MAIN(tst_QJniObject)
#include "tst_qjniobject.moc"
diff --git a/tests/auto/corelib/kernel/qjnitypes/CMakeLists.txt b/tests/auto/corelib/kernel/qjnitypes/CMakeLists.txt
index d3aef183a0..104b039d4d 100644
--- a/tests/auto/corelib/kernel/qjnitypes/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qjnitypes/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qjnitypes LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qjnitypes
SOURCES
tst_qjnitypes.cpp
diff --git a/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp b/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp
index 093c387dcc..bf582041f3 100644
--- a/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp
+++ b/tests/auto/corelib/kernel/qjnitypes/tst_qjnitypes.cpp
@@ -1,9 +1,12 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QtCore/qjnitypes.h>
+#include <QtCore/qjniarray.h>
+
+using namespace Qt::StringLiterals;
class tst_QJniTypes : public QObject
{
@@ -12,44 +15,62 @@ class tst_QJniTypes : public QObject
public:
tst_QJniTypes() = default;
+ static void nativeClassMethod(JNIEnv *, jclass, int);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(nativeClassMethod);
+
private slots:
void initTestCase();
void nativeMethod();
+ void construct();
+ void stringTypeCantBeArgument();
};
struct QtJavaWrapper {};
template<>
-constexpr auto QtJniTypes::typeSignature<QtJavaWrapper>()
+struct QtJniTypes::Traits<QtJavaWrapper>
{
- return QtJniTypes::String("Lorg/qtproject/qt/android/QtJavaWrapper;");
-}
+ static constexpr auto signature()
+ {
+ return QtJniTypes::CTString("Lorg/qtproject/qt/android/QtJavaWrapper;");
+ }
+};
template<>
-constexpr auto QtJniTypes::typeSignature<QJniObject>()
+struct QtJniTypes::Traits<QJniObject>
{
- return QtJniTypes::String("Ljava/lang/Object;");
-}
+ static constexpr auto signature()
+ {
+ return QtJniTypes::CTString("Ljava/lang/Object;");
+ }
+};
struct QtCustomJniObject : QJniObject {};
+
template<>
-constexpr auto QtJniTypes::typeSignature<QtCustomJniObject>()
+struct QtJniTypes::Traits<QtCustomJniObject>
{
- return QtJniTypes::String("Lorg/qtproject/qt/android/QtCustomJniObject;");
-}
+ static constexpr auto signature()
+ {
+ return QtJniTypes::CTString("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"));
+static_assert(QtJniTypes::Traits<QtJavaWrapper>::signature() == "Lorg/qtproject/qt/android/QtJavaWrapper;");
+static_assert(QtJniTypes::Traits<QtJavaWrapper>::signature() != "Ljava/lang/Object;");
+static_assert(!(QtJniTypes::Traits<QtJavaWrapper>::signature() == "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;");
+Q_DECLARE_JNI_CLASS(JavaType, "org/qtproject/qt/JavaType");
+static_assert(QtJniTypes::Traits<QtJniTypes::JavaType>::signature() == "Lorg/qtproject/qt/JavaType;");
+static_assert(QtJniTypes::Traits<QtJniTypes::JavaType[]>::signature() == "[Lorg/qtproject/qt/JavaType;");
-static_assert(QtJniTypes::className<jstring>() == "java/lang/String");
+Q_DECLARE_JNI_CLASS(String, "java/lang/String");
+static_assert(QtJniTypes::Traits<jstring>::className() == "java/lang/String");
+static_assert(QtJniTypes::Traits<QtJniTypes::String>::className() == "java/lang/String");
+static_assert(QtJniTypes::Traits<QtJniTypes::String>::signature() == "Ljava/lang/String;");
+static_assert(QtJniTypes::Traits<QtJniTypes::String[]>::signature() == "[Ljava/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::Traits<QtJniTypes::QtTextToSpeech>::className() == "org/qtproject/qt/android/speech/QtTextToSpeech");
static_assert(QtJniTypes::fieldSignature<jint>() == "I");
static_assert(QtJniTypes::fieldSignature<jint[]>() == "[I");
@@ -91,28 +112,49 @@ 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'));
+static_assert(QtJniTypes::CTString("ABCDE").startsWith("ABC"));
+static_assert(QtJniTypes::CTString("ABCDE").startsWith("A"));
+static_assert(QtJniTypes::CTString("ABCDE").startsWith("ABCDE"));
+static_assert(!QtJniTypes::CTString("ABCDE").startsWith("ABCDEF"));
+static_assert(!QtJniTypes::CTString("ABCDE").startsWith("9AB"));
+static_assert(QtJniTypes::CTString("ABCDE").startsWith('A'));
+static_assert(!QtJniTypes::CTString("ABCDE").startsWith('B'));
+
+static_assert(QtJniTypes::Traits<QJniArray<jobject>>::signature() == "[Ljava/lang/Object;");
+static_assert(QtJniTypes::Traits<QJniArray<jbyte>>::signature() == "[B");
+static_assert(QtJniTypes::isObjectType<QJniArray<jbyte>>());
+
+static_assert(QtJniTypes::CTString("ABCDE").endsWith("CDE"));
+static_assert(QtJniTypes::CTString("ABCDE").endsWith("E"));
+static_assert(QtJniTypes::CTString("ABCDE").endsWith("ABCDE"));
+static_assert(!QtJniTypes::CTString("ABCDE").endsWith("DEF"));
+static_assert(!QtJniTypes::CTString("ABCDE").endsWith("ABCDEF"));
+static_assert(QtJniTypes::CTString("ABCDE").endsWith('E'));
+static_assert(!QtJniTypes::CTString("ABCDE").endsWith('F'));
+
+enum UnscopedEnum {};
+enum class ScopedEnum {};
+enum class IntEnum : int {};
+enum class UnsignedEnum : unsigned {};
+enum class Int8Enum : int8_t {};
+enum class ShortEnum : short {};
+enum class LongEnum : quint64 {};
+enum class JIntEnum : jint {};
+
+static_assert(QtJniTypes::Traits<UnscopedEnum>::signature() == "I");
+static_assert(QtJniTypes::Traits<ScopedEnum>::signature() == "I");
+static_assert(QtJniTypes::Traits<IntEnum>::signature() == "I");
+static_assert(QtJniTypes::Traits<UnsignedEnum>::signature() == "I");
+static_assert(QtJniTypes::Traits<Int8Enum>::signature() == "B");
+static_assert(QtJniTypes::Traits<LongEnum>::signature() == "J");
+static_assert(QtJniTypes::Traits<JIntEnum>::signature() == "I");
void tst_QJniTypes::initTestCase()
{
}
-static bool nativeFunction(JNIEnv *, jclass, int, jstring, long)
+static bool nativeFunction(JNIEnv *, jclass, int, jstring, quint64)
{
return true;
}
@@ -120,12 +162,121 @@ Q_DECLARE_JNI_NATIVE_METHOD(nativeFunction)
static_assert(QtJniTypes::nativeMethodSignature(nativeFunction) == "(ILjava/lang/String;J)Z");
+static QString nativeFunctionStrings(JNIEnv *, jclass, const QString &, const QtJniTypes::String &)
+{
+ return QString();
+}
+Q_DECLARE_JNI_NATIVE_METHOD(nativeFunctionStrings)
+
+static_assert(QtJniTypes::nativeMethodSignature(nativeFunctionStrings)
+ == "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+
+static int forwardDeclaredNativeFunction(JNIEnv *, jobject, bool);
+Q_DECLARE_JNI_NATIVE_METHOD(forwardDeclaredNativeFunction)
+static int forwardDeclaredNativeFunction(JNIEnv *, jobject, bool) { return 0; }
+static_assert(QtJniTypes::nativeMethodSignature(forwardDeclaredNativeFunction) == "(Z)I");
+
+static_assert(QtJniTypes::nativeMethodSignature(tst_QJniTypes::nativeClassMethod) == "(I)V");
+void tst_QJniTypes::nativeClassMethod(JNIEnv *, jclass, int) {}
+
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");
+ {
+ const auto method = Q_JNI_NATIVE_METHOD(nativeFunction);
+ QVERIFY(method.fnPtr == QtJniMethods::va_nativeFunction);
+ QCOMPARE(method.name, "nativeFunction");
+ QCOMPARE(method.signature, "(ILjava/lang/String;J)Z");
+ }
+
+ {
+ const auto method = Q_JNI_NATIVE_METHOD(forwardDeclaredNativeFunction);
+ QVERIFY(method.fnPtr == QtJniMethods::va_forwardDeclaredNativeFunction);
+ }
+
+ {
+ const auto method = Q_JNI_NATIVE_SCOPED_METHOD(nativeClassMethod, tst_QJniTypes);
+ QVERIFY(method.fnPtr == va_nativeClassMethod);
+ }
+}
+
+void tst_QJniTypes::construct()
+{
+ using namespace QtJniTypes;
+
+ const QString text = u"Java String"_s;
+ String str(text);
+ QVERIFY(str.isValid());
+ QCOMPARE(str.toString(), text);
+
+ jobject jref = nullptr; // must be jobject, not jstring
+ {
+ // if jref would be a jstring, then this would call the
+ // Java String copy constructor!
+ String jstr(jref);
+ QVERIFY(!jstr.isValid());
+ }
+ jref = str.object<jstring>();
+ {
+ String jstr(jref);
+ QVERIFY(jstr.isValid());
+ QCOMPARE(jstr.toString(), text);
+ }
+
+ String str2 = str;
+ QCOMPARE(str.toString(), text);
+ String str3 = std::move(str2);
+ QCOMPARE(str3.toString(), text);
+}
+
+template <typename ...Arg>
+static constexpr bool isValidArgument(Arg &&...) noexcept
+{
+ return QtJniTypes::ValidSignatureTypesDetail<q20::remove_cvref_t<Arg>...>;
+}
+
+enum class Overload
+{
+ ClassNameAndMethod,
+ OnlyMethod,
+};
+
+template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+>
+static constexpr auto callStaticMethod(const char *className, const char *methodName, Args &&...)
+{
+ Q_UNUSED(className);
+ Q_UNUSED(methodName);
+ return Overload::ClassNameAndMethod;
+}
+
+template <typename Klass, typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+>
+static constexpr auto callStaticMethod(const char *methodName, Args &&...)
+{
+ Q_UNUSED(methodName);
+ return Overload::OnlyMethod;
+}
+
+void tst_QJniTypes::stringTypeCantBeArgument()
+{
+ const char *methodName = "staticEchoMethod";
+
+ static_assert(!isValidArgument(QtJniTypes::Traits<QtJniTypes::JavaType>::className()));
+ static_assert(!isValidArgument("someFunctionName"));
+ static_assert(!isValidArgument(methodName));
+ static_assert(!isValidArgument(QtJniTypes::Traits<QtJniTypes::JavaType>::className(),
+ "someFunctionName", methodName, 42));
+
+ static_assert(callStaticMethod<jstring, jint>("class name", "method name", 42)
+ == Overload::ClassNameAndMethod);
+ static_assert(callStaticMethod<QtJniTypes::JavaType, jint>("method name", 42)
+ == Overload::OnlyMethod);
}
QTEST_MAIN(tst_QJniTypes)
diff --git a/tests/auto/corelib/kernel/qmath/CMakeLists.txt b/tests/auto/corelib/kernel/qmath/CMakeLists.txt
index e0c9ef6b78..39a5a8b6df 100644
--- a/tests/auto/corelib/kernel/qmath/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qmath/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qmath.pro.
-
#####################################################################
## tst_qmath Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmath LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmath
SOURCES
tst_qmath.cpp
diff --git a/tests/auto/corelib/kernel/qmath/tst_qmath.cpp b/tests/auto/corelib/kernel/qmath/tst_qmath.cpp
index d3d470ea5f..1961b71d83 100644
--- a/tests/auto/corelib/kernel/qmath/tst_qmath.cpp
+++ b/tests/auto/corelib/kernel/qmath/tst_qmath.cpp
@@ -1,6 +1,6 @@
// 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <qmath.h>
diff --git a/tests/auto/corelib/kernel/qmetacontainer/CMakeLists.txt b/tests/auto/corelib/kernel/qmetacontainer/CMakeLists.txt
index 0e55db4ce6..fb58aebe73 100644
--- a/tests/auto/corelib/kernel/qmetacontainer/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qmetacontainer/CMakeLists.txt
@@ -1,15 +1,20 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qmetacontainer.pro.
-
#####################################################################
## tst_qmetacontainer Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmetacontainer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmetacontainer
SOURCES
tst_qmetacontainer.cpp
LIBRARIES
Qt::CorePrivate
+ Qt::TestPrivate
)
diff --git a/tests/auto/corelib/kernel/qmetacontainer/tst_qmetacontainer.cpp b/tests/auto/corelib/kernel/qmetacontainer/tst_qmetacontainer.cpp
index 606e94787a..cc1d8baa8e 100644
--- a/tests/auto/corelib/kernel/qmetacontainer/tst_qmetacontainer.cpp
+++ b/tests/auto/corelib/kernel/qmetacontainer/tst_qmetacontainer.cpp
@@ -1,7 +1,8 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
+#include <QtTest/private/qcomparisontesthelper_p.h>
#include <QtCore/qcontainerinfo.h>
#include <QtCore/qmetacontainer.h>
#include <QtCore/QMap>
@@ -157,6 +158,7 @@ private:
private slots:
void init();
+ void compareCompiles();
void testSequence_data();
void testSequence();
@@ -203,6 +205,12 @@ void tst_QMetaContainer::init()
};
}
+void tst_QMetaContainer::compareCompiles()
+{
+ QTestPrivate::testEqualityOperatorsCompile<QMetaSequence>();
+ QTestPrivate::testEqualityOperatorsCompile<QMetaAssociation>();
+}
+
void tst_QMetaContainer::cleanup()
{
qvector.clear();
@@ -497,6 +505,13 @@ void tst_QMetaContainer::testSequence()
QVERIFY(metaSequence.compareConstIterator(constIt, constEnd));
metaSequence.destroyConstIterator(constIt);
metaSequence.destroyConstIterator(constEnd);
+
+ QVERIFY(metaSequence.iface() != nullptr);
+ QMetaSequence defaultConstructed;
+ QVERIFY(defaultConstructed.iface() == nullptr);
+ QT_TEST_EQUALITY_OPS(QMetaSequence(), defaultConstructed, true);
+ QT_TEST_EQUALITY_OPS(QMetaSequence(), QMetaSequence(), true);
+ QT_TEST_EQUALITY_OPS(defaultConstructed, metaSequence, false);
}
void tst_QMetaContainer::testAssociation_data()
@@ -722,6 +737,12 @@ void tst_QMetaContainer::testAssociation()
QVERIFY(metaAssociation.compareConstIterator(constIt, constEnd));
metaAssociation.destroyConstIterator(constIt);
metaAssociation.destroyConstIterator(constEnd);
+
+ QVERIFY(metaAssociation.iface() != nullptr);
+ QMetaAssociation defaultConstructed;
+ QVERIFY(defaultConstructed.iface() == nullptr);
+ QT_TEST_EQUALITY_OPS(QMetaAssociation(), QMetaAssociation(), true);
+ QT_TEST_EQUALITY_OPS(QMetaAssociation(), metaAssociation, false);
}
QTEST_MAIN(tst_QMetaContainer)
diff --git a/tests/auto/corelib/kernel/qmetaenum/CMakeLists.txt b/tests/auto/corelib/kernel/qmetaenum/CMakeLists.txt
index 3f5bf4967f..46f3ece069 100644
--- a/tests/auto/corelib/kernel/qmetaenum/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qmetaenum/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qmetaenum.pro.
-
#####################################################################
## tst_qmetaenum Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmetaenum LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmetaenum
SOURCES
tst_qmetaenum.cpp
diff --git a/tests/auto/corelib/kernel/qmetaenum/tst_qmetaenum.cpp b/tests/auto/corelib/kernel/qmetaenum/tst_qmetaenum.cpp
index 980830e9ba..3d958a78fe 100644
--- a/tests/auto/corelib/kernel/qmetaenum/tst_qmetaenum.cpp
+++ b/tests/auto/corelib/kernel/qmetaenum/tst_qmetaenum.cpp
@@ -1,5 +1,5 @@
// 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -73,6 +73,9 @@ void tst_QMetaEnum::valuesToKeys()
QMetaEnum me = QMetaEnum::fromType<Qt::WindowFlags>();
QCOMPARE(me.valueToKeys(windowFlags), expected);
+ bool ok = false;
+ QCOMPARE(uint(me.keysToValue(expected, &ok)), windowFlags.toInt());
+ QVERIFY(ok);
}
void tst_QMetaEnum::defaultConstructed()
diff --git a/tests/auto/corelib/kernel/qmetamethod/CMakeLists.txt b/tests/auto/corelib/kernel/qmetamethod/CMakeLists.txt
index 979b578ce7..0d46aef8bd 100644
--- a/tests/auto/corelib/kernel/qmetamethod/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qmetamethod/CMakeLists.txt
@@ -1,13 +1,19 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qmetamethod.pro.
-
#####################################################################
## tst_qmetamethod Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmetamethod LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmetamethod
SOURCES
tst_qmetamethod.cpp
+ LIBRARIES
+ Qt::TestPrivate
)
diff --git a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp
index 4cc15159e7..59fb747524 100644
--- a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp
+++ b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp
@@ -1,9 +1,10 @@
// 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
+#include <QtTest/private/qcomparisontesthelper_p.h>
#include <QTypeRevision>
#include <qobject.h>
@@ -14,6 +15,7 @@ class tst_QMetaMethod : public QObject
Q_OBJECT
private slots:
+ void compareCompiles();
void method_data();
void method();
@@ -22,6 +24,7 @@ private slots:
void comparisonOperators();
void fromSignal();
+ void fromSignalOfNullSignalIsInvalid();
void gadget();
void revision();
@@ -165,6 +168,11 @@ QVariant MethodTestObject::qvariantSlotBoolIntUIntLonglongULonglongDoubleLongSho
}
void MethodTestObject::voidSlotNoParameterNames(bool, int) {}
+void tst_QMetaMethod::compareCompiles()
+{
+ QTestPrivate::testEqualityOperatorsCompile<QMetaMethod>();
+}
+
void tst_QMetaMethod::method_data()
{
QTest::addColumn<QByteArray>("signature");
@@ -646,6 +654,8 @@ void tst_QMetaMethod::method()
// Bogus indexes
QCOMPARE(method.parameterType(-1), 0);
QCOMPARE(method.parameterType(parameterTypes.size()), 0);
+ QT_TEST_EQUALITY_OPS(method, QMetaMethod(), false);
+ QT_TEST_EQUALITY_OPS(QMetaMethod(), QMetaMethod(), true);
}
void tst_QMetaMethod::invalidMethod()
@@ -658,6 +668,9 @@ void tst_QMetaMethod::invalidMethod()
QMetaMethod method3 = staticMetaObject.method(-1);
QVERIFY(!method3.isValid());
+ QT_TEST_EQUALITY_OPS(method, method2, true);
+ QT_TEST_EQUALITY_OPS(method2, method3, true);
+ QT_TEST_EQUALITY_OPS(method, method3, true);
}
void tst_QMetaMethod::comparisonOperators()
@@ -672,16 +685,9 @@ void tst_QMetaMethod::comparisonOperators()
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);
+ QT_TEST_EQUALITY_OPS(method, other, expectedEqual);
}
-
- QVERIFY(method != QMetaMethod());
- QVERIFY(QMetaMethod() != method);
- QVERIFY(!(method == QMetaMethod()));
- QVERIFY(!(QMetaMethod() == method));
+ QT_TEST_EQUALITY_OPS(method, QMetaMethod(), false);
}
}
@@ -690,8 +696,7 @@ void tst_QMetaMethod::comparisonOperators()
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));
+ QT_TEST_EQUALITY_OPS(method, constructor, false);
}
}
@@ -719,6 +724,12 @@ void tst_QMetaMethod::fromSignal()
#undef FROMSIGNAL_HELPER
}
+void tst_QMetaMethod::fromSignalOfNullSignalIsInvalid()
+{
+ constexpr decltype(&QObject::destroyed) ptr = nullptr;
+ QVERIFY(!QMetaMethod::fromSignal(ptr).isValid());
+}
+
class MyGadget {
Q_GADGET
public:
@@ -741,6 +752,7 @@ void tst_QMetaMethod::gadget()
QMetaMethod getValueMethod = MyGadget::staticMetaObject.method(idx);
QVERIFY(getValueMethod.isValid());
+ QT_TEST_EQUALITY_OPS(setValueMethod, getValueMethod, false);
{
MyGadget gadget;
QString string;
diff --git a/tests/auto/corelib/kernel/qmetaobject/CMakeLists.txt b/tests/auto/corelib/kernel/qmetaobject/CMakeLists.txt
index 53338d35b5..d17773ada6 100644
--- a/tests/auto/corelib/kernel/qmetaobject/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qmetaobject/CMakeLists.txt
@@ -1,13 +1,18 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmetaobject LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
set(tst_qmetaobject_SOURCES
tst_qmetaobject.cpp
forwarddeclared.h
forwarddeclared.cpp
)
-# Generated from qmetaobject.pro.
#####################################################################
## tst_qmetaobject Test:
diff --git a/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.cpp b/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.cpp
index 8772bc4e61..c736a63227 100644
--- a/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.cpp
+++ b/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.cpp
@@ -1,5 +1,5 @@
// 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "forwarddeclared.h"
diff --git a/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.h b/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.h
index 89fb0839c1..a0158f19cd 100644
--- a/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.h
+++ b/tests/auto/corelib/kernel/qmetaobject/forwarddeclared.h
@@ -1,5 +1,5 @@
// 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef FORWARDDECLARED_H
#define FORWARDDECLARED_H
diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
index 4c3bb8532b..182ec6daae 100644
--- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
+++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
@@ -1,13 +1,17 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QSignalSpy>
+#if QT_CONFIG(sortfilterproxymodel)
#include <QSortFilterProxyModel>
+#endif
#include <qobject.h>
#include <qmetaobject.h>
+#if QT_CONFIG(proxymodel)
#include <qabstractproxymodel.h>
+#endif
#include <private/qmetaobject_p.h>
Q_DECLARE_METATYPE(const QMetaObject *)
@@ -31,6 +35,8 @@ Q_DECLARE_METATYPE(const QMetaObject *)
# define Q_NO_ARG
#endif
+using namespace Qt::StringLiterals;
+
struct MyStruct
{
int i;
@@ -302,14 +308,16 @@ private slots:
void invokeTypedefTypes();
void invokeException();
void invokeQueuedAutoRegister();
+ void invokeFreeFunction();
+ void invokeBind();
void qtMetaObjectInheritance();
void normalizedSignature_data();
void normalizedSignature();
void normalizedType_data();
void normalizedType();
void customPropertyType();
- void checkScope_data();
- void checkScope();
+ void keysToValue_data();
+ void keysToValue(); // Also keyToValue()
void propertyNotify();
void propertyConstant();
void propertyFinal();
@@ -512,6 +520,8 @@ public slots:
qlonglong *sl15(qlonglong *);
MyForwardDeclaredType *sl16(MyForwardDeclaredType *);
+ void sl17(int *i) { *i = 242; }
+
void overloadedSlot();
void overloadedSlot(int, int);
void overloadedSlot(int);
@@ -1079,6 +1089,116 @@ void tst_QMetaObject::invokePointer()
QCOMPARE(obj.slotResult, QString("sl1:1"));
}
QCOMPARE(countedStructObjectsCount, 0);
+
+ // Invoking with parameters
+ QString result;
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, qReturnArg(result), u"bubu"_s));
+ QCOMPARE(obj.slotResult, u"sl1:bubu");
+ QCOMPARE(result, u"yessir");
+
+ // without taking return value
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, u"bubu"_s));
+ QCOMPARE(obj.slotResult, u"sl1:bubu");
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, Qt::DirectConnection, qReturnArg(result),
+ u"byebye"_s));
+ QCOMPARE(obj.slotResult, u"sl1:byebye");
+ QCOMPARE(result, u"yessir");
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, qOverload<int, int>(&QtTestObject::overloadedSlot), 1, 2));
+ QCOMPARE(obj.slotResult, u"overloadedSlot:1,2");
+
+ // non-const ref parameter
+ QString original = u"bubu"_s;
+ QString &ref = original;
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, qReturnArg(result), ref));
+ QCOMPARE(obj.slotResult, u"sl1:bubu");
+ QCOMPARE(result, u"yessir");
+
+ struct R {
+ bool operator()(int) { return true; }
+ int operator()(char) { return 15; }
+ int operator()(QString = {}, int = {}, int = {}) { return 242; }
+ } r;
+
+ // Test figuring out which operator() to call:
+ {
+ bool res = false;
+ QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res), 1));
+ QCOMPARE(res, true);
+ }
+ {
+ int res;
+ QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res), 'c'));
+ QCOMPARE(res, 15);
+ }
+ {
+ int res;
+ QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res)));
+ QCOMPARE(res, 242);
+ res = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res), u"bu"_s));
+ QCOMPARE(res, 242);
+ res = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res), u"bu"_s, 1));
+ QCOMPARE(res, 242);
+ res = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, r, qReturnArg(res), u"bu"_s, 1, 2));
+ QCOMPARE(res, 242);
+ }
+
+ {
+ auto lambda = [](const QString &s) { return s + s; };
+ QVERIFY(QMetaObject::invokeMethod(&obj, lambda, qReturnArg(result), u"bu"_s));
+ QCOMPARE(result, u"bubu");
+ }
+
+ {
+ auto lambda = [](const QString &s = u"bu"_s) { return s + s; };
+ QVERIFY(QMetaObject::invokeMethod(&obj, lambda, qReturnArg(result)));
+ QCOMPARE(result, u"bubu");
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, lambda, qReturnArg(result), u"bye"_s));
+ QCOMPARE(result, u"byebye");
+ }
+
+ {
+ auto lambda = [](const QString &s, qint64 a, qint16 b, qint8 c) {
+ return s + QString::number(a) + QString::number(b) + QString::number(c);
+ };
+ // Testing mismatching argument (int for qint64). The other arguments
+ // would static_assert for potential truncation if they were ints.
+ QVERIFY(QMetaObject::invokeMethod(&obj, lambda, qReturnArg(result), u"bu"_s, 1, qint16(2), qint8(3)));
+ QCOMPARE(result, u"bu123");
+ }
+ {
+ // Testing deduction
+ auto lambda = [](const QString &s, auto a) { return s + a; };
+ QVERIFY(QMetaObject::invokeMethod(&obj, lambda, qReturnArg(result), u"bu"_s, "bu"_L1));
+ QCOMPARE(result, u"bubu");
+
+ auto variadic = [](const QString &s, auto... a) { return s + (QString::number(a) + ...); };
+ QVERIFY(QMetaObject::invokeMethod(&obj, variadic, qReturnArg(result), u"bu"_s, 1, 2, 3, 4, 5, 6));
+ QCOMPARE(result, u"bu123456");
+ }
+ {
+ // Testing a functor returning void and accepting a pointer,
+ // this may trigger the pointer to be interpreted as the old void*
+ // return parameter.
+ bool invoked = false;
+ auto lambda = [&invoked](void *ptr) -> void {
+ Q_UNUSED(ptr);
+ invoked = true;
+ };
+ int i = 242;
+ QVERIFY(QMetaObject::invokeMethod(&obj, lambda, &i));
+ QVERIFY(invoked);
+
+ // member fn
+ i = 0;
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl17, &i));
+ QCOMPARE(i, 242);
+ }
}
void tst_QMetaObject::invokeQueuedMetaMember()
@@ -1343,6 +1463,12 @@ void tst_QMetaObject::invokeQueuedPointer()
QCOMPARE(var, 0);
}
QCOMPARE(countedStructObjectsCount, 0);
+
+ // Invoking with parameters
+ using namespace Qt::StringLiterals;
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, Qt::QueuedConnection, u"bubu"_s));
+ qApp->processEvents(QEventLoop::AllEvents);
+ QCOMPARE(obj.slotResult, u"sl1:bubu");
}
// this test is duplicated below
@@ -1762,6 +1888,18 @@ void tst_QMetaObject::invokeBlockingQueuedPointer()
Qt::BlockingQueuedConnection));
QCOMPARE(obj.slotResult, QString("sl1:hehe"));
}
+
+ // Test with parameters
+ QString result;
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl1, Qt::BlockingQueuedConnection,
+ qReturnArg(result), u"bubu"_s));
+ QCOMPARE(result, u"yessir");
+ QCOMPARE(obj.slotResult, u"sl1:bubu");
+
+ QVERIFY(QMetaObject::invokeMethod(&obj, &QtTestObject::sl2, Qt::BlockingQueuedConnection,
+ u"bubu"_s, u"baba"_s));
+ QCOMPARE(obj.slotResult, u"sl2:bububaba");
+
QVERIFY(QMetaObject::invokeMethod(&obj, [&](){obj.moveToThread(QThread::currentThread());}, Qt::BlockingQueuedConnection));
t.quit();
QVERIFY(t.wait());
@@ -1772,6 +1910,7 @@ void tst_QMetaObject::invokeBlockingQueuedPointer()
void tst_QMetaObject::qtMetaObjectInheritance()
{
QVERIFY(!QObject::staticMetaObject.superClass());
+#if QT_CONFIG(sortfilterproxymodel)
QCOMPARE(QSortFilterProxyModel::staticMetaObject.indexOfEnumerator("Qt::CaseSensitivity"), -1);
QCOMPARE(QSortFilterProxyModel::staticMetaObject.indexOfEnumerator("CaseSensitivity"), -1);
int indexOfSortCaseSensitivity = QSortFilterProxyModel::staticMetaObject.indexOfProperty("sortCaseSensitivity");
@@ -1779,6 +1918,7 @@ void tst_QMetaObject::qtMetaObjectInheritance()
QMetaProperty sortCaseSensitivity = QSortFilterProxyModel::staticMetaObject.property(indexOfSortCaseSensitivity);
QVERIFY(sortCaseSensitivity.isValid());
QCOMPARE(sortCaseSensitivity.enumerator().name(), "CaseSensitivity");
+#endif
}
struct MyType
@@ -1997,6 +2137,49 @@ void tst_QMetaObject::invokeQueuedAutoRegister()
QString("slotWithRegistrableArgument:myShared-myShared-myShared-myShared-00"));
}
+namespace FunctionTest {
+ static void function0() {}
+ static int functionNoExcept() noexcept
+ {
+ return 42;
+ }
+}
+
+void tst_QMetaObject::invokeFreeFunction()
+{
+ using namespace FunctionTest;
+ QtTestObject obj;
+ QMetaObject::invokeMethod(&obj, function0);
+ int retInt = -1;
+ QMetaObject::invokeMethod(&obj, functionNoExcept, &retInt);
+ QCOMPARE(retInt, functionNoExcept());
+}
+
+void tst_QMetaObject::invokeBind()
+{
+ QtTestObject obj;
+
+ struct {
+ int number;
+ QString string;
+ } results;
+
+ const auto function = [&results](int number, const QString &string) -> bool {
+ results.number = number;
+ results.string = string;
+ return true;
+ };
+
+ const int number = 42;
+ const QString string("Test");
+ const auto binding = std::bind(function, number, string);
+ bool ret = false;
+ QMetaObject::invokeMethod(&obj, binding, &ret);
+ QVERIFY(ret);
+ QCOMPARE(results.number, number);
+ QCOMPARE(results.string, string);
+}
+
void tst_QMetaObject::normalizedSignature_data()
{
QTest::addColumn<QString>("signature");
@@ -2162,7 +2345,7 @@ void tst_QMetaObject::customPropertyType()
QCOMPARE(prop.metaType().id(), QMetaType::QVariantList);
}
-void tst_QMetaObject::checkScope_data()
+void tst_QMetaObject::keysToValue_data()
{
QTest::addColumn<QObject *>("object");
QTest::addColumn<QByteArray>("name");
@@ -2176,7 +2359,7 @@ void tst_QMetaObject::checkScope_data()
}
-void tst_QMetaObject::checkScope()
+void tst_QMetaObject::keysToValue()
{
QFETCH(QObject *, object);
QFETCH(QByteArray, name);
@@ -2189,6 +2372,8 @@ void tst_QMetaObject::checkScope()
QVERIFY(!me.isFlag());
QCOMPARE(QByteArray(me.scope()), QByteArray("MyNamespace::" + name));
QCOMPARE(me.keyToValue("MyNamespace::" + name + "::MyEnum2", &ok), 1);
+ // Fully qualified unscoped enumerator
+ QCOMPARE(me.keyToValue("MyNamespace::" + name + "::MyEnum::MyEnum2", &ok), 1);
QCOMPARE(ok, true);
QCOMPARE(me.keyToValue(name + "::MyEnum2", &ok), -1);
QCOMPARE(ok, false);
@@ -2218,6 +2403,9 @@ void tst_QMetaObject::checkScope()
QCOMPARE(QByteArray(mf.scope()), QByteArray("MyNamespace::" + name));
QCOMPARE(mf.keysToValue("MyNamespace::" + name + "::MyFlag2", &ok), 2);
QCOMPARE(ok, true);
+ // Fully qualified
+ QCOMPARE(mf.keysToValue("MyNamespace::" + name + "::MyFlag::MyFlag2", &ok), 2);
+ QCOMPARE(ok, true);
QCOMPARE(mf.keysToValue(name + "::MyFlag2", &ok), -1);
QCOMPARE(ok, false);
QCOMPARE(mf.keysToValue("MyNamespace::MyFlag2", &ok), -1);
@@ -2227,7 +2415,12 @@ void tst_QMetaObject::checkScope()
QCOMPARE(mf.keysToValue("MyFlag", &ok), -1);
QCOMPARE(ok, false);
QCOMPARE(QLatin1String(mf.valueToKey(2)), QLatin1String("MyFlag2"));
- QCOMPARE(mf.keysToValue("MyNamespace::" + name + "::MyFlag1|MyNamespace::" + name + "::MyFlag2", &ok), 3);
+
+ const QByteArray prefix = "MyNamespace::" + name;
+ QCOMPARE(mf.keysToValue(prefix + "::MyFlag1|" + prefix + "::MyFlag2", &ok), 3);
+ QCOMPARE(ok, true);
+ // Fully qualified
+ QCOMPARE(mf.keysToValue(prefix + "::MyFlag::MyFlag1|" + prefix + "::MyFlag::MyFlag2", &ok), 3);
QCOMPARE(ok, true);
QCOMPARE(mf.keysToValue(name + "::MyFlag1|" + name + "::MyFlag2", &ok), -1);
QCOMPARE(ok, false);
@@ -2239,9 +2432,34 @@ void tst_QMetaObject::checkScope()
QCOMPARE(ok, true);
QCOMPARE(mf.keysToValue("MyFlag1|MyNamespace::" + name + "::MyFlag2", &ok), 3);
QCOMPARE(ok, true);
- QCOMPARE(mf.keysToValue("MyNamespace::" + name + "::MyFlag2|MyNamespace::" + name + "::MyFlag2", &ok), 2);
+ QCOMPARE(mf.keysToValue(prefix + "::MyFlag2|" + prefix + "::MyFlag2", &ok), 2);
+ QCOMPARE(ok, true);
+ // Fully qualified
+ QCOMPARE(mf.keysToValue(prefix + "::MyFlag::MyFlag2|" + prefix + "::MyFlag::MyFlag2", &ok), 2);
QCOMPARE(ok, true);
QCOMPARE(QLatin1String(mf.valueToKeys(3)), QLatin1String("MyFlag1|MyFlag2"));
+
+ // Test flags with extra '|'
+ QTest::ignoreMessage(QtWarningMsg,
+ QRegularExpression(u"QMetaEnum::keysToValue: malformed keys string, ends with '|'.+"_s));
+ QCOMPARE(mf.keysToValue("MyFlag1|MyFlag2|", &ok), -1);
+ QCOMPARE(ok, false);
+
+ QTest::ignoreMessage(QtWarningMsg,
+ QRegularExpression(u"QMetaEnum::keysToValue: malformed keys string, starts with '|'.+"_s));
+ QCOMPARE(mf.keysToValue("|MyFlag1|MyFlag2|", &ok), -1);
+ QCOMPARE(ok, false);
+
+ QTest::ignoreMessage(QtWarningMsg,
+ QRegularExpression(
+ u"QMetaEnum::keysToValue: malformed keys string, has two consecutive '|'.+"_s));
+ QCOMPARE(mf.keysToValue("MyFlag1||MyFlag2", &ok), -1);
+ QCOMPARE(ok, false);
+
+ // Test empty string
+ QTest::ignoreMessage(QtWarningMsg, "QMetaEnum::keysToValue: empty keys string.");
+ QCOMPARE(mf.keysToValue("", &ok), -1);
+ QCOMPARE(ok, false);
}
void tst_QMetaObject::propertyNotify()
@@ -2303,7 +2521,9 @@ void tst_QMetaObject::metaType()
{
QCOMPARE(QObject::staticMetaObject.metaType(), QMetaType::fromType<QObject>());
QCOMPARE(MyGadget::staticMetaObject.metaType(), QMetaType::fromType<MyGadget>());
+#if QT_CONFIG(proxymodel)
QCOMPARE(QAbstractProxyModel::staticMetaObject.metaType(), QMetaType::fromType<QAbstractProxyModel>());
+#endif
auto qtNameSpaceMetaType = Qt::staticMetaObject.metaType();
QVERIFY2(!qtNameSpaceMetaType.isValid(), qtNameSpaceMetaType.name());
}
diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/CMakeLists.txt b/tests/auto/corelib/kernel/qmetaobjectbuilder/CMakeLists.txt
index b55108c3cc..8551749db3 100644
--- a/tests/auto/corelib/kernel/qmetaobjectbuilder/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qmetaobjectbuilder.pro.
-
#####################################################################
## tst_qmetaobjectbuilder Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmetaobjectbuilder LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmetaobjectbuilder
SOURCES
tst_qmetaobjectbuilder.cpp
diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
index 38c896b6a3..18e4e2a4a9 100644
--- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
+++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QSignalSpy>
@@ -779,6 +779,26 @@ void tst_QMetaObjectBuilder::notifySignal()
void tst_QMetaObjectBuilder::enumerator()
{
+ static const QtPrivate::QMetaTypeInterface fooFlagMetaType = {
+ 0,
+ 8,
+ 8,
+ QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration | QMetaType::RelocatableType,
+ {},
+ nullptr,
+ "fooFlag",
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ };
+
QMetaObjectBuilder builder;
// Add an enumerator and check its attributes.
@@ -810,6 +830,7 @@ void tst_QMetaObjectBuilder::enumerator()
enum1.setIsFlag(true);
enum1.setIsScoped(true);
enum1.setEnumName(QByteArrayLiteral("fooFlag"));
+ enum1.setMetaType(QMetaType(&fooFlagMetaType));
QCOMPARE(enum1.addKey("ABC", 0), 0);
QCOMPARE(enum1.addKey("DEF", 1), 1);
QCOMPARE(enum1.addKey("GHI", -1), 2);
@@ -819,6 +840,7 @@ void tst_QMetaObjectBuilder::enumerator()
QVERIFY(enum1.isFlag());
QVERIFY(enum1.isScoped());
QCOMPARE(enum1.enumName(), QByteArray("fooFlag"));
+ QCOMPARE(enum1.metaType(), QMetaType(&fooFlagMetaType));
QCOMPARE(enum1.keyCount(), 3);
QCOMPARE(enum1.index(), 0);
QCOMPARE(enum1.key(0), QByteArray("ABC"));
diff --git a/tests/auto/corelib/kernel/qmetaproperty/CMakeLists.txt b/tests/auto/corelib/kernel/qmetaproperty/CMakeLists.txt
index 9762ea2642..49c07afd87 100644
--- a/tests/auto/corelib/kernel/qmetaproperty/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qmetaproperty/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qmetaproperty.pro.
-
#####################################################################
## tst_qmetaproperty Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmetaproperty LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmetaproperty
SOURCES
tst_qmetaproperty.cpp
diff --git a/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp b/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp
index f459068bef..3bf6211a53 100644
--- a/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp
+++ b/tests/auto/corelib/kernel/qmetaproperty/tst_qmetaproperty.cpp
@@ -1,6 +1,6 @@
// 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -22,7 +22,14 @@ struct CustomType
Q_DECLARE_METATYPE(CustomType)
-class tst_QMetaProperty : public QObject
+class Base : public QObject
+{
+ Q_OBJECT
+signals:
+ void baseSignal(int);
+};
+
+class tst_QMetaProperty : public Base
{
Q_OBJECT
Q_PROPERTY(EnumType value WRITE setValue READ getValue)
@@ -33,6 +40,7 @@ class tst_QMetaProperty : public QObject
Q_PROPERTY(int value10 READ value10 FINAL)
Q_PROPERTY(QMap<int, int> map MEMBER map)
Q_PROPERTY(CustomType custom MEMBER custom)
+ Q_PROPERTY(int propWithInheritedSig READ propWithInheritedSig NOTIFY baseSignal)
private slots:
void hasStdCppSet();
@@ -43,6 +51,7 @@ private slots:
void mapProperty();
void conversion();
void enumsFlags();
+ void notifySignalIndex();
public:
enum EnumType { EnumType1 };
@@ -57,6 +66,8 @@ public:
int value9() const { return 1; }
int value10() const { return 1; }
+ int propWithInheritedSig() const { return 0; }
+
QString value7;
QMap<int, int> map;
CustomType custom;
@@ -159,11 +170,55 @@ public:
{}
};
+enum FreeEnum {
+ FreeEnumValue1,
+ FreeEnumValue2
+};
+
+namespace MySpace {
+ enum NamespacedEnum {
+ NamespacedEnumValue1,
+ NamespacedEnumValue2
+ };
+};
+
+namespace MyQtSpace {
+ Q_NAMESPACE
+ enum NamespacedEnum {
+ NamespacedEnumValue1,
+ NamespacedEnumValue2
+ };
+ Q_DECLARE_FLAGS(NamespacedFlags, NamespacedEnum)
+ Q_FLAG_NS(NamespacedFlags)
+};
+
+namespace SeparateEnumNamespace {
+ Q_NAMESPACE
+ enum Enum {
+ Value1,
+ Value2
+ };
+ Q_ENUM_NS(Enum)
+};
+namespace SeparateFlagsNamespace {
+ Q_NAMESPACE
+ Q_DECLARE_FLAGS(Flags, SeparateEnumNamespace::Enum)
+ Q_FLAG_NS(Flags)
+}
+
class EnumFlagsTester : public QObject
{
Q_OBJECT
Q_PROPERTY(TestEnum enumProperty READ enumProperty WRITE setEnumProperty)
Q_PROPERTY(TestFlags flagProperty READ flagProperty WRITE setFlagProperty)
+
+ Q_PROPERTY(FreeEnum freeEnumProperty READ freeEnumProperty WRITE setFreeEnumProperty)
+ Q_PROPERTY(MySpace::NamespacedEnum namespacedEnumProperty READ namespacedEnumProperty WRITE setNamespacedEnumProperty)
+ Q_PROPERTY(MyQtSpace::NamespacedEnum qtNamespacedEnumProperty READ qtNamespacedEnumProperty WRITE setQtNamespacedEnumProperty)
+ Q_PROPERTY(MyQtSpace::NamespacedFlags qtNamespacedFlagProperty READ qtNamespacedFlagProperty WRITE setQtNamespacedFlagProperty)
+
+ Q_PROPERTY(SeparateEnumNamespace::Enum sepEnum READ sepEnum WRITE setSepEnum)
+ Q_PROPERTY(SeparateFlagsNamespace::Flags sepFlags READ sepFlags WRITE setSepFlags)
public:
enum TestEnum { e1, e2 };
Q_ENUM(TestEnum)
@@ -179,9 +234,35 @@ public:
TestFlags flagProperty() const { return m_flags; }
void setFlagProperty(TestFlags f) { m_flags = f; }
+ FreeEnum freeEnumProperty() const { return m_freeEnum; }
+ void setFreeEnumProperty(FreeEnum e) { m_freeEnum = e; }
+
+ MySpace::NamespacedEnum namespacedEnumProperty() const { return m_namespacedEnum; }
+ void setNamespacedEnumProperty(MySpace::NamespacedEnum e) { m_namespacedEnum = e; }
+
+ MyQtSpace::NamespacedEnum qtNamespacedEnumProperty() const { return m_qtNamespaceEnum; }
+ void setQtNamespacedEnumProperty(MyQtSpace::NamespacedEnum e) { m_qtNamespaceEnum = e; }
+
+ MyQtSpace::NamespacedFlags qtNamespacedFlagProperty() const { return m_qtNamespaceFlags; }
+ void setQtNamespacedFlagProperty(MyQtSpace::NamespacedFlags f) { m_qtNamespaceFlags = f; }
+
+ SeparateEnumNamespace::Enum sepEnum() const { return m_sepEnum; }
+ void setSepEnum(SeparateEnumNamespace::Enum e) { m_sepEnum = e; }
+
+ SeparateFlagsNamespace::Flags sepFlags() const { return m_sepFlags; }
+ void setSepFlags(SeparateFlagsNamespace::Flags f) { m_sepFlags = f; }
+
private:
TestEnum m_enum = e1;
TestFlags m_flags;
+
+ FreeEnum m_freeEnum = FreeEnum::FreeEnumValue1;
+ MySpace::NamespacedEnum m_namespacedEnum = MySpace::NamespacedEnumValue1;
+ MyQtSpace::NamespacedEnum m_qtNamespaceEnum = MyQtSpace::NamespacedEnumValue1;
+ MyQtSpace::NamespacedFlags m_qtNamespaceFlags;
+
+ SeparateEnumNamespace::Enum m_sepEnum = SeparateEnumNamespace::Value1;
+ SeparateFlagsNamespace::Flags m_sepFlags;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(EnumFlagsTester::TestFlags)
@@ -254,7 +335,7 @@ void tst_QMetaProperty::conversion()
void tst_QMetaProperty::enumsFlags()
{
// QTBUG-83689, verify that enumerations and flags can be assigned from int,
- // which is important for Qt Designer.
+ // which is important for Qt Widgets Designer.
EnumFlagsTester t;
auto mo = t.metaObject();
@@ -265,6 +346,7 @@ void tst_QMetaProperty::enumsFlags()
QVERIFY(enumProperty.metaType().flags().testFlag(QMetaType::IsEnumeration));
QVERIFY(enumProperty.write(&t, QVariant(int(EnumFlagsTester::e2))));
QCOMPARE(t.enumProperty(), EnumFlagsTester::e2);
+ QVERIFY(enumProperty.enumerator().isValid()); // OK: Q_ENUM
const int flagsIndex = mo->indexOfProperty("flagProperty");
QVERIFY(flagsIndex >= 0);
@@ -272,6 +354,70 @@ void tst_QMetaProperty::enumsFlags()
QVERIFY(flagsProperty.metaType().flags().testFlag(QMetaType::IsEnumeration));
QVERIFY(flagsProperty.write(&t, QVariant(int(EnumFlagsTester::flag2))));
QCOMPARE(t.flagProperty(), EnumFlagsTester::flag2);
+ QVERIFY(!flagsProperty.enumerator().isValid()); // Not using Q_FLAG
+
+ const int freeEnumIndex = mo->indexOfProperty("freeEnumProperty");
+ QVERIFY(freeEnumIndex >= 0);
+ auto freeEnumProperty = mo->property(freeEnumIndex);
+ QVERIFY(freeEnumProperty.metaType().flags().testFlag(QMetaType::IsEnumeration));
+ QVERIFY(freeEnumProperty.write(&t, QVariant(FreeEnumValue2)));
+ QCOMPARE(t.freeEnumProperty(), FreeEnumValue2);
+ QVERIFY(!freeEnumProperty.enumerator().isValid()); // Not using Q_ENUM
+
+ const int namespacedEnumIndex = mo->indexOfProperty("namespacedEnumProperty");
+ QVERIFY(namespacedEnumIndex >= 0);
+ auto namespacedEnumProperty = mo->property(namespacedEnumIndex);
+ QVERIFY(namespacedEnumProperty.metaType().flags().testFlag(QMetaType::IsEnumeration));
+ QVERIFY(namespacedEnumProperty.write(&t, QVariant(MySpace::NamespacedEnumValue2)));
+ QCOMPARE(t.namespacedEnumProperty(), MySpace::NamespacedEnumValue2);
+ QVERIFY(!namespacedEnumProperty.enumerator().isValid()); // Not using Q_NAMESPACE/Q_ENUM_NS
+
+ const int qtNamespacedEnumIndex = mo->indexOfProperty("qtNamespacedEnumProperty");
+ QVERIFY(qtNamespacedEnumIndex >= 0);
+ auto qtNamespacedEnumProperty = mo->property(qtNamespacedEnumIndex);
+ QVERIFY(qtNamespacedEnumProperty.metaType().flags().testFlag(QMetaType::IsEnumeration));
+ QVERIFY(qtNamespacedEnumProperty.write(&t, QVariant(MyQtSpace::NamespacedEnumValue2)));
+ QCOMPARE(t.qtNamespacedEnumProperty(), MyQtSpace::NamespacedEnumValue2);
+ QVERIFY(qtNamespacedEnumProperty.enumerator().isValid()); // OK: Q_ENUM_NS
+
+ const int qtNamespacedFlagIndex = mo->indexOfProperty("qtNamespacedFlagProperty");
+ QVERIFY(qtNamespacedFlagIndex >= 0);
+ auto qtNamespacedFlagProperty = mo->property(qtNamespacedFlagIndex);
+ QVERIFY(qtNamespacedFlagProperty.metaType().flags().testFlag(QMetaType::IsEnumeration));
+ QVERIFY(qtNamespacedFlagProperty.write(&t, QVariant(MyQtSpace::NamespacedFlags(MyQtSpace::NamespacedEnumValue2))));
+ QCOMPARE(t.qtNamespacedFlagProperty(), MyQtSpace::NamespacedFlags(MyQtSpace::NamespacedEnumValue2));
+ QVERIFY(qtNamespacedFlagProperty.enumerator().isValid()); // OK: Q_FLAG
+
+ const int sepEnumIndex = mo->indexOfProperty("sepEnum");
+ QVERIFY(sepEnumIndex >= 0);
+ auto sepEnumProperty = mo->property(sepEnumIndex);
+ QVERIFY(sepEnumProperty.metaType().flags().testFlag(QMetaType::IsEnumeration));
+ QVERIFY(sepEnumProperty.write(&t, QVariant(SeparateEnumNamespace::Value2)));
+ QCOMPARE(t.sepEnum(), SeparateEnumNamespace::Value2);
+ QVERIFY(sepEnumProperty.enumerator().isValid()); // OK: Q_ENUM_NS
+
+ const int sepFlagsIndex = mo->indexOfProperty("sepFlags");
+ QVERIFY(sepFlagsIndex >= 0);
+ auto sepFlagsProperty = mo->property(sepFlagsIndex);
+ QVERIFY(sepFlagsProperty.metaType().flags().testFlag(QMetaType::IsEnumeration));
+ QVERIFY(sepFlagsProperty.write(&t, QVariant(SeparateEnumNamespace::Value1)));
+ QCOMPARE(t.sepFlags(), SeparateEnumNamespace::Value1);
+ QVERIFY(!sepFlagsProperty.enumerator().isValid()); // NOK: the meta object is empty
+}
+
+
+void tst_QMetaProperty::notifySignalIndex()
+{
+ auto mo = this->metaObject();
+ auto propIndex = mo->indexOfProperty("propWithInheritedSig");
+ auto propWithInheritedSig = mo->property(propIndex);
+ QVERIFY(propWithInheritedSig.isValid());
+ QVERIFY(propWithInheritedSig.hasNotifySignal());
+ QVERIFY(propWithInheritedSig.notifySignalIndex() != -1);
+ QMetaMethod notifySignal = propWithInheritedSig.notifySignal();
+ QVERIFY(notifySignal.isValid());
+ QCOMPARE(notifySignal.name(), "baseSignal");
+ QCOMPARE(notifySignal.parameterCount(), 1);
}
QTEST_MAIN(tst_QMetaProperty)
diff --git a/tests/auto/corelib/kernel/qmetatype/CMakeLists.txt b/tests/auto/corelib/kernel/qmetatype/CMakeLists.txt
index 5ae87eb3a3..65bec3e187 100644
--- a/tests/auto/corelib/kernel/qmetatype/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qmetatype/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qmetatype.pro.
-
#####################################################################
## tst_qmetatype Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmetatype LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
list(APPEND test_data "./typeFlags.bin")
@@ -47,6 +51,7 @@ qt_internal_add_test(tst_qmetatype
../../../other/qvariant_common
LIBRARIES
Qt::CorePrivate
+ Qt::TestPrivate
Qt::Gui
qmetatype_lib1
qmetatype_lib2
diff --git a/tests/auto/corelib/kernel/qmetatype/lib1.cpp b/tests/auto/corelib/kernel/qmetatype/lib1.cpp
index 6a811241e9..e5478f87d9 100644
--- a/tests/auto/corelib/kernel/qmetatype/lib1.cpp
+++ b/tests/auto/corelib/kernel/qmetatype/lib1.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 Intel Corporation
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#define LIB_NAMESPACE Lib1
#include "lib_common.cpp"
diff --git a/tests/auto/corelib/kernel/qmetatype/lib2.cpp b/tests/auto/corelib/kernel/qmetatype/lib2.cpp
index b5a56f6c68..0a905e669a 100644
--- a/tests/auto/corelib/kernel/qmetatype/lib2.cpp
+++ b/tests/auto/corelib/kernel/qmetatype/lib2.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 Intel Corporation
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#define LIB_NAMESPACE Lib2
#include "lib_common.cpp"
diff --git a/tests/auto/corelib/kernel/qmetatype/lib_common.cpp b/tests/auto/corelib/kernel/qmetatype/lib_common.cpp
index f0554ec25c..179f539ccc 100644
--- a/tests/auto/corelib/kernel/qmetatype/lib_common.cpp
+++ b/tests/auto/corelib/kernel/qmetatype/lib_common.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 Intel Corporation
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qcollator.h>
#include "tst_qmetatype_libs.h"
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
index f0efff3436..81bf5d5ea8 100644
--- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmetatype.h"
@@ -13,6 +13,8 @@
#include <memory>
#include <vector>
+#include <QtCore/qflags.h>
+
Q_DECLARE_METATYPE(QMetaType::Type)
Q_DECLARE_METATYPE(QPartialOrdering)
@@ -121,6 +123,26 @@ static_assert(!QTypeTraits::has_operator_less_than_v<std::tuple<int, Nested2>>);
static_assert(QTypeTraits::has_operator_equal_v<std::tuple<int, Nested2>>);
static_assert(!QTypeTraits::has_operator_less_than_v<std::tuple<int, Nested2>>);
+// optionals of nesteds
+static_assert(QTypeTraits::has_operator_equal_v<std::optional<std::variant<QString>>>);
+static_assert(QTypeTraits::has_operator_less_than_v<std::optional<std::variant<QString>>>);
+static_assert(!QTypeTraits::has_operator_equal_v<std::optional<std::variant<NoOperators>>>);
+static_assert(!QTypeTraits::has_operator_less_than_v<std::optional<std::variant<NoOperators>>>);
+
+static_assert(QTypeTraits::has_operator_equal_v<std::optional<Nested>>);
+static_assert(!QTypeTraits::has_operator_less_than_v<std::optional<Nested>>);
+static_assert(QTypeTraits::has_operator_equal_v<std::optional<std::tuple<int, Nested>>>);
+static_assert(!QTypeTraits::has_operator_less_than_v<std::optional<std::tuple<int, Nested>>>);
+static_assert(QTypeTraits::has_operator_equal_v<std::optional<std::tuple<int, Nested>>>);
+static_assert(!QTypeTraits::has_operator_less_than_v<std::optional<std::tuple<int, Nested>>>);
+
+static_assert(QTypeTraits::has_operator_equal_v<std::optional<Nested2>>);
+static_assert(!QTypeTraits::has_operator_less_than_v<std::optional<Nested2>>);
+static_assert(QTypeTraits::has_operator_equal_v<std::optional<std::tuple<int, Nested2>>>);
+static_assert(!QTypeTraits::has_operator_less_than_v<std::optional<std::tuple<int, Nested2>>>);
+static_assert(QTypeTraits::has_operator_equal_v<std::optional<std::tuple<int, Nested2>>>);
+static_assert(!QTypeTraits::has_operator_less_than_v<std::optional<std::tuple<int, Nested2>>>);
+
}
struct BaseGenericType
@@ -1198,7 +1220,7 @@ void tst_QMetaType::flagsBinaryCompatibility6_0_data()
QTest::addColumn<quint32>("flags");
QFile file(QFINDTESTDATA("typeFlags.bin"));
- file.open(QIODevice::ReadOnly);
+ QVERIFY(file.open(QIODevice::ReadOnly));
QList<quint32> buffer;
QDataStream ds(&file);
ds >> buffer;
@@ -1838,6 +1860,58 @@ void tst_QMetaType::isEnum()
QVERIFY((QMetaType(type6).flags() & QMetaType::IsEnumeration) == QMetaType::IsEnumeration);
}
+enum E1 : unsigned char {};
+enum E2 : qlonglong {};
+enum class E3 : unsigned short {};
+
+namespace myflags {
+
+ Q_NAMESPACE
+
+ enum Flag1 : int { A, B };
+ enum Flag2 : short { X, Y };
+
+ Q_DECLARE_FLAGS(Flags1, myflags::Flag1);
+ Q_FLAG_NS(Flags1)
+ Q_DECLARE_FLAGS(Flags2, myflags::Flag2);
+ Q_FLAG_NS(Flags2)
+
+}
+
+template <typename T>
+using getUnderlyingTypeNormalized = std::conditional_t<
+ std::is_signed_v<std::underlying_type_t<T>>,
+ typename QIntegerForSize<sizeof(T)>::Signed,
+ typename QIntegerForSize<sizeof(T)>::Unsigned
+>;
+
+void tst_QMetaType::underlyingType_data()
+{
+ QTest::addColumn<QMetaType>("source");
+ QTest::addColumn<QMetaType>("underlying");
+
+ QTest::newRow("invalid") << QMetaType() << QMetaType();
+ QTest::newRow("plain") << QMetaType::fromType<isEnumTest_Enum1>()
+ << QMetaType::fromType<getUnderlyingTypeNormalized<isEnumTest_Enum1>>();
+ QTest::newRow("uchar") << QMetaType::fromType<E1>()
+ << QMetaType::fromType<getUnderlyingTypeNormalized<E1>>();
+ QTest::newRow("long") << QMetaType::fromType<E2>()
+ << QMetaType::fromType<getUnderlyingTypeNormalized<E2>>();
+ QTest::newRow("class_ushort") << QMetaType::fromType<E3>()
+ << QMetaType::fromType<getUnderlyingTypeNormalized<E3>>();
+ QTest::newRow("flags_int") << QMetaType::fromType<myflags::Flags1>()
+ << QMetaType::fromType<int>();
+ QTest::newRow("flags_short") << QMetaType::fromType<myflags::Flags2>()
+ << QMetaType::fromType<int>(); // sic, not short!
+}
+
+void tst_QMetaType::underlyingType()
+{
+ QFETCH(QMetaType, source);
+ QFETCH(QMetaType, underlying);
+ QCOMPARE(source.underlyingType(), underlying);
+}
+
void tst_QMetaType::isRegisteredStaticLess_data()
{
isRegistered_data();
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h
index 75bb363549..bcd2fe2def 100644
--- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h
+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include "tst_qmetatype_common.h"
@@ -91,6 +91,8 @@ private slots:
void isRegisteredStaticLess();
void isNotRegistered();
void isEnum();
+ void underlyingType_data();
+ void underlyingType();
void automaticTemplateRegistration_1();
void automaticTemplateRegistration_2(); // defined in tst_qmetatype3.cpp
void saveAndLoadBuiltin_data();
@@ -109,6 +111,7 @@ private slots:
void customDebugStream();
void unknownType();
void fromType();
+ void compareCompiles();
void operatorEq_data();
void operatorEq();
void operatorEq2_data();
@@ -118,6 +121,7 @@ private slots:
void typesWithInaccessibleDTors();
void voidIsNotUnknown();
void typeNameNormalization();
+ void typeNameInQtPrivate();
// Tests for deprecated APIs
#if QT_DEPRECATED_SINCE(6, 0)
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp
index c0650c7b1a..661c1f6072 100644
--- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp
+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype2.cpp
@@ -1,10 +1,11 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmetatype.h"
#include "tst_qmetatype_libs.h"
#include <QtCore/private/qmetaobjectbuilder_p.h>
+#include <QtTest/private/qcomparisontesthelper_p.h>
void tst_QMetaType::constRefs()
{
@@ -418,6 +419,11 @@ struct CharTemplate
} y;
};
+void tst_QMetaType::compareCompiles()
+{
+ QTestPrivate::testEqualityOperatorsCompile<QMetaType>();
+}
+
void tst_QMetaType::operatorEq_data()
{
QTest::addColumn<QMetaType>("typeA");
@@ -447,10 +453,7 @@ void tst_QMetaType::operatorEq()
QFETCH(QMetaType, typeB);
QFETCH(bool, eq);
- QCOMPARE(typeA == typeB, eq);
- QCOMPARE(typeB == typeA, eq);
- QCOMPARE(typeA != typeB, !eq);
- QCOMPARE(typeB != typeA, !eq);
+ QT_TEST_EQUALITY_OPS(typeA, typeB, eq);
#if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY)
// for built-in types or locally-defined types, this must also hold true
@@ -487,10 +490,10 @@ FOR_EACH_CORE_METATYPE(GET_METATYPE_FROM_TYPE)
QCOMPARE(fromId2.id(), type);
// confirm that they're all equal
- QCOMPARE(fromId1, fromId2);
- QCOMPARE(fromType1, fromType2);
- QCOMPARE(fromType1, fromId1);
- QCOMPARE(fromType2, fromId2);
+ QT_TEST_EQUALITY_OPS(fromId1, fromId2, true);
+ QT_TEST_EQUALITY_OPS(fromType1, fromType2, true);
+ QT_TEST_EQUALITY_OPS(fromType1, fromId1, true);
+ QT_TEST_EQUALITY_OPS(fromType2, fromId2, true);
#if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY)
// for built-in types (other than void), this must be true
@@ -541,7 +544,7 @@ void tst_QMetaType::operatorEqAcrossLibs()
// DO THIS FIRST:
// if this isn't a built-in type, then the QMetaTypeInterface::typeId is
// initially set to 0
- QCOMPARE(lib1Type, lib2Type);
+ QT_TEST_EQUALITY_OPS(lib1Type, lib2Type, true);
int actualTypeId = localType.id();
bool builtinTypeExpected = builtinTypeId != QMetaType::UnknownType;
@@ -559,8 +562,8 @@ void tst_QMetaType::operatorEqAcrossLibs()
QCOMPARE(lib2Type.id(), actualTypeId);
QCOMPARE(QByteArray(lib1Type.name()), QByteArray(localType.name()));
QCOMPARE(QByteArray(lib2Type.name()), QByteArray(localType.name()));
- QCOMPARE(lib1Type, localType);
- QCOMPARE(lib2Type, localType);
+ QT_TEST_EQUALITY_OPS(lib1Type, localType, true);
+ QT_TEST_EQUALITY_OPS(lib2Type, localType, true);
#if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY)
if (actualTypeId < QMetaType::FirstGuiType && actualTypeId != QMetaType::Void) {
@@ -640,6 +643,21 @@ void tst_QMetaType::typeNameNormalization()
}
}
+QT_BEGIN_NAMESPACE
+namespace QtPrivate { struct tst_QMetaType_TestType {}; }
+QT_END_NAMESPACE
+
+void tst_QMetaType::typeNameInQtPrivate()
+{
+ using T = QT_PREPEND_NAMESPACE(QtPrivate::tst_QMetaType_TestType);
+
+ // some compilers (GCC) are known to suppress the namespace prefix if the
+ // type and the function where it is expanded on are on the same namespace
+ static constexpr char expectedName[] = "QtPrivate::tst_QMetaType_TestType";
+ QMetaType mt = QMetaType::fromType<T>();
+ QCOMPARE(mt.name(), expectedName);
+}
+
#if QT_DEPRECATED_SINCE(6, 0)
void tst_QMetaType::testDeprecatedGetters()
{
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype3.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype3.cpp
index 95577e16da..779044589c 100644
--- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype3.cpp
+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype3.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "tst_qmetatype.h"
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype_common.h b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype_common.h
index e643209fbb..ea59b5cd02 100644
--- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype_common.h
+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype_common.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Used by both tst_qmetatype and tst_qsettings
diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype_libs.h b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype_libs.h
index 908e80f9a8..673fc9083c 100644
--- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype_libs.h
+++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype_libs.h
@@ -1,5 +1,5 @@
// Copyright (C) 2022 Intel Corporation
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef TST_QMETATYPE_LIBS_H
#define TST_QMETATYPE_LIBS_H
diff --git a/tests/auto/corelib/kernel/qmimedata/CMakeLists.txt b/tests/auto/corelib/kernel/qmimedata/CMakeLists.txt
index d3701a529e..ba1d5e12cd 100644
--- a/tests/auto/corelib/kernel/qmimedata/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qmimedata/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qmimedata.pro.
-
#####################################################################
## tst_qmimedata Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmimedata LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qmimedata
SOURCES
tst_qmimedata.cpp
diff --git a/tests/auto/corelib/kernel/qmimedata/tst_qmimedata.cpp b/tests/auto/corelib/kernel/qmimedata/tst_qmimedata.cpp
index 6bf00ff57e..e28a2e98cc 100644
--- a/tests/auto/corelib/kernel/qmimedata/tst_qmimedata.cpp
+++ b/tests/auto/corelib/kernel/qmimedata/tst_qmimedata.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -85,6 +85,14 @@ void tst_QMimeData::data() const
QCOMPARE(mimeData.data("text/markdown"), QByteArray("vikings"));
QCOMPARE(mimeData.data("text/html"), QByteArray("ninjas"));
QCOMPARE(mimeData.data("text/plain"), QByteArray("pirates"));
+
+ // URI list
+ QByteArray list = "https://example.com/\r\nhttps://example.net/\r\nhttps://example.org/\r\n";
+ mimeData.setData("text/uri-list", list);
+ QCOMPARE(mimeData.data("text/uri-list"), list);
+
+ mimeData.setData("text/uri-list", list.chopped(2)); // without the ending CRLF
+ QCOMPARE(mimeData.data("text/uri-list"), list);
}
void tst_QMimeData::formats() const
@@ -310,7 +318,8 @@ void tst_QMimeData::setUrls() const
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()) {
+ const auto allFormats = mimeData.formats();
+ for (const QString &format : allFormats) {
QVariant before = mimeData.retrieveData(format, QMetaType(QMetaType::QByteArray));
mimeData.setData(format, mimeData.data(format));
QVariant after = mimeData.retrieveData(format, QMetaType(QMetaType::QByteArray));
diff --git a/tests/auto/corelib/kernel/qobject/CMakeLists.txt b/tests/auto/corelib/kernel/qobject/CMakeLists.txt
index 276f6e64e0..46b8e2d238 100644
--- a/tests/auto/corelib/kernel/qobject/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qobject/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qobject.pro.
-
#####################################################################
## tst_qobject Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qobject LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qobject
SOURCES
tst_qobject.cpp
@@ -19,4 +23,4 @@ qt_internal_add_test(tst_qobject
## Scopes:
#####################################################################
add_subdirectory(signalbug)
-add_dependencies(tst_qobject signalbug_helper) # special case
+add_dependencies(tst_qobject signalbug_helper)
diff --git a/tests/auto/corelib/kernel/qobject/signalbug/CMakeLists.txt b/tests/auto/corelib/kernel/qobject/signalbug/CMakeLists.txt
index d04dd11b90..aefa1554b6 100644
--- a/tests/auto/corelib/kernel/qobject/signalbug/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qobject/signalbug/CMakeLists.txt
@@ -1,8 +1,6 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from signalbug.pro.
-
#####################################################################
## signalbug_helper Binary:
#####################################################################
diff --git a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.cpp b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.cpp
index 044f3ef230..bb8d7984f4 100644
--- a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.cpp
+++ b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "signalbug.h"
diff --git a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.h b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.h
index c1786e2cbc..ac124c2a1c 100644
--- a/tests/auto/corelib/kernel/qobject/signalbug/signalbug.h
+++ b/tests/auto/corelib/kernel/qobject/signalbug/signalbug.h
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef SIGNAL_BUG_H
#define SIGNAL_BUG_H
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index 207aae7608..6c387fde96 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -1,7 +1,7 @@
// Copyright (C) 2021 The Qt Company Ltd.
// Copyright (C) 2020 Olivier Goffart <ogoffart@woboq.com>
// Copyright (C) 2021 Intel Corporation.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// This test actually wants to practice narrowing conditions, so never define this.
#ifdef QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
@@ -39,6 +39,8 @@
#include <math.h>
+using namespace Qt::StringLiterals;
+
class tst_QObject : public QObject
{
Q_OBJECT
@@ -58,6 +60,7 @@ private slots:
void connectNotify_connectSlotsByName();
void connectDisconnectNotify_shadowing();
void connectReferenceToIncompleteTypes();
+ void connectAutoQueuedIncomplete();
void emitInDefinedOrder();
void customTypes();
void streamCustomTypes();
@@ -79,7 +82,9 @@ private slots:
void signalBlocking();
void blockingQueuedConnection();
void childEvents();
+ void parentEvents();
void installEventFilter();
+ void installEventFilterOrder();
void deleteSelfInSlot();
void disconnectSelfInSlotAndDeleteAfterEmit();
void dumpObjectInfo();
@@ -148,6 +153,7 @@ private slots:
void objectNameBinding();
void emitToDestroyedClass();
void declarativeData();
+ void asyncCallbackHelper();
};
struct QObjectCreatedOnShutdown
@@ -505,6 +511,12 @@ void tst_QObject::qobject_castTemplate()
QVERIFY(!::qobject_cast<ReceiverObject*>(o.data()));
}
+class DerivedObj : public QObject {
+ Q_OBJECT
+public:
+ using QObject::QObject;
+};
+
void tst_QObject::findChildren()
{
QObject o;
@@ -517,6 +529,10 @@ void tst_QObject::findChildren()
QTimer t1(&o);
QTimer t121(&o12);
QTimer emptyname(&o);
+ QObject oo;
+ QObject o21(&oo);
+ QObject o22(&oo);
+ QObject o23(&oo);
Q_SET_OBJECT_NAME(o);
Q_SET_OBJECT_NAME(o1);
@@ -527,6 +543,13 @@ void tst_QObject::findChildren()
Q_SET_OBJECT_NAME(t1);
Q_SET_OBJECT_NAME(t121);
emptyname.setObjectName("");
+ Q_SET_OBJECT_NAME(oo);
+ const QUtf8StringView utf8_name = u8"utf8 ⁎ obj";
+ o21.setObjectName(utf8_name);
+ const QStringView utf16_name = u"utf16 ⁎ obj";
+ o22.setObjectName(utf16_name);
+ constexpr QLatin1StringView L1_name("L1 ⁎ obj");
+ o23.setObjectName(L1_name);
QObject *op = nullptr;
@@ -557,6 +580,27 @@ void tst_QObject::findChildren()
op = o.findChild<QObject*>("o1");
QCOMPARE(op, &o1);
+ op = oo.findChild<QObject*>(utf8_name);
+ QCOMPARE(op, &o21);
+ op = oo.findChild<QObject*>(utf8_name.chopped(1));
+ QCOMPARE(op, nullptr);
+ const QUtf8StringView utf8_name_with_trailing_data = u8"utf8 ⁎ obj_data";
+ op = oo.findChild<QObject*>(utf8_name_with_trailing_data.chopped(5));
+ QCOMPARE(op, &o21);
+ op = oo.findChild<QObject*>(utf16_name);
+ QCOMPARE(op, &o22);
+ op = oo.findChild<QObject*>(utf16_name.chopped(1));
+ QCOMPARE(op, nullptr);
+ const QStringView utf16_name_with_trailing_data = u"utf16 ⁎ obj_data";
+ op = oo.findChild<QObject*>(utf16_name_with_trailing_data.chopped(5));
+ QCOMPARE(op, &o22);
+ op = oo.findChild<QObject*>(L1_name);
+ QCOMPARE(op, &o23);
+ op = oo.findChild<QObject*>(L1_name.chopped(1));
+ QCOMPARE(op, nullptr);
+ op = oo.findChild<QObject*>((L1_name + "_data"_L1).chopped(5));
+ QCOMPARE(op, &o23);
+
QList<QObject*> l;
QList<QTimer*> tl;
@@ -732,7 +776,20 @@ void tst_QObject::findChildren()
l = o.findChildren<QObject*>(QRegularExpression("^harry$"), Qt::FindDirectChildrenOnly);
QCOMPARE(l.size(), 0);
+ DerivedObj dr1(&o111);
+ DerivedObj dr2(&o111);
+ Q_SET_OBJECT_NAME(dr1);
+ Q_SET_OBJECT_NAME(dr2);
+
// empty and null string check
+ op = o.findChild<QObject*>(Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, &o1);
+ op = o.findChild<QTimer*>(Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, &t1);
+ op = o.findChild<DerivedObj*>(Qt::FindDirectChildrenOnly);
+ QCOMPARE(op, nullptr);
+ op = o.findChild<DerivedObj*>(Qt::FindChildrenRecursively);
+ QCOMPARE(op, &dr1);
op = o.findChild<QObject*>(QString(), Qt::FindDirectChildrenOnly);
QCOMPARE(op, &o1);
op = o.findChild<QObject*>("", Qt::FindDirectChildrenOnly);
@@ -893,6 +950,42 @@ void tst_QObject::connectReferenceToIncompleteTypes() {
QVERIFY(connection);
}
+struct Incomplete2;
+class QObjectWithIncomplete2 : public QObject {
+ Q_OBJECT
+
+public:
+ QObjectWithIncomplete2(QObject *parent=nullptr) : QObject(parent) {}
+signals:
+ void signalWithIncomplete(Incomplete2 *ptr);
+public slots:
+ void slotWithIncomplete(Incomplete2 *) { calledSlot = true; }
+ void run() { Q_EMIT signalWithIncomplete(nullptr); }
+public:
+ bool calledSlot = false;
+};
+
+void tst_QObject::connectAutoQueuedIncomplete()
+{
+ auto objectWithIncomplete1 = new QObjectWithIncomplete2();
+ auto objectWithIncomplete2 = new QObjectWithIncomplete2();
+ auto t = new QThread(this);
+ auto cleanup = qScopeGuard([&](){
+ t->quit();
+ QVERIFY(t->wait());
+ delete objectWithIncomplete1;
+ delete objectWithIncomplete2;
+ });
+
+ t->start();
+ objectWithIncomplete2->moveToThread(t);
+
+ connect(objectWithIncomplete2, &QObjectWithIncomplete2::signalWithIncomplete,
+ objectWithIncomplete1, &QObjectWithIncomplete2::slotWithIncomplete);
+ QMetaObject::invokeMethod(objectWithIncomplete2, "run", Qt::QueuedConnection);
+ QTRY_VERIFY(objectWithIncomplete1->calledSlot);
+}
+
static void connectDisconnectNotifyTestSlot() {}
void tst_QObject::connectDisconnectNotifyPMF()
@@ -1778,13 +1871,15 @@ void tst_QObject::moveToThread()
QObject *child = new QObject(object);
QCOMPARE(object->thread(), currentThread);
QCOMPARE(child->thread(), currentThread);
- object->moveToThread(0);
+ QVERIFY(object->moveToThread(nullptr));
QCOMPARE(object->thread(), (QThread *)0);
QCOMPARE(child->thread(), (QThread *)0);
- object->moveToThread(currentThread);
+ QVERIFY(object->moveToThread(currentThread));
QCOMPARE(object->thread(), currentThread);
QCOMPARE(child->thread(), currentThread);
- object->moveToThread(0);
+ QTest::ignoreMessage(QtWarningMsg, "QObject::moveToThread: Cannot move objects with a parent");
+ QVERIFY(!child->moveToThread(nullptr));
+ QVERIFY(object->moveToThread(nullptr));
QCOMPARE(object->thread(), (QThread *)0);
QCOMPARE(child->thread(), (QThread *)0);
// can delete an object with no thread anywhere
@@ -3047,6 +3142,8 @@ void tst_QObject::blockingQueuedConnection()
}
}
+static int s_eventSpyCounter = -1;
+
class EventSpy : public QObject
{
Q_OBJECT
@@ -3066,14 +3163,17 @@ public:
void clear()
{
events.clear();
+ thisCounter = -1;
}
bool eventFilter(QObject *object, QEvent *event) override
{
events.append(qMakePair(object, event->type()));
+ thisCounter = ++s_eventSpyCounter;
return false;
}
+ int thisCounter = -1;
private:
EventList events;
};
@@ -3162,6 +3262,78 @@ void tst_QObject::childEvents()
}
}
+void tst_QObject::parentEvents()
+{
+#ifdef QT_BUILD_INTERNAL
+ EventSpy::EventList expected;
+
+ {
+ // Parent events not enabled
+ QObject parent;
+ QObject child;
+
+ EventSpy spy;
+ child.installEventFilter(&spy);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 1)));
+
+ child.setParent(&parent);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 2)));
+
+ expected =
+ EventSpy::EventList();
+ QCOMPARE(spy.eventList(), expected);
+ spy.clear();
+
+ QCoreApplication::processEvents();
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&child, QEvent::Type(QEvent::User + 1))
+ << qMakePair(&child, QEvent::Type(QEvent::User + 2));
+ QCOMPARE(spy.eventList(), expected);
+ }
+
+ {
+ // Parent events enabled
+ QObject parent;
+ QObject child;
+ auto *childPrivate = QObjectPrivate::get(&child);
+ childPrivate->receiveParentEvents = true;
+
+ EventSpy spy;
+ child.installEventFilter(&spy);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 1)));
+
+ child.setParent(&parent);
+ child.setParent(nullptr);
+
+ QCoreApplication::postEvent(&child, new QEvent(QEvent::Type(QEvent::User + 2)));
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&child, QEvent::ParentAboutToChange)
+ << qMakePair(&child, QEvent::ParentChange)
+ << qMakePair(&child, QEvent::ParentAboutToChange)
+ << qMakePair(&child, QEvent::ParentChange);
+ QCOMPARE(spy.eventList(), expected);
+ spy.clear();
+
+ QCoreApplication::processEvents();
+
+ expected =
+ EventSpy::EventList()
+ << qMakePair(&child, QEvent::Type(QEvent::User + 1))
+ << qMakePair(&child, QEvent::Type(QEvent::User + 2));
+ QCOMPARE(spy.eventList(), expected);
+ }
+#else
+ QSKIP("Needs QT_BUILD_INTERNAL");
+#endif
+}
+
void tst_QObject::installEventFilter()
{
QEvent event(QEvent::User);
@@ -3203,6 +3375,70 @@ void tst_QObject::installEventFilter()
QVERIFY(spy.eventList().isEmpty());
}
+#define CHECK_FAIL(message) \
+do {\
+ if (QTest::currentTestFailed())\
+ QFAIL("failed one line above on " message);\
+} while (false)
+
+void tst_QObject::installEventFilterOrder()
+{
+ // installEventFilter() adds new objects to d_func()->extraData->eventFilters, which
+ // affects the order of calling each object's eventFilter() when processing the events.
+
+ QObject object;
+ EventSpy spy1, spy2, spy3;
+
+ auto clearSignalSpies = [&] {
+ for (auto *s : {&spy1, &spy2, &spy3})
+ s->clear();
+ s_eventSpyCounter = -1;
+ };
+
+ const EventSpy::EventList expected = { { &object, QEvent::Type(QEvent::User + 1) } };
+
+ // Call Order: from first to last
+ auto checkCallOrder = [&expected](const QList<EventSpy *> &spies) {
+ for (int i = 0; i < spies.size(); ++i) {
+ EventSpy *spy = spies.at(i);
+ QVERIFY2(spy->eventList() == expected,
+ QString("The spy %1 wasn't triggered exactly once.").arg(i).toLatin1());
+ QCOMPARE(spy->thisCounter, i);
+ }
+ };
+
+ // Install event filters and check the order of invocations:
+ // The last installed = the first called.
+ object.installEventFilter(&spy1);
+ object.installEventFilter(&spy2);
+ object.installEventFilter(&spy3);
+ clearSignalSpies();
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+ checkCallOrder({ &spy3, &spy2, &spy1 });
+ CHECK_FAIL("checkCallOrder() - 1st round");
+
+ // Install event filter for `spy1` again, which reorders spy1 in `eventFilters`
+ // (the list doesn't have duplicates).
+ object.installEventFilter(&spy1);
+ clearSignalSpies();
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+ checkCallOrder({ &spy1, &spy3, &spy2 });
+ CHECK_FAIL("checkCallOrder() - 2nd round");
+
+ // Remove event filter for `spy3`, ensure it's not called anymore and the
+ // existing filters order is preserved.
+ object.removeEventFilter(&spy3);
+ clearSignalSpies();
+ QCoreApplication::postEvent(&object, new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+ checkCallOrder({ &spy1, &spy2 });
+ CHECK_FAIL("checkCallOrder() - 3rd round");
+ QVERIFY(spy3.eventList().isEmpty());
+ QCOMPARE(spy3.thisCounter, -1);
+}
+
class EmitThread : public QThread
{ Q_OBJECT
public:
@@ -5945,15 +6181,15 @@ class ConnectToPrivateSlotPrivate;
class ConnectToPrivateSlot :public QObject {
Q_OBJECT
+ Q_DECLARE_PRIVATE(ConnectToPrivateSlot)
public:
ConnectToPrivateSlot();
void test(SenderObject *obj1) ;
- Q_DECLARE_PRIVATE(ConnectToPrivateSlot)
};
class ConnectToPrivateSlotPrivate : public QObjectPrivate {
-public:
Q_DECLARE_PUBLIC(ConnectToPrivateSlot)
+public:
int receivedCount;
QVariant receivedValue;
@@ -5965,6 +6201,8 @@ public:
receivedCount++;
receivedValue = v;
};
+
+ void testFromPrivate(SenderObject *obj);
};
ConnectToPrivateSlot::ConnectToPrivateSlot(): QObject(*new ConnectToPrivateSlotPrivate) {}
@@ -5991,6 +6229,14 @@ void ConnectToPrivateSlot::test(SenderObject* obj1) {
QVERIFY(!QObjectPrivate::disconnect(obj1, &SenderObject::signal2, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot));
}
+// Compile test to verify that we can use QObjectPrivate::connect in
+// the code of the private class, even if Q_DECLARE_PUBLIC is used in the
+// private section of the private class.
+void ConnectToPrivateSlotPrivate::testFromPrivate(SenderObject *obj)
+{
+ QVERIFY(QObjectPrivate::connect(obj, &SenderObject::signal1, this, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot));
+}
+
void tst_QObject::connectPrivateSlots()
{
SenderObject sender;
@@ -6027,6 +6273,7 @@ void tst_QObject::connectFunctorArgDifference()
connect(&timer, &QTimer::timeout, [=](){});
connect(&timer, &QTimer::objectNameChanged, [=](const QString &){});
+ connect(&timer, &QTimer::objectNameChanged, this, [](){});
connect(qApp, &QCoreApplication::aboutToQuit, [=](){});
connect(&timer, &QTimer::objectNameChanged, [=](){});
@@ -6254,11 +6501,46 @@ void tst_QObject::connectFunctorWithContextUnique()
QVERIFY(QObject::connect(&sender, &SenderObject::signal1, &receiver, &ReceiverObject::slot1));
receiver.count_slot1 = 0;
- QTest::ignoreMessage(QtWarningMsg, "QObject::connect(SenderObject, ReceiverObject): unique connections require a pointer to member function of a QObject subclass");
+ QVERIFY(QObject::connect(&sender, &SenderObject::signal2, &receiver, &ReceiverObject::slot2));
+ receiver.count_slot2 = 0;
+
+ const auto oredType = Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection);
+
+ // Will assert in debug builds, so only test in release builds
+#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
+ auto ignoreMsg = [] {
+ QTest::ignoreMessage(QtWarningMsg,
+ "QObject::connect(SenderObject, ReceiverObject): unique connections "
+ "require a pointer to member function of a QObject subclass");
+ };
+
+ ignoreMsg();
QVERIFY(!QObject::connect(&sender, &SenderObject::signal1, &receiver, [&](){ receiver.slot1(); }, Qt::UniqueConnection));
+ ignoreMsg();
+ QVERIFY(!QObject::connect(
+ &sender, &SenderObject::signal2, &receiver, [&]() { receiver.slot2(); }, oredType));
+#endif
+
sender.emitSignal1();
QCOMPARE(receiver.count_slot1, 1);
+
+ sender.emitSignal2();
+ QCOMPARE(receiver.count_slot2, 1);
+
+ // Check connecting to PMF doesn't hit the assert
+
+ QVERIFY(QObject::connect(&sender, &SenderObject::signal3, &receiver, &ReceiverObject::slot3,
+ Qt::UniqueConnection));
+ receiver.count_slot3 = 0;
+ sender.emitSignal3();
+ QCOMPARE(receiver.count_slot3, 1);
+
+ QVERIFY(QObject::connect(&sender, &SenderObject::signal4, &receiver, &ReceiverObject::slot4,
+ oredType));
+ receiver.count_slot4 = 0;
+ sender.emitSignal4();
+ QCOMPARE(receiver.count_slot4, 1);
}
class MyFunctor
@@ -6765,7 +7047,11 @@ struct QmlReceiver : public QtPrivate::QSlotObjectBase
, magic(0)
{}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
static void impl(int which, QSlotObjectBase *this_, QObject *, void **metaArgs, bool *ret)
+#else
+ static void impl(QSlotObjectBase *this_, QObject *, void **metaArgs, int which, bool *ret)
+#endif
{
switch (which) {
case Destroy: delete static_cast<QmlReceiver*>(this_); return;
@@ -8308,5 +8594,240 @@ void tst_QObject::declarativeData()
#endif
}
+/*
+ Compile-time test for the helpers in qobjectdefs_impl.h.
+*/
+class AsyncCaller : public QObject
+{
+ Q_OBJECT
+public:
+ ~AsyncCaller()
+ {
+ if (slotObject)
+ slotObject->destroyIfLastRef();
+ }
+ void callback0() {}
+ void callback1(const QString &) {}
+ void callbackInt(int) {}
+ int returnInt() const { return 0; }
+
+ static int staticCallback0() { return 0; }
+ static void staticCallback1(const QString &) {}
+
+ using Prototype0 = int(*)();
+ using Prototype1 = void(*)(QString);
+
+ template<typename Functor>
+ bool callMe0(const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *, Functor &&func)
+ {
+ if (slotObject) {
+ slotObject->destroyIfLastRef();
+ slotObject = nullptr;
+ }
+ QtPrivate::AssertCompatibleFunctions<Prototype0, Functor>();
+ slotObject = QtPrivate::makeCallableObject<Prototype0>(std::forward<Functor>(func));
+ return true;
+ }
+
+ template<typename Functor>
+ bool callMe0(Functor &&func)
+ {
+ return callMe0(nullptr, std::forward<Functor>(func));
+ }
+
+ template<typename Functor>
+ bool callMe1(const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *, Functor &&func)
+ {
+ if (slotObject) {
+ slotObject->destroyIfLastRef();
+ slotObject = nullptr;
+ }
+ QtPrivate::AssertCompatibleFunctions<Prototype1, Functor>();
+ slotObject = QtPrivate::makeCallableObject<Prototype1>(std::forward<Functor>(func));
+ return true;
+ }
+
+ template<typename Functor>
+ bool callMe1(Functor &&func)
+ {
+ return callMe1(nullptr, std::forward<Functor>(func));
+ }
+
+ QtPrivate::QSlotObjectBase *slotObject = nullptr;
+};
+
+static void freeFunction0() {}
+static void freeFunction1(QString) {}
+static void freeFunctionVariant(QVariant) {}
+
+template<typename Prototype, typename Functor>
+inline constexpr bool compiles(Functor &&) {
+ return QtPrivate::AreFunctionsCompatible<Prototype, Functor>::value;
+}
+
+void tst_QObject::asyncCallbackHelper()
+{
+ int result = 0;
+ QString arg1 = "Parameter";
+ void *argv[] = { &result, &arg1 };
+
+ auto lambda0 = []{};
+ auto lambda1 = [](const QString &) {};
+ auto lambda2 = [](const QString &, int) {};
+ const auto constLambda = [](const QString &) {};
+ auto moveOnlyLambda = [u = std::unique_ptr<int>()]{};
+ auto moveOnlyLambda1 = [u = std::unique_ptr<int>()](const QString &){};
+
+ SlotFunctor functor0;
+ SlotFunctorString functor1;
+
+ // no parameters provided or needed
+ static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::callback0));
+ static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::staticCallback0));
+ static_assert(compiles<AsyncCaller::Prototype0>(lambda0));
+ static_assert(compiles<AsyncCaller::Prototype0>(std::move(moveOnlyLambda)));
+ static_assert(compiles<AsyncCaller::Prototype0>(freeFunction0));
+ static_assert(compiles<AsyncCaller::Prototype0>(functor0));
+
+ // more parameters than needed
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::callback0));
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::staticCallback0));
+ static_assert(compiles<AsyncCaller::Prototype1>(lambda0));
+ static_assert(compiles<AsyncCaller::Prototype1>(freeFunction0));
+ static_assert(compiles<AsyncCaller::Prototype1>(functor0));
+
+ // matching parameter
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::callback1));
+ static_assert(compiles<AsyncCaller::Prototype1>(&AsyncCaller::staticCallback1));
+ static_assert(compiles<AsyncCaller::Prototype1>(lambda1));
+ static_assert(compiles<AsyncCaller::Prototype1>(std::move(moveOnlyLambda1)));
+ static_assert(compiles<AsyncCaller::Prototype1>(constLambda));
+ static_assert(compiles<AsyncCaller::Prototype1>(freeFunction1));
+ static_assert(compiles<AsyncCaller::Prototype1>(functor1));
+
+ // not enough parameters
+ static_assert(!compiles<AsyncCaller::Prototype0>(&AsyncCaller::callback1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(&AsyncCaller::staticCallback1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(lambda1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(constLambda));
+ static_assert(!compiles<AsyncCaller::Prototype0>(lambda2));
+ static_assert(!compiles<AsyncCaller::Prototype0>(freeFunction1));
+ static_assert(!compiles<AsyncCaller::Prototype0>(functor1));
+
+ // wrong parameter type
+ static_assert(!compiles<AsyncCaller::Prototype1>(&AsyncCaller::callbackInt));
+
+ // old-style slot name
+ static_assert(!compiles<AsyncCaller::Prototype0>("callback1"));
+
+ // slot with return value is ok, we just don't pass
+ // the return value through to anything.
+ static_assert(compiles<AsyncCaller::Prototype0>(&AsyncCaller::returnInt));
+
+ static_assert(compiles<AsyncCaller::Prototype1>(freeFunctionVariant));
+
+ std::function<int()> stdFunction0(&AsyncCaller::staticCallback0);
+ std::function<void(QString)> stdFunction1(&AsyncCaller::staticCallback1);
+ static_assert(compiles<AsyncCaller::Prototype0>(stdFunction0));
+ static_assert(compiles<AsyncCaller::Prototype1>(stdFunction1));
+
+ AsyncCaller caller;
+ // with context
+ QVERIFY(caller.callMe0(&caller, &AsyncCaller::callback0));
+ QVERIFY(caller.callMe0(&caller, &AsyncCaller::returnInt));
+ QVERIFY(caller.callMe0(&caller, &AsyncCaller::staticCallback0));
+ QVERIFY(caller.callMe0(&caller, lambda0));
+ QVERIFY(caller.callMe0(&caller, freeFunction0));
+ QVERIFY(caller.callMe0(&caller, std::move(moveOnlyLambda)));
+ QVERIFY(caller.callMe0(&caller, stdFunction0));
+
+ QVERIFY(caller.callMe1(&caller, &AsyncCaller::callback1));
+ QVERIFY(caller.callMe1(&caller, &AsyncCaller::staticCallback1));
+ QVERIFY(caller.callMe1(&caller, lambda1));
+ QVERIFY(caller.callMe1(&caller, freeFunction1));
+ QVERIFY(caller.callMe1(&caller, constLambda));
+ QVERIFY(caller.callMe1(&caller, stdFunction1));
+
+ // without context
+ QVERIFY(caller.callMe0(&AsyncCaller::staticCallback0));
+ QVERIFY(caller.callMe0(lambda0));
+ QVERIFY(caller.callMe0(freeFunction0));
+ QVERIFY(caller.callMe0(stdFunction0));
+
+ QVERIFY(caller.callMe1(&AsyncCaller::staticCallback1));
+ QVERIFY(caller.callMe1(lambda1));
+ QVERIFY(caller.callMe1(constLambda));
+ QVERIFY(caller.callMe1(std::move(moveOnlyLambda1)));
+ QVERIFY(caller.callMe1(freeFunction1));
+ QVERIFY(caller.callMe1(stdFunction1));
+
+ static const char *expectedPayload = "Hello World!";
+ {
+ struct MoveOnlyFunctor {
+ MoveOnlyFunctor() = default;
+ MoveOnlyFunctor(MoveOnlyFunctor &&) = default;
+ MoveOnlyFunctor(const MoveOnlyFunctor &) = delete;
+ ~MoveOnlyFunctor() = default;
+
+ int operator()() const {
+ qDebug().noquote() << payload;
+ return int(payload.length());
+ }
+ QString payload = expectedPayload;
+ } moveOnlyFunctor;
+ QVERIFY(caller.callMe0(std::move(moveOnlyFunctor)));
+ }
+ QTest::ignoreMessage(QtDebugMsg, expectedPayload);
+ caller.slotObject->call(nullptr, argv);
+ QCOMPARE(result, QLatin1String(expectedPayload).length());
+
+ // mutable lambda; same behavior as mutableFunctor - we copy the functor
+ // in the QCallableObject, so the original is not modified
+ int status = 0;
+ auto mutableLambda1 = [&status, calls = 0]() mutable { status = ++calls; };
+
+ mutableLambda1();
+ QCOMPARE(status, 1);
+ QVERIFY(caller.callMe0(mutableLambda1)); // this copies the lambda with count == 1
+ caller.slotObject->call(nullptr, argv); // this doesn't change mutableLambda1, but the copy
+ QCOMPARE(status, 2);
+ mutableLambda1();
+ QCOMPARE(status, 2); // and we are still at two
+
+ auto mutableLambda2 = [calls = 0]() mutable { return ++calls; };
+ QCOMPARE(mutableLambda2(), 1);
+ QVERIFY(caller.callMe0(mutableLambda2)); // this copies the lambda
+ caller.slotObject->call(nullptr, argv); // this call doesn't change mutableLambda2
+ QCOMPARE(mutableLambda2(), 2); // so we are still at 2
+
+ {
+ int called = -1;
+ struct MutableFunctor {
+ void operator()() { called = 0; }
+ int &called;
+ };
+ struct ConstFunctor
+ {
+ void operator()() const { called = 1; }
+ int &called;
+ };
+
+ MutableFunctor mf{called};
+ QMetaObject::invokeMethod(this, mf);
+ QCOMPARE(called, 0);
+ ConstFunctor cf{called};
+ QMetaObject::invokeMethod(this, cf);
+ QCOMPARE(called, 1);
+ QMetaObject::invokeMethod(this, [&called, u = std::unique_ptr<int>()]{ called = 2; });
+ QCOMPARE(called, 2);
+ QMetaObject::invokeMethod(this, [&called, count = 0]() mutable {
+ if (!count)
+ called = 3;
+ ++count;
+ });
+ QCOMPARE(called, 3);
+ }
+}
+
QTEST_MAIN(tst_QObject)
#include "tst_qobject.moc"
diff --git a/tests/auto/corelib/kernel/qpermission/CMakeLists.txt b/tests/auto/corelib/kernel/qpermission/CMakeLists.txt
index 918abc6628..1af0331186 100644
--- a/tests/auto/corelib/kernel/qpermission/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qpermission/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qpermission LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
if (NOT QT_FEATURE_permissions)
return()
endif()
diff --git a/tests/auto/corelib/kernel/qpermission/tst_qpermission.cpp b/tests/auto/corelib/kernel/qpermission/tst_qpermission.cpp
index 9eb7f7e829..dbf1d2dd84 100644
--- a/tests/auto/corelib/kernel/qpermission/tst_qpermission.cpp
+++ b/tests/auto/corelib/kernel/qpermission/tst_qpermission.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QPermission>
@@ -25,6 +25,11 @@ private Q_SLOTS:
void converting_Bluetooth() const { return converting_impl<QBluetoothPermission>(); }
void conversionMaintainsState() const;
+
+ void functorWithoutContext();
+ void functorWithContextInThread();
+ void receiverInThread();
+ void destroyedContextObject();
private:
template <typename T>
void converting_impl() const;
@@ -79,18 +84,18 @@ void tst_QPermission::conversionMaintainsState() const
QCOMPARE_EQ(loc.availability(), QLocationPermission::Availability::Always);
QCalendarPermission cal, cal_default;
- QCOMPARE_EQ(cal_default.isReadWrite(), false);
+ QCOMPARE_EQ(cal_default.accessMode(), QCalendarPermission::AccessMode::ReadOnly);
- cal.setReadWrite(true);
+ cal.setAccessMode(QCalendarPermission::AccessMode::ReadWrite);
- QCOMPARE_EQ(cal.isReadWrite(), true);
+ QCOMPARE_EQ(cal.accessMode(), QCalendarPermission::AccessMode::ReadWrite);
QContactsPermission con, con_default;
- QCOMPARE_EQ(con_default.isReadWrite(), false);
+ QCOMPARE_EQ(con_default.accessMode(), QContactsPermission::AccessMode::ReadOnly);
- con.setReadWrite(true);
+ con.setAccessMode(QContactsPermission::AccessMode::ReadWrite);
- QCOMPARE_EQ(con.isReadWrite(), true);
+ QCOMPARE_EQ(con.accessMode(), QContactsPermission::AccessMode::ReadWrite);
//
// QCameraPermission, QMicrophonePermission, QBluetoothPermission don't have
@@ -125,7 +130,7 @@ void tst_QPermission::conversionMaintainsState() const
auto v = p.value<QContactsPermission>();
QCOMPARE_NE(v, std::nullopt);
auto &r = *v;
- QCOMPARE_EQ(r.isReadWrite(), con.isReadWrite());
+ QCOMPARE_EQ(r.accessMode(), con.accessMode());
// check mismatched returns nullopt:
QCOMPARE_EQ(p.value<QLocationPermission>(), std::nullopt);
}
@@ -135,11 +140,145 @@ void tst_QPermission::conversionMaintainsState() const
auto v = p.value<QCalendarPermission>();
QCOMPARE_NE(v, std::nullopt);
auto &r = *v;
- QCOMPARE_EQ(r.isReadWrite(), cal.isReadWrite());
+ QCOMPARE_EQ(r.accessMode(), cal.accessMode());
// check mismatched returns nullopt:
QCOMPARE_EQ(p.value<QContactsPermission>(), std::nullopt);
}
}
+template <typename Func,
+ typename T = std::void_t<decltype(qApp->requestPermission(std::declval<DummyPermission>(),
+ std::declval<Func>()))>
+ >
+void wrapRequestPermission(const QPermission &p, Func &&f)
+{
+ qApp->requestPermission(p, std::forward<Func>(f));
+}
+
+template <typename Functor>
+using CompatibleTest = decltype(wrapRequestPermission(std::declval<QPermission>(), std::declval<Functor>()));
+
+
+// Compile test for context-less functor overloads
+void tst_QPermission::functorWithoutContext()
+{
+ int argc = 0;
+ char *argv = nullptr;
+ QCoreApplication app(argc, &argv);
+
+ DummyPermission dummy;
+#ifdef Q_OS_DARWIN
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Could not find permission plugin for DummyPermission.*"));
+#endif
+
+ qApp->requestPermission(dummy, [](const QPermission &permission){
+ QVERIFY(permission.value<DummyPermission>());
+ });
+ wrapRequestPermission(dummy, [](const QPermission &permission){
+ QVERIFY(permission.value<DummyPermission>());
+ });
+
+ auto compatible = [](const QPermission &) {};
+ using Compatible = decltype(compatible);
+ auto incompatible = [](const QString &) {};
+ using Incompatible = decltype(incompatible);
+
+ static_assert(qxp::is_detected_v<CompatibleTest, Compatible>);
+ static_assert(!qxp::is_detected_v<CompatibleTest, Incompatible>);
+}
+
+void tst_QPermission::functorWithContextInThread()
+{
+ int argc = 0;
+ 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);
+
+ // compile tests: none of these work and the error output isn't horrible
+ // qApp->requestPermission(dummy, &receiver, "&tst_QPermission::receiverInThread");
+ // qApp->requestPermission(dummy, &receiver, &tst_QPermission::receiverInThread);
+ // qApp->requestPermission(dummy, &receiver, &QObject::destroyed);
+}
+
+void tst_QPermission::destroyedContextObject()
+{
+ 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"
diff --git a/tests/auto/corelib/kernel/qpointer/CMakeLists.txt b/tests/auto/corelib/kernel/qpointer/CMakeLists.txt
index e94730ba90..0b2c4b7e45 100644
--- a/tests/auto/corelib/kernel/qpointer/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qpointer/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qpointer.pro.
-
#####################################################################
## tst_qpointer Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qpointer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
if (NOT QT_FEATURE_thread)
return()
endif()
@@ -16,6 +20,7 @@ qt_internal_add_test(tst_qpointer
tst_qpointer.cpp
LIBRARIES
Qt::Gui
+ Qt::TestPrivate
)
## Scopes:
diff --git a/tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp b/tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp
index 9886ab84f2..b88f1b5b0f 100644
--- a/tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp
+++ b/tests/auto/corelib/kernel/qpointer/tst_qpointer.cpp
@@ -1,7 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
+#include <QtTest/private/qcomparisontesthelper_p.h>
#include <QRunnable>
#include <QThreadPool>
@@ -19,8 +20,11 @@ public:
private slots:
void constructors();
+ void ctad();
+ void conversion();
void destructor();
void assignment_operators();
+ void compareCompiles();
void equality_operators();
void swap();
void isNull();
@@ -34,14 +38,114 @@ private slots:
void constQPointer();
};
+// check that nullptr QPointer construction is Q_CONSTINIT:
+[[maybe_unused]] Q_CONSTINIT static QPointer<QFile> s_file1;
+[[maybe_unused]] Q_CONSTINIT static QPointer<QFile> s_file2 = {};
+[[maybe_unused]] Q_CONSTINIT static QPointer<QFile> s_file3 = nullptr;
+[[maybe_unused]] Q_CONSTINIT static QPointer<QFile> s_file4 = 0; // legacy nullptr
+
void tst_QPointer::constructors()
{
+ struct Derived : QObject {};
+ Derived derived;
+
QPointer<QObject> p1;
QPointer<QObject> p2(this);
QPointer<QObject> p3(p2);
+ QPointer<QObject> p4 = &derived;
QCOMPARE(p1, QPointer<QObject>(0));
QCOMPARE(p2, QPointer<QObject>(this));
QCOMPARE(p3, QPointer<QObject>(this));
+ QCOMPARE(p4, &derived);
+}
+
+void tst_QPointer::ctad()
+{
+
+ {
+ QObject o;
+ QPointer po = &o;
+ static_assert(std::is_same_v<decltype(po), QPointer<QObject>>);
+ QPointer poc = po;
+ static_assert(std::is_same_v<decltype(poc), QPointer<QObject>>);
+ QPointer pom = std::move(po);
+ static_assert(std::is_same_v<decltype(pom), QPointer<QObject>>);
+ }
+ {
+ const QObject co;
+ QPointer pco = &co;
+ static_assert(std::is_same_v<decltype(pco), QPointer<const QObject>>);
+ QPointer pcoc = pco;
+ static_assert(std::is_same_v<decltype(pcoc), QPointer<const QObject>>);
+ QPointer pcom = std::move(pco);
+ static_assert(std::is_same_v<decltype(pcom), QPointer<const QObject>>);
+ }
+ {
+ QFile f;
+ QPointer pf = &f;
+ static_assert(std::is_same_v<decltype(pf), QPointer<QFile>>);
+ QPointer pfc = pf;
+ static_assert(std::is_same_v<decltype(pfc), QPointer<QFile>>);
+ QPointer pfm = std::move(pf);
+ static_assert(std::is_same_v<decltype(pfm), QPointer<QFile>>);
+ }
+ {
+ const QFile cf;
+ QPointer pcf = &cf;
+ static_assert(std::is_same_v<decltype(pcf), QPointer<const QFile>>);
+ QPointer pcfc = pcf;
+ static_assert(std::is_same_v<decltype(pcfc), QPointer<const QFile>>);
+ QPointer pcfm = std::move(pcf);
+ static_assert(std::is_same_v<decltype(pcfm), QPointer<const QFile>>);
+ }
+}
+
+void tst_QPointer::conversion()
+{
+ // copy-conversion:
+ {
+ QFile file;
+ QPointer<QFile> pf = &file;
+ QCOMPARE_EQ(pf, &file);
+ QPointer<const QIODevice> pio = pf;
+ QCOMPARE_EQ(pio, &file);
+ QCOMPARE_EQ(pio.get(), &file);
+ QCOMPARE_EQ(pio, pf);
+ QCOMPARE_EQ(pio.get(), pf.get());
+
+ // reset
+ pio = nullptr;
+ QCOMPARE_EQ(pio, nullptr);
+ QCOMPARE_EQ(pio.get(), nullptr);
+
+ // copy-assignment
+ QCOMPARE_EQ(pf, &file);
+ pio = pf;
+ QCOMPARE_EQ(pio, &file);
+ QCOMPARE_EQ(pio.get(), &file);
+ QCOMPARE_EQ(pio, pf);
+ QCOMPARE_EQ(pio.get(), pf.get());
+ }
+ // move-conversion:
+ {
+ QFile file;
+ QPointer<QFile> pf = &file;
+ QCOMPARE_EQ(pf, &file);
+ QPointer<const QIODevice> pio = std::move(pf);
+ QCOMPARE_EQ(pf, nullptr);
+ QCOMPARE_EQ(pio, &file);
+ QCOMPARE_EQ(pio.get(), &file);
+
+ // reset
+ pio = nullptr;
+ QCOMPARE_EQ(pio, nullptr);
+ QCOMPARE_EQ(pio.get(), nullptr);
+
+ // move-assignment
+ pio = QPointer<QFile>(&file);
+ QCOMPARE_EQ(pio, &file);
+ QCOMPARE_EQ(pio.get(), &file);
+ }
}
void tst_QPointer::destructor()
@@ -101,12 +205,21 @@ void tst_QPointer::assignment_operators()
delete object;
}
+void tst_QPointer::compareCompiles()
+{
+ QTestPrivate::testEqualityOperatorsCompile<QPointer<QObject>>();
+ QTestPrivate::testEqualityOperatorsCompile<QPointer<QObject>, QObject*>();
+ QTestPrivate::testEqualityOperatorsCompile<QPointer<QObject>, QWidget*>();
+ QTestPrivate::testEqualityOperatorsCompile<QPointer<QObject>, QPointer<QWidget>>();
+ QTestPrivate::testEqualityOperatorsCompile<QPointer<QObject>, std::nullptr_t>();
+}
+
void tst_QPointer::equality_operators()
{
QPointer<QObject> p1;
QPointer<QObject> p2;
- QVERIFY(p1 == p2);
+ QT_TEST_EQUALITY_OPS(p1, p2, true);
QObject *object = nullptr;
#ifndef QT_NO_WIDGETS
@@ -114,16 +227,15 @@ void tst_QPointer::equality_operators()
#endif
p1 = object;
- QVERIFY(p1 == p2);
- QVERIFY(p1 == object);
+ QT_TEST_EQUALITY_OPS(p1, p2, true);
+ QT_TEST_EQUALITY_OPS(p1, object, true);
p2 = object;
- QVERIFY(p2 == p1);
- QVERIFY(p2 == object);
-
+ QT_TEST_EQUALITY_OPS(p2, p1, true);
+ QT_TEST_EQUALITY_OPS(p2, object, true);
p1 = this;
- QVERIFY(p1 != p2);
+ QT_TEST_EQUALITY_OPS(p1, p2, false);
p2 = p1;
- QVERIFY(p1 == p2);
+ QT_TEST_EQUALITY_OPS(p1, p2, true);
// compare to zero
p1 = nullptr;
@@ -131,19 +243,13 @@ void tst_QPointer::equality_operators()
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);
+ QT_TEST_EQUALITY_OPS(p1, nullptr, true);
+ QT_TEST_EQUALITY_OPS(p2, nullptr, false);
+ QT_TEST_EQUALITY_OPS(p1, object, true);
+ QT_TEST_EQUALITY_OPS(p2, object, false);
#ifndef QT_NO_WIDGETS
- QVERIFY(p1 == widget);
- QVERIFY(widget == p1);
- QVERIFY(p2 != widget);
- QVERIFY(widget != p2);
+ QT_TEST_EQUALITY_OPS(p1, widget, true);
+ QT_TEST_EQUALITY_OPS(p2, widget, false);
#endif
}
diff --git a/tests/auto/corelib/kernel/qproperty/CMakeLists.txt b/tests/auto/corelib/kernel/qproperty/CMakeLists.txt
index a53fc98e96..177465d2ee 100644
--- a/tests/auto/corelib/kernel/qproperty/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qproperty/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qproperty.pro.
-
#####################################################################
## tst_qproperty Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qproperty LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qproperty
SOURCES
tst_qproperty.cpp
diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
index e9b2218497..cc7edb8bf2 100644
--- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
+++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp
@@ -1,11 +1,12 @@
// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QObject>
#include <QSignalSpy>
#include <qtest.h>
#include <qproperty.h>
#include <private/qproperty_p.h>
+#include <private/qobject_p.h>
#if __has_include(<source_location>) && __cplusplus >= 202002L && !defined(Q_QDOC)
#include <source_location>
@@ -28,6 +29,7 @@ class tst_QProperty : public QObject
{
Q_OBJECT
private slots:
+ void inheritQUntypedPropertyData();
void functorBinding();
void basicDependencies();
void multipleDependencies();
@@ -66,6 +68,7 @@ private slots:
void quntypedBindableApi();
void readonlyConstQBindable();
void qobjectBindableManualNotify();
+ void qobjectBindableReallocatedBindingStorage();
void qobjectBindableSignalTakingNewValue();
void testNewStuff();
@@ -74,6 +77,7 @@ private slots:
void metaProperty();
void modifyObserverListWhileIterating();
+ void noDoubleCapture();
void compatPropertyNoDobuleNotification();
void compatPropertySignals();
@@ -87,6 +91,8 @@ private slots:
void noDoubleNotification();
void groupedNotifications();
void groupedNotificationConsistency();
+ void bindingGroupMovingBindingData();
+ void bindingGroupBindingDeleted();
void uninstalledBindingDoesNotEvaluate();
void notify();
@@ -101,8 +107,64 @@ private slots:
void notifyAfterAllDepsGone();
void propertyAdaptorBinding();
+ void propertyUpdateViaSignaledProperty();
+
+ void derefFromObserver();
};
+namespace {
+template <class T>
+constexpr auto isDerivedFromQUntypedPropertyData = std::is_base_of_v<QUntypedPropertyData, T>;
+
+template <typename Property>
+constexpr auto isDerivedFromQUntypedPropertyDataFunc(const Property &property)
+{
+ Q_UNUSED(property);
+ return isDerivedFromQUntypedPropertyData<Property>;
+}
+
+template <typename Property>
+constexpr auto isDerivedFromQUntypedPropertyDataFunc(Property *property)
+{
+ Q_UNUSED(property);
+ return isDerivedFromQUntypedPropertyData<Property>;
+}
+} // namespace
+
+void tst_QProperty::inheritQUntypedPropertyData()
+{
+ class propertyPublic : public QUntypedPropertyData
+ {
+ };
+ class propertyPrivate : private QUntypedPropertyData
+ {
+ };
+
+ // Compile time test
+ static_assert(isDerivedFromQUntypedPropertyData<propertyPublic>);
+ static_assert(isDerivedFromQUntypedPropertyData<propertyPrivate>);
+ static_assert(isDerivedFromQUntypedPropertyData<QPropertyData<int>>);
+ static_assert(isDerivedFromQUntypedPropertyData<QProperty<int>>);
+
+ // Run time test
+ propertyPublic _propertyPublic;
+ propertyPrivate _propertyPrivate;
+ QPropertyData<int> qpropertyData;
+ QProperty<int> qproperty;
+ std::unique_ptr<propertyPublic> _propertyPublicPtr{ new propertyPublic };
+ std::unique_ptr<propertyPrivate> _propertyPrivatePtr{ new propertyPrivate };
+ std::unique_ptr<QPropertyData<int>> qpropertyDataPtr{ new QPropertyData<int> };
+ std::unique_ptr<QProperty<int>> qpropertyPtr{ new QProperty<int> };
+ QVERIFY(isDerivedFromQUntypedPropertyDataFunc(_propertyPublic));
+ QVERIFY(isDerivedFromQUntypedPropertyDataFunc(_propertyPrivate));
+ QVERIFY(isDerivedFromQUntypedPropertyDataFunc(qpropertyData));
+ QVERIFY(isDerivedFromQUntypedPropertyDataFunc(qproperty));
+ QVERIFY(isDerivedFromQUntypedPropertyDataFunc(_propertyPublicPtr.get()));
+ QVERIFY(isDerivedFromQUntypedPropertyDataFunc(_propertyPrivatePtr.get()));
+ QVERIFY(isDerivedFromQUntypedPropertyDataFunc(qpropertyDataPtr.get()));
+ QVERIFY(isDerivedFromQUntypedPropertyDataFunc(qpropertyPtr.get()));
+}
+
void tst_QProperty::functorBinding()
{
QProperty<int> property([]() { return 42; });
@@ -255,6 +317,7 @@ void tst_QProperty::bindingAfterUse()
void tst_QProperty::bindingFunctionDtorCalled()
{
+ DtorCounter::counter = 0;
DtorCounter dc;
{
QProperty<int> prop;
@@ -1185,6 +1248,23 @@ void tst_QProperty::qobjectBindableManualNotify()
QCOMPARE(object.foo(), 1);
}
+
+struct ReallocObject : QObject {
+ ReallocObject()
+ { v.setBinding([this] { return x.value() + y.value() + z.value(); }); }
+ Q_OBJECT_BINDABLE_PROPERTY(ReallocObject, int, v)
+ Q_OBJECT_BINDABLE_PROPERTY(ReallocObject, int, x)
+ Q_OBJECT_BINDABLE_PROPERTY(ReallocObject, int, y)
+ Q_OBJECT_BINDABLE_PROPERTY(ReallocObject, int, z)
+};
+
+void tst_QProperty::qobjectBindableReallocatedBindingStorage()
+{
+ ReallocObject object;
+ object.x = 1;
+ QCOMPARE(object.v.value(), 1);
+}
+
void tst_QProperty::qobjectBindableSignalTakingNewValue()
{
// Given an object of type MyQObject,
@@ -1472,6 +1552,22 @@ void tst_QProperty::modifyObserverListWhileIterating()
}
}
+void tst_QProperty::noDoubleCapture()
+{
+ QProperty<long long> size;
+ size = 3;
+ QProperty<int> max;
+ max.setBinding([&size]() -> int {
+ // each loop run attempts to capture size
+ for (int i = 0; i < size; ++i) {}
+ return size.value();
+ });
+ auto bindingPriv = QPropertyBindingPrivate::get(max.binding());
+ QCOMPARE(bindingPriv->dependencyObserverCount, 1U);
+ size = 4; // should not crash
+ QCOMPARE(max.value(), 4);
+}
+
class CompatPropertyTester : public QObject
{
Q_OBJECT
@@ -1673,6 +1769,14 @@ class PropertyAdaptorTester : public QObject
Q_PROPERTY(int foo1 READ foo WRITE setFoo)
signals:
+ void dummySignal1();
+ void dummySignal2();
+ void dummySignal3();
+ void dummySignal4();
+ void dummySignal5();
+ void dummySignal6();
+ void dummySignal7();
+ void dummySignal8();
void fooChanged(int newFoo);
public slots:
@@ -1701,9 +1805,11 @@ void tst_QProperty::propertyAdaptorBinding()
// Check binding of non BINDABLE property
PropertyAdaptorTester object;
+ // set up a dummy connection (needed to verify that the QBindable avoids an out-of-bounds read)
+ QObject::connect(&object, &PropertyAdaptorTester::dummySignal1, [](){});
+ QBindable<int> binding(&object, "foo");
QObject::connect(&object, &PropertyAdaptorTester::fooChanged, &object,
&PropertyAdaptorTester::fooHasChanged);
- QBindable<int> binding(&object, "foo");
binding.setBinding([&]() { return source + 1; });
QCOMPARE(object.foo(), 6);
QCOMPARE(object.fooChangedCount, 1);
@@ -1779,13 +1885,14 @@ void tst_QProperty::propertyAdaptorBinding()
QCOMPARE(object.fooChangedCount, 7);
// Check update group
- Qt::beginPropertyUpdateGroup();
- source.setValue(23);
- source2.setValue(22);
- QCOMPARE(object.foo(), 43);
- QCOMPARE(dest1.value(), 43);
- QCOMPARE(object.fooChangedCount, 7);
- Qt::endPropertyUpdateGroup();
+ {
+ const QScopedPropertyUpdateGroup guard;
+ source.setValue(23);
+ source2.setValue(22);
+ QCOMPARE(object.foo(), 43);
+ QCOMPARE(dest1.value(), 43);
+ QCOMPARE(object.fooChangedCount, 7);
+ }
QCOMPARE(object.foo(), 45);
QCOMPARE(dest1.value(), 45);
QCOMPARE(object.fooChangedCount, 8);
@@ -2082,27 +2189,29 @@ void tst_QProperty::groupedNotifications()
QCOMPARE(nNotifications, 1);
expected = 2;
- Qt::beginPropertyUpdateGroup();
- a = 1;
- QCOMPARE(b.value(), 0);
- QCOMPARE(c.value(), 0);
- QCOMPARE(d.value(), 0);
- QCOMPARE(nNotifications, 1);
- Qt::endPropertyUpdateGroup();
+ {
+ const QScopedPropertyUpdateGroup guard;
+ a = 1;
+ QCOMPARE(b.value(), 0);
+ QCOMPARE(c.value(), 0);
+ QCOMPARE(d.value(), 0);
+ QCOMPARE(nNotifications, 1);
+ }
QCOMPARE(b.value(), 1);
QCOMPARE(c.value(), 1);
QCOMPARE(e.value(), 2);
QCOMPARE(nNotifications, 2);
expected = 7;
- Qt::beginPropertyUpdateGroup();
- a = 2;
- d = 3;
- QCOMPARE(b.value(), 1);
- QCOMPARE(c.value(), 1);
- QCOMPARE(d.value(), 3);
- QCOMPARE(nNotifications, 2);
- Qt::endPropertyUpdateGroup();
+ {
+ const QScopedPropertyUpdateGroup guard;
+ a = 2;
+ d = 3;
+ QCOMPARE(b.value(), 1);
+ QCOMPARE(c.value(), 1);
+ QCOMPARE(d.value(), 3);
+ QCOMPARE(nNotifications, 2);
+ }
QCOMPARE(b.value(), 2);
QCOMPARE(c.value(), 2);
QCOMPARE(e.value(), 7);
@@ -2125,13 +2234,71 @@ void tst_QProperty::groupedNotificationConsistency()
j = 1;
QVERIFY(!areEqual); // value changed runs before j = 1
- Qt::beginPropertyUpdateGroup();
- i = 2;
- j = 2;
- Qt::endPropertyUpdateGroup();
+ {
+ const QScopedPropertyUpdateGroup guard;
+ i = 2;
+ j = 2;
+ }
QVERIFY(areEqual); // value changed runs after everything has been evaluated
}
+void tst_QProperty::bindingGroupMovingBindingData()
+{
+ auto tester = std::make_unique<ClassWithNotifiedProperty>();
+ auto testerPriv = QObjectPrivate::get(tester.get());
+
+ auto dummyNotifier = tester->property.addNotifier([](){});
+ auto bindingData = testerPriv->bindingStorage.bindingData(&tester->property);
+ QVERIFY(bindingData); // we have a notifier, so there should be binding data
+
+ Qt::beginPropertyUpdateGroup();
+ auto cleanup = qScopeGuard([](){ Qt::endPropertyUpdateGroup(); });
+ tester->property = 42;
+ QCOMPARE(testerPriv->bindingStorage.bindingData(&tester->property), bindingData);
+ auto proxyData = QPropertyBindingDataPointer::proxyData(bindingData);
+ // as we've modified the property, we now should have a proxy for the delayed notification
+ QVERIFY(proxyData);
+ // trigger binding data reallocation
+ std::array<QUntypedPropertyData, 10> propertyDataArray;
+ for (auto&& data: propertyDataArray)
+ testerPriv->bindingStorage.bindingData(&data, true);
+ // binding data has moved
+ QVERIFY(testerPriv->bindingStorage.bindingData(&tester->property) != bindingData);
+ bindingData = testerPriv->bindingStorage.bindingData(&tester->property);
+ // the proxy data has been updated
+ QCOMPARE(proxyData->originalBindingData, bindingData);
+
+ tester.reset();
+ // the property data is gone, proxyData should have been informed
+ QCOMPARE(proxyData->originalBindingData, nullptr);
+ QVERIFY(proxyData);
+}
+
+void tst_QProperty::bindingGroupBindingDeleted()
+{
+ auto deleter = std::make_unique<ClassWithNotifiedProperty>();
+ auto toBeDeleted = std::make_unique<ClassWithNotifiedProperty>();
+
+ bool calledHandler = false;
+ deleter->property.setBinding([&](){
+ int newValue = toBeDeleted->property;
+ if (newValue == 42)
+ toBeDeleted.reset();
+ return newValue;
+ });
+ auto handler = toBeDeleted->property.onValueChanged([&]() { calledHandler = true; } );
+ {
+ Qt::beginPropertyUpdateGroup();
+ auto cleanup = qScopeGuard([](){ Qt::endPropertyUpdateGroup(); });
+ QVERIFY(toBeDeleted);
+ toBeDeleted->property = 42;
+ // ASAN should not complain here
+ }
+ QVERIFY(!toBeDeleted);
+ // the change notification is sent, even if the binding is deleted during evaluation
+ QVERIFY(calledHandler);
+}
+
void tst_QProperty::uninstalledBindingDoesNotEvaluate()
{
QProperty<int> i;
@@ -2206,17 +2373,24 @@ void tst_QProperty::selfBindingShouldNotCrash()
void tst_QProperty::qpropertyAlias()
{
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
std::unique_ptr<QProperty<int>> i {new QProperty<int>};
QPropertyAlias<int> alias(i.get());
QVERIFY(alias.isValid());
alias.setValue(42);
QCOMPARE(i->value(), 42);
QProperty<int> j;
+ bool notifierCalled = false;
+ auto myNotifier = alias.addNotifier([&](){notifierCalled = true;});
i->setBinding([&]() -> int { return j; });
+ QVERIFY(notifierCalled);
j.setValue(42);
QCOMPARE(alias.value(), 42);
i.reset();
QVERIFY(!alias.isValid());
+ QT_WARNING_POP
+#endif
}
void tst_QProperty::scheduleNotify()
@@ -2256,6 +2430,151 @@ void tst_QProperty::notifyAfterAllDepsGone()
QCOMPARE(changeCounter, 2);
}
+class TestObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int signaled READ signaled WRITE setSignaled NOTIFY signaledChanged FINAL)
+ Q_PROPERTY(int bindable1 READ bindable1 WRITE setBindable1 BINDABLE bindable1Bindable NOTIFY bindable1Changed FINAL)
+ Q_PROPERTY(int bindable2 READ bindable2 WRITE setBindable2 BINDABLE bindable2Bindable NOTIFY bindable2Changed FINAL)
+
+public:
+ int signaled() const
+ {
+ return m_signaled;
+ }
+
+ void setSignaled(int newSignaled)
+ {
+ if (m_signaled == newSignaled)
+ return;
+ m_signaled = newSignaled;
+ emit signaledChanged();
+ }
+
+ int bindable1() const
+ {
+ return m_bindable1;
+ }
+
+ void setBindable1(int newBindable1)
+ {
+ if (m_bindable1 == newBindable1)
+ return;
+ m_bindable1 = newBindable1;
+ emit bindable1Changed();
+ }
+
+ QBindable<int> bindable1Bindable()
+ {
+ return QBindable<int>(&m_bindable1);
+ }
+
+ int bindable2() const
+ {
+ return m_bindable2;
+ }
+
+ void setBindable2(int newBindable2)
+ {
+ if (m_bindable2 == newBindable2)
+ return;
+ m_bindable2 = newBindable2;
+ emit bindable2Changed();
+ }
+
+ QBindable<int> bindable2Bindable()
+ {
+ return QBindable<int>(&m_bindable2);
+ }
+
+signals:
+ void signaledChanged();
+ void bindable1Changed();
+ void bindable2Changed();
+
+private:
+ int m_signaled = 0;
+ Q_OBJECT_COMPAT_PROPERTY(TestObject, int, m_bindable1, &TestObject::setBindable1, &TestObject::bindable1Changed);
+ Q_OBJECT_COMPAT_PROPERTY(TestObject, int, m_bindable2, &TestObject::setBindable2, &TestObject::bindable2Changed);
+};
+
+void tst_QProperty::propertyUpdateViaSignaledProperty()
+{
+ TestObject o;
+ QProperty<int> rootTrigger;
+ QProperty<int> signalTrigger;
+
+ o.bindable1Bindable().setBinding([&]() {
+ return rootTrigger.value();
+ });
+
+ QObject::connect(&o, &TestObject::bindable1Changed, &o, [&]() {
+ // Signaled changes only once, doesn't actually depend on bindable1.
+ // In reality, there could be some complicated calculation behind this that changes
+ // on certain checkpoints, but not on every iteration.
+ o.setSignaled(40);
+ });
+
+ o.bindable2Bindable().setBinding([&]() {
+ return signalTrigger.value() - o.bindable1();
+ });
+
+ QObject::connect(&o, &TestObject::signaledChanged, &o, [&]() {
+ signalTrigger.setValue(o.signaled());
+ });
+
+ rootTrigger.setValue(2);
+ QCOMPARE(o.bindable1(), 2);
+ QCOMPARE(o.bindable2(), 38);
+ rootTrigger.setValue(3);
+ QCOMPARE(o.bindable1(), 3);
+ QCOMPARE(o.bindable2(), 37);
+ rootTrigger.setValue(4);
+ QCOMPARE(o.bindable1(), 4);
+ QCOMPARE(o.bindable2(), 36);
+}
+
+void tst_QProperty::derefFromObserver()
+{
+ int triggered = 0;
+ QProperty<int> source(11);
+
+ DtorCounter::counter = 0;
+ DtorCounter dc;
+
+ QProperty<int> target([&triggered, &source, dc]() mutable {
+ dc.shouldIncrement = true;
+ return ++triggered + source.value();
+ });
+ QCOMPARE(triggered, 1);
+
+ {
+ auto propObserver = std::make_unique<QPropertyObserver>();
+ QPropertyObserverPointer propObserverPtr { propObserver.get() };
+ propObserverPtr.setBindingToNotify(QPropertyBindingPrivate::get(target.binding()));
+
+ QBindingObserverPtr bindingPtr(propObserver.get());
+
+ QCOMPARE(triggered, 1);
+ source = 25;
+ QCOMPARE(triggered, 2);
+ QCOMPARE(target, 27);
+
+ target.setBinding([]() { return 8; });
+ QCOMPARE(target, 8);
+
+ // The QBindingObserverPtr still holds on to the binding.
+ QCOMPARE(dc.counter, 0);
+ }
+
+ // The binding is actually gone now.
+ QCOMPARE(dc.counter, 1);
+
+ source = 26;
+ QCOMPARE(triggered, 2);
+ QCOMPARE(target, 8);
+}
+
QTEST_MAIN(tst_QProperty);
#undef QT_SOURCE_LOCATION_NAMESPACE
diff --git a/tests/auto/corelib/kernel/qsignalblocker/CMakeLists.txt b/tests/auto/corelib/kernel/qsignalblocker/CMakeLists.txt
index cd3ce66799..0effacb018 100644
--- a/tests/auto/corelib/kernel/qsignalblocker/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qsignalblocker/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qsignalblocker.pro.
-
#####################################################################
## tst_qsignalblocker Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qsignalblocker LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qsignalblocker
SOURCES
tst_qsignalblocker.cpp
diff --git a/tests/auto/corelib/kernel/qsignalblocker/tst_qsignalblocker.cpp b/tests/auto/corelib/kernel/qsignalblocker/tst_qsignalblocker.cpp
index 3345707a74..bd1f149a3f 100644
--- a/tests/auto/corelib/kernel/qsignalblocker/tst_qsignalblocker.cpp
+++ b/tests/auto/corelib/kernel/qsignalblocker/tst_qsignalblocker.cpp
@@ -1,5 +1,5 @@
// 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -11,6 +11,7 @@ class tst_QSignalBlocker : public QObject
private slots:
void signalBlocking();
void moveAssignment();
+ void dismiss();
};
void tst_QSignalBlocker::signalBlocking()
@@ -133,5 +134,15 @@ void tst_QSignalBlocker::moveAssignment()
QVERIFY(!o2.signalsBlocked());
}
+void tst_QSignalBlocker::dismiss()
+{
+ QObject obj;
+ {
+ QSignalBlocker blocker(obj);
+ blocker.dismiss();
+ }
+ QVERIFY(obj.signalsBlocked());
+}
+
QTEST_MAIN(tst_QSignalBlocker)
#include "tst_qsignalblocker.moc"
diff --git a/tests/auto/corelib/kernel/qsignalmapper/CMakeLists.txt b/tests/auto/corelib/kernel/qsignalmapper/CMakeLists.txt
index 43557c5c20..b5de408ea8 100644
--- a/tests/auto/corelib/kernel/qsignalmapper/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qsignalmapper/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qsignalmapper.pro.
-
#####################################################################
## tst_qsignalmapper Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qsignalmapper LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qsignalmapper
SOURCES
tst_qsignalmapper.cpp
diff --git a/tests/auto/corelib/kernel/qsignalmapper/tst_qsignalmapper.cpp b/tests/auto/corelib/kernel/qsignalmapper/tst_qsignalmapper.cpp
index aee5751e47..de54cd5adc 100644
--- a/tests/auto/corelib/kernel/qsignalmapper/tst_qsignalmapper.cpp
+++ b/tests/auto/corelib/kernel/qsignalmapper/tst_qsignalmapper.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
diff --git a/tests/auto/corelib/kernel/qsocketnotifier/BLACKLIST b/tests/auto/corelib/kernel/qsocketnotifier/BLACKLIST
deleted file mode 100644
index 664ad32582..0000000000
--- a/tests/auto/corelib/kernel/qsocketnotifier/BLACKLIST
+++ /dev/null
@@ -1,3 +0,0 @@
-[unexpectedDisconnection]
-windows ci
-
diff --git a/tests/auto/corelib/kernel/qsocketnotifier/CMakeLists.txt b/tests/auto/corelib/kernel/qsocketnotifier/CMakeLists.txt
index df322880c3..2a802b5f4d 100644
--- a/tests/auto/corelib/kernel/qsocketnotifier/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qsocketnotifier/CMakeLists.txt
@@ -1,7 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qsocketnotifier.pro.
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qsocketnotifier LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
if(NOT QT_FEATURE_private_tests)
return()
@@ -20,9 +24,6 @@ qt_internal_add_test(tst_qsocketnotifier
Qt::NetworkPrivate
)
-#### Keys ignored in scope 1:.:.:qsocketnotifier.pro:<TRUE>:
-# _REQUIREMENTS = "qtConfig(private_tests)"
-
## Scopes:
#####################################################################
diff --git a/tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp b/tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp
index c78650efba..17bba73dab 100644
--- a/tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp
+++ b/tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QTest>
#include <QtTest/QSignalSpy>
@@ -24,6 +24,7 @@
# undef min
#endif // Q_CC_MSVC
+using namespace std::chrono_literals;
class tst_QSocketNotifier : public QObject
{
@@ -131,11 +132,11 @@ public slots:
++sequence;
if (sequence == 1) {
// read from both ends
- (void) readEnd1->read(data1, sizeof(data1));
- (void) readEnd2->read(data2, sizeof(data2));
+ QCOMPARE(readEnd1->read(data1, sizeof(data1)), 1);
+ QCOMPARE(readEnd2->read(data2, sizeof(data2)), 1);
emit finished();
} else if (sequence == 2) {
- // we should never get here
+ // check that we can't read now because we've read our byte
QCOMPARE(readEnd2->read(data2, sizeof(data2)), qint64(-2));
QVERIFY(readEnd2->isValid());
}
@@ -151,7 +152,7 @@ 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
+ other notifier will also emit activated(). This was causing an
unexpected disconnection in QAbstractSocket.
The use case is that somebody calls one of the
@@ -187,8 +188,9 @@ void tst_QSocketNotifier::unexpectedDisconnection()
writeEnd1->waitForBytesWritten();
writeEnd2->waitForBytesWritten();
- writeEnd1->flush();
- writeEnd2->flush();
+ // ensure both read ends are ready for reading, before the event loop
+ QVERIFY(readEnd1.waitForRead(5s));
+ QVERIFY(readEnd2.waitForRead(5s));
UnexpectedDisconnectTester tester(&readEnd1, &readEnd2);
@@ -374,7 +376,7 @@ void tst_QSocketNotifier::asyncMultipleDatagram()
&tst_QSocketNotifier::async_readDatagramSlot);
// activate socket notifiers
- QTestEventLoop::instance().enterLoopMSecs(100);
+ QTestEventLoop::instance().enterLoop(100ms);
m_asyncSender->writeDatagram("1", makeNonAny(m_asyncReceiver->localAddress()), port);
m_asyncSender->writeDatagram("2", makeNonAny(m_asyncReceiver->localAddress()), port);
diff --git a/tests/auto/corelib/kernel/qtimer/BLACKLIST b/tests/auto/corelib/kernel/qtimer/BLACKLIST
deleted file mode 100644
index e69de29bb2..0000000000
--- a/tests/auto/corelib/kernel/qtimer/BLACKLIST
+++ /dev/null
diff --git a/tests/auto/corelib/kernel/qtimer/CMakeLists.txt b/tests/auto/corelib/kernel/qtimer/CMakeLists.txt
index ef7cb92a23..6bb3b15850 100644
--- a/tests/auto/corelib/kernel/qtimer/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qtimer/CMakeLists.txt
@@ -1,22 +1,34 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qtimer.pro.
-
-#####################################################################
-## tst_qtimer Test:
-#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qtimer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
if (NOT QT_FEATURE_thread)
return()
endif()
-qt_internal_add_test(tst_qtimer
- SOURCES
- tst_qtimer.cpp
- LIBRARIES
- Qt::CorePrivate
-)
+function(addTimerTest test)
+ qt_internal_add_test(${test}
+ SOURCES
+ tst_qtimer.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::TestPrivate
+ )
+endfunction()
+
+addTimerTest(tst_qtimer)
+
+if(QT_FEATURE_glib AND UNIX)
+ addTimerTest(tst_qtimer_no_glib)
+ qt_internal_extend_target(tst_qtimer_no_glib
+ DEFINES
+ DISABLE_GLIB
+ tst_QTimer=tst_QTimer_no_glib # Class name in the unittest
+ )
+endif()
-## Scopes:
-#####################################################################
diff --git a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
index 15e08a50a2..40190ca465 100644
--- a/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
+++ b/tests/auto/corelib/kernel/qtimer/tst_qtimer.cpp
@@ -1,16 +1,27 @@
// Copyright (C) 2020 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+/* WARNING: this source-code is reused by another test.
+
+ As Qt built with GUI support may use a different backend for its event loops
+ and other timer-related matters, it is important to test it in that form, as
+ well as in its GUI-less form. So this source file is reused by a build config
+ in the GUI module. Similarly, testing with and without glib is supported,
+ where relevant (see DISABLE_GLIB below).
+*/
#ifdef QT_GUI_LIB
+// When compiled as tests/auto/gui/kernel/qguitimer/'s source-code:
# include <QtGui/QGuiApplication>
#else
+// When compiled as tests/auto/corelib/kernel/qtimer/'s source-code:
# include <QtCore/QCoreApplication>
#endif
#include <QtCore/private/qglobal_p.h>
#include <QTest>
#include <QSignalSpy>
+#include <QtTest/private/qpropertytesthelper_p.h>
#include <qtimer.h>
#include <qthread.h>
@@ -21,6 +32,15 @@
#include <unistd.h>
#endif
+#ifdef DISABLE_GLIB
+static bool glibDisabled = []() {
+ qputenv("QT_NO_GLIB", "1");
+ return true;
+}();
+#endif
+
+using namespace std::chrono_literals;
+
class tst_QTimer : public QObject
{
Q_OBJECT
@@ -32,6 +52,12 @@ private slots:
void zeroTimer();
void singleShotTimeout();
void timeout();
+ void singleShotNormalizes_data();
+ void singleShotNormalizes();
+ void sequentialTimers_data();
+ void sequentialTimers();
+ void singleShotSequentialTimers_data();
+ void singleShotSequentialTimers();
void remainingTime();
void remainingTimeInitial_data();
void remainingTimeInitial();
@@ -56,6 +82,7 @@ private slots:
void singleShotToFunctors();
void singleShot_chrono();
void singleShot_static();
+ void crossThreadSingleShotToFunctor_data();
void crossThreadSingleShotToFunctor();
void timerOrder();
void timerOrder_data();
@@ -68,6 +95,10 @@ private slots:
void bindToTimer();
void bindTimer();
+ void automatedBindingTests();
+
+ void negativeInterval();
+ void testTimerId();
};
void tst_QTimer::zeroTimer()
@@ -119,6 +150,141 @@ void tst_QTimer::timeout()
QTRY_VERIFY_WITH_TIMEOUT(timeoutSpy.size() > oldCount, TIMEOUT_TIMEOUT);
}
+void tst_QTimer::singleShotNormalizes_data()
+{
+ QTest::addColumn<QByteArray>("slotName");
+
+ QTest::newRow("normalized") << QByteArray(SLOT(exitLoop()));
+
+ QTest::newRow("space-before") << QByteArray(SLOT( exitLoop()));
+ QTest::newRow("space-after") << QByteArray(SLOT(exitLoop ()));
+ QTest::newRow("space-around") << QByteArray(SLOT( exitLoop ()));
+ QTest::newRow("spaces-before") << QByteArray(SLOT( exitLoop()));
+ QTest::newRow("spaces-after") << QByteArray(SLOT(exitLoop ()));
+ QTest::newRow("spaces-around") << QByteArray(SLOT( exitLoop ()));
+
+ QTest::newRow("space-in-parens") << QByteArray(SLOT(exitLoop( )));
+ QTest::newRow("spaces-in-parens") << QByteArray(SLOT(exitLoop( )));
+ QTest::newRow("space-after-parens") << QByteArray(SLOT(exitLoop() ));
+ QTest::newRow("spaces-after-parens") << QByteArray(SLOT(exitLoop() ));
+}
+
+void tst_QTimer::singleShotNormalizes()
+{
+ static constexpr auto TestTimeout = 250ms;
+ QFETCH(QByteArray, slotName);
+ QEventLoop loop;
+
+ // control test: regular connection
+ {
+ QTimer timer;
+ QVERIFY(QObject::connect(&timer, SIGNAL(timeout()), &QTestEventLoop::instance(), slotName));
+ timer.setSingleShot(true);
+ timer.start(1);
+ QTestEventLoop::instance().enterLoop(TestTimeout);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ }
+
+ // non-zero time
+ QTimer::singleShot(1, &QTestEventLoop::instance(), slotName);
+ QTestEventLoop::instance().enterLoop(TestTimeout);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QTimer::singleShot(1ms, &QTestEventLoop::instance(), slotName);
+ QTestEventLoop::instance().enterLoop(TestTimeout);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ // zero time
+ QTimer::singleShot(0, &QTestEventLoop::instance(), slotName);
+ QTestEventLoop::instance().enterLoop(TestTimeout);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+
+ QTimer::singleShot(0ms, &QTestEventLoop::instance(), slotName);
+ QTestEventLoop::instance().enterLoop(TestTimeout);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+}
+
+void tst_QTimer::sequentialTimers_data()
+{
+#ifdef Q_OS_WIN
+ QSKIP("The API used by QEventDispatcherWin32 doesn't respect the order");
+#endif
+ QTest::addColumn<QList<int>>("timeouts");
+ auto addRow = [](const QList<int> &l) {
+ QByteArray name;
+ int last = -1;
+ for (int i = 0; i < l.size(); ++i) {
+ Q_ASSERT_X(l[i] >= last, "tst_QTimer", "input list must be sorted");
+ name += QByteArray::number(l[i]) + ',';
+ }
+ name.chop(1);
+ QTest::addRow("%s", name.constData()) << l;
+ };
+ // PreciseTimers
+ addRow({0, 0, 0, 0, 0, 0});
+ addRow({0, 1, 2});
+ addRow({1, 1, 1, 2, 2, 2, 2});
+ addRow({1, 2, 3});
+ addRow({19, 19, 19});
+ // CoarseTimer for setInterval
+ addRow({20, 20, 20, 20, 20});
+ addRow({25, 25, 25, 25, 25, 25, 50});
+}
+
+void tst_QTimer::sequentialTimers()
+{
+ QFETCH(const QList<int>, timeouts);
+ QByteArray result, expected;
+ std::vector<std::unique_ptr<QTimer>> timers;
+ expected.resize(timeouts.size());
+ result.reserve(timeouts.size());
+ timers.reserve(timeouts.size());
+ for (int i = 0; i < timeouts.size(); ++i) {
+ auto timer = std::make_unique<QTimer>();
+ timer->setSingleShot(true);
+ timer->setInterval(timeouts[i]);
+
+ char c = 'A' + i;
+ expected[i] = c;
+ QObject::connect(timer.get(), &QTimer::timeout, this, [&result, c = c]() {
+ result.append(c);
+ });
+ timers.push_back(std::move(timer));
+ }
+
+ // start the timers
+ for (auto &timer : timers)
+ timer->start();
+
+ QTestEventLoop::instance().enterLoopMSecs(timeouts.last() * 2 + 10);
+
+ QCOMPARE(result, expected);
+}
+
+void tst_QTimer::singleShotSequentialTimers_data()
+{
+ sequentialTimers_data();
+}
+
+void tst_QTimer::singleShotSequentialTimers()
+{
+ QFETCH(const QList<int>, timeouts);
+ QByteArray result, expected;
+ expected.resize(timeouts.size());
+ result.reserve(timeouts.size());
+ for (int i = 0; i < timeouts.size(); ++i) {
+ char c = 'A' + i;
+ expected[i] = c;
+ QTimer::singleShot(timeouts[i], this, [&result, c = c]() {
+ result.append(c);
+ });
+ }
+
+ QTestEventLoop::instance().enterLoopMSecs(timeouts.last() * 2 + 10);
+
+ QCOMPARE(result, expected);
+}
+
void tst_QTimer::remainingTime()
{
QTimer tested;
@@ -512,6 +678,7 @@ void tst_QTimer::moveToThread()
#endif
QTimer ti1;
QTimer ti2;
+ ti1.setSingleShot(true);
ti1.start(MOVETOTHREAD_TIMEOUT);
ti2.start(MOVETOTHREAD_TIMEOUT);
QVERIFY((ti1.timerId() & 0xffffff) != (ti2.timerId() & 0xffffff));
@@ -650,12 +817,10 @@ void tst_QTimer::timerFiresOnlyOncePerProcessEvents()
class TimerIdPersistsAfterThreadExitThread : public QThread
{
public:
- QTimer *timer;
- int timerId, returnValue;
+ QTimer *timer = nullptr;
+ Qt::TimerId timerId = Qt::TimerId::Invalid;
+ int returnValue = -1;
- TimerIdPersistsAfterThreadExitThread()
- : QThread(), timer(0), timerId(-1), returnValue(-1)
- { }
~TimerIdPersistsAfterThreadExitThread()
{
delete timer;
@@ -667,11 +832,15 @@ public:
timer = new QTimer;
connect(timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
timer->start(100);
- timerId = timer->timerId();
+ timerId = timer->id();
returnValue = eventLoop.exec();
}
};
+namespace {
+int operator&(Qt::TimerId id, int i) { return qToUnderlying(id) & i; }
+}
+
void tst_QTimer::timerIdPersistsAfterThreadExit()
{
TimerIdPersistsAfterThreadExitThread thread;
@@ -697,6 +866,19 @@ void tst_QTimer::cancelLongTimer()
QVERIFY(!timer.isActive());
}
+void tst_QTimer::testTimerId()
+{
+ QTimer timer;
+ timer.start(100ms);
+ QVERIFY(timer.isActive());
+ QCOMPARE_GT(timer.timerId(), 0);
+ QCOMPARE_GT(timer.id(), Qt::TimerId::Invalid);
+ timer.stop();
+ QVERIFY(!timer.isActive());
+ QCOMPARE(timer.timerId(), -1);
+ QCOMPARE(timer.id(), Qt::TimerId::Invalid);
+}
+
class TimeoutCounter : public QObject
{
Q_OBJECT
@@ -871,7 +1053,7 @@ void tst_QTimer::singleShotToFunctors()
thread.wait();
struct MoveOnly : CountedStruct {
- Q_DISABLE_COPY(MoveOnly);
+ Q_DISABLE_COPY(MoveOnly)
MoveOnly(MoveOnly &&o) : CountedStruct(std::move(o)) {};
MoveOnly(int *c) : CountedStruct(c) {}
};
@@ -1005,26 +1187,53 @@ void tst_QTimer::postedEventsShouldNotStarveTimers()
}
struct DummyFunctor {
- void operator()() {}
+ static QThread *callThread;
+ void operator()() {
+ callThread = QThread::currentThread();
+ callThread->quit();
+ }
};
+QThread *DummyFunctor::callThread = nullptr;
+
+void tst_QTimer::crossThreadSingleShotToFunctor_data()
+{
+ QTest::addColumn<int>("timeout");
+
+ QTest::addRow("zero-timer") << 0;
+ QTest::addRow("1ms") << 1;
+}
void tst_QTimer::crossThreadSingleShotToFunctor()
{
- // We're testing for crashes here, so the test simply running to
- // completion is considered a success
- QThread t;
- t.start();
+ QFETCH(int, timeout);
+ // We're also testing for crashes here, so the test simply running to
+ // completion is part of the success
+ DummyFunctor::callThread = nullptr;
- QObject* o = new QObject();
+ QThread t;
+ std::unique_ptr<QObject> o(new QObject());
o->moveToThread(&t);
- for (int i = 0; i < 10000; i++) {
- QTimer::singleShot(0, o, DummyFunctor());
- }
+ QTimer::singleShot(timeout, o.get(), DummyFunctor());
- t.quit();
+ // wait enough time for the timer to have timed out before the timer
+ // could be start in the receiver's thread.
+ QTest::qWait(10 + timeout * 10);
+ t.start();
t.wait();
- delete o;
+ QCOMPARE(DummyFunctor::callThread, &t);
+
+ // continue with a stress test - the calling thread is busy, the
+ // timer should still fire and no crashes.
+ DummyFunctor::callThread = nullptr;
+ t.start();
+ for (int i = 0; i < 10000; i++)
+ QTimer::singleShot(timeout, o.get(), DummyFunctor());
+
+ t.wait();
+ o.reset();
+
+ QCOMPARE(DummyFunctor::callThread, &t);
}
void tst_QTimer::callOnTimeout()
@@ -1097,6 +1306,23 @@ void tst_QTimer::bindToTimer()
timer.stop();
QVERIFY(!active);
+
+ auto ignoreMsg = [] {
+ QTest::ignoreMessage(QtWarningMsg,
+ "QObject::startTimer: Timers cannot have negative intervals");
+ };
+
+ // also test that using negative interval updates the binding correctly
+ timer.start(100);
+ QVERIFY(active);
+ ignoreMsg();
+ timer.setInterval(-100);
+ QVERIFY(!active);
+ timer.start(100);
+ QVERIFY(active);
+ ignoreMsg();
+ timer.start(-100);
+ QVERIFY(!active);
}
void tst_QTimer::bindTimer()
@@ -1141,6 +1367,73 @@ void tst_QTimer::bindTimer()
QCOMPARE(timer.timerType(), Qt::VeryCoarseTimer);
}
+void tst_QTimer::automatedBindingTests()
+{
+ QTimer timer;
+
+ QVERIFY(!timer.isSingleShot());
+ QTestPrivate::testReadWritePropertyBasics(timer, true, false, "singleShot");
+ if (QTest::currentTestFailed()) {
+ qDebug("Failed property test for QTimer::singleShot");
+ return;
+ }
+
+ QCOMPARE_NE(timer.interval(), 10);
+ QTestPrivate::testReadWritePropertyBasics(timer, 10, 20, "interval");
+ if (QTest::currentTestFailed()) {
+ qDebug("Failed property test for QTimer::interval");
+ return;
+ }
+
+ QCOMPARE_NE(timer.timerType(), Qt::PreciseTimer);
+ QTestPrivate::testReadWritePropertyBasics(timer, Qt::PreciseTimer, Qt::CoarseTimer,
+ "timerType");
+ if (QTest::currentTestFailed()) {
+ qDebug("Failed property test for QTimer::timerType");
+ return;
+ }
+
+ timer.start(1000);
+ QVERIFY(timer.isActive());
+ QTestPrivate::testReadOnlyPropertyBasics(timer, true, false, "active",
+ [&timer]() { timer.stop(); });
+ if (QTest::currentTestFailed()) {
+ qDebug("Failed property test for QTimer::active");
+ return;
+ }
+}
+
+void tst_QTimer::negativeInterval()
+{
+ auto ignoreMsg = [] {
+ QTest::ignoreMessage(QtWarningMsg,
+ "QObject::startTimer: Timers cannot have negative intervals");
+ };
+
+ QTimer timer;
+
+ // Starting with a negative interval does not change active state.
+ ignoreMsg();
+ timer.start(-100ms);
+ QVERIFY(!timer.isActive());
+
+ // Updating the interval to a negative value stops the timer and changes
+ // the active state.
+ timer.start(100ms);
+ QVERIFY(timer.isActive());
+ ignoreMsg();
+ timer.setInterval(-100);
+ QVERIFY(!timer.isActive());
+
+ // Starting with a negative interval when already started leads to stop
+ // and inactive state.
+ timer.start(100);
+ QVERIFY(timer.isActive());
+ ignoreMsg();
+ timer.start(-100ms);
+ QVERIFY(!timer.isActive());
+}
+
class OrderHelper : public QObject
{
Q_OBJECT
@@ -1214,14 +1507,10 @@ void tst_QTimer::timerOrder_data()
void tst_QTimer::timerOrderBackgroundThread()
{
-#if !QT_CONFIG(cxx11_future)
- QSKIP("This test requires QThread::create");
-#else
auto *thread = QThread::create([this]() { timerOrder(); });
thread->start();
QVERIFY(thread->wait());
delete thread;
-#endif
}
struct StaticSingleShotUser
diff --git a/tests/auto/corelib/kernel/qtranslator/CMakeLists.txt b/tests/auto/corelib/kernel/qtranslator/CMakeLists.txt
index dcf977c739..92b6edb17c 100644
--- a/tests/auto/corelib/kernel/qtranslator/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qtranslator/CMakeLists.txt
@@ -1,13 +1,17 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qtranslator.pro.
-
#####################################################################
## tst_qtranslator Test:
#####################################################################
-if (NOT QT_CONFIG_thread)
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qtranslator LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+if (NOT QT_FEATURE_thread)
return()
endif()
@@ -31,7 +35,6 @@ qt_internal_add_resource(tst_qtranslator "qtranslator"
${qtranslator_resource_files}
)
-
## Scopes:
#####################################################################
@@ -51,6 +54,3 @@ if(ANDROID)
${android_testdata_resource_files}
)
endif()
-
-#### Keys ignored in scope 3:.:.:qtranslator.pro:else:
-# TESTDATA = "dependencies_la.qm" "hellotr_empty.qm" "hellotr_la.qm" "msgfmt_from_po.qm"
diff --git a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp
index 372a7a9db1..c76500ea11 100644
--- a/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp
+++ b/tests/auto/corelib/kernel/qtranslator/tst_qtranslator.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QWaitCondition>
@@ -95,7 +95,7 @@ void tst_QTranslator::load()
{
QFile file(filepath);
- file.open(QFile::ReadOnly);
+ QVERIFY(file.open(QFile::ReadOnly));
QByteArray data = file.readAll();
QTranslator tor;
QVERIFY(tor.load((const uchar *)data.constData(), data.length()));
@@ -119,7 +119,7 @@ void tst_QTranslator::load()
void tst_QTranslator::loadLocale()
{
QLocale locale;
- auto localeName = locale.uiLanguages().value(0).replace('-', '_');
+ auto localeName = locale.uiLanguages(QLocale::TagSeparator::Underscore).value(0);
if (localeName.isEmpty())
QSKIP("This test requires at least one available UI language.");
@@ -329,7 +329,7 @@ void tst_QTranslator::dependencies()
{
QTranslator tor( 0 );
QFile file("dependencies_la.qm");
- file.open(QFile::ReadOnly);
+ QVERIFY(file.open(QFile::ReadOnly));
QByteArray data = file.readAll();
QVERIFY(tor.load((const uchar *)data.constData(), data.length()));
QVERIFY(!tor.isEmpty());
diff --git a/tests/auto/corelib/kernel/qvariant/CMakeLists.txt b/tests/auto/corelib/kernel/qvariant/CMakeLists.txt
index 437f4e0601..eae9c0d30e 100644
--- a/tests/auto/corelib/kernel/qvariant/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qvariant/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qvariant.pro.
-
#####################################################################
## tst_qvariant Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qvariant LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Resources:
set(qvariant_resource_files
"stream/qt4.9/"
@@ -21,6 +25,7 @@ qt_internal_add_test(tst_qvariant
LIBRARIES
Qt::CorePrivate
Qt::Gui
+ Qt::TestPrivate
TESTDATA ${qvariant_resource_files}
BUILTIN_TESTDATA
)
diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
index fbd17f94b2..a8e2c7bda3 100644
--- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
+++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
@@ -1,10 +1,52 @@
// Copyright (C) 2021 The Qt Company Ltd.
// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
// Copyright (C) 2016 Intel Corporation.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qvariant.h>
+// don't assume <type_traits>
+template <typename T, typename U>
+constexpr inline bool my_is_same_v = false;
+template <typename T>
+constexpr inline bool my_is_same_v<T, T> = true;
+
+#define CHECK_IMPL(func, arg, Variant, cvref, R) \
+ static_assert(my_is_same_v<decltype( func < arg >(std::declval< Variant cvref >())), R cvref >)
+
+#define CHECK_GET_IF(Variant, cvref) \
+ CHECK_IMPL(get_if, int, Variant, cvref *, int)
+
+#define CHECK_GET(Variant, cvref) \
+ CHECK_IMPL(get, int, Variant, cvref, int)
+
+CHECK_GET_IF(QVariant, /* unadorned */);
+CHECK_GET_IF(QVariant, const);
+
+CHECK_GET(QVariant, &);
+CHECK_GET(QVariant, const &);
+CHECK_GET(QVariant, &&);
+CHECK_GET(QVariant, const &&);
+
+// check for a type derived from QVariant:
+
+struct MyVariant : QVariant
+{
+ using QVariant::QVariant;
+};
+
+CHECK_GET_IF(MyVariant, /* unadorned */);
+CHECK_GET_IF(MyVariant, const);
+
+CHECK_GET(MyVariant, &);
+CHECK_GET(MyVariant, const &);
+CHECK_GET(MyVariant, &&);
+CHECK_GET(MyVariant, const &&);
+
+#undef CHECK_GET_IF
+#undef CHECK_GET
+#undef CHECK_IMPL
+
#include <QTest>
// Please stick to alphabetic order.
@@ -34,6 +76,7 @@
#include <QUrl>
#include <QUuid>
+#include <private/qcomparisontesthelper_p.h>
#include <private/qlocale_p.h>
#include <private/qmetatype_p.h>
#include "tst_qvariant_common.h"
@@ -44,7 +87,10 @@
#include <variant>
#include <unordered_map>
+using namespace Qt::StringLiterals;
+
class CustomNonQObject;
+struct NonDefaultConstructible;
template<typename T, typename = void>
struct QVariantFromValueCompiles
@@ -129,6 +175,12 @@ private slots:
void canConvert_data();
void canConvert();
+
+ void canConvertAndConvert_ReturnFalse_WhenConvertingBetweenPointerAndValue_data();
+ void canConvertAndConvert_ReturnFalse_WhenConvertingBetweenPointerAndValue();
+
+ void canConvertAndConvert_ReturnFalse_WhenConvertingQObjectBetweenPointerAndValue();
+
void convert();
void toSize_data();
@@ -239,6 +291,7 @@ private slots:
void variantHash();
void convertToQUint8() const;
+ void compareCompiles() const;
void compareNumerics_data() const;
void compareNumerics() const;
void comparePointers() const;
@@ -254,6 +307,7 @@ private slots:
void loadBrokenUserType();
void invalidDate() const;
+ void compareCustomTypes_data() const;
void compareCustomTypes() const;
void timeToDateTime() const;
void copyingUserTypes() const;
@@ -326,14 +380,41 @@ private slots:
void preferDirectConversionOverInterfaces();
void mutableView();
+ void canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPointerAndValue();
+
void moveOperations();
void equalsWithoutMetaObject();
void constructFromIncompatibleMetaType_data();
void constructFromIncompatibleMetaType();
+ void constructFromQtLT65MetaType();
void copyNonDefaultConstructible();
+ void inplaceConstruct();
+ void emplace();
+
+ void getIf_int() { getIf_impl(42); }
+ void getIf_QString() { getIf_impl(u"string"_s); };
+ void getIf_NonDefaultConstructible();
+ void getIfSpecial();
+
+ void get_int() { get_impl(42); }
+ void get_QString() { get_impl(u"string"_s); }
+ void get_NonDefaultConstructible();
+
private:
+ using StdVariant = std::variant<std::monostate,
+ // list here all the types with which we instantiate getIf_impl:
+ int,
+ QString,
+ NonDefaultConstructible
+ >;
+ template <typename T>
+ void getIf_impl(T t) const;
+ template <typename T>
+ void get_impl(T t) const;
+ template<typename T>
+ void canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPointerAndValue_impl(const QByteArray &typeName);
void dataStream_data(QDataStream::Version version);
void loadQVariantFromDataStream(QDataStream::Version version);
void saveQVariantFromDataStream(QDataStream::Version version);
@@ -364,11 +445,21 @@ void tst_QVariant::constructor()
QVERIFY(var3.isNull());
QVERIFY(var3.isValid());
+ QVariant var3a = QVariant::fromMetaType(QMetaType::fromType<QString>());
+ QCOMPARE(var3a.typeName(), "QString");
+ QVERIFY(var3a.isNull());
+ QVERIFY(var3a.isValid());
+
QVariant var4 {QMetaType()};
QCOMPARE(var4.typeId(), QMetaType::UnknownType);
QVERIFY(var4.isNull());
QVERIFY(!var4.isValid());
+ QVariant var4a = QVariant::fromMetaType(QMetaType());
+ QCOMPARE(var4a.typeId(), QMetaType::UnknownType);
+ QVERIFY(var4a.isNull());
+ QVERIFY(!var4a.isValid());
+
QVariant var5(QLatin1String("hallo"));
QCOMPARE(var5.typeId(), QMetaType::QString);
QCOMPARE(var5.typeName(), "QString");
@@ -411,6 +502,14 @@ void tst_QVariant::constructor_invalid()
}
{
QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type"));
+ QVariant variant = QVariant::fromMetaType(QMetaType(typeId));
+ QVERIFY(!variant.isValid());
+ QVERIFY(variant.isNull());
+ QCOMPARE(variant.typeId(), int(QMetaType::UnknownType));
+ QCOMPARE(variant.userType(), int(QMetaType::UnknownType));
+ }
+ {
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("^Trying to construct an instance of an invalid type"));
QVariant variant(QMetaType(typeId), /* copy */ nullptr);
QVERIFY(!variant.isValid());
QVERIFY(variant.isNull());
@@ -445,6 +544,9 @@ void tst_QVariant::isNull()
var3 = QVariant(QMetaType::fromType<QString>());
QVERIFY( var3.isNull() );
+ var3.setValue(QString());
+ QVERIFY( !var3.isNull() );
+
QVariant var4( 0 );
QVERIFY( !var4.isNull() );
@@ -622,6 +724,100 @@ QT_WARNING_POP
#endif // QT_DEPRECATED_SINCE(6, 0)
}
+namespace {
+
+// Used for testing canConvert/convert of QObject derived types
+struct QObjectDerived : QObject
+{
+ Q_OBJECT
+};
+
+// Adds a test table row for checking value <-> pointer conversion
+// If type is a pointer, the target type is value type and vice versa.
+template<typename T>
+void addRowForPointerValueConversion()
+{
+ using ValueType = std::remove_pointer_t<T>;
+ if constexpr (!std::is_same_v<ValueType, std::nullptr_t>) {
+
+ static ValueType instance{}; // static since we may need a pointer to a valid object
+
+ QVariant variant;
+ if constexpr (std::is_pointer_v<T>)
+ variant = QVariant::fromValue(&instance);
+ else
+ variant = QVariant::fromValue(instance);
+
+ // Toggle pointer/value type
+ using TargetType = std::conditional_t<std::is_pointer_v<T>, ValueType, T *>;
+
+ const QMetaType fromType = QMetaType::fromType<T>();
+ const QMetaType toType = QMetaType::fromType<TargetType>();
+
+ QTest::addRow("%s->%s", fromType.name(), toType.name())
+ << variant << QMetaType::fromType<TargetType>();
+ }
+}
+
+} // namespace
+
+void tst_QVariant::canConvertAndConvert_ReturnFalse_WhenConvertingBetweenPointerAndValue_data()
+{
+ QTest::addColumn<QVariant>("variant");
+ QTest::addColumn<QMetaType>("targetType");
+
+#define ADD_ROW(typeName, typeNameId, realType) \
+ addRowForPointerValueConversion<realType>(); \
+ addRowForPointerValueConversion<realType *>();
+
+ // Add rows for static primitive types
+ QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(ADD_ROW)
+
+ // Add rows for static core types
+ QT_FOR_EACH_STATIC_CORE_CLASS(ADD_ROW)
+#undef ADD_ROW
+
+}
+
+void tst_QVariant::canConvertAndConvert_ReturnFalse_WhenConvertingBetweenPointerAndValue()
+{
+ QFETCH(QVariant, variant);
+ QFETCH(QMetaType, targetType);
+
+ QVERIFY(!variant.canConvert(targetType));
+
+ QVERIFY(!variant.convert(targetType));
+
+ // As per the documentation, when QVariant::convert fails, the
+ // QVariant is cleared and changed to the requested type.
+ QVERIFY(variant.isNull());
+ QCOMPARE(variant.metaType(), targetType);
+}
+
+void tst_QVariant::canConvertAndConvert_ReturnFalse_WhenConvertingQObjectBetweenPointerAndValue()
+{
+ // Types derived from QObject are non-copyable and require their own test.
+ // We only test pointer -> value conversion, because constructing a QVariant
+ // from a non-copyable object will just set the QVariant to null.
+
+ QObjectDerived object;
+ QVariant variant = QVariant::fromValue(&object);
+
+ constexpr QMetaType targetType = QMetaType::fromType<QObjectDerived>();
+ QVERIFY(!variant.canConvert(targetType));
+
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ QRegularExpression(".*does not support destruction and copy construction"));
+
+ QVERIFY(!variant.convert(targetType));
+
+ // When the QVariant::convert fails, the QVariant is cleared, and since the target type is
+ // invalid for QVariant, the QVariant's type is also cleared to an unknown type.
+ QVERIFY(variant.isNull());
+ QCOMPARE(variant.metaType(), QMetaType());
+}
+
void tst_QVariant::convert()
{
// verify that after convert(), the variant's type has been changed
@@ -631,7 +827,6 @@ void tst_QVariant::convert()
QCOMPARE(var.toInt(), 0);
}
-
void tst_QVariant::toInt_data()
{
QTest::addColumn<QVariant>("value");
@@ -1568,6 +1763,10 @@ void tst_QVariant::operator_eq_eq_data()
QVariant mIntString(QByteArray("-42"));
QVariant mIntQString(QString("-42"));
+ QVariant mIntZero(0);
+ QVariant mIntStringZero(QByteArray("0"));
+ QVariant mIntQStringZero(QString("0"));
+
QVariant mUInt(42u);
QVariant mUIntString(QByteArray("42"));
QVariant mUIntQString(QString("42"));
@@ -1608,6 +1807,9 @@ void tst_QVariant::operator_eq_eq_data()
QVariant mBoolString(QByteArray("false"));
QVariant mBoolQString(QString("false"));
+ QVariant mTextString(QByteArray("foobar"));
+ QVariant mTextQString(QString("foobar"));
+
QTest::newRow( "double_int" ) << QVariant(42.0) << QVariant(42) << true;
QTest::newRow( "float_int" ) << QVariant(42.f) << QVariant(42) << true;
QTest::newRow( "mInt_mIntString" ) << mInt << mIntString << false;
@@ -1615,6 +1817,21 @@ void tst_QVariant::operator_eq_eq_data()
QTest::newRow( "mInt_mIntQString" ) << mInt << mIntQString << true;
QTest::newRow( "mIntQString_mInt" ) << mIntQString << mInt << true;
+ QTest::newRow( "mIntZero_mIntStringZero" ) << mIntZero << mIntStringZero << false;
+ QTest::newRow( "mIntStringZero_mIntZero" ) << mIntStringZero << mIntZero << false;
+ QTest::newRow( "mIntZero_mIntQStringZero" ) << mIntZero << mIntQStringZero << true;
+ QTest::newRow( "mIntQStringZero_mIntZero" ) << mIntQStringZero << mIntZero << true;
+
+ QTest::newRow( "mInt_mTextString" ) << mInt << mTextString << false;
+ QTest::newRow( "mTextString_mInt" ) << mTextString << mInt << false;
+ QTest::newRow( "mInt_mTextQString" ) << mInt << mTextQString << false;
+ QTest::newRow( "mTextQString_mInt" ) << mTextQString << mInt << false;
+
+ QTest::newRow( "mIntZero_mTextString" ) << mIntZero << mTextString << false;
+ QTest::newRow( "mTextString_mIntZero" ) << mTextString << mIntZero << false;
+ QTest::newRow( "mIntZero_mTextQString" ) << mIntZero << mTextQString << false;
+ QTest::newRow( "mTextQString_mIntZero" ) << mTextQString << mIntZero << false;
+
QTest::newRow( "mUInt_mUIntString" ) << mUInt << mUIntString << false;
QTest::newRow( "mUIntString_mUInt" ) << mUIntString << mUInt << false;
QTest::newRow( "mUInt_mUIntQString" ) << mUInt << mUIntQString << true;
@@ -1625,6 +1842,11 @@ void tst_QVariant::operator_eq_eq_data()
QTest::newRow( "mDouble_mDoubleQString" ) << mDouble << mDoubleQString << true;
QTest::newRow( "mDoubleQString_mDouble" ) << mDoubleQString << mDouble << true;
+ QTest::newRow( "mDouble_mTextString" ) << mDouble << mTextString << false;
+ QTest::newRow( "mTextString_mDouble" ) << mTextString << mDouble << false;
+ QTest::newRow( "mDouble_mTextQString" ) << mDouble << mTextQString << false;
+ QTest::newRow( "mTextQString_mDouble" ) << mTextQString << mDouble << false;
+
QTest::newRow( "mFloat_mFloatString" ) << mFloat << mFloatString << false;
QTest::newRow( "mFloatString_mFloat" ) << mFloatString << mFloat << false;
QTest::newRow( "mFloat_mFloatQString" ) << mFloat << mFloatQString << true;
@@ -1791,7 +2013,7 @@ void tst_QVariant::operator_eq_eq()
QFETCH( QVariant, left );
QFETCH( QVariant, right );
QFETCH( bool, equal );
- QCOMPARE( left == right, equal );
+ QT_TEST_EQUALITY_OPS(left, right, equal);
}
#if QT_DEPRECATED_SINCE(6, 0)
@@ -1968,6 +2190,8 @@ void tst_QVariant::userType()
QVariant userVar;
userVar.setValue(data);
+ QVERIFY(QMetaType::fromName("MyType").isValid());
+ QCOMPARE(QMetaType::fromName("MyType"), QMetaType::fromType<MyType>());
QVERIFY(userVar.typeId() > QMetaType::User);
QCOMPARE(userVar.userType(), qMetaTypeId<MyType>());
QCOMPARE(userVar.typeName(), "MyType");
@@ -2091,7 +2315,15 @@ void tst_QVariant::podUserType()
pod.a = 10;
pod.b = 20;
+ // one of these two must register the type
+ // (QVariant::fromValue calls QMetaType::fromType)
QVariant pod_as_variant = QVariant::fromValue(pod);
+ QMetaType mt = QMetaType::fromType<MyTypePOD>();
+ QCOMPARE(pod_as_variant.metaType(), mt);
+ QCOMPARE(pod_as_variant.metaType().name(), mt.name());
+ QCOMPARE(QMetaType::fromName(mt.name()), mt);
+ QCOMPARE_NE(pod_as_variant.typeId(), 0);
+
MyTypePOD pod2 = qvariant_cast<MyTypePOD>(pod_as_variant);
QCOMPARE(pod.a, pod2.a);
@@ -2754,6 +2986,11 @@ void tst_QVariant::convertToQUint8() const
}
}
+void tst_QVariant::compareCompiles() const
+{
+ QTestPrivate::testEqualityOperatorsCompile<QVariant>();
+}
+
void tst_QVariant::compareNumerics_data() const
{
QTest::addColumn<QVariant>("v1");
@@ -2771,9 +3008,11 @@ void tst_QVariant::compareNumerics_data() const
QString::number(v.toULongLong()) :
QString::number(v.toLongLong());
switch (v.typeId()) {
- case QMetaType::Char:
case QMetaType::Char16:
+ return QString::number(qvariant_cast<char16_t>(v));
case QMetaType::Char32:
+ return QString::number(qvariant_cast<char32_t>(v));
+ case QMetaType::Char:
case QMetaType::UChar:
return QString::number(v.toUInt());
case QMetaType::SChar:
@@ -2917,7 +3156,7 @@ QT_WARNING_POP
addComparePair(LLONG_MIN, quint64(LLONG_MIN) + 1);
addComparePair(LLONG_MIN + 1, quint64(LLONG_MIN) + 1);
addComparePair(LLONG_MIN, LLONG_MAX - 1);
- addComparePair(LLONG_MIN, LLONG_MAX);
+ // addComparePair(LLONG_MIN, LLONG_MAX); // already added by addSingleType()
// floating point
addComparePair(0.f, 0);
@@ -2926,7 +3165,6 @@ QT_WARNING_POP
addComparePair(0.f, Q_UINT64_C(0));
addComparePair(0.f, 0.);
addComparePair(0.f, 1.);
- addComparePair(0.f, 1.);
addComparePair(float(1 << 24), 1 << 24);
addComparePair(float(1 << 24) - 1, (1 << 24) - 1);
addComparePair(-float(1 << 24), 1 << 24);
@@ -2936,7 +3174,7 @@ QT_WARNING_POP
addComparePair(qQNaN(), std::numeric_limits<float>::quiet_NaN());
if (sizeof(qreal) == sizeof(double)) {
addComparePair(std::numeric_limits<float>::min(), std::numeric_limits<double>::min());
- addComparePair(std::numeric_limits<float>::min(), std::numeric_limits<double>::min());
+ addComparePair(std::numeric_limits<float>::min(), std::numeric_limits<double>::max());
addComparePair(std::numeric_limits<float>::max(), std::numeric_limits<double>::min());
addComparePair(std::numeric_limits<float>::max(), std::numeric_limits<double>::max());
addComparePair(double(Q_INT64_C(1) << 53), Q_INT64_C(1) << 53);
@@ -2999,25 +3237,38 @@ void tst_QVariant::compareNumerics() const
QFETCH(QPartialOrdering, result);
QCOMPARE(QVariant::compare(v1, v2), result);
- QEXPECT_FAIL("invalid-invalid", "needs fixing", Continue);
- if (result == QPartialOrdering::Equivalent)
- QCOMPARE_EQ(v1, v2);
- else
- QCOMPARE_NE(v1, v2);
+ QEXPECT_FAIL("invalid-invalid", "needs fixing", Abort);
+ QT_TEST_EQUALITY_OPS(v1, v2, is_eq(result));
}
void tst_QVariant::comparePointers() const
{
- class MyClass
- {
- };
+ class NonQObjectClass {};
+ const std::array<NonQObjectClass, 2> arr{ NonQObjectClass{}, NonQObjectClass{} };
+
+ const QVariant nonObjV1 = QVariant::fromValue<const void*>(&arr[0]);
+ const QVariant nonObjV2 = QVariant::fromValue<const void*>(&arr[1]);
+
+ Qt::partial_ordering expectedOrdering = Qt::partial_ordering::equivalent;
+ QCOMPARE(QVariant::compare(nonObjV1, nonObjV1), expectedOrdering);
+ QT_TEST_EQUALITY_OPS(nonObjV1, nonObjV1, is_eq(expectedOrdering));
- MyClass myClass;
+ expectedOrdering = Qt::partial_ordering::less;
+ QCOMPARE(QVariant::compare(nonObjV1, nonObjV2), expectedOrdering);
+ QT_TEST_EQUALITY_OPS(nonObjV1, nonObjV2, is_eq(expectedOrdering));
- QVariant v = QVariant::fromValue<void *>(&myClass);
- QVariant v2 = QVariant::fromValue<void *>(&myClass);
+ class QObjectClass : public QObject
+ {
+ public:
+ QObjectClass(QObject *parent = nullptr) : QObject(parent) {}
+ };
+ const QObjectClass c1;
+ const QObjectClass c2;
- QCOMPARE(v, v2);
+ const QVariant objV1 = QVariant::fromValue(&c1);
+ const QVariant objV2 = QVariant::fromValue(&c2);
+ QT_TEST_EQUALITY_OPS(objV1, objV1, true);
+ QT_TEST_EQUALITY_OPS(objV1, objV2, false);
}
struct Data {};
@@ -3216,35 +3467,49 @@ Q_DECLARE_METATYPE(WontCompare);
struct WillCompare
{
int x;
+
+ friend bool operator==(const WillCompare &a, const WillCompare &b)
+ { return a.x == b.x; }
+ friend bool operator<(const WillCompare &a, const WillCompare &b)
+ { return a.x < b.x; }
};
-bool operator==(const WillCompare &a, const WillCompare &b) { return a.x == b.x; }
Q_DECLARE_METATYPE(WillCompare);
-void tst_QVariant::compareCustomTypes() const
+void tst_QVariant::compareCustomTypes_data() const
{
- {
- WontCompare f1{0};
- const QVariant variant1(QVariant::fromValue(f1));
+ QTest::addColumn<QVariant>("v1");
+ QTest::addColumn<QVariant>("v2");
+ QTest::addColumn<Qt::partial_ordering>("expectedOrdering");
- WontCompare f2{1};
- const QVariant variant2(QVariant::fromValue(f2));
+ QTest::newRow("same_uncomparable")
+ << QVariant::fromValue(WontCompare{0})
+ << QVariant::fromValue(WontCompare{0})
+ << Qt::partial_ordering::unordered;
- /* No comparison operator exists. */
- QVERIFY(variant1 != variant2);
- QVERIFY(variant1 != variant1);
- QVERIFY(variant2 != variant2);
- }
- {
- WillCompare f1{0};
- const QVariant variant1(QVariant::fromValue(f1));
+ QTest::newRow("same_comparable")
+ << QVariant::fromValue(WillCompare{0})
+ << QVariant::fromValue(WillCompare{0})
+ << Qt::partial_ordering::equivalent;
- WillCompare f2 {1};
- const QVariant variant2(QVariant::fromValue(f2));
+ QTest::newRow("different_comparable")
+ << QVariant::fromValue(WillCompare{1})
+ << QVariant::fromValue(WillCompare{0})
+ << Qt::partial_ordering::greater;
- QVERIFY(variant1 != variant2);
- QCOMPARE(variant1, variant1);
- QCOMPARE(variant2, variant2);
- }
+ QTest::newRow("qdatetime_vs_comparable")
+ << QVariant::fromValue(QDateTime::currentDateTimeUtc())
+ << QVariant::fromValue(WillCompare{0})
+ << Qt::partial_ordering::unordered;
+}
+
+void tst_QVariant::compareCustomTypes() const
+{
+ QFETCH(const QVariant, v1);
+ QFETCH(const QVariant, v2);
+ QFETCH(const Qt::partial_ordering, expectedOrdering);
+
+ QCOMPARE(QVariant::compare(v1, v2), expectedOrdering);
+ QT_TEST_EQUALITY_OPS(v1, v2, is_eq(expectedOrdering));
}
void tst_QVariant::timeToDateTime() const
{
@@ -4165,7 +4430,8 @@ void tst_QVariant::dataStream_data(QDataStream::Version version)
path = path.prepend(":/stream/").append("/");
QDir dir(path);
uint i = 0;
- foreach (const QFileInfo &fileInfo, dir.entryInfoList(QStringList() << "*.bin")) {
+ const auto entries = dir.entryInfoList(QStringList{u"*.bin"_s});
+ for (const QFileInfo &fileInfo : entries) {
QTest::newRow((path + fileInfo.fileName()).toLatin1()) << fileInfo.filePath();
i += 1;
}
@@ -4982,6 +5248,13 @@ template <auto value> static void testVariantEnum()
QVERIFY(var2.convert(QMetaType::fromType<int>()));
QCOMPARE(var2.value<int>(), static_cast<int>(value));
+ QVariant strVar = QString::number(qToUnderlying(value));
+ QVariant baVar = QByteArray::number(qToUnderlying(value));
+ QCOMPARE(strVar.value<Enum>(), value);
+ QCOMPARE(baVar.value<Enum>(), value);
+ QCOMPARE(var.value<QString>(), strVar);
+ QCOMPARE(var.value<QByteArray>(), baVar);
+
// unary + to silence gcc warning
if (losslessConvertToInt) {
int intValue = static_cast<int>(value);
@@ -5280,6 +5553,16 @@ void tst_QVariant::shouldDeleteVariantDataWorksForAssociative()
void tst_QVariant::fromStdVariant()
{
+#define CHECK_EQUAL(lhs, rhs, type) do { \
+ QCOMPARE(lhs.typeId(), rhs.typeId()); \
+ if (lhs.isNull()) { \
+ QVERIFY(rhs.isNull()); \
+ } else { \
+ QVERIFY(!rhs.isNull()); \
+ QCOMPARE(get< type >(lhs), get< type >(rhs)); \
+ } \
+ } while (false)
+
{
typedef std::variant<int, bool> intorbool_t;
intorbool_t stdvar = 5;
@@ -5287,21 +5570,38 @@ void tst_QVariant::fromStdVariant()
QVERIFY(!qvar.isNull());
QCOMPARE(qvar.typeId(), QMetaType::Int);
QCOMPARE(qvar.value<int>(), std::get<int>(stdvar));
+ {
+ const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
+ CHECK_EQUAL(qv2, qvar, int);
+ }
+
stdvar = true;
qvar = QVariant::fromStdVariant(stdvar);
QVERIFY(!qvar.isNull());
QCOMPARE(qvar.typeId(), QMetaType::Bool);
QCOMPARE(qvar.value<bool>(), std::get<bool>(stdvar));
+ {
+ const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
+ CHECK_EQUAL(qv2, qvar, bool);
+ }
}
{
std::variant<std::monostate, int> stdvar;
QVariant qvar = QVariant::fromStdVariant(stdvar);
QVERIFY(!qvar.isValid());
+ {
+ const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
+ CHECK_EQUAL(qv2, qvar, int); // fake type, they're empty
+ }
stdvar = -4;
qvar = QVariant::fromStdVariant(stdvar);
QVERIFY(!qvar.isNull());
QCOMPARE(qvar.typeId(), QMetaType::Int);
QCOMPARE(qvar.value<int>(), std::get<int>(stdvar));
+ {
+ const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
+ CHECK_EQUAL(qv2, qvar, int);
+ }
}
{
std::variant<int, bool, QChar> stdvar = QChar::fromLatin1(' ');
@@ -5309,7 +5609,25 @@ void tst_QVariant::fromStdVariant()
QVERIFY(!qvar.isNull());
QCOMPARE(qvar.typeId(), QMetaType::QChar);
QCOMPARE(qvar.value<QChar>(), std::get<QChar>(stdvar));
+ {
+ const auto qv2 = QVariant::fromStdVariant(std::move(stdvar));
+ CHECK_EQUAL(qv2, qvar, QChar);
+ }
}
+ // rvalue fromStdVariant() actually moves:
+ {
+ const auto foo = u"foo"_s;
+ std::variant<QString, QByteArray> stdvar = foo;
+ QVariant qvar = QVariant::fromStdVariant(std::move(stdvar));
+ const auto ps = get_if<QString>(&stdvar);
+ QVERIFY(ps);
+ QVERIFY(ps->isNull()); // QString was moved from
+ QVERIFY(!qvar.isNull());
+ QCOMPARE(qvar.typeId(), QMetaType::QString);
+ QCOMPARE(get<QString>(qvar), foo);
+ }
+
+#undef CHECK_EQUAL
}
void tst_QVariant::qt4UuidDataStream()
@@ -5461,6 +5779,48 @@ void tst_QVariant::mutableView()
QCOMPARE(extracted.text, nullptr);
}
+template<typename T>
+void tst_QVariant::canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPointerAndValue_impl(
+ const QByteArray &typeName)
+{
+ T instance{};
+
+ // Value -> Pointer
+ QVariant value = QVariant::fromValue(instance);
+ QVERIFY2(!value.canView<T *>(), typeName);
+ QCOMPARE(value.view<T *>(), nullptr); // Expect default constructed pointer
+
+ // Pointer -> Value
+ QVariant pointer = QVariant::fromValue(&instance);
+ QVERIFY2(!pointer.canView<T>(), typeName);
+ QCOMPARE(pointer.view<T>(), T{}); // Expect default constructed. Note: Weak test since instance
+ // is default constructed, but we detect data corruption
+}
+
+void tst_QVariant::canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPointerAndValue()
+{
+#define ADD_TEST_IMPL(typeName, typeNameId, realType) \
+ canViewAndView_ReturnFalseAndDefault_WhenConvertingBetweenPointerAndValue_impl<realType>( \
+ #typeName);
+
+ // Add tests for static primitive types
+ QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(ADD_TEST_IMPL)
+
+ // Add tests for static core types
+ QT_FOR_EACH_STATIC_CORE_CLASS(ADD_TEST_IMPL)
+#undef ADD_TEST_IMPL
+}
+
+struct MoveTester
+{
+ bool wasMoved = false;
+ MoveTester() = default;
+ MoveTester(const MoveTester &) {}; // non-trivial on purpose
+ MoveTester(MoveTester &&other) { other.wasMoved = true; }
+ MoveTester& operator=(const MoveTester &) = default;
+ MoveTester& operator=(MoveTester &&other) {other.wasMoved = true; return *this;}
+};
+
void tst_QVariant::moveOperations()
{
{
@@ -5482,6 +5842,30 @@ void tst_QVariant::moveOperations()
v = QVariant::fromValue(list);
v2 = std::move(v);
QVERIFY(v2.value<std::list<int>>() == list);
+
+ {
+ MoveTester tester;
+ QVariant::fromValue(tester);
+ QVERIFY(!tester.wasMoved);
+ QVariant::fromValue(std::move(tester));
+ QVERIFY(tester.wasMoved);
+ }
+ {
+ const MoveTester tester;
+ QVariant::fromValue(std::move(tester));
+ QVERIFY(!tester.wasMoved); // we don't want to move from const variables
+ }
+ {
+ QVariant var(std::in_place_type<MoveTester>);
+ const auto p = get_if<MoveTester>(&var);
+ QVERIFY(p);
+ auto &tester = *p;
+ QVERIFY(!tester.wasMoved);
+ [[maybe_unused]] auto copy = var.value<MoveTester>();
+ QVERIFY(!tester.wasMoved);
+ [[maybe_unused]] auto moved = std::move(var).value<MoveTester>();
+ QVERIFY(tester.wasMoved);
+ }
}
class NoMetaObject : public QObject {};
@@ -5552,6 +5936,13 @@ private:
~Indestructible() {}
};
+struct NotCopyable
+{
+ NotCopyable() = default;
+ NotCopyable(const NotCopyable&) = delete;
+ NotCopyable &operator=(const NotCopyable &) = delete;
+};
+
void tst_QVariant::constructFromIncompatibleMetaType_data()
{
QTest::addColumn<QMetaType>("type");
@@ -5562,6 +5953,7 @@ void tst_QVariant::constructFromIncompatibleMetaType_data()
addRow(QMetaType::fromType<NonDefaultConstructible>());
addRow(QMetaType::fromType<QObject>());
addRow(QMetaType::fromType<Indestructible>());
+ addRow(QMetaType::fromType<NotCopyable>());
}
void tst_QVariant::constructFromIncompatibleMetaType()
@@ -5592,23 +5984,353 @@ void tst_QVariant::constructFromIncompatibleMetaType()
QVERIFY(!QVariant(regular).convert(type));
}
+void tst_QVariant::constructFromQtLT65MetaType()
+{
+ auto qsizeIface = QtPrivate::qMetaTypeInterfaceForType<QSize>();
+
+ QtPrivate::QMetaTypeInterface qsize64Iface = {
+ /*revision*/0,
+ 8,
+ 8,
+ QMetaType::NeedsConstruction | QMetaType::NeedsDestruction,
+ 0,
+ qsizeIface->metaObjectFn,
+ "FakeQSize",
+ qsizeIface->defaultCtr,
+ qsizeIface->copyCtr,
+ qsizeIface->moveCtr,
+ /*dtor =*/ nullptr,
+ qsizeIface->equals,
+ qsizeIface->lessThan,
+ qsizeIface->debugStream,
+ qsizeIface->dataStreamOut,
+ qsizeIface->dataStreamIn,
+ /*legacyregop =*/ nullptr
+ };
+ QVariant var{ QMetaType(&qsize64Iface) };
+ QVERIFY(var.isValid());
+}
+
void tst_QVariant::copyNonDefaultConstructible()
{
NonDefaultConstructible ndc(42);
- QVariant var(QMetaType::fromType<NonDefaultConstructible>(), &ndc);
+ QVariant var = QVariant::fromValue(ndc);
QVERIFY(var.isDetached());
QCOMPARE(var.metaType(), QMetaType::fromType<NonDefaultConstructible>());
QVERIFY(var.constData() != &ndc);
// qvariant_cast<T> and QVariant::value<T> don't compile
- QCOMPARE(*static_cast<const NonDefaultConstructible *>(var.constData()), ndc);
+ QCOMPARE(get<NonDefaultConstructible>(std::as_const(var)), ndc);
QVariant var2 = var;
var2.detach(); // force another copy
QVERIFY(var2.isDetached());
QVERIFY(var2.constData() != var.constData());
+ QCOMPARE(get<NonDefaultConstructible>(std::as_const(var2)),
+ get<NonDefaultConstructible>(std::as_const(var)));
QCOMPARE(var2, var);
}
+void tst_QVariant::inplaceConstruct()
+{
+ {
+ NonDefaultConstructible ndc(42);
+ QVariant var(std::in_place_type<NonDefaultConstructible>, 42);
+ QVERIFY(get_if<NonDefaultConstructible>(&var));
+ QCOMPARE(get<NonDefaultConstructible>(var), ndc);
+ }
+
+ {
+ std::vector<int> vec {1, 2, 3, 4};
+ QVariant var(std::in_place_type<std::vector<int>>, {1, 2, 3, 4});
+ QVERIFY(get_if<std::vector<int>>(&var));
+ QCOMPARE(get<std::vector<int>>(var), vec);
+ }
+}
+
+struct LargerThanInternalQVariantStorage {
+ char data[6 * sizeof(void *)];
+};
+
+struct alignas(256) LargerThanInternalQVariantStorageOveraligned {
+ char data[6 * sizeof(void *)];
+};
+
+struct alignas(128) SmallerAlignmentEvenLargerSize {
+ char data[17 * sizeof(void *)];
+};
+
+void tst_QVariant::emplace()
+{
+ {
+ // can emplace non default constructible + can emplace on null variant
+ NonDefaultConstructible ndc(42);
+ QVariant var;
+ var.emplace<NonDefaultConstructible>(42);
+ QVERIFY(get_if<NonDefaultConstructible>(&var));
+ QCOMPARE(get<NonDefaultConstructible>(var), ndc);
+ }
+ {
+ // can emplace using ctor taking initializer_list
+ QVariant var;
+ var.emplace<std::vector<int>>({0, 1, 2, 3, 4});
+ auto vecPtr = get_if<std::vector<int>>(&var);
+ QVERIFY(vecPtr);
+ QCOMPARE(vecPtr->size(), 5U);
+ for (int i = 0; i < 5; ++i)
+ QCOMPARE(vecPtr->at(size_t(i)), i);
+ }
+ // prequisites for the test
+ QCOMPARE_LE(sizeof(std::vector<int>), sizeof(std::string));
+ QCOMPARE(alignof(std::vector<int>), alignof(std::string));
+ {
+ // emplace can reuse storage
+ auto var = QVariant::fromValue(std::string{});
+ QVERIFY(var.data_ptr().is_shared);
+ auto data = var.constData();
+ std::vector<int> &vec = var.emplace<std::vector<int>>(3, 42);
+ /* alignment is the same, so the pointer is exactly the same;
+ no offset change */
+ auto expected = std::vector<int>{42, 42, 42};
+ QCOMPARE(get_if<std::vector<int>>(&var), &vec);
+ QCOMPARE(get<std::vector<int>>(var), expected);
+ QCOMPARE(var.constData(), data);
+ }
+ {
+ // emplace can't reuse storage if the variant is shared
+ auto var = QVariant::fromValue(std::string{});
+ [[maybe_unused]] QVariant causesSharing = var;
+ QVERIFY(var.data_ptr().is_shared);
+ auto data = var.constData();
+ var.emplace<std::vector<int>>(3, 42);
+ auto expected = std::vector<int>{42, 42, 42};
+ QVERIFY(get_if<std::vector<int>>(&var));
+ QCOMPARE(get<std::vector<int>>(var), expected);
+ QCOMPARE_NE(var.constData(), data);
+ }
+ {
+ // emplace puts element into the correct place - non-shared
+ QVERIFY(QVariant::Private::canUseInternalSpace(QMetaType::fromType<QString>().iface()));
+ QVariant var;
+ var.emplace<QString>(QChar('x'));
+ QVERIFY(!var.data_ptr().is_shared);
+ }
+ {
+ // emplace puts element into the correct place - shared
+ QVERIFY(!QVariant::Private::canUseInternalSpace(QMetaType::fromType<std::string>().iface()));
+ QVariant var;
+ var.emplace<std::string>(42, 'x');
+ QVERIFY(var.data_ptr().is_shared);
+ }
+ {
+ // emplace does not reuse the storage if alignment is too large
+ auto iface = QMetaType::fromType<LargerThanInternalQVariantStorage>().iface();
+ QVERIFY(!QVariant::Private::canUseInternalSpace(iface));
+ auto var = QVariant::fromValue(LargerThanInternalQVariantStorage{});
+ auto data = var.constData();
+ var.emplace<LargerThanInternalQVariantStorageOveraligned>();
+ QCOMPARE_NE(var.constData(), data);
+ }
+ {
+ // emplace does reuse the storage if new alignment and size are together small enough
+ auto iface = QMetaType::fromType<LargerThanInternalQVariantStorageOveraligned>().iface();
+ QVERIFY(!QVariant::Private::canUseInternalSpace(iface));
+ auto var = QVariant::fromValue(LargerThanInternalQVariantStorageOveraligned{});
+ auto data = var.constData();
+ var.emplace<SmallerAlignmentEvenLargerSize>();
+ // no exact match below - the alignment is after all different
+ QCOMPARE_LE(quintptr(var.constData()), quintptr(data));
+ QCOMPARE_LE(quintptr(var.constData()),
+ quintptr(data) + sizeof(LargerThanInternalQVariantStorageOveraligned));
+ }
+}
+
+void tst_QVariant::getIf_NonDefaultConstructible()
+{
+ getIf_impl(NonDefaultConstructible{42});
+}
+
+void tst_QVariant::getIfSpecial()
+{
+ QVariant v{QString{}}; // used to be a null QVariant in Qt 5
+ QCOMPARE_NE(get_if<QString>(&v), nullptr); // not anymore...
+}
+
+void tst_QVariant::get_NonDefaultConstructible()
+{
+ get_impl(NonDefaultConstructible{42});
+}
+
+template <typename T>
+T mutate(const T &t) { return t + t; }
+template <>
+NonDefaultConstructible mutate(const NonDefaultConstructible &t)
+{
+ return NonDefaultConstructible{t.i + t.i};
+}
+
+template <typename T>
+QVariant make_null_QVariant_of_type()
+{
+ return QVariant(QMetaType::fromType<T>());
+}
+
+template <typename T>
+void tst_QVariant::getIf_impl(T t) const
+{
+ QVariant v = QVariant::fromValue(t);
+
+ QVariant null;
+ QVERIFY(null.isNull());
+
+ [[maybe_unused]]
+ QVariant nulT;
+ if constexpr (std::is_default_constructible_v<T>) {
+ // typed null QVariants don't work with non-default-constuctable types
+ nulT = make_null_QVariant_of_type<T>();
+ QVERIFY(nulT.isNull());
+ }
+
+ QVariant date = QDate(2023, 3, 3);
+ static_assert(!std::is_same_v<T, QDate>);
+
+ // for behavioral comparison:
+ StdVariant stdn = {}, stdv = t;
+
+ // returns nullptr on type mismatch:
+ {
+ // const
+ QCOMPARE_EQ(get_if<T>(&std::as_const(stdn)), nullptr);
+ QCOMPARE_EQ(get_if<T>(&std::as_const(date)), nullptr);
+ // mutable
+ QCOMPARE_EQ(get_if<T>(&stdn), nullptr);
+ QCOMPARE_EQ(get_if<T>(&date), nullptr);
+ }
+
+ // returns nullptr on null variant (QVariant only):
+ {
+ QCOMPARE_EQ(get_if<T>(&std::as_const(null)), nullptr);
+ QCOMPARE_EQ(get_if<T>(&null), nullptr);
+ if constexpr (std::is_default_constructible_v<T>) {
+ // const access return nullptr
+ QCOMPARE_EQ(get_if<T>(&std::as_const(nulT)), nullptr);
+ // but mutable access makes typed null QVariants non-null (like data())
+ QCOMPARE_NE(get_if<T>(&nulT), nullptr);
+ QVERIFY(!nulT.isNull());
+ nulT = make_null_QVariant_of_type<T>(); // reset to null state
+ }
+ }
+
+ // const access:
+ {
+ auto ps = get_if<T>(&std::as_const(stdv));
+ static_assert(std::is_same_v<decltype(ps), const T*>);
+ QCOMPARE_NE(ps, nullptr);
+ QCOMPARE_EQ(*ps, t);
+
+ auto pv = get_if<T>(&std::as_const(v));
+ static_assert(std::is_same_v<decltype(ps), const T*>);
+ QCOMPARE_NE(pv, nullptr);
+ QCOMPARE_EQ(*pv, t);
+ }
+
+ // mutable access:
+ {
+ T t2 = mutate(t);
+
+ auto ps = get_if<T>(&stdv);
+ static_assert(std::is_same_v<decltype(ps), T*>);
+ QCOMPARE_NE(ps, nullptr);
+ QCOMPARE_EQ(*ps, t);
+ *ps = t2;
+ auto ps2 = get_if<T>(&stdv);
+ QCOMPARE_NE(ps2, nullptr);
+ QCOMPARE_EQ(*ps2, t2);
+
+ auto pv = get_if<T>(&v);
+ static_assert(std::is_same_v<decltype(pv), T*>);
+ QCOMPARE_NE(pv, nullptr);
+ QCOMPARE_EQ(*pv, t);
+ *pv = t2;
+ auto pv2 = get_if<T>(&v);
+ QCOMPARE_NE(pv2, nullptr);
+ QCOMPARE_EQ(*pv2, t2);
+
+ // typed null QVariants become non-null (data() behavior):
+ if constexpr (std::is_default_constructible_v<T>) {
+ QVERIFY(nulT.isNull());
+ auto pn = get_if<T>(&nulT);
+ QVERIFY(!nulT.isNull());
+ static_assert(std::is_same_v<decltype(pn), T*>);
+ QCOMPARE_NE(pn, nullptr);
+ QCOMPARE_EQ(*pn, T{});
+ *pn = t2;
+ auto pn2 = get_if<T>(&nulT);
+ QCOMPARE_NE(pn2, nullptr);
+ QCOMPARE_EQ(*pn2, t2);
+ }
+ }
+}
+
+template <typename T>
+void tst_QVariant::get_impl(T t) const
+{
+ QVariant v = QVariant::fromValue(t);
+
+ // for behavioral comparison:
+ StdVariant stdv = t;
+
+ #define FOR_EACH_CVREF(op) \
+ op(/*unadorned*/, &&) \
+ op(&, &) \
+ op(&&, &&) \
+ op(const, const &&) \
+ op(const &, const &) \
+ op(const &&, const &&) \
+ /* end */
+
+
+ #define CHECK_RETURN_TYPE_OF(Variant, cvref_in, cvref_out) \
+ static_assert(std::is_same_v< \
+ decltype(get<T>(std::declval<Variant cvref_in >())), \
+ T cvref_out \
+ >); \
+ /* end */
+ #define CHECK_RETURN_TYPE(cvref_in, cvref_out) \
+ CHECK_RETURN_TYPE_OF(StdVariant, cvref_in, cvref_out) \
+ CHECK_RETURN_TYPE_OF(QVariant, cvref_in, cvref_out) \
+ /* end */
+ FOR_EACH_CVREF(CHECK_RETURN_TYPE)
+ #undef CHECK_RETURN_TYPE
+
+ #undef FOR_EACH_CVREF
+
+ // const access:
+ {
+ auto &&rs = get<T>(std::as_const(stdv));
+ QCOMPARE_EQ(rs, t);
+
+ auto &&rv = get<T>(std::as_const(v));
+ QCOMPARE_EQ(rv, t);
+ }
+
+ // mutable access:
+ {
+ T t2 = mutate(t);
+
+ auto &&rs = get<T>(stdv);
+ QCOMPARE_EQ(rs, t);
+ rs = t2;
+ auto &&rs2 = get<T>(stdv);
+ QCOMPARE_EQ(rs2, t2);
+
+ auto &&rv = get<T>(v);
+ QCOMPARE_EQ(rv, t);
+ rv = t2;
+ auto &&rv2 = get<T>(v);
+ QCOMPARE_EQ(rv2, t2);
+ }
+}
+
QTEST_MAIN(tst_QVariant)
#include "tst_qvariant.moc"
diff --git a/tests/auto/corelib/kernel/qwineventnotifier/CMakeLists.txt b/tests/auto/corelib/kernel/qwineventnotifier/CMakeLists.txt
index 5e1f2e2f8c..81b2c9a58c 100644
--- a/tests/auto/corelib/kernel/qwineventnotifier/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qwineventnotifier/CMakeLists.txt
@@ -1,12 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
-# Generated from qwineventnotifier.pro.
-
#####################################################################
## tst_qwineventnotifier Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qwineventnotifier LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qwineventnotifier
SOURCES
tst_qwineventnotifier.cpp
diff --git a/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp b/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp
index 74122b6c83..bf08f85fb5 100644
--- a/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp
+++ b/tests/auto/corelib/kernel/qwineventnotifier/tst_qwineventnotifier.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QTestEventLoop>
diff --git a/tests/auto/corelib/kernel/qwinregistrykey/CMakeLists.txt b/tests/auto/corelib/kernel/qwinregistrykey/CMakeLists.txt
index 762debd51f..aeda6f0033 100644
--- a/tests/auto/corelib/kernel/qwinregistrykey/CMakeLists.txt
+++ b/tests/auto/corelib/kernel/qwinregistrykey/CMakeLists.txt
@@ -1,6 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qwinregistrykey LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qwinregistrykey
SOURCES
tst_qwinregistrykey.cpp
diff --git a/tests/auto/corelib/kernel/qwinregistrykey/tst_qwinregistrykey.cpp b/tests/auto/corelib/kernel/qwinregistrykey/tst_qwinregistrykey.cpp
index 897f638dc9..a5dca8a3d5 100644
--- a/tests/auto/corelib/kernel/qwinregistrykey/tst_qwinregistrykey.cpp
+++ b/tests/auto/corelib/kernel/qwinregistrykey/tst_qwinregistrykey.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QObject>