summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/CMakeLists.txt3
-rw-r--r--tests/auto/cmake/mockplugins/.cmake.conf2
-rw-r--r--tests/auto/cmake/test_generating_cpp_exports/.cmake.conf2
-rw-r--r--tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECT.json3
-rw-r--r--tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECTandQ_PROPERTY.json3
-rw-r--r--tests/auto/cmake/test_static_resources/.cmake.conf2
-rw-r--r--tests/auto/corelib/animation/qparallelanimationgroup/tst_qparallelanimationgroup.cpp4
-rw-r--r--tests/auto/corelib/io/CMakeLists.txt2
-rw-r--r--tests/auto/corelib/io/qioring/tst_qioring.cpp15
-rw-r--r--tests/auto/corelib/io/qurl/tst_qurl.cpp2
-rw-r--r--tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp87
-rw-r--r--tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp30
-rw-r--r--tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp37
-rw-r--r--tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp282
-rw-r--r--tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp8
-rw-r--r--tests/auto/corelib/thread/qfuture/tst_qfuture.cpp79
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp106
-rw-r--r--tests/auto/gui/math3d/qvectornd/tst_qvectornd.cpp77
-rw-r--r--tests/auto/other/qaccessibility/tst_qaccessibility.cpp116
-rw-r--r--tests/auto/tools/moc/allmocs_baseline_in.json180
-rw-r--r--tests/auto/tools/moc/tst_moc.cpp20
-rw-r--r--tests/auto/tools/mochelpers/tst_mochelpers.cpp7
-rw-r--r--tests/auto/tools/rcc/data/legal/rcc_legal.cpp2
-rw-r--r--tests/auto/util/testrunner/CMakeLists.txt14
-rw-r--r--tests/auto/util/testrunner/qt_mock_test-log.xml50
-rwxr-xr-xtests/auto/util/testrunner/qt_mock_test.py221
-rwxr-xr-xtests/auto/util/testrunner/tst_qt_testrunner.py581
-rw-r--r--tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp1
-rw-r--r--tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp22
-rw-r--r--tests/benchmarks/gui/painting/qcolor/tst_qcolor.cpp37
30 files changed, 1942 insertions, 53 deletions
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt
index ac8aece707b..7dd9340f51b 100644
--- a/tests/auto/CMakeLists.txt
+++ b/tests/auto/CMakeLists.txt
@@ -4,6 +4,9 @@
# Order by dependency [*], then alphabetic. [*] If bugs in part A of
# our source would break tests of part B, then test A before B.
+
+add_subdirectory(util/testrunner)
+
set(run_dbus_tests OFF)
if (QT_FEATURE_dbus)
set(run_dbus_tests ON)
diff --git a/tests/auto/cmake/mockplugins/.cmake.conf b/tests/auto/cmake/mockplugins/.cmake.conf
index be788d10f8e..846c4f3b923 100644
--- a/tests/auto/cmake/mockplugins/.cmake.conf
+++ b/tests/auto/cmake/mockplugins/.cmake.conf
@@ -1 +1 @@
-set(QT_REPO_MODULE_VERSION "6.11.0")
+set(QT_REPO_MODULE_VERSION "6.12.0")
diff --git a/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf b/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf
index be788d10f8e..846c4f3b923 100644
--- a/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf
+++ b/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf
@@ -1 +1 @@
-set(QT_REPO_MODULE_VERSION "6.11.0")
+set(QT_REPO_MODULE_VERSION "6.12.0")
diff --git a/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECT.json b/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECT.json
index 9bd20506429..28be7330cc1 100644
--- a/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECT.json
+++ b/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECT.json
@@ -14,6 +14,9 @@
]
}
],
+ "hashes": {
+ "MetaType": "0$swya0mP+olQ6EImtfZ4HW3dVkKs"
+ },
"inputFile": "MetaType.h",
"outputRevision": 69
}
diff --git a/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECTandQ_PROPERTY.json b/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECTandQ_PROPERTY.json
index fe80985f796..576668df12f 100644
--- a/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECTandQ_PROPERTY.json
+++ b/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECTandQ_PROPERTY.json
@@ -30,6 +30,9 @@
]
}
],
+ "hashes": {
+ "MetaType": "0$NMxUTKrEcV2vk8Gr4Jl/SR4Q7/c"
+ },
"inputFile": "MetaType.h",
"outputRevision": 69
}
diff --git a/tests/auto/cmake/test_static_resources/.cmake.conf b/tests/auto/cmake/test_static_resources/.cmake.conf
index be788d10f8e..846c4f3b923 100644
--- a/tests/auto/cmake/test_static_resources/.cmake.conf
+++ b/tests/auto/cmake/test_static_resources/.cmake.conf
@@ -1 +1 @@
-set(QT_REPO_MODULE_VERSION "6.11.0")
+set(QT_REPO_MODULE_VERSION "6.12.0")
diff --git a/tests/auto/corelib/animation/qparallelanimationgroup/tst_qparallelanimationgroup.cpp b/tests/auto/corelib/animation/qparallelanimationgroup/tst_qparallelanimationgroup.cpp
index c2c09a234c8..d134afc4323 100644
--- a/tests/auto/corelib/animation/qparallelanimationgroup/tst_qparallelanimationgroup.cpp
+++ b/tests/auto/corelib/animation/qparallelanimationgroup/tst_qparallelanimationgroup.cpp
@@ -463,9 +463,7 @@ void tst_QParallelAnimationGroup::deleteChildrenWithRunningGroup()
QCOMPARE(group.state(), QAnimationGroup::Running);
QCOMPARE(anim1->state(), QAnimationGroup::Running);
- QTest::qWaitFor([&]{
- return group.currentLoopTime() > 0;
- }, 200ms);
+ QVERIFY(QTest::qWaitFor([&] { return group.currentLoopTime() > 0; }, 200ms));
delete anim1;
QCOMPARE(group.animationCount(), 0);
diff --git a/tests/auto/corelib/io/CMakeLists.txt b/tests/auto/corelib/io/CMakeLists.txt
index c0d5ea3136e..10327ceaefb 100644
--- a/tests/auto/corelib/io/CMakeLists.txt
+++ b/tests/auto/corelib/io/CMakeLists.txt
@@ -9,7 +9,7 @@ endif()
if(QT_FEATURE_private_tests)
add_subdirectory(qabstractfileengine)
add_subdirectory(qfileinfo)
- if(LINUX AND QT_FEATURE_liburing)
+ if((LINUX AND QT_FEATURE_liburing) OR (WIN32 AND QT_FEATURE_windows_ioring))
add_subdirectory(qioring)
endif()
add_subdirectory(qipaddress)
diff --git a/tests/auto/corelib/io/qioring/tst_qioring.cpp b/tests/auto/corelib/io/qioring/tst_qioring.cpp
index 1128bcd7979..75d4fe68c55 100644
--- a/tests/auto/corelib/io/qioring/tst_qioring.cpp
+++ b/tests/auto/corelib/io/qioring/tst_qioring.cpp
@@ -5,7 +5,12 @@
#include <QtCore/private/qioring_p.h>
+#ifdef Q_OS_WIN
+#include <QtCore/qt_windows.h>
+#include <io.h>
+#else
#include <QtCore/private/qcore_unix_p.h>
+#endif
using namespace Qt::StringLiterals;
using namespace std::chrono_literals;
@@ -30,7 +35,13 @@ private:
void tst_QIORing::closeFile(qintptr fd)
{
+#ifdef Q_OS_WIN
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ HANDLE h = HANDLE(fd);
+ CloseHandle(h);
+#else
QT_CLOSE(fd);
+#endif
}
qintptr tst_QIORing::openHelper(QIORing *ring, const QString &path, QIODevice::OpenMode flags)
@@ -109,7 +120,11 @@ void tst_QIORing::read()
QFile file(QFINDTESTDATA("data/input.txt"));
QVERIFY(file.open(QIODevice::ReadOnly));
int fd = file.handle();
+#ifdef Q_OS_WIN
+ qintptr nativeFd = _get_osfhandle(fd);
+#else
qintptr nativeFd = fd;
+#endif
QIORing ring;
QVERIFY(ring.ensureInitialized());
diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp
index 71389abc976..a4ef698d380 100644
--- a/tests/auto/corelib/io/qurl/tst_qurl.cpp
+++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp
@@ -925,7 +925,7 @@ void tst_QUrl::resolving_data()
QTest::newRow("/-on-empty-no-authority") << "scheme:" << "/" << "scheme:/";
QTest::newRow(".-on-empty-no-authority") << "scheme:" << "." << "scheme:";
QTest::newRow("./-on-empty-no-authority") << "scheme:" << "./" << "scheme:";
- QTest::newRow(".//-on-empty-no-authority") << "scheme:" << "./" << "scheme:";
+ QTest::newRow(".//-on-empty-no-authority") << "scheme:" << ".//" << "scheme:";
QTest::newRow("..-on-empty-no-authority") << "scheme:" << ".." << "scheme:";
QTest::newRow("../-on-empty-no-authority") << "scheme:" << "../" << "scheme:";
diff --git a/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp b/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp
index eab791e57eb..29e26f99bdd 100644
--- a/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp
+++ b/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp
@@ -220,8 +220,8 @@ API_TEST(moveRows, moveRows(0, 0, 0))
API_TEST(moveTreeRows, moveRows(QList<int>{0, 0}, 0, QList<int>{0, 0}))
API_TEST(insertColumn, insertColumn(0))
-API_TEST(insertColumnWithData, insertColumn(0, {}))
-API_TEST(insertColumns, insertColumns(0, std::declval<Range&>()))
+API_TEST(insertColumnWithData, insertColumn(0, QList<int>{0}))
+API_TEST(insertColumns, insertColumns(0, QList<int>{0}))
API_TEST(removeColumn, removeColumn(0))
API_TEST(removeColumns, removeColumns(0, 0))
API_TEST(moveColumn, moveColumn(0, 0))
@@ -849,7 +849,7 @@ void tst_QRangeModelAdapter::insertColumn_API()
static_assert(has_insertColumnWithData(d.tableOfNumbers));
static_assert(!has_insertColumnWithData(d.constTableOfNumbers));
- static_assert(has_insertColumnWithData(d.tableOfPointers));
+ static_assert(!has_insertColumnWithData(d.tableOfPointers));
}
void tst_QRangeModelAdapter::insertColumns_API()
@@ -863,7 +863,7 @@ void tst_QRangeModelAdapter::insertColumns_API()
static_assert(has_insertColumns(d.tableOfNumbers));
static_assert(!has_insertColumns(d.constTableOfNumbers));
- static_assert(has_insertColumns(d.tableOfPointers));
+ static_assert(!has_insertColumns(d.tableOfPointers));
static_assert(!has_insertColumns(d.tableOfRowPointers));
static_assert(!has_insertColumns(d.listOfNamedRoles));
static_assert(!has_insertColumns(d.m_tree));
@@ -1025,8 +1025,18 @@ void tst_QRangeModelAdapter::modelReset()
QCOMPARE(adapter[0], 3);
QCOMPARE(adapter, (std::vector<int>{3, 2, 1}));
+ modelAboutToBeResetSpy.clear();
+ modelResetSpy.clear();
std::vector<int> modifiedData = adapter;
+
+ adapter.assign(modifiedData.begin(), modifiedData.end());
+ QCOMPARE(modelResetSpy.count(), 1);
+ adapter.setRange(std::vector<int>{3, 2, 1});
+ QCOMPARE(modelResetSpy.count(), 2);
+ std::vector<short> shorts = {10, 11, 12};
+ adapter.assign(shorts.begin(), shorts.end());
+ QCOMPARE(modelResetSpy.count(), 3);
}
{
@@ -1533,7 +1543,7 @@ void tst_QRangeModelAdapter::tableWriteAccess()
QTest::ignoreMessage(QtCriticalMsg,
QRegularExpression("Not able to assign QVariant"));
QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Writing value of type Object\\* to "
- "role Qt::RangeModelAdapterRole at index .* of the model failed"));
+ "role Qt::RangeModelAdapterRole at index .* failed"));
#endif
adapter.at(0, 0) = new Object;
QCOMPARE(dataChangedSpy.count(), 0);
@@ -2620,19 +2630,44 @@ using ObjectTree = std::vector<ObjectTreeItem>;
class ObjectTreeItem : public ObjectRow
{
public:
- ObjectTreeItem(Object *item = nullptr)
+ ObjectTreeItem() = default;
+
+ explicit ObjectTreeItem(Object *item)
{
m_objects[0] = item;
}
+ ObjectTreeItem(const ObjectTreeItem &other) = delete;
+ ObjectTreeItem &operator=(const ObjectTreeItem &other) = delete;
+ ObjectTreeItem(ObjectTreeItem &&other) noexcept
+ {
+ m_children = std::move(other.m_children);
+ m_objects = std::move(other.m_objects);
+ other.m_objects = {};
+ }
+
+ ObjectTreeItem &operator=(ObjectTreeItem &&other) noexcept
+ {
+ m_children = std::move(other.m_children);
+ m_objects = std::move(other.m_objects);
+ other.m_objects = {};
+ return *this;
+ }
+
+ ~ObjectTreeItem()
+ {
+ qDeleteAll(m_objects);
+ }
+
ObjectTreeItem *parentRow() const { return m_parentRow; }
void setParentRow(ObjectTreeItem *parentRow) { m_parentRow = parentRow; }
const auto &childRows() const { return m_children; }
auto &childRows() { return m_children; }
private:
- template <std::size_t I> // read-only is enough for this
- friend decltype(auto) get(const ObjectTreeItem &row) { return row.m_objects[I]; }
+ template <std::size_t I, typename Item,
+ std::enable_if_t<std::is_same_v<q20::remove_cvref_t<Item>, ObjectTreeItem>, bool> = true>
+ friend decltype(auto) get(Item &&row) { return q23::forward_like<Item>(row.m_objects[I]); }
ObjectTreeItem *m_parentRow = nullptr;
std::optional<ObjectTree> m_children = std::nullopt;
@@ -2645,9 +2680,7 @@ namespace std {
void tst_QRangeModelAdapter::insertAutoConnectObjects()
{
- ObjectTree emptyTree;
-
- QRangeModelAdapter adapter(emptyTree);
+ QRangeModelAdapter adapter(ObjectTree{});
QSignalSpy dataChangedSpy(adapter.model(), &QAbstractItemModel::dataChanged);
adapter.model()->setAutoConnectPolicy(QRangeModel::AutoConnectPolicy::Full);
@@ -2661,11 +2694,11 @@ void tst_QRangeModelAdapter::insertAutoConnectObjects()
Object *newChild = new Object;
auto firstRow = adapter.begin();
- (*firstRow).children() = ObjectTree{
- ObjectTreeItem(newChild),
- ObjectTreeItem(),
- ObjectTreeItem()
- };
+ {
+ ObjectTree children(3);
+ children[0] = ObjectTreeItem(newChild);
+ (*firstRow).children() = std::move(children);
+ }
QCOMPARE(dataChangedSpy.count(), 0);
QVERIFY(adapter.hasChildren(0));
newChild->setString("0.0");
@@ -2683,12 +2716,14 @@ void tst_QRangeModelAdapter::insertAutoConnectObjects()
newChild = new Object;
Object *newGrandChild = new Object;
ObjectTreeItem newBranch(newChild);
- newBranch.childRows() = ObjectTree{
- ObjectTreeItem(), // skip the first column
- ObjectTreeItem(newGrandChild),
- ObjectTreeItem()
- };
- adapter.at({0, 2}) = newBranch;
+ {
+ ObjectTree children(3);
+ // skip the first row to verify that we continue through nullptr
+ children[1] = ObjectTreeItem(newGrandChild);
+ newBranch.childRows() = std::move(children);
+ }
+ adapter.at({0, 2}) = std::move(newBranch);
+ QCOMPARE(adapter.rowCount({0, 2}), 3);
QCOMPARE(dataChangedSpy.count(), 1);
newChild->setNumber(1);
QCOMPARE(dataChangedSpy.count(), 2);
@@ -2698,9 +2733,11 @@ void tst_QRangeModelAdapter::insertAutoConnectObjects()
QCOMPARE(dataChangedSpy.count(), 1);
dataChangedSpy.clear();
- // newGrandChild = new Object;
- // adapter.at({0, 2, 0}, 0) = newGrandChild;
- // newGrandChild->setString("0.2.0");
+ newGrandChild = new Object;
+ adapter.at({0, 2, 0}, 0) = newGrandChild;
+ QCOMPARE(dataChangedSpy.count(), 1);
+ newGrandChild->setString("0.2.0");
+ QCOMPARE(dataChangedSpy.count(), 2);
}
QTEST_MAIN(tst_QRangeModelAdapter)
diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
index 696bcdc07d7..0fc7538c515 100644
--- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
+++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp
@@ -291,6 +291,8 @@ class tst_QMetaObject : public QObject
Q_PROPERTY(int value8 READ value8)
Q_PROPERTY(int value9 READ value9 CONSTANT)
Q_PROPERTY(int value10 READ value10 FINAL)
+ Q_PROPERTY(int value11 READ value10 VIRTUAL)
+ Q_PROPERTY(int value12 READ value10 OVERRIDE)
public:
enum EnumType { EnumType1 };
@@ -358,6 +360,8 @@ private slots:
void propertyNotify();
void propertyConstant();
void propertyFinal();
+ void propertyVirtual();
+ void propertyOverride();
void metaType();
@@ -2727,6 +2731,32 @@ void tst_QMetaObject::propertyFinal()
QVERIFY(!prop.isFinal());
}
+void tst_QMetaObject::propertyVirtual()
+{
+ const QMetaObject *mo = metaObject();
+
+ QMetaProperty prop = mo->property(mo->indexOfProperty("value11"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.isVirtual());
+
+ prop = mo->property(mo->indexOfProperty("value9"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.isVirtual());
+}
+
+void tst_QMetaObject::propertyOverride()
+{
+ const QMetaObject *mo = metaObject();
+
+ QMetaProperty prop = mo->property(mo->indexOfProperty("value12"));
+ QVERIFY(prop.isValid());
+ QVERIFY(prop.isOverride());
+
+ prop = mo->property(mo->indexOfProperty("value9"));
+ QVERIFY(prop.isValid());
+ QVERIFY(!prop.isOverride());
+}
+
void tst_QMetaObject::metaType()
{
QCOMPARE(QObject::staticMetaObject.metaType(), QMetaType::fromType<QObject>());
diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
index 67643606fa3..a441ed8f7ee 100644
--- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
+++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
@@ -77,6 +77,9 @@ class SomethingOfEverything : public QObject
Q_PROPERTY(SomethingEnum eprop READ eprop)
Q_PROPERTY(SomethingFlagEnum fprop READ fprop)
Q_PROPERTY(QLocale::Language language READ language)
+ Q_PROPERTY(QString virtualP READ prop VIRTUAL)
+ // Doesn't override anything, used only to verify MOC handling of OVERRIDE keyword
+ Q_PROPERTY(QString overrideP READ prop OVERRIDE)
public:
Q_INVOKABLE SomethingOfEverything() {}
~SomethingOfEverything() {}
@@ -577,6 +580,8 @@ void tst_QMetaObjectBuilder::property()
QVERIFY(!nullProp.isEnumOrFlag());
QVERIFY(!nullProp.isConstant());
QVERIFY(!nullProp.isFinal());
+ QVERIFY(!nullProp.isVirtual());
+ QVERIFY(!nullProp.isOverride());
QCOMPARE(nullProp.index(), 0);
QCOMPARE(nullProp.revision(), 0);
@@ -596,6 +601,8 @@ void tst_QMetaObjectBuilder::property()
QVERIFY(!prop1.isEnumOrFlag());
QVERIFY(!prop1.isConstant());
QVERIFY(!prop1.isFinal());
+ QVERIFY(!prop1.isVirtual());
+ QVERIFY(!prop1.isOverride());
QCOMPARE(prop1.revision(), 0);
QCOMPARE(prop1.index(), 0);
QCOMPARE(builder.propertyCount(), 1);
@@ -616,6 +623,8 @@ void tst_QMetaObjectBuilder::property()
QVERIFY(!prop2.isEnumOrFlag());
QVERIFY(!prop2.isConstant());
QVERIFY(!prop2.isFinal());
+ QVERIFY(!prop2.isVirtual());
+ QVERIFY(!prop2.isOverride());
QCOMPARE(prop2.revision(), 0);
QCOMPARE(prop2.index(), 1);
QCOMPARE(builder.propertyCount(), 2);
@@ -669,6 +678,8 @@ void tst_QMetaObjectBuilder::property()
QVERIFY(!prop2.isEnumOrFlag());
QVERIFY(!prop2.isConstant());
QVERIFY(!prop2.isFinal());
+ QVERIFY(!prop2.isVirtual());
+ QVERIFY(!prop2.isOverride());
QCOMPARE(prop2.revision(), 0);
// Remove prop1 and check that prop2 becomes index 0.
@@ -686,6 +697,8 @@ void tst_QMetaObjectBuilder::property()
QVERIFY(!prop2.isEnumOrFlag());
QVERIFY(!prop2.isConstant());
QVERIFY(!prop2.isFinal());
+ QVERIFY(!prop2.isVirtual());
+ QVERIFY(!prop2.isOverride());
QCOMPARE(prop2.revision(), 0);
QCOMPARE(prop2.index(), 0);
@@ -711,6 +724,8 @@ void tst_QMetaObjectBuilder::property()
prop2.setEnumOrFlag(false); \
prop2.setConstant(false); \
prop2.setFinal(false); \
+ prop2.setVirtual(false); \
+ prop2.setOverride(false); \
prop2.setBindable(false); \
prop2.setRequired(false); \
} while (0)
@@ -727,6 +742,8 @@ void tst_QMetaObjectBuilder::property()
prop2.setEnumOrFlag(true); \
prop2.setConstant(true); \
prop2.setFinal(true); \
+ prop2.setVirtual(true); \
+ prop2.setOverride(true); \
prop2.setBindable(true); \
prop2.setRequired(true); \
} while (0)
@@ -742,6 +759,8 @@ void tst_QMetaObjectBuilder::property()
(prop2.isEnumOrFlag() ? 1 : 0) + \
(prop2.isConstant() ? 1 : 0) + \
(prop2.isFinal() ? 1 : 0) + \
+ (prop2.isVirtual() ? 1 : 0) + \
+ (prop2.isOverride() ? 1 : 0) + \
(prop2.isBindable() ? 1 : 0) + \
(prop2.isRequired() ? 1 : 0))
#define CHECK_FLAG(setFunc,isFunc) \
@@ -766,6 +785,8 @@ void tst_QMetaObjectBuilder::property()
CHECK_FLAG(setConstant, isConstant);
CHECK_FLAG(setBindable, isBindable);
CHECK_FLAG(setFinal, isFinal);
+ CHECK_FLAG(setVirtual, isVirtual);
+ CHECK_FLAG(setOverride, isOverride);
CHECK_FLAG(setRequired, isRequired);
SET_ALL_FLAGS();
QCOMPARE(COUNT_FLAGS(), flagCounter);
@@ -782,6 +803,22 @@ void tst_QMetaObjectBuilder::property()
QCOMPARE(prototypeProp.notifySignal().signature(), QByteArray("propChanged(QString)"));
QCOMPARE(builder.methodCount(), 1);
QCOMPARE(builder.method(0).signature(), QByteArray("propChanged(QString)"));
+
+ // virt specifiers
+ { //Q_PROPERTY(int virtualP READ prop VIRTUAL)
+ QMetaProperty prototype = SomethingOfEverything::staticMetaObject.property(7);
+ QMetaPropertyBuilder prototypeProp = builder.addProperty(prototype);
+ QCOMPARE(prototypeProp.isVirtual(), true);
+ QCOMPARE(prototypeProp.isOverride(), false);
+ QCOMPARE(prototypeProp.isFinal(), false);
+ }
+ { // Q_PROPERTY(int overrideP READ prop OVERRIDE)
+ QMetaProperty prototype = SomethingOfEverything::staticMetaObject.property(8);
+ QMetaPropertyBuilder prototypeProp = builder.addProperty(prototype);
+ QCOMPARE(prototypeProp.isVirtual(), false);
+ QCOMPARE(prototypeProp.isOverride(), true);
+ QCOMPARE(prototypeProp.isFinal(), false);
+ }
}
void tst_QMetaObjectBuilder::variantProperty()
diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
index 2fcfd056882..a30ed901fe6 100644
--- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
+++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
@@ -6,6 +6,8 @@
#include <qvariant.h>
#include <QtCore/qttypetraits.h>
+#include <QtCore/qsequentialiterable.h>
+#include <QtCore/qassociativeiterable.h>
// don't assume <type_traits>
template <typename T, typename U>
@@ -5025,7 +5027,7 @@ void sortIterable(QMetaSequence::Iterable *iterable)
}
template<typename Container>
-static void testSequentialIteration()
+static void testMetaSequenceIteration()
{
QFETCH(bool, hasSizeAccessor);
QFETCH(bool, hasIndexedAccessors);
@@ -5157,7 +5159,7 @@ static void testSequentialIteration()
}
template<typename Container>
-static void testAssociativeIteration()
+static void testMetaAssociationIteration()
{
using Key = typename Container::key_type;
using Mapped = typename Container::mapped_type;
@@ -5228,12 +5230,268 @@ static void testAssociativeIteration()
QCOMPARE(f, iter.constEnd());
}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && QT_DEPRECATED_SINCE(6, 15)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+
+template<typename Iterator>
+void sortIterable(QSequentialIterable *iterable)
+{
+ std::sort(Iterator(iterable->mutableBegin()), Iterator(iterable->mutableEnd()),
+ [&](const QVariant &a, const QVariant &b) {
+ return a.toInt() < b.toInt();
+ });
+}
+
+template<typename Container>
+static void testSequentialIteration()
+{
+ QFETCH(bool, hasSizeAccessor);
+ const auto ignoreSizeWarning = [hasSizeAccessor]() {
+ if (hasSizeAccessor)
+ return;
+ QTest::ignoreMessage(
+ QtWarningMsg,
+ "size() called on an iterable without native size accessor. This is slow");
+ };
+ QTest::failOnWarning();
+
+ int numSeen = 0;
+ Container sequence;
+ ContainerAPI<Container>::insert(sequence, 1);
+ ContainerAPI<Container>::insert(sequence, 2);
+ ContainerAPI<Container>::insert(sequence, 3);
+
+ QVariant listVariant = QVariant::fromValue(sequence);
+ QVERIFY(listVariant.canConvert<QVariantList>());
+ QVariantList varList = listVariant.value<QVariantList>();
+ ignoreSizeWarning();
+ QCOMPARE(varList.size(), (int)std::distance(sequence.begin(), sequence.end()));
+ QSequentialIterable listIter = listVariant.view<QSequentialIterable>();
+ ignoreSizeWarning();
+ QCOMPARE(varList.size(), listIter.size());
+
+ typename Container::iterator containerIter = sequence.begin();
+ const typename Container::iterator containerEnd = sequence.end();
+ ignoreSizeWarning();
+ for (int i = 0, end = listIter.size(); i < end; ++i, ++containerIter, ++numSeen)
+ {
+ QVERIFY(ContainerAPI<Container >::compare(listIter.at(i), *containerIter));
+ QVERIFY(ContainerAPI<Container >::compare(listIter.at(i), varList.at(i)));
+ }
+ QCOMPARE(numSeen, (int)std::distance(sequence.begin(), sequence.end()));
+ QCOMPARE(containerIter, containerEnd);
+
+ numSeen = 0;
+ containerIter = sequence.begin();
+ for (QVariant v : listIter) {
+ QVERIFY(ContainerAPI<Container>::compare(v, *containerIter));
+ QVERIFY(ContainerAPI<Container>::compare(v, varList.at(numSeen)));
+ ++containerIter;
+ ++numSeen;
+ }
+ QCOMPARE(numSeen, (int)std::distance(sequence.begin(), sequence.end()));
+
+ auto compareLists = [&]() {
+ int numSeen = 0;
+ auto varList = listVariant.value<QVariantList>();
+ auto varIter = varList.begin();
+ for (const QVariant &v : std::as_const(listIter)) {
+ QVERIFY(ContainerAPI<Container>::compare(v, *varIter));
+ ++varIter;
+ ++numSeen;
+ }
+ QCOMPARE(varIter, varList.end());
+ numSeen = 0;
+ auto constVarIter = varList.constBegin();
+ for (QVariant v : listIter) {
+ QVERIFY(ContainerAPI<Container>::compare(v, *constVarIter));
+ ++constVarIter;
+ ++numSeen;
+ }
+ QCOMPARE(numSeen, (int)std::distance(varList.begin(), varList.end()));
+ };
+ compareLists();
+
+ QVariant first = listIter.at(0);
+ QVariant second = listIter.at(1);
+ QVariant third = listIter.at(2);
+ compareLists();
+ listIter.addValue(third);
+ compareLists();
+ listIter.addValue(second);
+ compareLists();
+ listIter.addValue(first);
+ compareLists();
+
+ QCOMPARE(listIter.size(), 6);
+
+ if (listIter.canRandomAccessIterate())
+ sortIterable<QSequentialIterable::RandomAccessIterator>(&listIter);
+ else if (listIter.canReverseIterate())
+ sortIterable<QSequentialIterable::BidirectionalIterator>(&listIter);
+ else if (listIter.canForwardIterate())
+ return; // std::sort cannot sort with only forward iterators.
+ else
+ QFAIL("The container has no meaningful iterators");
+
+ compareLists();
+ ignoreSizeWarning();
+ QCOMPARE(listIter.size(), 6);
+ QCOMPARE(listIter.at(0), first);
+ QCOMPARE(listIter.at(1), first);
+ QCOMPARE(listIter.at(2), second);
+ QCOMPARE(listIter.at(3), second);
+ QCOMPARE(listIter.at(4), third);
+ QCOMPARE(listIter.at(5), third);
+
+ if (listIter.metaContainer().canRemoveValue()) {
+ listIter.removeValue();
+ compareLists();
+ ignoreSizeWarning();
+ QCOMPARE(listIter.size(), 5);
+ QCOMPARE(listIter.at(0), first);
+ QCOMPARE(listIter.at(1), first);
+ QCOMPARE(listIter.at(2), second);
+ QCOMPARE(listIter.at(3), second);
+ QCOMPARE(listIter.at(4), third);
+ } else {
+ // QString and QByteArray have no pop_back or pop_front and it's unclear what other
+ // method we should use to remove an item.
+ QVERIFY((std::is_same_v<Container, QString> || std::is_same_v<Container, QByteArray>));
+ }
+
+ auto i = listIter.mutableBegin();
+ QVERIFY(i != listIter.mutableEnd());
+
+ *i = QStringLiteral("17");
+ if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<int>())
+ QCOMPARE(listIter.at(0).toInt(), 17);
+ else if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<bool>())
+ QCOMPARE(listIter.at(0).toBool(), false);
+
+ *i = QStringLiteral("true");
+ if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<int>())
+ QCOMPARE(listIter.at(0).toInt(), 0);
+ else if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<bool>())
+ QCOMPARE(listIter.at(0).toBool(), true);
+}
+
+template<typename Container>
+static void testAssociativeIteration()
+{
+ using Key = typename Container::key_type;
+ using Mapped = typename Container::mapped_type;
+
+ int numSeen = 0;
+ Container mapping;
+ mapping[5] = true;
+ mapping[15] = false;
+
+ QVariant mappingVariant = QVariant::fromValue(mapping);
+ QVariantMap varMap = mappingVariant.value<QVariantMap>();
+ QVariantMap varHash = mappingVariant.value<QVariantMap>();
+ QAssociativeIterable mappingIter = mappingVariant.view<QAssociativeIterable>();
+
+ typename Container::const_iterator containerIter = mapping.begin();
+ const typename Container::const_iterator containerEnd = mapping.end();
+ for ( ;containerIter != containerEnd; ++containerIter, ++numSeen)
+ {
+ Mapped expected = KeyGetter<Container>::value(containerIter);
+ Key key = KeyGetter<Container>::get(containerIter);
+ Mapped actual = qvariant_cast<Mapped>(mappingIter.value(key));
+ QCOMPARE(qvariant_cast<Mapped>(varMap.value(QString::number(key))), expected);
+ QCOMPARE(qvariant_cast<Mapped>(varHash.value(QString::number(key))), expected);
+ QCOMPARE(actual, expected);
+ const QAssociativeIterable::const_iterator it = mappingIter.find(key);
+ QVERIFY(it != mappingIter.end());
+ QCOMPARE(it.value().value<Mapped>(), expected);
+ }
+ QCOMPARE(numSeen, (int)std::distance(mapping.begin(), mapping.end()));
+ QCOMPARE(containerIter, containerEnd);
+ QVERIFY(mappingIter.find(10) == mappingIter.end());
+
+ auto i = mappingIter.mutableFind(QStringLiteral("nonono"));
+ QCOMPARE(i, mappingIter.mutableEnd());
+ i = mappingIter.mutableFind(QStringLiteral("5"));
+ QVERIFY(i != mappingIter.mutableEnd());
+
+ *i = QStringLiteral("17");
+
+ if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<int>())
+ QCOMPARE(mappingIter.value(5).toInt(), 17);
+ else if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<bool>())
+ QCOMPARE(mappingIter.value(5).toBool(), true);
+
+ *i = QStringLiteral("true");
+ if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<int>())
+ QCOMPARE(mappingIter.value(5).toInt(), 0);
+ else if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<bool>())
+ QCOMPARE(mappingIter.value(5).toBool(), true);
+
+ QVERIFY(mappingIter.containsKey("5"));
+ mappingIter.removeKey(QStringLiteral("5"));
+ QCOMPARE(mappingIter.find(5), mappingIter.end());
+
+ mappingIter.setValue(5, 44);
+ if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<int>())
+ QCOMPARE(mappingIter.value(5).toInt(), 44);
+ else if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<bool>())
+ QCOMPARE(mappingIter.value(5).toBool(), true);
+
+ // Test that find() does not coerce
+ auto container = Container();
+ container[0] = true;
+
+ QVariant containerVariant = QVariant::fromValue(container);
+ QAssociativeIterable iter = containerVariant.value<QAssociativeIterable>();
+ auto f = iter.constFind(QStringLiteral("anything"));
+ QCOMPARE(f, iter.constEnd());
+}
+
+template<typename T>
+static void addRowSequential(const char *name, bool hasSizeAccessor, bool hasIndexedAccessor)
+{
+ QTest::newRow(name)
+ << &testMetaSequenceIteration<T> << hasSizeAccessor << hasIndexedAccessor;
+ QTest::addRow("%s_old", name)
+ << &testSequentialIteration<T> << hasSizeAccessor << hasIndexedAccessor;
+}
+
+template<typename C>
+static void addRowAssociative(const char *name)
+{
+ QTest::newRow(name)
+ << &testMetaAssociationIteration<C>;
+ QTest::addRow("%s_old", name)
+ << &testAssociativeIteration<C>;
+}
+
+QT_WARNING_POP
+#else
+
+template<typename T>
+static void addRowSequential(const char *name, bool hasSizeAccessor, bool hasIndexedAccessor)
+{
+ QTest::newRow(name)
+ << &testMetaSequenceIteration<T> << hasSizeAccessor << hasIndexedAccessor;
+}
+
+template<typename C>
+static void addRowAssociative(const char *name)
+{
+ QTest::newRow(name)
+ << &testMetaAssociationIteration<C>;
+}
+
+#endif // QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && QT_DEPRECATED_SINCE(6, 15)
+
void tst_QVariant::iterateSequentialContainerElements_data()
{
QTest::addColumn<QFunctionPointer>("testFunction");
QTest::addColumn<bool>("hasSizeAccessor");
QTest::addColumn<bool>("hasIndexedAccessors");
-#define ADD(T) QTest::newRow(#T) << &testSequentialIteration<T> << true << true
+#define ADD(T) addRowSequential<T>(#T, true, true)
ADD(QQueue<int>);
ADD(QQueue<QVariant>);
ADD(QQueue<QString>);
@@ -5253,13 +5511,13 @@ void tst_QVariant::iterateSequentialContainerElements_data()
ADD(QByteArray);
#undef ADD
-#define ADD(T) QTest::newRow(#T) << &testSequentialIteration<T> << true << false
+#define ADD(T) addRowSequential<T>(#T, true, false)
ADD(std::list<int>);
ADD(std::list<QVariant>);
ADD(std::list<QString>);
#undef ADD
-#define ADD(T) QTest::newRow(#T) << &testSequentialIteration<T> << false << false
+#define ADD(T) addRowSequential<T>(#T, false, false)
#ifdef TEST_FORWARD_LIST
ADD(std::forward_list<int>);
ADD(std::forward_list<QVariant>);
@@ -5271,7 +5529,7 @@ void tst_QVariant::iterateSequentialContainerElements_data()
void tst_QVariant::iterateAssociativeContainerElements_data()
{
QTest::addColumn<QFunctionPointer>("testFunction");
-#define ADD(C, K, V) QTest::newRow(#C #K #V) << &testAssociativeIteration<C<K, V>>;
+#define ADD(C, K, V) addRowAssociative<C<K, V>>(#C "<" #K "," #V ">");
ADD(QHash, int, bool);
ADD(QHash, int, int);
ADD(QMap, int, bool);
@@ -6448,15 +6706,15 @@ void tst_QVariant::get_NonDefaultConstructible()
struct QVariantWrapper
{
public:
- static constexpr bool canNoexceptConvertToQVariant
+ static constexpr bool CanNoexceptConvertToQVariant
= std::is_nothrow_copy_constructible_v<QVariant>;
- static constexpr bool canNoexceptAssignQVariant
+ static constexpr bool CanNoexceptAssignQVariant
= std::is_nothrow_copy_assignable_v<QVariant>;
QVariantWrapper(QVariant *content = nullptr) noexcept : m_content(content) {}
- QVariant content() const noexcept(canNoexceptConvertToQVariant) { return *m_content; }
- void setContent(const QVariant &content) noexcept(canNoexceptAssignQVariant)
+ QVariant content() const noexcept(CanNoexceptConvertToQVariant) { return *m_content; }
+ void setContent(const QVariant &content) noexcept(CanNoexceptAssignQVariant)
{
*m_content = content;
}
@@ -6468,14 +6726,14 @@ private:
QT_BEGIN_NAMESPACE
template<>
QVariant::ConstReference<QVariantWrapper>::operator QVariant() const
- noexcept(QVariantWrapper::canNoexceptConvertToQVariant)
+ noexcept(QVariantWrapper::CanNoexceptConvertToQVariant)
{
return m_referred.content();
}
template<>
QVariant::Reference<QVariantWrapper> &QVariant::Reference<QVariantWrapper>::operator=(
- const QVariant &content) noexcept(QVariantWrapper::canNoexceptAssignQVariant)
+ const QVariant &content) noexcept(QVariantWrapper::CanNoexceptAssignQVariant)
{
m_referred.setContent(content);
return *this;
diff --git a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp
index 2d9d351286f..f1f88891015 100644
--- a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp
+++ b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp
@@ -63,11 +63,11 @@ using namespace Qt::StringLiterals;
#elif defined(Q_OS_WIN)
# undef dll_VALID
# define dll_VALID true
-//# ifdef QT_NO_DEBUG
+# if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC)
+# define SUFFIX "d.dll"
+# else
# define SUFFIX ".dll"
-//# else
-//# define SUFFIX "d.dll"
-//# endif
+# endif
# define PREFIX ""
#else // all other Unix
diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
index 806b6b43161..f7d98eb15e2 100644
--- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
+++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
@@ -5656,6 +5656,12 @@ void tst_QFuture::cancelChainWithContext()
int onCancelCnt = 0;
bool unexpectedThread = false;
+ // For in-other-thread case we need a semaphore to make sure that
+ // the needed continuation is executed.
+ // Fot the in-main-thread case it's guaranteed by the unwrap()
+ // implementation.
+ QSemaphore sem;
+
auto f = p1.future()
.then(context, [&]() {
if (QThread::currentThread() != thread)
@@ -5666,6 +5672,8 @@ void tst_QFuture::cancelChainWithContext()
if (QThread::currentThread() != thread)
unexpectedThread = true;
++thenCnt;
+ if (inOtherThread)
+ sem.release();
return f;
}).unwrap()
.then(context, [&]{
@@ -5685,6 +5693,8 @@ void tst_QFuture::cancelChainWithContext()
});
p1.finish();
+ if (inOtherThread)
+ sem.acquire();
f.cancelChain();
p2.finish();
f.waitForFinished();
@@ -5737,6 +5747,75 @@ void tst_QFuture::cancelChainWithContext()
QCOMPARE_EQ(thenCnt, 4);
QCOMPARE_EQ(onCancelCnt, 0);
}
+ // cancelling propagates through unwrap()
+ {
+ QPromise<void> p1, p2;
+ p1.start();
+ p2.start();
+
+ int thenCnt = 0;
+ int onCancelCnt = 0;
+ bool unexpectedThread = false;
+
+ // For in-other-thread case we need a semaphore to make sure that
+ // the first continuation is executed.
+ // Fot the in-main-thread case it's guaranteed by the unwrap()
+ // implementation.
+ QSemaphore sem;
+
+ auto f = p1.future()
+ .then(context, [&, f2 = p2.future()]() {
+ if (QThread::currentThread() != thread)
+ unexpectedThread = true;
+ ++thenCnt;
+ if (inOtherThread)
+ sem.release();
+ return f2;
+ }).unwrap()
+ .onCanceled(context, [&] {
+ if (QThread::currentThread() != thread)
+ unexpectedThread = true;
+ ++onCancelCnt;
+ })
+ .then([&]() {
+ if (QThread::currentThread() != thread)
+ unexpectedThread = true;
+ ++thenCnt;
+ return QtFuture::makeReadyVoidFuture();
+ }).unwrap()
+ .onCanceled([&] {
+ if (QThread::currentThread() != thread)
+ unexpectedThread = true;
+ ++onCancelCnt;
+ })
+ .then(context, [&]{
+ if (QThread::currentThread() != thread)
+ unexpectedThread = true;
+ ++thenCnt;
+ return QtFuture::makeReadyVoidFuture();
+ }).unwrap()
+ .onCanceled([&] {
+ if (QThread::currentThread() != thread)
+ unexpectedThread = true;
+ ++onCancelCnt;
+ })
+ .then(context, [&]{
+ if (QThread::currentThread() != thread)
+ unexpectedThread = true;
+ ++thenCnt;
+ });
+
+ p1.finish();
+ if (inOtherThread)
+ sem.acquire();
+ f.cancelChain();
+ p2.finish();
+ f.waitForFinished();
+
+ QVERIFY(!unexpectedThread);
+ QCOMPARE_EQ(thenCnt, 1);
+ QCOMPARE_EQ(onCancelCnt, 3);
+ }
}
void tst_QFuture::cancelChainOnAnOverwrittenFuture()
diff --git a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
index 86dfa5faffc..4c089091f8d 100644
--- a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
+++ b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
@@ -57,6 +57,7 @@ private slots:
void multipleReadersLoop();
void multipleWritersLoop();
void multipleReadersWritersLoop();
+ void heavyLoadLocks();
void countingTest();
void limitedReaders();
void deleteOnUnlock();
@@ -603,6 +604,111 @@ public:
}
};
+class HeavyLoadLockThread : public QThread
+{
+public:
+ QReadWriteLock &testRwlock;
+ const qsizetype iterations;
+ const int numThreads;
+ inline HeavyLoadLockThread(QReadWriteLock &l, qsizetype iters, int numThreads, QVector<QAtomicInt *> &counters):
+ testRwlock(l),
+ iterations(iters),
+ numThreads(numThreads),
+ counters(counters)
+ { }
+
+private:
+ QVector<QAtomicInt *> &counters;
+ QAtomicInt *getCounter(qsizetype index)
+ {
+ QReadLocker locker(&testRwlock);
+ /*
+ The index is increased monotonically, so the index
+ being requested should be always within or at the end of the
+ counters vector.
+ */
+ Q_ASSERT(index <= counters.size());
+ if (counters.size() <= index || counters[index] == nullptr) {
+ locker.unlock();
+ QWriteLocker wlocker(&testRwlock);
+ if (counters.size() <= index)
+ counters.resize(index + 1, nullptr);
+ if (counters[index] == nullptr)
+ counters[index] = new QAtomicInt(0);
+ return counters[index];
+ }
+ return counters[index];
+ }
+ void releaseCounter(qsizetype index)
+ {
+ QWriteLocker locker(&testRwlock);
+ delete counters[index];
+ counters[index] = nullptr;
+ }
+
+public:
+ void run() override
+ {
+ for (qsizetype i = 0; i < iterations; ++i) {
+ QAtomicInt *counter = getCounter(i);
+ /*
+ Here each counter is accessed by each thread
+ and increaed only once. As a result, when the
+ counter reaches numThreads, i.e. the fetched
+ value before the increment is numThreads-1,
+ we know all threads have accessed this counter
+ and we can delete it safely.
+ */
+ int prev = counter->fetchAndAddRelaxed(1);
+ if (prev == numThreads - 1) {
+#ifdef QT_BUILDING_UNDER_TSAN
+ /*
+ Under TSAN, deleting and freeing an object
+ will trigger a write operation on the memory
+ of the object. Since we used fetchAndAddRelaxed
+ to update the counter, TSAN will report a data
+ race when deleting the counter here. To avoid
+ the false positive, we simply reset the counter
+ to 0 here, with ordered semantics to establish
+ the sequence to ensure the the free-ing option
+ happens after all fetchAndAddRelaxed operations
+ in other threads.
+
+ When not building under TSAN, deleting the counter
+ will not result in any data read or written to the
+ memory region of the counter, so no data race will
+ happen.
+ */
+ counter->fetchAndStoreOrdered(0);
+#endif
+ releaseCounter(i);
+ }
+ }
+ }
+};
+
+/*
+ Multiple threads racing acquiring and releasing
+ locks on the same indices.
+*/
+
+void tst_QReadWriteLock::heavyLoadLocks()
+{
+ constexpr qsizetype iterations = 65536 * 4;
+ constexpr int numThreads = 8;
+ QVector<QAtomicInt *> counters;
+ QReadWriteLock testLock;
+ std::array<std::unique_ptr<HeavyLoadLockThread>, numThreads> threads;
+ for (auto &thread : threads)
+ thread = std::make_unique<HeavyLoadLockThread>(testLock, iterations, numThreads, counters);
+ for (auto &thread : threads)
+ thread->start();
+ for (auto &thread : threads)
+ thread->wait();
+ QVERIFY(counters.size() == iterations);
+ for (qsizetype i = 0; i < iterations; ++i)
+ QVERIFY(counters[i] == nullptr);
+}
/*
A writer acquires a read-lock, a reader locks
diff --git a/tests/auto/gui/math3d/qvectornd/tst_qvectornd.cpp b/tests/auto/gui/math3d/qvectornd/tst_qvectornd.cpp
index 3272ffac0ee..18d8b604dff 100644
--- a/tests/auto/gui/math3d/qvectornd/tst_qvectornd.cpp
+++ b/tests/auto/gui/math3d/qvectornd/tst_qvectornd.cpp
@@ -4,6 +4,9 @@
#include <QVector2D>
#include <QVector3D>
#include <QVector4D>
+
+#include <QtCore/qdatastream.h>
+
#ifdef QVARIANT_H
# error "This test requires qvector{2,3,4}d.h to not include qvariant.h"
#endif
@@ -162,6 +165,8 @@ private slots:
void metaTypes();
void structuredBinding();
+ void nonFiniteValuesStreamingRoundTrip_data();
+ void nonFiniteValuesStreamingRoundTrip();
};
// Test the creation of QVector2D objects in various ways:
@@ -2759,6 +2764,78 @@ void tst_QVectorND::structuredBinding()
}
}
+void tst_QVectorND::nonFiniteValuesStreamingRoundTrip_data()
+{
+ QTest::addColumn<float>("value");
+
+ constexpr auto inf = std::numeric_limits<float>::infinity();
+ constexpr auto NaN = std::numeric_limits<float>::quiet_NaN();
+
+ QTest::addRow("+∞") << +inf;
+ QTest::addRow("-∞") << -inf;
+ QTest::addRow("NaN") << NaN;
+
+}
+
+void tst_QVectorND::nonFiniteValuesStreamingRoundTrip()
+{
+ QFETCH(const float, value);
+
+ const QVector2D i2{value, value};
+ const QVector3D i3{value, value, value};
+ const QVector4D i4{value, value, value, value};
+
+ QByteArray buffer;
+
+ {
+ QDataStream s(&buffer, QIODevice::WriteOnly);
+ s << i2 << i3 << i4;
+ QCOMPARE(s.status(), QDataStream::Status::Ok);
+ }
+
+ {
+ QVector2D o2 = {0, 0};
+ QVector3D o3 = {1, 0, -1};
+ QVector4D o4 = {0, 1, 2, 3};
+
+ QDataStream s(&buffer, QIODevice::ReadOnly);
+ s >> o2;
+ QCOMPARE(s.status(), QDataStream::Status::Ok);
+ s >> o3;
+ QCOMPARE(s.status(), QDataStream::Status::Ok);
+ s >> o4;
+ QCOMPARE(s.status(), QDataStream::Status::Ok);
+
+ constexpr auto convert_to_binary = [](float v) {
+ uint r;
+ static_assert(sizeof v == sizeof r);
+ memcpy(&r, &v, sizeof v);
+ return r;
+ };
+
+ #define CHECK(n, what) \
+ do { \
+ const auto i ## n ## what = convert_to_binary(i ## n . what ()); \
+ const auto o ## n ## what = convert_to_binary(o ## n . what ()); \
+ QCOMPARE(i ## n ## what, o ## n ## what); \
+ } while (false)
+
+ CHECK(2, x);
+ CHECK(2, y);
+
+ CHECK(3, x);
+ CHECK(3, y);
+ CHECK(3, z);
+
+ CHECK(4, x);
+ CHECK(4, y);
+ CHECK(4, z);
+ CHECK(4, w);
+
+ #undef CHECK
+ }
+}
+
QTEST_APPLESS_MAIN(tst_QVectorND)
#include "tst_qvectornd.moc"
diff --git a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp
index 305f48c95ee..4fbaed0d7b3 100644
--- a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp
+++ b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp
@@ -2988,6 +2988,10 @@ void tst_QAccessibility::listTest()
model->appendRow({new QStandardItem("Norway"), new QStandardItem("Oslo"), new QStandardItem("NOK")});
model->appendRow({new QStandardItem("Germany"), new QStandardItem("Berlin"), new QStandardItem("EUR")});
model->appendRow({new QStandardItem("Australia"), new QStandardItem("Brisbane"), new QStandardItem("AUD")});
+ model->item(0, 1)->setCheckable(true);
+ model->item(1, 1)->setCheckable(true);
+ model->item(2, 1)->setCheckable(true);
+ model->item(2, 1)->setCheckState(Qt::Checked);
auto lvHolder = std::make_unique<QListView>();
auto listView = lvHolder.get();
listView->setModel(model);
@@ -3016,16 +3020,19 @@ void tst_QAccessibility::listTest()
QCOMPARE(iface->indexOfChild(child1), 0);
QCOMPARE(child1->text(QAccessible::Name), QString("Oslo"));
QCOMPARE(child1->role(), QAccessible::ListItem);
+ QVERIFY(child1->state().checkable);
QAccessibleInterface *child2 = iface->child(1);
QVERIFY(child2);
QCOMPARE(iface->indexOfChild(child2), 1);
QCOMPARE(child2->text(QAccessible::Name), QString("Berlin"));
+ QVERIFY(child2->state().checkable);
QAccessibleInterface *child3 = iface->child(2);
QVERIFY(child3);
QCOMPARE(iface->indexOfChild(child3), 2);
QCOMPARE(child3->text(QAccessible::Name), QString("Brisbane"));
+ QVERIFY(child3->state().checkable);
}
// Check that application is accessible parent, since it's a top-level widget
@@ -3044,9 +3051,21 @@ void tst_QAccessibility::listTest()
// skip focus event tests on platforms where window focus cannot be ensured
const bool checkFocus = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation);
if (checkFocus) {
+ QVERIFY(QTest::qWaitForWindowActive(listView));
+
QAccessibleEvent focusEvent(listView, QAccessible::Focus);
focusEvent.setChild(1);
QVERIFY(QTestAccessibility::containsEvent(&focusEvent));
+
+ // check the item
+ QVERIFY(!iface->child(1)->state().checked);
+ QTest::keyClick(listView, Qt::Key_Space);
+ QVERIFY(iface->child(1)->state().checked);
+ QAccessible::State s;
+ s.checked = true;
+ QAccessibleStateChangeEvent checkedStateChangedEvent(listView, s);
+ checkedStateChangedEvent.setChild(1);
+ QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent));
}
QTest::mouseClick(listView->viewport(), Qt::LeftButton, { }, listView->visualRect(model->index(2, listView->modelColumn())).center());
@@ -3058,6 +3077,16 @@ void tst_QAccessibility::listTest()
QAccessibleEvent focusEvent2(listView, QAccessible::Focus);
focusEvent2.setChild(2);
QVERIFY(QTestAccessibility::containsEvent(&focusEvent2));
+
+ // uncheck the item
+ QVERIFY(iface->child(2)->state().checked);
+ QTest::keyClick(listView, Qt::Key_Space);
+ QVERIFY(!iface->child(2)->state().checked);
+ QAccessible::State s;
+ s.checked = true;
+ QAccessibleStateChangeEvent checkedStateChangedEvent(listView, s);
+ checkedStateChangedEvent.setChild(2);
+ QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent));
}
QAccessibleTableInterface *table = iface->tableInterface();
@@ -3174,6 +3203,7 @@ void tst_QAccessibility::treeTest()
QTreeWidgetItem *item1 = new QTreeWidgetItem;
item1->setText(0, "Picasso");
item1->setText(1, "Guernica");
+ item1->setCheckState(0, Qt::Unchecked);
root1->addChild(item1);
QTreeWidgetItem *item2 = new QTreeWidgetItem;
@@ -3188,6 +3218,7 @@ void tst_QAccessibility::treeTest()
QTreeWidgetItem *item3 = new QTreeWidgetItem;
item3->setText(0, "Klimt");
item3->setText(1, "The Kiss");
+ item3->setCheckState(0, Qt::Checked);
root2->addChild(item3);
treeView->resize(400,400);
@@ -3291,6 +3322,40 @@ void tst_QAccessibility::treeTest()
QCOMPARE(table2->columnDescription(0), QString("Artist"));
QCOMPARE(table2->columnDescription(1), QString("Work"));
+ // skip accessible state change event tests on platforms where window focus cannot be ensured
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(
+ QPlatformIntegration::WindowActivation)) {
+ QVERIFY(QTest::qWaitForWindowActive(treeView.get()));
+
+ // check item1 (Picasso)
+ QVERIFY(cell1->state().checkable);
+ QVERIFY(!cell1->state().checked);
+ treeView->setCurrentItem(item1);
+ QTest::keyClick(treeView.get(), Qt::Key_Space);
+ QVERIFY(cell1->state().checked);
+ {
+ QAccessible::State s;
+ s.checked = true;
+ QAccessibleStateChangeEvent checkedStateChangedEvent(treeView.get(), s);
+ checkedStateChangedEvent.setChild(iface->indexOfChild(cell1));
+ QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent));
+ }
+
+ // uncheck item3 (Klimt)
+ QVERIFY(cell2->state().checkable);
+ QVERIFY(cell2->state().checked);
+ treeView->setCurrentItem(item3);
+ QTest::keyClick(treeView.get(), Qt::Key_Space);
+ QVERIFY(!cell2->state().checked);
+ {
+ QAccessible::State s;
+ s.checked = true;
+ QAccessibleStateChangeEvent checkedStateChangedEvent(treeView.get(), s);
+ checkedStateChangedEvent.setChild(iface->indexOfChild(cell2));
+ QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent));
+ }
+ }
+
QTestAccessibility::clearEvents();
}
@@ -3315,6 +3380,7 @@ void tst_QAccessibility::tableTest()
for (int i = 0; i<9; ++i) {
QTableWidgetItem *item = new QTableWidgetItem;
item->setText(QString::number(i/3) + QString(".") + QString::number(i%3));
+ item->setCheckState(Qt::Unchecked);
tableView->setItem(i/3, i%3, item);
}
@@ -3582,6 +3648,56 @@ void tst_QAccessibility::tableTest()
tableView->horizontalHeader()->setVisible(false);
}
+
+ // skip accessible state change event tests on platforms where window focus cannot be ensured
+ if (QGuiApplicationPrivate::platformIntegration()->hasCapability(
+ QPlatformIntegration::WindowActivation)) {
+ QVERIFY(QTest::qWaitForWindowActive(tableView));
+
+ // check cell (0, 0)
+ tableView->setCurrentItem(tableView->item(0, 0));
+ QVERIFY(cell00->state().checkable);
+ QVERIFY(!cell00->state().checked);
+ QTest::keyClick(tableView, Qt::Key_Space);
+ QVERIFY(cell00->state().checked);
+ {
+ QAccessible::State s;
+ s.checked = true;
+ QAccessibleStateChangeEvent checkedStateChangedEvent(tableView, s);
+ checkedStateChangedEvent.setChild(iface->indexOfChild(cell00));
+ QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent));
+ }
+
+ QTestAccessibility::clearEvents();
+
+ // uncheck cell (0, 0) again
+ QTest::keyClick(tableView, Qt::Key_Space);
+ QVERIFY(!cell00->state().checked);
+ {
+ QAccessible::State s;
+ s.checked = true;
+ QAccessibleStateChangeEvent checkedStateChangedEvent(tableView, s);
+ checkedStateChangedEvent.setChild(iface->indexOfChild(cell00));
+ QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent));
+ }
+ }
+
+ {
+ QTestAccessibility::clearEvents();
+ auto cell0 = table2->cellAt(0, 2);
+ auto cell1 = table2->cellAt(1, 2);
+ auto cell2 = table2->cellAt(2, 2);
+ auto cell3 = table2->cellAt(3, 2);
+ QAccessibleObjectDestroyedEvent event0(cell0);
+ QAccessibleObjectDestroyedEvent event1(cell1);
+ QAccessibleObjectDestroyedEvent event2(cell2);
+ QAccessibleObjectDestroyedEvent event3(cell3);
+ tableView->removeColumn(2);
+ QVERIFY_EVENT(&event0);
+ QVERIFY_EVENT(&event1);
+ QVERIFY_EVENT(&event2);
+ QVERIFY_EVENT(&event3);
+ }
tvHolder.reset();
QVERIFY(!QAccessible::accessibleInterface(id00));
QTestAccessibility::clearEvents();
diff --git a/tests/auto/tools/moc/allmocs_baseline_in.json b/tests/auto/tools/moc/allmocs_baseline_in.json
index d8e6c4df538..d36bdb907ae 100644
--- a/tests/auto/tools/moc/allmocs_baseline_in.json
+++ b/tests/auto/tools/moc/allmocs_baseline_in.json
@@ -23,6 +23,9 @@
]
}
],
+ "hashes": {
+ "BackslashNewlines": "0$VAkStqKr6pw5W81yZ3rCFn98h9U"
+ },
"inputFile": "backslash-newlines.h",
"outputRevision": 69
},
@@ -41,6 +44,9 @@
]
}
],
+ "hashes": {
+ "IfdefedClass": "0$OochknExuVUAqq2zc5gt2Ldj4+4"
+ },
"inputFile": "c-comments.h",
"outputRevision": 69
},
@@ -75,6 +81,9 @@
"qualifiedClassName": "CStyleEnums"
}
],
+ "hashes": {
+ "CStyleEnums": "0$fqbHabuJUT79FJr/S/6WsAiFcJI"
+ },
"inputFile": "cstyle-enums.h",
"outputRevision": 69
},
@@ -325,6 +334,11 @@
]
}
],
+ "hashes": {
+ "CXX11Enums": "0$SPzFG1raX8GCqiq6g21PoU/7ixs",
+ "CXX11Enums2": "0$uYFCI2+nT8TI8jLcKAvO8vp9OYA",
+ "CXX11Enums3": "0$bRezVo4UbDNuGatmLEd0/n8K8FA"
+ },
"inputFile": "cxx11-enums.h",
"outputRevision": 69
},
@@ -727,6 +741,17 @@
]
}
],
+ "hashes": {
+ "ExplicitOverrideControlBase": "0$PFsRqLGh0wP5gfqwF9CVeUi9EVU",
+ "ExplicitOverrideControlFinalCxx11": "0$RQTN9wEJCVZ1N4IcieHs2LuRKY8",
+ "ExplicitOverrideControlFinalCxx11OverrideCxx11": "0$ZEBCUhi5oEGiDVdmoqD+x4n1KJk",
+ "ExplicitOverrideControlFinalQt": "0$GuS6YD8Kcf/9KyehmFsrmDWW42M",
+ "ExplicitOverrideControlFinalQtOverrideQt": "0$q48mSBftA2LJFVSB4uqTmKs2gOc",
+ "ExplicitOverrideControlOverrideCxx11": "0$he1BJyFAtIcrEf74bZY1d54aCbc",
+ "ExplicitOverrideControlOverrideQt": "0$dWQ+iDw9oKEYXiSgy2izIISyRMg",
+ "ExplicitOverrideControlSealed": "0$3nq5psg7nFNDlPnBpaYraCr3CHQ",
+ "ExplicitOverrideControlSealedOverride": "0$I7ySe3SgUSARroswRh74wJoZkkA"
+ },
"inputFile": "cxx11-explicit-override-control.h",
"outputRevision": 69
},
@@ -850,6 +875,17 @@
]
}
],
+ "hashes": {
+ "ExportedFinalTestClassCpp11": "0$eUEIDQi+/lB9KOjB+oul4rZe9xE",
+ "ExportedFinalTestClassCpp11X": "0$g03sLKAEeX8BLjKjFsG3MMfunaY",
+ "ExportedFinalTestClassQt": "0$fYFeeRSmEnokVTW4eIiCU6PR5HM",
+ "ExportedFinalTestClassQtX": "0$7+xgvb70xVV3Xot9q7FhSm8mUew",
+ "ExportedSealedTestClass": "0$jzcARHlbTm1whxXOtg9edtN1ctg",
+ "ExportedSealedTestClassX": "0$tEmYiad6hqJMlGaCnNc2GhZtDgM",
+ "FinalTestClassCpp11": "0$Opb/8DnbfVjpH2CZvHo9Rv93iV8",
+ "FinalTestClassQt": "0$DtnHOKRNbQ5Y3QbO5AeLM4qYDW0",
+ "SealedTestClass": "0$VUU19XJgT+KhqRBlwQmXkd0yVgQ"
+ },
"inputFile": "cxx11-final-classes.h",
"outputRevision": 69
},
@@ -937,6 +973,9 @@
]
}
],
+ "hashes": {
+ "CXX11TrailingReturn": "0$UrmUM8dix1r2pcj94cUzTALq0x8"
+ },
"inputFile": "cxx11-trailing-return.h",
"outputRevision": 69
},
@@ -977,6 +1016,10 @@
"qualifiedClassName": "CXX17Namespace::A::B::C::D"
}
],
+ "hashes": {
+ "CXX17Namespace::A::B::C::D": "0$WwEpzp6jKMGQfcvBrtsAJOy01KM",
+ "CXX17Namespace::A::B::C::D::ClassInNamespace": "0$/g09nq8R/tK4tC37pIgfRw/mBog"
+ },
"inputFile": "cxx17-namespaces.h",
"outputRevision": 69
},
@@ -1007,6 +1050,9 @@
]
}
],
+ "hashes": {
+ "DirInIncludePath": "0$Yj0uFD5gTp9Ie67oMAyjAEUetbo"
+ },
"inputFile": "dir-in-include-path.h",
"outputRevision": 69
},
@@ -1025,6 +1071,9 @@
]
}
],
+ "hashes": {
+ "Foo": "0$CB6VdqlszayDKadxVcYxPz1KzvI"
+ },
"inputFile": "enum_with_include.h",
"outputRevision": 69
},
@@ -1057,6 +1106,9 @@
]
}
],
+ "hashes": {
+ "StringLiterals": "0$ZMhjewm0f9PVVN6Nf31Slot+IRo"
+ },
"inputFile": "escapes-in-string-literals.h",
"outputRevision": 69
},
@@ -1295,6 +1347,9 @@
]
}
],
+ "hashes": {
+ "ForwardDeclaredParamClass": "0$1v+gvQyQz9IWyRMSBZJfmhZpCw4"
+ },
"inputFile": "forward-declared-param.h",
"outputRevision": 69
},
@@ -1329,6 +1384,9 @@
]
}
],
+ "hashes": {
+ "FunctionWithAttributes": "0$PHwt9wN4wGfmeCWQSXJ4CFyLKCk"
+ },
"inputFile": "function-with-attributes.h",
"outputRevision": 69
},
@@ -1364,6 +1422,10 @@
]
}
],
+ "hashes": {
+ "DerivedGadgetWithEnums": "0$ziLf2XE3jIzXb4mHc1AtXn2Pr+w",
+ "GadgetWithNoEnums": "0$Jahs+W0ABVb+g0vSoUQ80hLQCzk"
+ },
"inputFile": "gadgetwithnoenums.h",
"outputRevision": 69
},
@@ -1400,6 +1462,11 @@
]
}
],
+ "hashes": {
+ "GrandParentGadget::BaseGadget": "0$edRI9jp0xvXAhzuu71v5ByBmvbw",
+ "GrandParentGadget::CRTPDerivedGadget": "0$Y3W4552o/BUqjdHf2NxgqKGkRmI",
+ "GrandParentGadget::DerivedGadget": "0$AhUcr6i4Hne/y216sE3UnzvaBks"
+ },
"inputFile": "grand-parent-gadget-class.h",
"outputRevision": 69
},
@@ -1471,6 +1538,10 @@
"qualifiedClassName": "SomeRandomNamespace"
}
],
+ "hashes": {
+ "SomeRandomNamespace": "0$haTJ0XROzXzF0eXH4sreUExdPxg",
+ "TestFwdProperties": "0$3+84uwm9jhX5WgZzqClAZM6h7Hg"
+ },
"inputFile": "moc_include.h",
"outputRevision": 69
},
@@ -1541,6 +1612,11 @@
"qualifiedClassName": "FooNamespace::FooNestedNamespace::FooMoreNestedNamespace"
}
],
+ "hashes": {
+ "FooNamespace": "0$Z0uaI0MMFqu0YzU7vExYNRUcBhs",
+ "FooNamespace::FooNestedNamespace": "0$c0mGYICDOoC4QPdVm5HRH9ApH68",
+ "FooNamespace::FooNestedNamespace::FooMoreNestedNamespace": "0$FHRRWVs1L+xEcXWY318xVxYlx+Y"
+ },
"inputFile": "namespace.h",
"outputRevision": 69
},
@@ -1572,6 +1648,10 @@
]
}
],
+ "hashes": {
+ "QTBUG_101141::Base": "0$U366MOIqv3M+uAirFGzc/YkYdoA",
+ "QTBUG_101141::Derived": "0$RhyA1CgCTHJhP+oLRVIDYv85oNQ"
+ },
"inputFile": "namespaced-base-class.h",
"outputRevision": 69
},
@@ -1664,6 +1744,10 @@
]
}
],
+ "hashes": {
+ "Foo::Bar": "0$bYnwxHCP+WvK/RySc0Cz7hJ/Mv4",
+ "Foo::Baz": "0$/xEtUkDT353vJHNrTTLCovKNWyo"
+ },
"inputFile": "namespaced-flags.h",
"outputRevision": 69
},
@@ -1700,6 +1784,9 @@
]
}
],
+ "hashes": {
+ "MyBooooooostishClass": "0$RDRAZTtVcMrrVnaW3ccG5tm5kvU"
+ },
"inputFile": "no-keywords.h",
"outputRevision": 69
},
@@ -1718,6 +1805,9 @@
]
}
],
+ "hashes": {
+ "NonGadgetParent::Derived": "0$3bsnxOwlHgO7cAngQLFI5JRtpDE"
+ },
"inputFile": "non-gadget-parent-class.h",
"outputRevision": 69
},
@@ -1784,6 +1874,9 @@
]
}
],
+ "hashes": {
+ "OldStyleCast": "0$tg567Jxb/wczCGYgDo7CHjLQaF8"
+ },
"inputFile": "oldstyle-casts.h",
"outputRevision": 69
},
@@ -2027,6 +2120,9 @@
]
}
],
+ "hashes": {
+ "PD::ParseDefine": "0$gtPGvhjjReF7/9ujAvwWNaRWEdk"
+ },
"inputFile": "parse-defines.h",
"outputRevision": 69
},
@@ -2045,6 +2141,9 @@
]
}
],
+ "hashes": {
+ "TestPluginMetaData": "0$D43pha4BOvdLMUbKSF0Pw0b2rfQ"
+ },
"inputFile": "plugin_metadata.h",
"outputRevision": 69
},
@@ -2149,6 +2248,9 @@
]
}
],
+ "hashes": {
+ "TestPointeeCanBeIncomplete": "0$BXzN6PCuKk/qhZeYZaaNyUNKW/8"
+ },
"inputFile": "pointery_to_incomplete.h",
"outputRevision": 69
},
@@ -2230,6 +2332,10 @@
]
}
],
+ "hashes": {
+ "PureVirtualSignalsImpl": "0$+RHy7vy0RgivuAtQW3Dohe0R+qk",
+ "PureVirtualSignalsTest": "0$31whJazqnFwWMc3cO9MaT9wBSy8"
+ },
"inputFile": "pure-virtual-signals.h",
"outputRevision": 69
},
@@ -2302,6 +2408,10 @@
]
}
],
+ "hashes": {
+ "QEnum64Object": "0$mPIUjAUmfYvqxkkkXghMo9RAiZA",
+ "QFlags64Object": "0$o86jnlU0tZ0uyjOrEZ4/oOXAKfk"
+ },
"inputFile": "qflags64object.h",
"outputRevision": 69
},
@@ -2358,6 +2468,10 @@
]
}
],
+ "hashes": {
+ "InvokableBeforeInline": "0$t5nSESnZz1Liw62inFXd2SZTQTg",
+ "InvokableBeforeReturnType": "0$q7unyP9EifVhdxdSVIaFweQMDs4"
+ },
"inputFile": "qinvokable.h",
"outputRevision": 69
},
@@ -2397,6 +2511,9 @@
]
}
],
+ "hashes": {
+ "QmlMacro": "0$ohmDXSSvwPL9cUf7TItBqGRGOXM"
+ },
"inputFile": "qmlmacro.h",
"outputRevision": 69
},
@@ -2433,6 +2550,9 @@
]
}
],
+ "hashes": {
+ "TestQPrivateSlots": "0$akM0QTpV2o3AF3v6YBUh+iY1NsM"
+ },
"inputFile": "qprivateslots.h",
"outputRevision": 69
},
@@ -2456,6 +2576,9 @@
"qualifiedClassName": "QTBUG_35657::A"
}
],
+ "hashes": {
+ "QTBUG_35657::A": "0$ExnqnOcM0keSitP69QEKBZXt4Es"
+ },
"inputFile": "qtbug-35657-gadget.h",
"outputRevision": 69
},
@@ -2490,6 +2613,9 @@
]
}
],
+ "hashes": {
+ "QTBUG_35657::B": "0$72RlEvuVajxk55rzeym5e5E7LmA"
+ },
"inputFile": "related-metaobjects-in-gadget.h",
"outputRevision": 69
},
@@ -2547,6 +2673,10 @@
]
}
],
+ "hashes": {
+ "QTBUG_2151::A": "0$wwzOwB54lloQnRalZ9IiAf2oCII",
+ "QTBUG_2151::B": "0$b4fk7Aaf9GubBohh4VCfABMTEJM"
+ },
"inputFile": "related-metaobjects-in-namespaces.h",
"outputRevision": 69
},
@@ -3029,6 +3159,28 @@
]
}
],
+ "hashes": {
+ "NS1::DependingNestedGadget": "0$AjMR5Q0D2DupU0l/8Fl2wE0HX/w",
+ "NS1::DependingNestedObject": "0$DopB5FLdxxGgVBjg2pb8LDui8hA",
+ "NS1::DependingObject": "0$NBnkXMTHy3+HN77VtSKqsQrn2Ds",
+ "NS1::Gadget": "0$x+7FBtXOnCa+gBCDQYYJELL4q6I",
+ "NS1::Nested::Gadget": "0$QrFH5wzXn6x40CgbEt6o8+GwAEw",
+ "NS1::Nested::Object": "0$pMIVvuQb0tyLZ5mwRFw8QDUTsPE",
+ "NS1::NestedUnsused::Gadget": "0$ksUC+FXicub96bqLdCnYlMjTIQg",
+ "NS1::NestedUnsused::Object": "0$Cu9dTusUon5OJbsbCNDszsTWsk4",
+ "NS1::Object": "0$FkizYy4XPEX9QPIyL2eE9bFYc5E",
+ "NS2::DependingNestedGadget": "0$DMnk8/Na+tVbOCW2bDplJtp+qfI",
+ "NS2::DependingNestedObject": "0$us8HSPRnoTuPP+yEIj9VoF0+wgY",
+ "NS2::DependingObject": "0$Y9OXadBw3CKbCLiiIcwarWKy4Ko",
+ "NS2::Gadget": "0$1c2uSbAFT5taj/PGDXyGdXgWhk0",
+ "NS2::Nested::Gadget": "0$1Xmv0ccjZFLq/Y68TxyTQNopa8E",
+ "NS2::Nested::Object": "0$usRIIUM8ix6bMuqFyxtLIGnTAew",
+ "NS2::NestedUnsused::Gadget": "0$V8PumQY6aJmatQ/V72bPVRUyPtM",
+ "NS2::NestedUnsused::Object": "0$HJl5p4/beto2yDugYIrnCfTIRm0",
+ "NS2::Object": "0$qx8DIVSOVAgRXwqE1JjvDXnuM44",
+ "Unsused::Gadget": "0$9DYMYjqKdVlC6OknSGIF2TCoZJo",
+ "Unsused::Object": "0$KW85gaAnMYh4a0phjBcM1vGAyKw"
+ },
"inputFile": "related-metaobjects-name-conflict.h",
"outputRevision": 69
},
@@ -3070,6 +3222,9 @@
]
}
],
+ "hashes": {
+ "SignalWithDefaultArg": "0$nxiVjK8ieRlRO/uDdq0gKtPSgTU"
+ },
"inputFile": "signal-with-default-arg.h",
"outputRevision": 69
},
@@ -3099,6 +3254,9 @@
]
}
],
+ "hashes": {
+ "KDAB": "0$grAoknUYC0BkMxt4HLH2mGR4Tu8"
+ },
"inputFile": "single-quote-digit-separator-n3781.h",
"outputRevision": 69
},
@@ -3180,6 +3338,9 @@
]
}
],
+ "hashes": {
+ "SlotsWithVoidTemplateTest": "0$/08YvkDzSSJmzKO0L6t3PWzQTpA"
+ },
"inputFile": "slots-with-void-template.h",
"outputRevision": 69
},
@@ -3198,6 +3359,9 @@
]
}
],
+ "hashes": {
+ "Task192552": "0$QGM+ZcuSQWGxY2IolngyVZy8sy4"
+ },
"inputFile": "task192552.h",
"outputRevision": 69
},
@@ -3228,6 +3392,10 @@
]
}
],
+ "hashes": {
+ "NS_A::NS_B::TestObject": "0$IMqa1PfGS0EaXXK+9Ch24g/nP2A",
+ "NS_A::NS_Main::TestMain": "0$0z3ZNVKocxpdZHzRFtoTKQimUm0"
+ },
"inputFile": "task234909.h",
"outputRevision": 69
},
@@ -3403,6 +3571,9 @@
]
}
],
+ "hashes": {
+ "TypenameWithUnsigned": "0$jwklF5QEzeV58Ui5C0aT9ZFwDVo"
+ },
"inputFile": "task240368.h",
"outputRevision": 69
},
@@ -3421,6 +3592,9 @@
]
}
],
+ "hashes": {
+ "Task87883": "0$wz5QuoSTJ+0peRmMDYqL9p8q5q4"
+ },
"inputFile": "task87883.h",
"outputRevision": 69
},
@@ -3507,6 +3681,9 @@
]
}
],
+ "hashes": {
+ "MyTechPreviewObject": "0$70fB9sh1BICrgFbeYIYRC8QX+PQ"
+ },
"inputFile": "tech-preview.h",
"outputRevision": 69
},
@@ -3591,6 +3768,9 @@
]
}
],
+ "hashes": {
+ "BBB::Foo": "0$QBU7ysZZuCx+IZ4+vUQEQ84tqu8"
+ },
"inputFile": "trigraphs.h",
"outputRevision": 69
},
diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp
index fb30d66e6ec..7001d676878 100644
--- a/tests/auto/tools/moc/tst_moc.cpp
+++ b/tests/auto/tools/moc/tst_moc.cpp
@@ -2576,6 +2576,26 @@ void tst_Moc::warnings_data()
<< QString()
<< u"standard input:2:1: error: Parse error at \"NONSENSE\""_s;
+ QTest::newRow("VIRTUAL FINAL property")
+ << "class X { \n Q_PROPERTY(int p READ p VIRTUAL FINAL) \n };"_ba << QStringList() << 1
+ << QString()
+ << u"standard input:2:1: error: Issue with property declaration p: "
+ u"The VIRTUAL cannot be combined with FINAL, as these attributes are mutually exclusive"_s;
+
+ QTest::newRow("FINAL OVERRIDE property")
+ << "class X { \n Q_PROPERTY(int p READ p FINAL OVERRIDE) \n };"_ba << QStringList() << 1
+ << QString()
+ << u"standard input:2:1: error: Issue with property declaration p: "
+ u"OVERRIDE is redundant when property is marked FINAL"_s;
+
+ QTest::newRow("VIRTUAL OVERRIDE property")
+ << "class X { \n Q_PROPERTY(int p READ p VIRTUAL OVERRIDE) \n };"_ba << QStringList()
+ << 1 << QString()
+ << u"standard input:2:1: error: Issue with property declaration p: VIRTUAL is "
+ u"redundant when overriding a property."
+ u" The OVERRIDE must only be used when actually overriding an existing property;"
+ u" using it on a new property is an error."_s;
+
#ifdef Q_OS_UNIX // Limit to Unix because the error message is platform-dependent
QTest::newRow("Q_PLUGIN_METADATA: unreadable file")
<< QByteArray("class X { \n Q_PLUGIN_METADATA(FILE \".\") \n };")
diff --git a/tests/auto/tools/mochelpers/tst_mochelpers.cpp b/tests/auto/tools/mochelpers/tst_mochelpers.cpp
index 7e5d18d160e..ae80f0c2b58 100644
--- a/tests/auto/tools/mochelpers/tst_mochelpers.cpp
+++ b/tests/auto/tools/mochelpers/tst_mochelpers.cpp
@@ -104,7 +104,7 @@ void tst_MocHelpers::classinfoDataGroup()
{
constexpr auto data = QtMocHelpers::metaObjectData<void, void>(0, dummyStringData,
QtMocHelpers::UintData{}, QtMocHelpers::UintData{},
- QtMocHelpers::UintData{}, QtMocHelpers::UintData{},
+ QtMocHelpers::UintData{}, -1, QtMocHelpers::UintData{},
QtMocHelpers::ClassInfos({{1, 2}, {3, 4}}));
checkClassInfos(data.staticData.data);
}
@@ -612,7 +612,7 @@ void tst_MocHelpers::constructorUintGroup()
constexpr auto data = QtMocHelpers::metaObjectData<void, void>(0, dummyStringData,
QtMocHelpers::UintData{}, QtMocHelpers::UintData{},
- QtMocHelpers::UintData{}, constructors);
+ QtMocHelpers::UintData{}, -1, constructors);
checkConstructors(data.staticData.data, data.relocatingData.metaTypes);
}
@@ -676,7 +676,7 @@ void tst_MocHelpers::uintArrayNoMethods()
QtMocHelpers::EnumData<E2>(7, 6, EnumIsFlag | EnumIsScoped)
.add({ { 7, E2::V0 }, { 10, E2::V1 }, }),
QtMocHelpers::EnumData<QFlags<E1>>(11, 1, EnumIsFlag).add({ { 3, E1::AnEnumValue } }),
- }, QtMocHelpers::UintData{}, QtMocHelpers::ClassInfos({{1, 2}, {3, 4}}));
+ }, -1, QtMocHelpers::UintData{}, QtMocHelpers::ClassInfos({{1, 2}, {3, 4}}));
auto &data = mo.staticData.data;
auto &metaTypes = mo.relocatingData.metaTypes;
@@ -724,6 +724,7 @@ void tst_MocHelpers::uintArray()
.add({ { 7, E2::V0 }, { 10, E2::V1 }, }),
QtMocHelpers::EnumData<QFlags<E1>>(11, 1, EnumIsFlag).add({ { 3, E1::AnEnumValue } }),
},
+ -1,
QtMocHelpers::UintData{
QtMocHelpers::ConstructorData<NoType(QObject *)>(1, QtMocConstants::AccessPublic,
{{ { QMetaType::QObjectStar, 2 } }}
diff --git a/tests/auto/tools/rcc/data/legal/rcc_legal.cpp b/tests/auto/tools/rcc/data/legal/rcc_legal.cpp
index 248ab2e3b48..96f87d192e7 100644
--- a/tests/auto/tools/rcc/data/legal/rcc_legal.cpp
+++ b/tests/auto/tools/rcc/data/legal/rcc_legal.cpp
@@ -3,7 +3,7 @@
** Copyright (C) 2024 Intel Corporation.
** SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
**
-** Created by: The Resource Compiler for Qt version 6.11.0
+** Created by: The Resource Compiler for Qt version 6.12.0
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
diff --git a/tests/auto/util/testrunner/CMakeLists.txt b/tests/auto/util/testrunner/CMakeLists.txt
new file mode 100644
index 00000000000..5ca8406f854
--- /dev/null
+++ b/tests/auto/util/testrunner/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Copyright (C) 2025 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+# Run the qt-testrunner test only inside the CI.
+if(DEFINED ENV{COIN_UNIQUE_JOB_ID} AND NOT IOS)
+ qt_internal_create_test_script(
+ NAME tst_qt_testrunner
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tst_qt_testrunner.py" ARGS -v
+ WORKING_DIRECTORY "${test_working_dir}"
+ OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/tst_qt_testrunner_Wrapper$<CONFIG>.cmake"
+ ENVIRONMENT "TESTRUNNER" ""
+ )
+endif()
diff --git a/tests/auto/util/testrunner/qt_mock_test-log.xml b/tests/auto/util/testrunner/qt_mock_test-log.xml
new file mode 100644
index 00000000000..a164bec9f9c
--- /dev/null
+++ b/tests/auto/util/testrunner/qt_mock_test-log.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<TestCase name="qt_mock_test">
+ <Environment>
+ <QtVersion>MOCK</QtVersion>
+ <QtBuild>MOCK</QtBuild>
+ <QTestVersion>6.3.0</QTestVersion>
+ </Environment>
+ <TestFunction name="initTestCase">
+ <Incident type="{{initTestCase_result}}" file="" line="0" />
+ <Duration msecs="0.00004"/>
+ </TestFunction>
+ <TestFunction name="always_pass">
+ <Incident type="{{always_pass_result}}" file="" line="0" />
+ <Duration msecs="0.71704"/>
+ </TestFunction>
+ <TestFunction name="always_fail">
+ <Incident type="{{always_fail_result}}" file="" line="0" />
+ <Duration msecs="0.828272"/>
+ </TestFunction>
+ <TestFunction name="always_crash">
+ <Incident type="{{always_crash_result}}" file="" line="0" />
+ <Duration msecs="0.828272"/>
+ </TestFunction>
+
+ <!-- The strings like this one "{{fail_then_pass:2_result}}"
+ are just template strings that will be replaced by the test driver
+ before each test. The colon doesn't have a special meaning.
+ The datatags in the following tests are just "2", "5", "6".
+ We don't strictly need datatags because the tests don't include
+ specific testing for datatags. It's just that adding a couple
+ of datatags to this XML template, complicates it a bit and
+ tests somewhat that functionality as a side-effect.
+ -->
+ <TestFunction name="fail_then_pass">
+ <Incident type="{{fail_then_pass:2_result}}" file="" line="0">
+ <DataTag><![CDATA[2]]></DataTag>
+ </Incident>
+ <Incident type="{{fail_then_pass:5_result}}" file="" line="0">
+ <DataTag><![CDATA[5]]></DataTag>
+ </Incident>
+ <Incident type="{{fail_then_pass:6_result}}" file="" line="0">
+ <DataTag><![CDATA[6]]></DataTag>
+ </Incident>
+ </TestFunction>
+ <TestFunction name="fail_then_crash">
+ <Incident type="{{fail_then_crash_result}}" file="" line="0" />
+ <Duration msecs="0.828272"/>
+ </TestFunction>
+ <Duration msecs="1904.9"/>
+</TestCase>
diff --git a/tests/auto/util/testrunner/qt_mock_test.py b/tests/auto/util/testrunner/qt_mock_test.py
new file mode 100755
index 00000000000..af8fdf24509
--- /dev/null
+++ b/tests/auto/util/testrunner/qt_mock_test.py
@@ -0,0 +1,221 @@
+#!/usr/bin/env python3
+# Copyright (C) 2021 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+
+# This is an artificial test, mimicking the Qt tests, for example tst_whatever.
+# Its purpose is to assist in testing qt-testrunner.py.
+#
+# Mode A:
+#
+# If invoked with a test function argument, it runs that test function.
+#
+# Usage:
+#
+# $0 always_pass
+# $0 always_fail
+# $0 always_crash
+# $0 fail_then_pass:N # where N is the number of failing runs before passing
+#
+# Needs environment variable:
+# + QT_MOCK_TEST_STATE_FILE :: points to a unique filename, to be written
+# for keeping the state of the fail_then_pass:N tests.
+#
+# Mode B:
+#
+# If invoked without any argument, it runs the tests listed in the
+# variable QT_MOCK_TEST_RUN_LIST. If variable is empty it just runs
+# the always_pass test. It also understands qtestlib's `-o outfile.xml,xml`
+# option for writing a mock testlog in a file. Requires environment variables:
+# + QT_MOCK_TEST_STATE_FILE :: See above
+# + QT_MOCK_TEST_XML_TEMPLATE_FILE :: may point to the template XML file
+# located in the same source directory. Without this variable, the
+# option `-o outfile.xml,xml` will be ignored.
+# + QT_MOCK_TEST_RUN_LIST :: may contain a comma-separated list of test
+# that should run.
+# + QT_MOCK_TEST_CRASH_CLEANLY :: if set to 1, then the executable will
+# crash (exit with a high exit code)
+# after successfully running the given tests and writing the XML logfile.
+
+
+
+import sys
+import os
+import traceback
+from tst_qt_testrunner import write_xml_log
+
+
+MY_NAME = os.path.basename(sys.argv[0])
+STATE_FILE = None
+XML_TEMPLATE = None
+XML_OUTPUT_FILE = None
+CRASH_CLEANLY = False
+
+
+def crash():
+ sys.exit(131)
+
+def put_failure(test_name):
+ with open(STATE_FILE, "a") as f:
+ f.write(test_name + "\n")
+def get_failures(test_name):
+ n = 0
+ try:
+ with open(STATE_FILE) as f:
+ for line in f:
+ if line.strip() == test_name:
+ n += 1
+ except FileNotFoundError:
+ return 0
+ return n
+
+# Only care about the XML log output file.
+def parse_output_argument(a):
+ global XML_OUTPUT_FILE
+ if a.endswith(",xml"):
+ XML_OUTPUT_FILE = a[:-4]
+
+# Strip qtestlib specific arguments.
+# Only care about the "-o ...,xml" argument.
+def clean_cmdline():
+ args = []
+ prev_arg = None
+ skip_next_arg = True # Skip argv[0]
+ for a in sys.argv:
+ if skip_next_arg:
+ if prev_arg == "-o":
+ parse_output_argument(a)
+ prev_arg = None
+ skip_next_arg = False
+ continue
+ if a in ("-o", "-maxwarnings"):
+ skip_next_arg = True
+ prev_arg = a
+ continue
+ if a in ("-v1", "-v2", "-vs"):
+ print("VERBOSE RUN")
+ if "QT_LOGGING_RULES" in os.environ:
+ print("Environment has QT_LOGGING_RULES:",
+ os.environ["QT_LOGGING_RULES"])
+ continue
+ args.append(a)
+ return args
+
+
+def log_test(testcase, result,
+ testsuite=MY_NAME.rpartition(".")[0]):
+ print("%-7s: %s::%s()" % (result, testsuite, testcase))
+
+def log_xml(fail_list):
+ if XML_OUTPUT_FILE and XML_TEMPLATE is not None:
+ if XML_TEMPLATE == "":
+ # If the template is an empty file, then write an empty output file
+ with open(XML_OUTPUT_FILE, "w"):
+ pass
+ else:
+ write_xml_log(XML_OUTPUT_FILE, failure=fail_list)
+
+# Return the exit code
+def run_test(testname):
+ if testname == "initTestCase":
+ exit_code = 1 # specifically test that initTestCase fails
+ elif testname == "always_pass":
+ exit_code = 0
+ elif testname == "always_fail":
+ exit_code = 1
+ elif testname == "always_crash":
+ exit_code = 131
+ elif testname == "fail_then_crash":
+ previous_fails = get_failures(testname)
+ if previous_fails == 0:
+ put_failure(testname)
+ exit_code = 1
+ else:
+ exit_code = 131
+ elif testname.startswith("fail_then_pass"):
+ wanted_fails = int(testname.partition(":")[2])
+ previous_fails = get_failures(testname)
+ if previous_fails < wanted_fails:
+ put_failure(testname)
+ exit_code = 1
+ else:
+ exit_code = 0
+ else:
+ assert False, "Unknown argument: %s" % testname
+
+ if exit_code == 0:
+ log_test(testname, "PASS")
+ elif exit_code == 1:
+ log_test(testname, "FAIL!")
+ else:
+ log_test(testname, "CRASH!")
+
+ return exit_code
+
+def no_args_run():
+ try:
+ run_list = os.environ["QT_MOCK_TEST_RUN_LIST"].split(",")
+ except KeyError:
+ run_list = ["always_pass"]
+
+ total_result = True
+ fail_list = []
+ for test in run_list:
+ test_exit_code = run_test(test)
+ if test_exit_code not in (0, 1):
+ crash()
+ if test_exit_code != 0:
+ fail_list.append(test)
+ total_result = total_result and (test_exit_code == 0)
+
+ log_xml(fail_list)
+
+ if CRASH_CLEANLY:
+ # Crash despite all going well and writing all output files cleanly.
+ crash()
+
+ if total_result:
+ sys.exit(0)
+ else:
+ sys.exit(1)
+
+
+def main():
+ global STATE_FILE
+ # Will fail if env var is not set.
+ STATE_FILE = os.environ["QT_MOCK_TEST_STATE_FILE"]
+
+ global XML_TEMPLATE
+ if "QT_MOCK_TEST_XML_TEMPLATE_FILE" in os.environ:
+ with open(os.environ["QT_MOCK_TEST_XML_TEMPLATE_FILE"]) as f:
+ XML_TEMPLATE = f.read()
+
+ global CRASH_CLEANLY
+ if ("QT_MOCK_TEST_CRASH_CLEANLY" in os.environ
+ and os.environ["QT_MOCK_TEST_CRASH_CLEANLY"] == "1"
+ ):
+ CRASH_CLEANLY = True
+
+ args = clean_cmdline()
+
+ if len(args) == 0:
+ no_args_run()
+ assert False, "Unreachable!"
+ else: # run single test function
+ exit_code = run_test(args[0])
+ # Write "fail" in the XML log only if the specific run has failed.
+ if exit_code != 0:
+ log_xml([args[0]])
+ else:
+ log_xml([])
+ sys.exit(exit_code)
+
+
+# TODO write XPASS test that does exit(1)
+
+if __name__ == "__main__":
+ try:
+ main()
+ except Exception as e:
+ traceback.print_exc()
+ exit(128) # Something went wrong with this script
diff --git a/tests/auto/util/testrunner/tst_qt_testrunner.py b/tests/auto/util/testrunner/tst_qt_testrunner.py
new file mode 100755
index 00000000000..1134fa0427f
--- /dev/null
+++ b/tests/auto/util/testrunner/tst_qt_testrunner.py
@@ -0,0 +1,581 @@
+#!/usr/bin/env python3
+# Copyright (C) 2021 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+
+import sys
+import os
+import re
+import glob
+import subprocess
+
+from subprocess import STDOUT, PIPE
+from tempfile import TemporaryDirectory, mkstemp
+
+MY_NAME = os.path.basename(__file__)
+my_dir = os.path.dirname(__file__)
+testrunner = os.path.join(my_dir, "..", "..", "..", "..",
+ "util", "testrunner", "qt-testrunner.py")
+mock_test = os.path.join(my_dir, "qt_mock_test.py")
+xml_log_template = os.path.join(my_dir, "qt_mock_test-log.xml")
+
+with open(xml_log_template) as f:
+ XML_TEMPLATE = f.read()
+
+
+import unittest
+
+def setUpModule():
+ global TEMPDIR
+ TEMPDIR = TemporaryDirectory(prefix="tst_qt_testrunner-")
+
+ global EMPTY_FILE
+ EMPTY_FILE = os.path.join(TEMPDIR.name, "EMPTY")
+ with open(EMPTY_FILE, "w") as f:
+ pass
+
+ filename = os.path.join(TEMPDIR.name, "file_1")
+ print("setUpModule(): setting up temporary directory and env var"
+ " QT_MOCK_TEST_STATE_FILE=" + filename + " and"
+ " QT_MOCK_TEST_XML_TEMPLATE_FILE=" + xml_log_template)
+
+ os.environ["QT_MOCK_TEST_STATE_FILE"] = filename
+ os.environ["QT_MOCK_TEST_XML_TEMPLATE_FILE"] = xml_log_template
+ os.environ["QT_TESTRUNNER_TESTING"] = "1"
+
+def tearDownModule():
+ print("\ntearDownModule(): Cleaning up temporary directory:",
+ TEMPDIR.name)
+ del os.environ["QT_MOCK_TEST_STATE_FILE"]
+ TEMPDIR.cleanup()
+
+
+# Helper to run a command and always capture output
+def run(args : list, **kwargs):
+ if args[0].endswith(".py"):
+ # Make sure we run python executables with the same python version.
+ # It also helps on Windows, that .py files are not directly executable.
+ args = [ sys.executable, *args ]
+ if DEBUG:
+ print("Running: ", args, flush=True)
+ proc = subprocess.run(args, stdout=PIPE, stderr=STDOUT, **kwargs)
+ if DEBUG and proc.stdout:
+ print(proc.stdout.decode(), flush=True)
+ return proc
+
+# Helper to run qt-testrunner.py with proper testing arguments.
+# Always append --log-dir=TEMPDIR unless specifically told not to.
+def run_testrunner(xml_filename=None, log_dir=None,
+ testrunner_args=None,
+ wrapper_script=None, wrapper_args=None,
+ qttest_args=None, env=None):
+
+ args = [ testrunner ]
+ if xml_filename:
+ args += [ "--parse-xml-testlog", xml_filename ]
+ if log_dir == None:
+ args += [ "--log-dir", TEMPDIR.name ]
+ elif log_dir != "":
+ args += [ "--log-dir", log_dir ]
+ if testrunner_args:
+ args += testrunner_args
+
+ if wrapper_script:
+ args += [ wrapper_script ]
+ if wrapper_args:
+ args += wrapper_args
+
+ args += [ mock_test ]
+ if qttest_args:
+ args += qttest_args
+
+ return run(args, env=env)
+
+# Write the XML_TEMPLATE to filename, replacing the templated results.
+def write_xml_log(filename, failure=None, inject_message=None):
+ data = XML_TEMPLATE
+ if failure is None:
+ failure = []
+ elif isinstance(failure, str):
+ failure = [ failure ]
+ # Replace what was asked to fail with "fail"
+ for x in failure:
+ data = data.replace("{{" + x + "_result}}", "fail")
+ # Replace the rest with "pass"
+ data = re.sub(r"{{[^}]+}}", "pass", data)
+ # Inject possible <Message> tags inside the first <TestFunction>
+ if inject_message:
+ i = data.index("</TestFunction>")
+ data = data[:i] + inject_message + data[i:]
+ with open(filename, "w") as f:
+ f.write(data)
+
+
+# Test that qt_mock_test.py behaves well. This is necessary to properly
+# test qt-testrunner.
+class Test_qt_mock_test(unittest.TestCase):
+ def setUp(self):
+ state_file = os.environ["QT_MOCK_TEST_STATE_FILE"]
+ if os.path.exists(state_file):
+ os.remove(state_file)
+ def assertProcessCrashed(self, proc):
+ if DEBUG:
+ print("process returncode is:", proc.returncode)
+ self.assertTrue(proc.returncode < 0 or
+ proc.returncode >= 128)
+
+ def test_always_pass(self):
+ proc = run([mock_test, "always_pass"])
+ self.assertEqual(proc.returncode, 0)
+ def test_always_fail(self):
+ proc = run([mock_test, "always_fail"])
+ self.assertEqual(proc.returncode, 1)
+ def test_fail_then_pass_2(self):
+ proc = run([mock_test, "fail_then_pass:2"])
+ self.assertEqual(proc.returncode, 1)
+ proc = run([mock_test, "fail_then_pass:2"])
+ self.assertEqual(proc.returncode, 1)
+ proc = run([mock_test, "fail_then_pass:2"])
+ self.assertEqual(proc.returncode, 0)
+ def test_fail_then_pass_1(self):
+ proc = run([mock_test, "fail_then_pass:1"])
+ self.assertEqual(proc.returncode, 1)
+ proc = run([mock_test, "fail_then_pass:1"])
+ self.assertEqual(proc.returncode, 0)
+ def test_fail_then_pass_many_tests(self):
+ proc = run([mock_test, "fail_then_pass:1"])
+ self.assertEqual(proc.returncode, 1)
+ proc = run([mock_test, "fail_then_pass:2"])
+ self.assertEqual(proc.returncode, 1)
+ proc = run([mock_test, "fail_then_pass:1"])
+ self.assertEqual(proc.returncode, 0)
+ proc = run([mock_test, "fail_then_pass:2"])
+ self.assertEqual(proc.returncode, 1)
+ proc = run([mock_test, "fail_then_pass:2"])
+ self.assertEqual(proc.returncode, 0)
+ def test_fail_then_crash(self):
+ proc = run([mock_test, "fail_then_crash"])
+ self.assertEqual(proc.returncode, 1)
+ proc = run([mock_test, "fail_then_crash"])
+ self.assertProcessCrashed(proc)
+ def test_xml_file_is_written(self):
+ filename = os.path.join(TEMPDIR.name, "testlog.xml")
+ proc = run([mock_test, "-o", filename+",xml"])
+ self.assertEqual(proc.returncode, 0)
+ self.assertTrue(os.path.exists(filename))
+ self.assertGreater(os.path.getsize(filename), 0)
+ os.remove(filename)
+ # Test it will write an empty XML file if template is empty
+ def test_empty_xml_file_is_written(self):
+ my_env = {
+ **os.environ,
+ "QT_MOCK_TEST_XML_TEMPLATE_FILE": EMPTY_FILE
+ }
+ filename = os.path.join(TEMPDIR.name, "testlog.xml")
+ proc = run([mock_test, "-o", filename+",xml"],
+ env=my_env)
+ self.assertEqual(proc.returncode, 0)
+ self.assertTrue(os.path.exists(filename))
+ self.assertEqual(os.path.getsize(filename), 0)
+ os.remove(filename)
+ def test_crash_cleanly(self):
+ proc = run([mock_test],
+ env={ **os.environ, "QT_MOCK_TEST_CRASH_CLEANLY":"1" })
+ if DEBUG:
+ print("returncode:", proc.returncode)
+ self.assertProcessCrashed(proc)
+
+
+# Find in @path, files that start with @testname and end with @pattern,
+# where @pattern is a glob-like string.
+def find_test_logs(testname=None, path=None, pattern="-*[0-9].xml"):
+ if testname is None:
+ testname = os.path.basename(mock_test)
+ if path is None:
+ path = TEMPDIR.name
+ pattern = os.path.join(path, testname + pattern)
+ logfiles = glob.glob(pattern)
+ if DEBUG:
+ print(f"Test ({testname}) logfiles found: ", logfiles)
+ return logfiles
+
+# Test regular invocations of qt-testrunner.
+class Test_testrunner(unittest.TestCase):
+ def setUp(self):
+ state_file = os.environ["QT_MOCK_TEST_STATE_FILE"]
+ if os.path.exists(state_file):
+ os.remove(state_file)
+ # The mock_test honors only the XML output arguments, the rest are ignored.
+ old_logfiles = find_test_logs(pattern="*.xml")
+ for fname in old_logfiles:
+ os.remove(os.path.join(TEMPDIR.name, fname))
+ self.env = dict(os.environ)
+ self.testrunner_args = []
+ def prepare_env(self, run_list=None):
+ if run_list is not None:
+ self.env['QT_MOCK_TEST_RUN_LIST'] = ",".join(run_list)
+ def run2(self):
+ return run_testrunner(testrunner_args=self.testrunner_args, env=self.env)
+
+ def test_simple_invocation(self):
+ # All tests pass.
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 0)
+ def test_always_pass(self):
+ self.prepare_env(run_list=["always_pass"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 0)
+ def test_output_files_are_generated(self):
+ proc = self.run2()
+ xml_output_files = find_test_logs()
+ self.assertEqual(len(xml_output_files), 1)
+ def test_always_fail(self):
+ self.prepare_env(run_list=["always_fail"])
+ proc = self.run2()
+ # TODO verify that re-runs==max_repeats
+ self.assertEqual(proc.returncode, 2)
+ def test_flaky_pass_1(self):
+ self.prepare_env(run_list=["always_pass,fail_then_pass:1"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 0)
+ def test_flaky_pass_5(self):
+ self.prepare_env(run_list=["always_pass,fail_then_pass:1,fail_then_pass:5"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 0)
+ def test_flaky_fail(self):
+ self.prepare_env(run_list=["always_pass,fail_then_pass:6"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 2)
+ def test_flaky_pass_fail(self):
+ self.prepare_env(run_list=["always_pass,fail_then_pass:1,fail_then_pass:6"])
+ proc = self.run2()
+ # TODO verify that one func was re-run and passed but the other failed.
+ self.assertEqual(proc.returncode, 2)
+ def test_initTestCase_fail_crash(self):
+ self.prepare_env(run_list=["initTestCase,always_pass"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+ def test_fail_then_crash(self):
+ self.prepare_env(run_list=["fail_then_crash"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+
+ # By testing --no-extra-args, we ensure qt-testrunner works for
+ # tst_selftests and the other NON_XML_GENERATING_TESTS.
+ def test_no_extra_args_pass(self):
+ self.testrunner_args += ["--no-extra-args"]
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 0)
+ def test_no_extra_args_fail(self):
+ self.prepare_env(run_list=["always_fail"])
+ self.testrunner_args += ["--no-extra-args"]
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+ def test_no_extra_args_reruns_only_once_1(self):
+ self.prepare_env(run_list=["fail_then_pass:1"])
+ self.testrunner_args += ["--no-extra-args"]
+ proc = self.run2()
+ # The 1st rerun PASSed.
+ self.assertEqual(proc.returncode, 0)
+ def test_no_extra_args_reruns_only_once_2(self):
+ self.prepare_env(run_list=["fail_then_pass:2"])
+ self.testrunner_args += ["--no-extra-args"]
+ proc = self.run2()
+ # We never re-run more than once, so the exit code shows FAIL.
+ self.assertEqual(proc.returncode, 3)
+
+ # If no XML file is found by qt-testrunner, it is usually considered a
+ # CRASH and the whole test is re-run. Even when the return code is zero.
+ # It is a PASS only if the test is not capable of XML output (see no_extra_args above).
+ def test_no_xml_log_written_pass_crash(self):
+ del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"]
+ self.prepare_env(run_list=["always_pass"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+ # On the 2nd iteration of the full test, both of the tests pass.
+ # Still it's a CRASH because no XML file was found.
+ def test_no_xml_log_written_fail_then_pass_crash(self):
+ del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"]
+ self.prepare_env(run_list=["always_pass,fail_then_pass:1"])
+ proc = self.run2()
+ # TODO verify that the whole test has run twice.
+ self.assertEqual(proc.returncode, 3)
+ # Even after 2 iterations of the full test we still get failures but no XML file,
+ # and this is considered a CRASH.
+ def test_no_xml_log_written_crash(self):
+ del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"]
+ self.prepare_env(run_list=["fail_then_pass:2"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+
+ def test_empty_xml_crash_1(self):
+ self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] = EMPTY_FILE
+ self.prepare_env(run_list=["always_pass"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+ def test_empty_xml_crash_2(self):
+ self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] = EMPTY_FILE
+ self.prepare_env(run_list=["always_fail"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+
+ # test qFatal should be a crash in all cases.
+ def test_qfatal_crash_1(self):
+ fatal_xml_message = """
+ <Message type="qfatal" file="" line="0">
+ <DataTag><![CDATA[modal]]></DataTag>
+ <Description><![CDATA[Failed to initialize graphics backend for OpenGL.]]></Description>
+ </Message>
+ """
+ logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml")
+ write_xml_log(logfile, failure=None, inject_message=fatal_xml_message)
+ del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"]
+ self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1"
+ self.prepare_env(run_list=["always_pass"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+ def test_qfatal_crash_2(self):
+ fatal_xml_message = """
+ <Message type="qfatal" file="" line="0">
+ <DataTag><![CDATA[modal]]></DataTag>
+ <Description><![CDATA[Failed to initialize graphics backend for OpenGL.]]></Description>
+ </Message>
+ """
+ logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml")
+ write_xml_log(logfile, failure="always_fail", inject_message=fatal_xml_message)
+ del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"]
+ self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1"
+ self.prepare_env(run_list=["always_pass,always_fail"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+
+ def test_qwarn_is_ignored_1(self):
+ qwarn_xml_message = """
+ <Message type="qwarn" file="" line="0">
+ <DataTag><![CDATA[modal]]></DataTag>
+ <Description><![CDATA[Failed to create RHI (backend 2)]]></Description>
+ </Message>
+ """
+ logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml")
+ write_xml_log(logfile, failure=None, inject_message=qwarn_xml_message)
+ del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"]
+ self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1"
+ self.prepare_env(run_list=["always_pass"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 0)
+ def test_qwarn_is_ignored_2(self):
+ fatal_xml_message = """
+ <Message type="qfatal" file="" line="0">
+ <DataTag><![CDATA[modal]]></DataTag>
+ <Description><![CDATA[Failed to initialize graphics backend for OpenGL.]]></Description>
+ </Message>
+ <Message type="qwarn" file="" line="0">
+ <DataTag><![CDATA[modal]]></DataTag>
+ <Description><![CDATA[Failed to create RHI (backend 2)]]></Description>
+ </Message>
+ """
+ logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml")
+ write_xml_log(logfile, failure=None, inject_message=fatal_xml_message)
+ del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"]
+ self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1"
+ self.prepare_env(run_list=["always_pass"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+
+ # If a test returns success but XML contains failures, it's a CRASH.
+ def test_wrong_xml_log_written_1_crash(self):
+ logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml")
+ write_xml_log(logfile, failure="always_fail")
+ del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"]
+ self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1"
+ self.prepare_env(run_list=["always_pass"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+ # If a test returns failure but XML contains only pass, it's a CRASH.
+ def test_wrong_xml_log_written_2_crash(self):
+ logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml")
+ write_xml_log(logfile)
+ del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"]
+ self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1"
+ self.prepare_env(run_list=["always_fail"])
+ proc = self.run2()
+ self.assertEqual(proc.returncode, 3)
+
+ def create_wrapper(self, filename, content=None):
+ if not content:
+ content='exec "$@"'
+ filename = os.path.join(TEMPDIR.name, filename)
+ with open(filename, "w") as f:
+ f.write(f'#!/bin/sh\n{content}\n')
+ self.wrapper_script = f.name
+ os.chmod(self.wrapper_script, 0o700)
+
+ # Test that it re-runs the full executable in case of crash, even if the
+ # XML file is valid and shows one specific test failing.
+ def test_crash_reruns_full_QTQAINFRA_5226(self):
+ self.env["QT_MOCK_TEST_RUN_LIST"] = "always_fail"
+ # Tell qt_mock_test to crash after writing a proper XML file.
+ self.env["QT_MOCK_TEST_CRASH_CLEANLY"] = "1"
+ proc = self.run2()
+ # Verify qt-testrunner exited with 3 which means CRASH.
+ self.assertEqual(proc.returncode, 3)
+ # Verify that a full executable re-run happened that re-runs 2 times,
+ # instead of individual functions that re-run 5 times.
+ xml_output_files = find_test_logs()
+ self.assertEqual(len(xml_output_files), 2)
+
+ # Test that qt-testrunner detects the correct executable name even if we
+ # use a special wrapper script, and that it uses that in the XML log filename.
+ @unittest.skipUnless(os.name == "posix", "Wrapper script needs POSIX shell")
+ def test_wrapper(self):
+ self.create_wrapper("coin_vxworks_qemu_runner.sh")
+ proc = run_testrunner(wrapper_script=self.wrapper_script,
+ env=self.env)
+ self.assertEqual(proc.returncode, 0)
+ xml_output_files = find_test_logs()
+ self.assertEqual(len(xml_output_files), 1)
+
+ # The "androidtestrunner" wrapper is special. It expects the QTest arguments after "--".
+ # So our mock androidtestrunner wrapper ignores everything before "--"
+ # and executes our hardcoded mock_test with the arguments that follow.
+ def create_mock_anroidtestrunner_wrapper(self):
+ self.create_wrapper("androidtestrunner", content=
+ 'while [ "$1" != "--" ]; do shift; done; shift; exec {} "$@"'.format(mock_test))
+
+ @unittest.skipUnless(os.name == "posix", "Wrapper script needs POSIX shell")
+ def test_androidtestrunner_with_aab(self):
+ self.create_mock_anroidtestrunner_wrapper()
+ # Copied from our CI logs. The only relevant option is --aab.
+ androidtestrunner_args= [
+ '--path', '/home/qt/work/qt/qtdeclarative_standalone_tests/tests/auto/quickcontrols/qquickpopup/android-build-tst_qquickpopup',
+ '--adb', '/opt/android/sdk/platform-tools/adb', '--skip-install-root',
+ '--ndk-stack', '/opt/android/android-ndk-r27c/ndk-stack',
+ '--manifest', '/home/qt/work/qt/qtdeclarative_standalone_tests/tests/auto/quickcontrols/qquickpopup/android-build-tst_qquickpopup/app/AndroidManifest.xml',
+ '--make', '"/opt/cmake-3.30.5/bin/cmake" --build /home/qt/work/qt/qtdeclarative_standalone_tests --target tst_qquickpopup_make_aab',
+ '--aab', '/home/qt/work/qt/qtdeclarative_standalone_tests/tests/auto/quickcontrols/qquickpopup/android-build-tst_qquickpopup/tst_qquickpopup.aab',
+ '--bundletool', '/opt/bundletool/bundletool', '--timeout', '1425'
+ ]
+ # In COIN CI, TESTRUNNER="qt-testrunner.py --". That's why we append "--".
+ proc = run_testrunner(testrunner_args=["--"],
+ wrapper_script=self.wrapper_script,
+ wrapper_args=androidtestrunner_args,
+ env=self.env)
+ self.assertEqual(proc.returncode, 0)
+ xml_output_files = find_test_logs("tst_qquickpopup")
+ self.assertEqual(len(xml_output_files), 1)
+ # similar to above but with "--apk"
+ @unittest.skipUnless(os.name == "posix", "Wrapper script needs POSIX shell")
+ def test_androidtestrunner_with_apk(self):
+ self.create_mock_anroidtestrunner_wrapper()
+ androidtestrunner_args= ['--blah', '--apk', '/whatever/waza.apk', 'blue']
+ proc = run_testrunner(testrunner_args=["--"],
+ wrapper_script=self.wrapper_script,
+ wrapper_args=androidtestrunner_args,
+ env=self.env)
+ self.assertEqual(proc.returncode, 0)
+ xml_output_files = find_test_logs("waza")
+ self.assertEqual(len(xml_output_files), 1)
+ # similar to above but with neither "--apk" nor "--aab". qt-testrunner throws error.
+ @unittest.skipUnless(os.name == "posix", "Wrapper script needs POSIX shell")
+ def test_androidtestrunner_fail_to_detect_filename(self):
+ self.create_mock_anroidtestrunner_wrapper()
+ androidtestrunner_args= ['--blah', '--argh', '/whatever/waza.apk', 'waza.aab']
+ proc = run_testrunner(testrunner_args=["--"],
+ wrapper_script=self.wrapper_script,
+ wrapper_args=androidtestrunner_args,
+ env=self.env)
+ self.assertEqual(proc.returncode, 1)
+ xml_output_files = find_test_logs("waza")
+ self.assertEqual(len(xml_output_files), 0)
+
+
+# Test qt-testrunner script with an existing XML log file:
+# qt-testrunner.py qt_mock_test.py --parse-xml-testlog file.xml
+# qt-testrunner should repeat the testcases that are logged as
+# failures and fail or pass depending on how the testcases behave.
+# Different XML files are generated for the following test cases.
+# + No failure logged. qt-testrunner should exit(0)
+# + The "always_pass" test has failed. qt-testrunner should exit(0).
+# + The "always_fail" test has failed. qt-testrunner should exit(2).
+# + The "always_crash" test has failed. qt-testrunner should exit(3)
+# since the re-run will crash.
+# + The "fail_then_pass:2" test failed. qt-testrunner should exit(0).
+# + The "fail_then_pass:5" test failed. qt-testrunner should exit(2).
+# + The "initTestCase" failed which is listed as NO_RERUN thus
+# qt-testrunner should exit(3).
+class Test_testrunner_with_xml_logfile(unittest.TestCase):
+ # Runs before every single test function, creating a unique temp file.
+ def setUp(self):
+ (_handle, self.xml_file) = mkstemp(
+ suffix=".xml", prefix="qt_mock_test-log-",
+ dir=TEMPDIR.name)
+ os.close(_handle)
+ if os.path.exists(os.environ["QT_MOCK_TEST_STATE_FILE"]):
+ os.remove(os.environ["QT_MOCK_TEST_STATE_FILE"])
+ def tearDown(self):
+ os.remove(self.xml_file)
+ del self.xml_file
+ # Run testrunner specifically for the tests here, with --parse-xml-testlog.
+ def run3(self, testrunner_args=None):
+ return run_testrunner(self.xml_file,
+ testrunner_args=testrunner_args)
+
+ def test_no_failure(self):
+ write_xml_log(self.xml_file, failure=None)
+ proc = self.run3()
+ self.assertEqual(proc.returncode, 0)
+ def test_always_pass_failed(self):
+ write_xml_log(self.xml_file, failure="always_pass")
+ proc = self.run3()
+ self.assertEqual(proc.returncode, 0)
+ def test_always_pass_failed_max_repeats_0(self):
+ write_xml_log(self.xml_file, failure="always_pass")
+ proc = self.run3(testrunner_args=["--max-repeats", "0"])
+ self.assertEqual(proc.returncode, 2)
+ def test_always_fail_failed(self):
+ write_xml_log(self.xml_file, failure="always_fail")
+ proc = self.run3()
+ self.assertEqual(proc.returncode, 2)
+ # Assert that one of the re-runs was in verbose mode
+ matches = re.findall("VERBOSE RUN",
+ proc.stdout.decode())
+ self.assertEqual(len(matches), 1)
+ # Assert that the environment was altered too
+ self.assertIn("QT_LOGGING_RULES", proc.stdout.decode())
+ def test_always_crash_crashed(self):
+ write_xml_log(self.xml_file, failure="always_crash")
+ proc = self.run3()
+ self.assertEqual(proc.returncode, 3)
+ def test_fail_then_pass_2_failed(self):
+ write_xml_log(self.xml_file, failure="fail_then_pass:2")
+ proc = self.run3()
+ self.assertEqual(proc.returncode, 0)
+ def test_fail_then_pass_5_failed(self):
+ write_xml_log(self.xml_file, failure="fail_then_pass:5")
+ proc = self.run3()
+ self.assertEqual(proc.returncode, 2)
+ def test_with_two_failures(self):
+ write_xml_log(self.xml_file,
+ failure=["always_pass", "fail_then_pass:2"])
+ proc = self.run3()
+ self.assertEqual(proc.returncode, 0)
+ # Check that test output is properly interleaved with qt-testrunner's logging.
+ matches = re.findall(r"(PASS|FAIL!).*\n.*Test process exited with code",
+ proc.stdout.decode())
+ self.assertEqual(len(matches), 4)
+ def test_initTestCase_fail_crash(self):
+ write_xml_log(self.xml_file, failure="initTestCase")
+ proc = self.run3()
+ self.assertEqual(proc.returncode, 3)
+
+
+if __name__ == "__main__":
+
+ DEBUG = False
+ if "--debug" in sys.argv:
+ sys.argv.remove("--debug")
+ DEBUG = True
+
+ # We set failfast=True as we do not want the test suite to continue if the
+ # tests of qt_mock_test failed. The next ones depend on it.
+ unittest.main(failfast=True)
diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp
index 10a67daa02a..0f8a6da823e 100644
--- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp
+++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp
@@ -3572,6 +3572,7 @@ void tst_QComboBox::popupPositionAfterStyleChange()
QFrame *container = box.findChild<QComboBoxPrivateContainer *>();
QVERIFY(container);
QVERIFY(QTest::qWaitForWindowExposed(container));
+ container->resize(80, 80);
// Select the last menu item, which will close the popup. This item is then expected
// to be centered on top of the combobox the next time the popup opens.
diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
index 1f57ae43fae..50e8646499d 100644
--- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
+++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp
@@ -127,6 +127,7 @@ private slots:
#endif
void invisibleActions();
+ void execReturnsWidgetAction();
protected slots:
void onActivated(QAction*);
@@ -2335,5 +2336,26 @@ void tst_QMenu::dontSelectDisabledActionByShortcut()
}
#endif
+void tst_QMenu::execReturnsWidgetAction()
+{
+ QWidget window;
+
+ QMenu menu(&window);
+ QWidgetAction *widgetAction = new QWidgetAction(&menu);
+ QPushButton *menuButton = new QPushButton("Button", &menu);
+ widgetAction->setDefaultWidget(menuButton);
+ QObject::connect(menuButton, &QPushButton::clicked, widgetAction, &QAction::trigger);
+
+ menu.addAction("First");
+ menu.addAction(widgetAction);
+ menu.addAction("Last");
+
+ window.show();
+ QVERIFY(QTest::qWaitForWindowActive(&window));
+
+ QTimer::singleShot(0, menuButton, &QPushButton::click);
+ QCOMPARE(menu.exec(window.geometry().center()), widgetAction);
+}
+
QTEST_MAIN(tst_QMenu)
#include "tst_qmenu.moc"
diff --git a/tests/benchmarks/gui/painting/qcolor/tst_qcolor.cpp b/tests/benchmarks/gui/painting/qcolor/tst_qcolor.cpp
index 02fe3f986e9..54ba45c1256 100644
--- a/tests/benchmarks/gui/painting/qcolor/tst_qcolor.cpp
+++ b/tests/benchmarks/gui/painting/qcolor/tst_qcolor.cpp
@@ -2,8 +2,24 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qtest.h>
+
#include <QColor>
+#include <vector>
+
+static std::vector<QColor> all_rgb_colors()
+{
+ std::vector<QColor> colors;
+ colors.reserve(256 * 256 * 256);
+ for (int r = 0; r < 256; ++r) {
+ for (int g = 0; g < 256; ++g) {
+ for (int b = 0; b < 256; ++b)
+ colors.emplace_back(r, g, b);
+ }
+ }
+ return colors;
+}
+
class tst_QColor : public QObject
{
@@ -12,6 +28,11 @@ class tst_QColor : public QObject
private slots:
void nameRgb();
void nameArgb();
+ void toHsl();
+ void toHsv();
+
+private:
+ const std::vector<QColor> m_all_rgb = all_rgb_colors();
};
void tst_QColor::nameRgb()
@@ -32,6 +53,22 @@ void tst_QColor::nameArgb()
}
}
+void tst_QColor::toHsl()
+{
+ QBENCHMARK {
+ for (const QColor &c : m_all_rgb)
+ [[maybe_unused]] const auto r = c.toHsl();
+ }
+}
+
+void tst_QColor::toHsv()
+{
+ QBENCHMARK {
+ for (const QColor &c : m_all_rgb)
+ [[maybe_unused]] const auto r = c.toHsv();
+ }
+}
+
QTEST_MAIN(tst_QColor)
#include "tst_qcolor.moc"