diff options
Diffstat (limited to 'tests/auto/tools/moc/tst_moc.cpp')
-rw-r--r-- | tests/auto/tools/moc/tst_moc.cpp | 694 |
1 files changed, 539 insertions, 155 deletions
diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index cd69ea893c..d24dfa11f7 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -1,40 +1,18 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Copyright (C) 2020 Olivier Goffart <ogoffart@woboq.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2020 Olivier Goffart <ogoffart@woboq.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> #include <stdio.h> +#include <optional> #include <qobject.h> #include <qmetaobject.h> #include <qjsondocument.h> -#include <qversionnumber.h> #include <qregularexpression.h> +#include <qtyperevision.h> + +#include <private/qobject_p.h> #include "using-namespaces.h" #include "assign-namespace.h" @@ -81,14 +59,69 @@ #include "fwdclass2.h" #include "fwdclass3.h" +#include "signal-with-default-arg.h" + +#include "qmlmacro.h" + +#include "tech-preview.h" + +using namespace Qt::StringLiterals; + #ifdef Q_MOC_RUN // check that moc can parse these constructs, they are being used in Windows winsock2.h header #define STRING_HASH_HASH(x) ("foo" ## x ## "bar") const char *string_hash_hash = STRING_HASH_HASH("baz"); #endif +#if defined(Q_MOC_RUN) || __cplusplus > 202002L +/* Check that nested inline namespaces are at least not causing moc to break. + Check it even outside of C++20 mode as moc gets passed the wrong __cplusplus version + and also to increase coverage, given how few C++20 configurations exist in the CI at the time + of writing this comment. +*/ +namespace A::inline B {} +namespace A { + namespace B::inline C {} +} +#endif + + +namespace TokenStartingWithNumber +{ +Q_NAMESPACE + +#define FOR_EACH_ITEM( CALL ) \ + CALL( EXAMPLE ) \ + CALL( 123_EXAMPLE ) \ + CALL( OTHER_EXAMPLE ) + +enum FooItems +{ + +#define ENUM_ITEM(NAME, ...) FOO ## NAME, + FOR_EACH_ITEM( ENUM_ITEM ) +}; + +Q_ENUM_NS(FooItems) +} + Q_DECLARE_METATYPE(const QMetaObject*); +#define TESTEXPORTMACRO Q_DECL_EXPORT + +#if !defined(Q_MOC_RUN) && !defined(Q_NOREPLY) +# define Q_NOREPLY +#endif + +struct TagTest : QObject { + Q_OBJECT + + Q_INVOKABLE Q_NOREPLY inline int test() {return 0;} +public slots: + Q_NOREPLY virtual inline void pamOpen(int){} +}; + + namespace TestNonQNamespace { struct TestGadget { @@ -106,6 +139,12 @@ public: Key2 }; Q_ENUM(TestGEnum2) + + enum TestGEnum3: quint8 { + Key1 = 23, + Key2 + }; + Q_ENUM(TestGEnum3) }; } @@ -124,6 +163,12 @@ namespace TestQNamespace { }; Q_ENUM_NS(TestEnum2) + enum TestEnum3: qint8 { + Key1 = 23, + Key2 + }; + Q_ENUM_NS(TestEnum3) + // try to dizzy moc by adding a struct in between struct TestGadget { Q_GADGET @@ -136,8 +181,35 @@ namespace TestQNamespace { Key1 = 23, Key2 }; + enum TestGEnum3: qint16 { + Key1 = 33, + Key2 + }; Q_ENUM(TestGEnum1) Q_ENUM(TestGEnum2) + Q_ENUM(TestGEnum3) + }; + + struct TestGadgetExport { + Q_GADGET_EXPORT(TESTEXPORTMACRO) + Q_CLASSINFO("key", "exported") + public: + enum class TestGeEnum1 { + Key1 = 20, + Key2 + }; + Q_ENUM(TestGeEnum1) + enum class TestGeEnum2 { + Key1 = 23, + Key2 + }; + Q_ENUM(TestGeEnum2) + enum TestGeEnum3: quint16 { + Key1 = 26, + Key2 + }; + Q_ENUM(TestGeEnum3) + }; enum class TestFlag1 { @@ -157,8 +229,27 @@ namespace TestQNamespace { Q_FLAG_NS(TestFlag2) } +namespace TestSameEnumNamespace { + Q_NAMESPACE -#define TESTEXPORTMACRO Q_DECL_EXPORT + enum class TestSameEnumNamespace { + Key1 = 1, + Key2 = 2, + }; + Q_ENUM_NS(TestSameEnumNamespace) +} + +namespace TestNestedSameEnumNamespace { +namespace a { + Q_NAMESPACE + // enum class with the same name as the enclosing nested namespace + enum class a { + Key11 = 11, + Key12 = 12, + }; + Q_ENUM_NS(a) +} +} namespace TestExportNamespace { Q_NAMESPACE_EXPORT(TESTEXPORTMACRO) @@ -189,6 +280,24 @@ public: CreatableGadget creatableGadget; // Force the compiler to use the constructor +struct ParentWithSignalWithArgument : QObject { + Q_OBJECT + Q_PROPERTY(int i READ i WRITE setI NOTIFY iChanged) + +public: + int i() const {return 0;} + void setI(int) {} + +signals: + void iChanged(int); +}; + +struct SignalWithArgumentInParent : ParentWithSignalWithArgument +{ + Q_OBJECT + Q_PROPERTY(int otherI READ i WRITE setI NOTIFY iChanged) +}; + struct MyStruct {}; struct MyStruct2 {}; @@ -308,6 +417,7 @@ class TestClassinfoWithEscapes: public QObject Q_CLASSINFO("cpp c*/omment", "f/*oo") Q_CLASSINFO("endswith\\", "Or?\?/") Q_CLASSINFO("newline\n inside\n", "Or \r") + Q_CLASSINFO("\xffz", "\0012") public slots: void slotWithAReallyLongName(int) { } @@ -346,6 +456,8 @@ QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wignored-qualifiers") QT_WARNING_DISABLE_GCC("-Wignored-qualifiers") +using ObjectCRef = const QObject &; + class TestClass : public MyNamespace::TestSuperClass, public DONT_CONFUSE_MOC(MyStruct), public DONT_CONFUSE_MOC_EVEN_MORE(MyStruct2, dummy, ignored) { @@ -541,6 +653,7 @@ signals: // public slots: void const slotWithSillyConst() {} + void slotTakingCRefViaTypedef(ObjectCRef o) { this->setObjectName(o.objectName()); } public: Q_INVOKABLE void const slotWithSillyConst2() {} @@ -569,6 +682,10 @@ private slots: QT_WARNING_POP +// quick test to verify that moc handles the L suffix +// correctly in the preprocessor +#if 2000L < 1 +#else class PropertyTestClass : public QObject { Q_OBJECT @@ -578,6 +695,7 @@ public: Q_ENUM(TestEnum) }; +#endif class PropertyUseClass : public QObject { @@ -670,6 +788,7 @@ private slots: void preprocessorConditionals(); void blackslashNewlines(); void slotWithSillyConst(); + void slotTakingCRefViaTypedef(); void testExtraData(); void testExtraDataForEnum(); void namespaceTypeProperty(); @@ -695,6 +814,7 @@ private slots: void templateGtGt(); void qprivateslots(); void qprivateproperties(); + void anonymousProperties(); void warnOnPropertyWithoutREAD(); void constructors(); void typenameWithUnsigned(); @@ -739,6 +859,7 @@ private slots: void optionsFileError_data(); void optionsFileError(); void testQNamespace(); + void testNestedQNamespace(); void cxx17Namespaces(); void cxxAttributes(); void mocJsonOutput(); @@ -748,6 +869,10 @@ private slots: void observerMetaCall(); void setQPRopertyBinding(); void privateQPropertyShim(); + void readWriteThroughBindable(); + void invokableCtors(); + void virtualInlineTaggedSlot(); + void tokenStartingWithNumber(); signals: void sigWithUnsignedArg(unsigned foo); @@ -759,6 +884,7 @@ signals: void constSignal2(int arg) const; void member4Changed(); void member5Changed(const QString &newVal); + void sigWithDefaultArg(int i = 12); private: bool user1() { return true; }; @@ -784,6 +910,14 @@ private: }; +#define VERIFY_NO_ERRORS(proc) do { \ + auto &&p = proc; \ + const QByteArray stderr = p.readAllStandardError(); \ + QVERIFY2(stderr.isEmpty(), stderr.data()); \ + QCOMPARE(p.exitCode(), 0); \ + } while (false) + + void tst_Moc::initTestCase() { QString binpath = QLibraryInfo::path(QLibraryInfo::BinariesPath); @@ -798,10 +932,9 @@ void tst_Moc::initTestCase() QProcess proc; proc.start(qtpaths, QStringList() << "-query" << "QT_INSTALL_HEADERS"); QVERIFY(proc.waitForFinished()); - QCOMPARE(proc.exitCode(), 0); + VERIFY_NO_ERRORS(proc); QByteArray output = proc.readAllStandardOutput(); QVERIFY(!output.isEmpty()); - QCOMPARE(proc.readAllStandardError(), QByteArray()); qtIncludePath = QString::fromLocal8Bit(output).trimmed(); QFileInfo fi(qtIncludePath); QVERIFY(fi.exists()); @@ -836,10 +969,9 @@ void tst_Moc::oldStyleCasts() QProcess proc; proc.start(m_moc, QStringList(m_sourceDirectory + QStringLiteral("/oldstyle-casts.h"))); QVERIFY(proc.waitForFinished()); - QCOMPARE(proc.exitCode(), 0); + VERIFY_NO_ERRORS(proc); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); - QCOMPARE(proc.readAllStandardError(), QByteArray()); QStringList args; args << "-c" << "-x" << "c++" << "-Wold-style-cast" << "-I" << "." @@ -850,8 +982,7 @@ void tst_Moc::oldStyleCasts() proc.closeWriteChannel(); QVERIFY(proc.waitForFinished()); - QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString()); - QCOMPARE(proc.exitCode(), 0); + VERIFY_NO_ERRORS(proc); #else QSKIP("Only tested on linux/gcc"); #endif @@ -872,8 +1003,8 @@ void tst_Moc::warnOnExtraSignalSlotQualifiaction() QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + - QString(":43:1: warning: Function declaration Test::badFunctionDeclaration contains extra qualification. Ignoring as signal or slot.\n") + - header + QString(":46:1: warning: parsemaybe: Function declaration Test::anotherOne contains extra qualification. Ignoring as signal or slot.\n")); + QString(":18:1: warning: Function declaration Test::badFunctionDeclaration contains extra qualification. Ignoring as signal or slot.\n") + + header + QString(":21:1: warning: parsemaybe: Function declaration Test::anotherOne contains extra qualification. Ignoring as signal or slot.\n")); #else QSKIP("Only tested on unix/gcc"); #endif @@ -908,10 +1039,9 @@ void tst_Moc::inputFileNameWithDotsButNoExtension() proc.setWorkingDirectory(m_sourceDirectory + QStringLiteral("/task71021")); proc.start(m_moc, QStringList("../Header")); QVERIFY(proc.waitForFinished()); - QCOMPARE(proc.exitCode(), 0); + VERIFY_NO_ERRORS(proc); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); - QCOMPARE(proc.readAllStandardError(), QByteArray()); QStringList args; args << "-c" << "-x" << "c++" << "-I" << ".." @@ -922,8 +1052,7 @@ void tst_Moc::inputFileNameWithDotsButNoExtension() proc.closeWriteChannel(); QVERIFY(proc.waitForFinished()); - QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString()); - QCOMPARE(proc.exitCode(), 0); + VERIFY_NO_ERRORS(proc); #else QSKIP("Only tested on linux/gcc"); #endif @@ -946,12 +1075,12 @@ void tst_Moc::supportConstSignals() QSignalSpy spy1(this, SIGNAL(constSignal1())); QVERIFY(spy1.isEmpty()); emit constSignal1(); - QCOMPARE(spy1.count(), 1); + QCOMPARE(spy1.size(), 1); QSignalSpy spy2(this, SIGNAL(constSignal2(int))); QVERIFY(spy2.isEmpty()); emit constSignal2(42); - QCOMPARE(spy2.count(), 1); + QCOMPARE(spy2.size(), 1); QCOMPARE(spy2.at(0).at(0).toInt(), 42); } @@ -974,13 +1103,15 @@ void tst_Moc::classinfoWithEscapes() const QMetaObject *mobj = &TestClassinfoWithEscapes::staticMetaObject; QCOMPARE(mobj->methodCount() - mobj->methodOffset(), 1); - QCOMPARE(mobj->classInfoCount(), 5); + QCOMPARE(mobj->classInfoCount(), 6); QCOMPARE(mobj->classInfo(2).name(), "cpp c*/omment"); QCOMPARE(mobj->classInfo(2).value(), "f/*oo"); QCOMPARE(mobj->classInfo(3).name(), "endswith\\"); QCOMPARE(mobj->classInfo(3).value(), "Or?\?/"); QCOMPARE(mobj->classInfo(4).name(), "newline\n inside\n"); QCOMPARE(mobj->classInfo(4).value(), "Or \r"); + QCOMPARE(mobj->classInfo(5).name(), "\xff" "z"); + QCOMPARE(mobj->classInfo(5).value(), "\001" "2"); QMetaMethod mm = mobj->method(mobj->methodOffset()); QCOMPARE(mm.methodSignature(), QByteArray("slotWithAReallyLongName(int)")); @@ -1058,6 +1189,15 @@ void tst_Moc::slotWithSillyConst() QVERIFY(mobj->indexOfSlot("slotWithVoidStar(void*)") != -1); } +void tst_Moc::slotTakingCRefViaTypedef() +{ + TestClass tst; + QObject obj; + obj.setObjectName("works"); + QMetaObject::invokeMethod(&tst, "slotTakingCRefViaTypedef", Q_ARG(ObjectCRef, obj)); + QCOMPARE(obj.objectName(), "works"); +} + void tst_Moc::testExtraData() { const QMetaObject *mobj = &PropertyTestClass::staticMetaObject; @@ -1166,7 +1306,7 @@ void tst_Moc::warnOnMultipleInheritance() QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + - QString(":43:1: warning: Class Bar inherits from two QObject subclasses QWindow and Foo. This is not supported!\n")); + QString(":18:1: warning: Class Bar inherits from two QObject subclasses QWindow and Foo. This is not supported!\n")); #else QSKIP("Only tested on linux/gcc"); #endif @@ -1189,11 +1329,7 @@ void tst_Moc::ignoreOptionClashes() if (!finished) qWarning("waitForFinished failed. QProcess error: %d", (int)proc.error()); QVERIFY(finished); - if (proc.exitCode() != 0) { - qDebug() << proc.readAllStandardError(); - } - QCOMPARE(proc.exitCode(), 0); - QCOMPARE(proc.readAllStandardError(), QByteArray()); + VERIFY_NO_ERRORS(proc); QByteArray mocOut = proc.readAllStandardOutput(); // If -pthread wasn't ignored, it was parsed as a prefix of "thread/", which breaks compilation. @@ -1207,7 +1343,7 @@ void tst_Moc::ignoreOptionClashes() proc.closeWriteChannel(); QVERIFY(proc.waitForFinished()); - QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString()); + VERIFY_NO_ERRORS(proc); #else QSKIP("Only tested on linux/gcc"); #endif @@ -1230,7 +1366,7 @@ void tst_Moc::forgottenQInterface() QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + - QString(":45:1: warning: Class Test implements the interface MyInterface but does not list it in Q_INTERFACES. qobject_cast to MyInterface will not work!\n")); + QString(":20:1: warning: Class Test implements the interface MyInterface but does not list it in Q_INTERFACES. qobject_cast to MyInterface will not work!\n")); #else QSKIP("Only tested on linux/gcc"); #endif @@ -1259,9 +1395,9 @@ void tst_Moc::winNewline() QVERIFY(f.open(QIODevice::ReadOnly)); // no QIODevice::Text! QByteArray data = f.readAll(); f.close(); - for (int i = 0; i < data.count(); ++i) { + for (int i = 0; i < data.size(); ++i) { if (data.at(i) == QLatin1Char('\r')) { - QVERIFY(i < data.count() - 1); + QVERIFY(i < data.size() - 1); ++i; QCOMPARE(data.at(i), '\n'); } else { @@ -1312,11 +1448,7 @@ void tst_Moc::frameworkSearchPath() if (!finished) qWarning("waitForFinished failed. QProcess error: %d", (int)proc.error()); QVERIFY(finished); - if (proc.exitCode() != 0) { - qDebug() << proc.readAllStandardError(); - } - QCOMPARE(proc.exitCode(), 0); - QCOMPARE(proc.readAllStandardError(), QByteArray()); + VERIFY_NO_ERRORS(proc); #else QSKIP("Only tested/relevant on unixy platforms"); #endif @@ -1348,11 +1480,9 @@ void tst_Moc::templateGtGt() QProcess proc; proc.start(m_moc, QStringList(m_sourceDirectory + QStringLiteral("/template-gtgt.h"))); QVERIFY(proc.waitForFinished()); - QCOMPARE(proc.exitCode(), 0); + VERIFY_NO_ERRORS(proc); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); - QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); - QVERIFY(mocWarning.isEmpty()); #else QSKIP("Only tested on unix/gcc"); #endif @@ -1369,8 +1499,7 @@ void tst_Moc::defineMacroViaCmdline() proc.start(m_moc, args); QVERIFY(proc.waitForFinished()); - QCOMPARE(proc.exitCode(), 0); - QCOMPARE(proc.readAllStandardError(), QByteArray()); + VERIFY_NO_ERRORS(proc); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else @@ -1389,8 +1518,7 @@ void tst_Moc::defineMacroViaForcedInclude() proc.start(m_moc, args); QVERIFY(proc.waitForFinished()); - QCOMPARE(proc.exitCode(), 0); - QCOMPARE(proc.readAllStandardError(), QByteArray()); + VERIFY_NO_ERRORS(proc); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else @@ -1409,8 +1537,7 @@ void tst_Moc::defineMacroViaForcedIncludeRelative() proc.start(m_moc, args); QVERIFY(proc.waitForFinished()); - QCOMPARE(proc.exitCode(), 0); - QCOMPARE(proc.readAllStandardError(), QByteArray()); + VERIFY_NO_ERRORS(proc); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else @@ -1455,8 +1582,7 @@ void tst_Moc::environmentIncludePaths() proc.setProcessEnvironment(env); proc.start(m_moc, args); QVERIFY(proc.waitForFinished()); - QCOMPARE(proc.exitCode(), 0); - QCOMPARE(proc.readAllStandardError(), QByteArray()); + VERIFY_NO_ERRORS(proc); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else @@ -1466,9 +1592,9 @@ void tst_Moc::environmentIncludePaths() // tst_Moc::specifyMetaTagsFromCmdline() // plugin_metadata.h contains a plugin which we register here. Since we're not building this -// application as a plugin, we need top copy some of the initializer code found in qplugin.h: -extern "C" QObject *qt_plugin_instance(); -extern "C" QPluginMetaData qt_plugin_query_metadata_v2(); +// application as a plugin, we need to copy some of the initializer code found in qplugin.h: +extern "C" Q_DECL_EXPORT QObject *qt_plugin_instance(); +extern "C" Q_DECL_EXPORT QPluginMetaData qt_plugin_query_metadata_v2(); class StaticPluginInstance{ public: StaticPluginInstance() { @@ -1479,7 +1605,8 @@ public: static StaticPluginInstance staticInstance; void tst_Moc::specifyMetaTagsFromCmdline() { - foreach (const QStaticPlugin &plugin, QPluginLoader::staticPlugins()) { + const auto staticPlugins = QPluginLoader::staticPlugins(); + for (const QStaticPlugin &plugin : staticPlugins) { const QString iid = plugin.metaData().value(QLatin1String("IID")).toString(); if (iid == QLatin1String("test.meta.tags")) { const QJsonArray metaTagsUriList = plugin.metaData().value("uri").toArray(); @@ -1497,41 +1624,43 @@ void tst_Moc::specifyMetaTagsFromCmdline() { void tst_Moc::invokable() { + const int fooIndex = 4; { const QMetaObject &mobj = InvokableBeforeReturnType::staticMetaObject; - QCOMPARE(mobj.methodCount(), 6); - QCOMPARE(mobj.method(5).methodSignature(), QByteArray("foo()")); + QCOMPARE(mobj.methodCount(), 5); + QCOMPARE(mobj.method(fooIndex).methodSignature(), QByteArray("foo()")); } { const QMetaObject &mobj = InvokableBeforeInline::staticMetaObject; - QCOMPARE(mobj.methodCount(), 7); - QCOMPARE(mobj.method(5).methodSignature(), QByteArray("foo()")); - QCOMPARE(mobj.method(6).methodSignature(), QByteArray("bar()")); + QCOMPARE(mobj.methodCount(), 6); + QCOMPARE(mobj.method(fooIndex).methodSignature(), QByteArray("foo()")); + QCOMPARE(mobj.method(fooIndex + 1).methodSignature(), QByteArray("bar()")); } } void tst_Moc::singleFunctionKeywordSignalAndSlot() { + const int mySignalIndex = 4; { const QMetaObject &mobj = SingleFunctionKeywordBeforeReturnType::staticMetaObject; - QCOMPARE(mobj.methodCount(), 7); - QCOMPARE(mobj.method(5).methodSignature(), QByteArray("mySignal()")); - QCOMPARE(mobj.method(6).methodSignature(), QByteArray("mySlot()")); + QCOMPARE(mobj.methodCount(), 6); + QCOMPARE(mobj.method(mySignalIndex).methodSignature(), QByteArray("mySignal()")); + QCOMPARE(mobj.method(mySignalIndex + 1).methodSignature(), QByteArray("mySlot()")); } { const QMetaObject &mobj = SingleFunctionKeywordBeforeInline::staticMetaObject; - QCOMPARE(mobj.methodCount(), 7); - QCOMPARE(mobj.method(5).methodSignature(), QByteArray("mySignal()")); - QCOMPARE(mobj.method(6).methodSignature(), QByteArray("mySlot()")); + QCOMPARE(mobj.methodCount(), 6); + QCOMPARE(mobj.method(mySignalIndex).methodSignature(), QByteArray("mySignal()")); + QCOMPARE(mobj.method(mySignalIndex + 1).methodSignature(), QByteArray("mySlot()")); } { const QMetaObject &mobj = SingleFunctionKeywordAfterInline::staticMetaObject; - QCOMPARE(mobj.methodCount(), 7); - QCOMPARE(mobj.method(5).methodSignature(), QByteArray("mySignal()")); - QCOMPARE(mobj.method(6).methodSignature(), QByteArray("mySlot()")); + QCOMPARE(mobj.methodCount(), 6); + QCOMPARE(mobj.method(mySignalIndex).methodSignature(), QByteArray("mySignal()")); + QCOMPARE(mobj.method(mySignalIndex + 1).methodSignature(), QByteArray("mySlot()")); } } @@ -1619,6 +1748,54 @@ void tst_Moc::qprivateproperties() QVERIFY(zapBindable.isBindable()); } + +class AnonymousPropertyTest1 : public QObject +{ + Q_OBJECT + QT_ANONYMOUS_PROPERTY(int READ foo WRITE setFoo) +public: + int foo() { return mFoo ; } + void setFoo(int value) { mFoo = value; } + +private: + int mFoo = 0; +}; + +class AnonymousPropertyTest2 : public QObject +{ + Q_OBJECT + QT_ANONYMOUS_PRIVATE_PROPERTY(d, int READ bar WRITE setBar) + + class MyDPointer { + public: + int bar() { return mBar ; } + void setBar(int value) { mBar = value; } + private: + int mBar = 0; + }; + +public: + AnonymousPropertyTest2(QObject *parent = nullptr) : QObject(parent), d (new MyDPointer) {} + MyDPointer *d_func() {return d.data();} + const MyDPointer *d_func() const {return d.data();} + +private: + QScopedPointer<MyDPointer> d; +}; + +void tst_Moc::anonymousProperties() +{ + AnonymousPropertyTest1 test1; + + test1.setProperty("", 17); + QCOMPARE(test1.property(""), QVariant::fromValue(17)); + + AnonymousPropertyTest2 test2; + + test2.setProperty("", 27); + QCOMPARE(test2.property(""), QVariant::fromValue(27)); +} + void tst_Moc::warnOnPropertyWithoutREAD() { #ifdef MOC_CROSS_COMPILED @@ -1634,7 +1811,7 @@ void tst_Moc::warnOnPropertyWithoutREAD() QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + - QString(":36:1: warning: Property declaration foo has neither an associated QProperty<> member, nor a READ accessor function nor an associated MEMBER variable. The property will be invalid.\n")); + QString(":11:1: warning: Property declaration foo has neither an associated QProperty<> member, nor a READ accessor function nor an associated MEMBER variable. The property will be invalid.\n")); #else QSKIP("Only tested on unix/gcc"); #endif @@ -1744,8 +1921,8 @@ void tst_Moc::warnOnVirtualSignal() QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); - QCOMPARE(mocWarning, header + QString(":38:1: warning: Signals cannot be declared virtual\n") + - header + QString(":40:1: warning: Signals cannot be declared virtual\n")); + QCOMPARE(mocWarning, header + QString(":13:1: warning: Signals cannot be declared virtual\n") + + header + QString(":15:1: warning: Signals cannot be declared virtual\n")); #else QSKIP("Only tested on unix/gcc"); #endif @@ -1863,11 +2040,10 @@ void tst_Moc::notifyError() const QString header = m_sourceDirectory + QStringLiteral("/error-on-wrong-notify.h"); proc.start(m_moc, QStringList(header)); QVERIFY(proc.waitForFinished()); - QCOMPARE(proc.exitCode(), 0); + VERIFY_NO_ERRORS(proc); QCOMPARE(proc.exitStatus(), QProcess::NormalExit); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); - QCOMPARE(proc.readAllStandardError(), QByteArray()); QStringList args; args << "-c" << "-x" << "c++" << "-I" << "." @@ -2085,7 +2261,7 @@ void tst_Moc::warnings_data() << QStringList() << 0 << QString() - << QString("standard input:0:1: note: No relevant classes found. No output generated."); + << QString("standard input: note: No relevant classes found. No output generated."); // passing "-nn" should suppress "no relevant classes" note QTest::newRow("-nn") @@ -2227,7 +2403,7 @@ void tst_Moc::warnings_data() << QStringList() << 1 << QString("IGNORE_ALL_STDOUT") - << QString(":-1:1: error: Unexpected character in macro argument list."); + << QString(": error: Unexpected character in macro argument list."); QTest::newRow("Missing header warning") << QByteArray("class X : public QObject { Q_OBJECT };") @@ -2274,6 +2450,13 @@ void tst_Moc::warnings_data() << QString() << QString("standard input:3:1: error: Parse error at \"decltype\""); + QTest::newRow("QTBUG-36367: report correct error location") + << "class X { \n Q_PROPERTY(Foo* foo NONSENSE foo) \n };"_ba + << QStringList() + << 1 + << QString() + << u"standard input:2:1: error: Parse error at \"NONSENSE\""_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 };") @@ -2354,23 +2537,29 @@ void tst_Moc::cxx11Enums_data() QTest::addColumn<QByteArray>("enumName"); QTest::addColumn<char>("prefix"); QTest::addColumn<bool>("isScoped"); + QTest::addColumn<bool>("isTyped"); const QMetaObject *meta1 = &CXX11Enums::staticMetaObject; const QMetaObject *meta2 = &CXX11Enums2::staticMetaObject; - - QTest::newRow("EnumClass") << meta1 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true; - QTest::newRow("EnumClass 2") << meta2 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true; - QTest::newRow("TypedEnum") << meta1 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false; - QTest::newRow("TypedEnum 2") << meta2 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false; - QTest::newRow("TypedEnumClass") << meta1 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true; - QTest::newRow("TypedEnumClass 2") << meta2 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true; - QTest::newRow("NormalEnum") << meta1 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false; - QTest::newRow("NormalEnum 2") << meta2 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false; - QTest::newRow("ClassFlags") << meta1 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true; - QTest::newRow("ClassFlags 2") << meta2 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true; - QTest::newRow("EnumStruct") << meta1 << QByteArray("EnumStruct") << QByteArray("EnumStruct") << 'G' << true; - QTest::newRow("TypedEnumStruct") << meta1 << QByteArray("TypedEnumStruct") << QByteArray("TypedEnumStruct") << 'H' << true; - QTest::newRow("StructFlags") << meta1 << QByteArray("StructFlags") << QByteArray("StructFlag") << 'I' << true; + const QMetaObject *meta3 = &CXX11Enums3::staticMetaObject; + + QTest::newRow("EnumClass") << meta1 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true << false; + QTest::newRow("EnumClass 2") << meta2 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true << false; + QTest::newRow("EnumClass 3") << meta3 << QByteArray("EnumClass") << QByteArray("EnumClass") << 'A' << true << false; + QTest::newRow("TypedEnum") << meta1 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false << true; + QTest::newRow("TypedEnum 2") << meta2 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false << true; + QTest::newRow("TypedEnum 3") << meta3 << QByteArray("TypedEnum") << QByteArray("TypedEnum") << 'B' << false << true; + QTest::newRow("TypedEnumClass") << meta1 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true << true; + QTest::newRow("TypedEnumClass 2") << meta2 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true << true; + QTest::newRow("TypedEnumClass 3") << meta3 << QByteArray("TypedEnumClass") << QByteArray("TypedEnumClass") << 'C' << true << true; + QTest::newRow("NormalEnum") << meta1 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false << false; + QTest::newRow("NormalEnum 2") << meta2 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false << false; + QTest::newRow("NormalEnum 3") << meta3 << QByteArray("NormalEnum") << QByteArray("NormalEnum") << 'D' << false << false; + QTest::newRow("ClassFlags") << meta1 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true << false; + QTest::newRow("ClassFlags 2") << meta2 << QByteArray("ClassFlags") << QByteArray("ClassFlag") << 'F' << true << false; + QTest::newRow("EnumStruct") << meta1 << QByteArray("EnumStruct") << QByteArray("EnumStruct") << 'G' << true << false; + QTest::newRow("TypedEnumStruct") << meta1 << QByteArray("TypedEnumStruct") << QByteArray("TypedEnumStruct") << 'H' << true << true; + QTest::newRow("StructFlags") << meta1 << QByteArray("StructFlags") << QByteArray("StructFlag") << 'I' << true << false; } void tst_Moc::cxx11Enums() @@ -2382,24 +2571,39 @@ void tst_Moc::cxx11Enums() QFETCH(QByteArray, enumName); QFETCH(char, prefix); QFETCH(bool, isScoped); + QFETCH(bool, isTyped); int idx = meta->indexOfEnumerator(typeName); QVERIFY(idx != -1); QCOMPARE(meta->indexOfEnumerator(enumName), idx); - QCOMPARE(meta->enumerator(idx).enclosingMetaObject(), meta); - QCOMPARE(meta->enumerator(idx).isValid(), true); - QCOMPARE(meta->enumerator(idx).keyCount(), 4); - QCOMPARE(meta->enumerator(idx).name(), typeName.constData()); - QCOMPARE(meta->enumerator(idx).enumName(), enumName.constData()); - bool isFlag = meta->enumerator(idx).isFlag(); + const QMetaEnum metaEnum = meta->enumerator(idx); + QCOMPARE(metaEnum.enclosingMetaObject(), meta); + QCOMPARE(metaEnum.isValid(), true); + QCOMPARE(metaEnum.keyCount(), 4); + QCOMPARE(metaEnum.name(), typeName.constData()); + QCOMPARE(metaEnum.enumName(), enumName.constData()); + + const QMetaType metaType = metaEnum.metaType(); + const bool isUnsigned = metaType.flags() & QMetaType::IsUnsignedEnumeration; + if (isTyped) { + QCOMPARE(size_t(metaType.sizeOf()), sizeof(char)); + QCOMPARE(isUnsigned, !std::is_signed_v<char>); + } else if (isScoped) { + QCOMPARE(size_t(metaType.sizeOf()), sizeof(int)); + QCOMPARE(isUnsigned, !std::is_signed_v<int>); + } else { + // underlying type is implementation defined + } + + bool isFlag = metaEnum.isFlag(); for (int i = 0; i < 4; i++) { QByteArray v = prefix + QByteArray::number(i); const int value = isFlag ? (1 << i) : i; - QCOMPARE(meta->enumerator(idx).keyToValue(v), value); - QCOMPARE(meta->enumerator(idx).valueToKey(value), v.constData()); + QCOMPARE(metaEnum.keyToValue(v), value); + QCOMPARE(metaEnum.valueToKey(value), v.constData()); } - QCOMPARE(meta->enumerator(idx).isScoped(), isScoped); + QCOMPARE(metaEnum.isScoped(), isScoped); } void tst_Moc::cxx11TrailingReturn() @@ -2494,7 +2698,7 @@ void tst_Moc::memberProperties() if (!signal.isEmpty()) { - QCOMPARE(notifySpy.count(), 1); + QCOMPARE(notifySpy.size(), 1); if (prop.notifySignal().parameterNames().size() > 0) { QList<QVariant> arguments = notifySpy.takeFirst(); QCOMPARE(arguments.size(), 1); @@ -2504,7 +2708,7 @@ void tst_Moc::memberProperties() notifySpy.clear(); // a second write with the same value should not cause the signal to be emitted again QCOMPARE(prop.write(pObj, writeValue), expectedWriteResult); - QCOMPARE(notifySpy.count(), 0); + QCOMPARE(notifySpy.size(), 0); } } @@ -3584,10 +3788,9 @@ void tst_Moc::preprocessorOnly() QProcess proc; proc.start(m_moc, QStringList() << "-E" << m_sourceDirectory + QStringLiteral("/pp-dollar-signs.h")); QVERIFY(proc.waitForFinished()); - QCOMPARE(proc.exitCode(), 0); + VERIFY_NO_ERRORS(proc); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); - QCOMPARE(proc.readAllStandardError(), QByteArray()); QVERIFY(mocOut.contains("$$ = parser->createFoo()")); #else @@ -3738,7 +3941,7 @@ void tst_Moc::relatedMetaObjectsNameConflict() { typedef QList<const QMetaObject *> QMetaObjects; QFETCH(const QMetaObject*, dependingObject); - QFETCH(QMetaObjects, relatedMetaObjects); + QFETCH(const QMetaObjects, relatedMetaObjects); // load all specified metaobjects int a set QSet<const QMetaObject*> dependency; @@ -3749,7 +3952,7 @@ void tst_Moc::relatedMetaObjectsNameConflict() } // check if all required metaobjects are specified - foreach (const QMetaObject *mo, relatedMetaObjects) + for (const QMetaObject *mo : relatedMetaObjects) QVERIFY(dependency.contains(mo)); // check if no additional metaobjects ara specified @@ -3816,6 +4019,7 @@ class VeryLongStringData : public QObject #define repeat32768(V) repeat16384(V) repeat16384(V) #define repeat65534(V) repeat32768(V) repeat16384(V) repeat8192(V) repeat4096(V) repeat2048(V) repeat1024(V) repeat512(V) repeat256(V) repeat128(V) repeat64(V) repeat32(V) repeat16(V) repeat8(V) repeat4(V) repeat2(V) + Q_CLASSINFO("\1" "23\xff", "String with CRLF.\r\n") Q_CLASSINFO(repeat65534("n"), repeat65534("i")) Q_CLASSINFO(repeat65534("e"), repeat65534("r")) Q_CLASSINFO(repeat32768("o"), repeat32768("b")) @@ -3864,25 +4068,26 @@ void tst_Moc::unnamedNamespaceObjectsAndGadgets() void tst_Moc::veryLongStringData() { const QMetaObject *mobj = &VeryLongStringData::staticMetaObject; - QCOMPARE(mobj->classInfoCount(), 4); - - QCOMPARE(mobj->classInfo(0).name()[0], 'n'); - QCOMPARE(mobj->classInfo(0).value()[0], 'i'); - QCOMPARE(mobj->classInfo(1).name()[0], 'e'); - QCOMPARE(mobj->classInfo(1).value()[0], 'r'); - QCOMPARE(mobj->classInfo(2).name()[0], 'o'); - QCOMPARE(mobj->classInfo(2).value()[0], 'b'); - QCOMPARE(mobj->classInfo(3).name()[0], ':'); - QCOMPARE(mobj->classInfo(3).value()[0], ')'); - - QCOMPARE(strlen(mobj->classInfo(0).name()), static_cast<size_t>(65534)); - QCOMPARE(strlen(mobj->classInfo(0).value()), static_cast<size_t>(65534)); - QCOMPARE(strlen(mobj->classInfo(1).name()), static_cast<size_t>(65534)); - QCOMPARE(strlen(mobj->classInfo(1).value()), static_cast<size_t>(65534)); - QCOMPARE(strlen(mobj->classInfo(2).name()), static_cast<size_t>(32768)); - QCOMPARE(strlen(mobj->classInfo(2).value()), static_cast<size_t>(32768)); - QCOMPARE(strlen(mobj->classInfo(3).name()), static_cast<size_t>(1)); - QCOMPARE(strlen(mobj->classInfo(3).value()), static_cast<size_t>(1)); + int startAt = 1; // some other classinfo added to the beginning + QCOMPARE(mobj->classInfoCount(), startAt + 4); + + QCOMPARE(mobj->classInfo(startAt + 0).name()[0], 'n'); + QCOMPARE(mobj->classInfo(startAt + 0).value()[0], 'i'); + QCOMPARE(mobj->classInfo(startAt + 1).name()[0], 'e'); + QCOMPARE(mobj->classInfo(startAt + 1).value()[0], 'r'); + QCOMPARE(mobj->classInfo(startAt + 2).name()[0], 'o'); + QCOMPARE(mobj->classInfo(startAt + 2).value()[0], 'b'); + QCOMPARE(mobj->classInfo(startAt + 3).name()[0], ':'); + QCOMPARE(mobj->classInfo(startAt + 3).value()[0], ')'); + + QCOMPARE(strlen(mobj->classInfo(startAt + 0).name()), static_cast<size_t>(65534)); + QCOMPARE(strlen(mobj->classInfo(startAt + 0).value()), static_cast<size_t>(65534)); + QCOMPARE(strlen(mobj->classInfo(startAt + 1).name()), static_cast<size_t>(65534)); + QCOMPARE(strlen(mobj->classInfo(startAt + 1).value()), static_cast<size_t>(65534)); + QCOMPARE(strlen(mobj->classInfo(startAt + 2).name()), static_cast<size_t>(32768)); + QCOMPARE(strlen(mobj->classInfo(startAt + 2).value()), static_cast<size_t>(32768)); + QCOMPARE(strlen(mobj->classInfo(startAt + 3).name()), static_cast<size_t>(1)); + QCOMPARE(strlen(mobj->classInfo(startAt + 3).value()), static_cast<size_t>(1)); } void tst_Moc::gadgetHierarchy() @@ -3918,10 +4123,12 @@ void tst_Moc::optionsFileError() } static void checkEnum(const QMetaEnum &enumerator, const QByteArray &name, - const QList<QPair<QByteArray, int>> &keys) + const QList<QPair<QByteArray, int>> &keys, + const QMetaType underlyingType = QMetaType::fromType<int>()) { QCOMPARE(name, QByteArray{enumerator.name()}); QCOMPARE(keys.size(), enumerator.keyCount()); + QCOMPARE(underlyingType, enumerator.metaType().underlyingType()); for (int i = 0; i < enumerator.keyCount(); ++i) { QCOMPARE(keys[i].first, QByteArray{enumerator.key(i)}); QCOMPARE(keys[i].second, enumerator.value(i)); @@ -3936,23 +4143,57 @@ public: FooNamespace::Enum1 prop() { return FooNamespace::Enum1::Key2; } }; +void tst_Moc::testNestedQNamespace() +{ + QCOMPARE(TestSameEnumNamespace::staticMetaObject.enumeratorCount(), 1); + checkEnum(TestSameEnumNamespace::staticMetaObject.enumerator(0), "TestSameEnumNamespace", + {{"Key1", 1}, {"Key2", 2}}); + QMetaEnum meta1 = QMetaEnum::fromType<TestSameEnumNamespace::TestSameEnumNamespace>(); + QVERIFY(meta1.isValid()); + QCOMPARE(meta1.name(), "TestSameEnumNamespace"); + QCOMPARE(meta1.enclosingMetaObject(), &TestSameEnumNamespace::staticMetaObject); + QCOMPARE(meta1.keyCount(), 2); + + // QTBUG-112996 + QCOMPARE(TestNestedSameEnumNamespace::a::staticMetaObject.enumeratorCount(), 1); + checkEnum(TestNestedSameEnumNamespace::a::staticMetaObject.enumerator(0), "a", + {{"Key11", 11}, {"Key12", 12}}); + QMetaEnum meta2 = QMetaEnum::fromType<TestNestedSameEnumNamespace::a::a>(); + QVERIFY(meta2.isValid()); + QCOMPARE(meta2.name(), "a"); + QCOMPARE(meta2.enclosingMetaObject(), &TestNestedSameEnumNamespace::a::staticMetaObject); + QCOMPARE(meta2.keyCount(), 2); +} + void tst_Moc::testQNamespace() { - QCOMPARE(TestQNamespace::staticMetaObject.enumeratorCount(), 4); + QCOMPARE(TestQNamespace::staticMetaObject.enumeratorCount(), 5); checkEnum(TestQNamespace::staticMetaObject.enumerator(0), "TestEnum1", {{"Key1", 11}, {"Key2", 12}}); checkEnum(TestQNamespace::staticMetaObject.enumerator(1), "TestEnum2", {{"Key1", 17}, {"Key2", 18}}); - checkEnum(TestQNamespace::staticMetaObject.enumerator(2), "TestFlag1", + checkEnum(TestQNamespace::staticMetaObject.enumerator(2), "TestEnum3", + {{"Key1", 23}, {"Key2", 24}}, QMetaType::fromType<qint8>()); + checkEnum(TestQNamespace::staticMetaObject.enumerator(3), "TestFlag1", {{"None", 0}, {"Flag1", 1}, {"Flag2", 2}, {"Any", 1 | 2}}); - checkEnum(TestQNamespace::staticMetaObject.enumerator(3), "TestFlag2", + checkEnum(TestQNamespace::staticMetaObject.enumerator(4), "TestFlag2", {{"None", 0}, {"Flag1", 4}, {"Flag2", 8}, {"Any", 4 | 8}}); - QCOMPARE(TestQNamespace::TestGadget::staticMetaObject.enumeratorCount(), 2); + QCOMPARE(TestQNamespace::TestGadget::staticMetaObject.enumeratorCount(), 3); checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(0), "TestGEnum1", {{"Key1", 13}, {"Key2", 14}}); checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(1), "TestGEnum2", {{"Key1", 23}, {"Key2", 24}}); + checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(2), "TestGEnum3", + {{"Key1", 33}, {"Key2", 34}}, QMetaType::fromType<qint16>()); + + QCOMPARE(TestQNamespace::TestGadgetExport::staticMetaObject.enumeratorCount(), 3); + checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(0), "TestGeEnum1", + {{"Key1", 20}, {"Key2", 21}}); + checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(1), "TestGeEnum2", + {{"Key1", 23}, {"Key2", 24}}); + checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(2), "TestGeEnum3", + {{"Key1", 26}, {"Key2", 27}}, QMetaType::fromType<quint16>()); QMetaEnum meta = QMetaEnum::fromType<TestQNamespace::TestEnum1>(); QVERIFY(meta.isValid()); @@ -3995,7 +4236,9 @@ void tst_Moc::cxx17Namespaces() void tst_Moc::cxxAttributes() { +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED auto so = CppAttribute::staticMetaObject; +QT_WARNING_POP QCOMPARE(so.className(), "CppAttribute"); QCOMPARE(so.enumeratorCount(), 0); QVERIFY(so.indexOfSignal("deprecatedSignal") != 1); @@ -4030,9 +4273,12 @@ void tst_Moc::cxxAttributes() void tst_Moc::mocJsonOutput() { - const auto readFile = [](const QString &fileName) { + const auto readFile = [](const QString &fileName) -> std::optional<QJsonDocument> { QFile f(fileName); - f.open(QIODevice::ReadOnly); + if (!f.open(QIODevice::ReadOnly)) { + qWarning() << "Could not open file" << fileName << f.errorString(); + return std::nullopt; + } return QJsonDocument::fromJson(f.readAll()); }; @@ -4048,8 +4294,10 @@ void tst_Moc::mocJsonOutput() QVERIFY2(QFile::exists(actualFile), qPrintable(actualFile)); QVERIFY2(QFile::exists(expectedFile), qPrintable(expectedFile)); - QJsonDocument actualOutput = readFile(actualFile); - QJsonDocument expectedOutput = readFile(expectedFile); + std::optional<QJsonDocument> actualOutput = readFile(actualFile); + QVERIFY(actualOutput); + std::optional<QJsonDocument> expectedOutput = readFile(expectedFile); + QVERIFY(expectedOutput); const auto showPotentialDiff = [](const QJsonDocument &actual, const QJsonDocument &expected) -> QByteArray { #if defined(Q_OS_UNIX) @@ -4078,11 +4326,13 @@ void tst_Moc::mocJsonOutput() return "Error waiting for diff process to finish."; return diffProc.readAllStandardOutput(); #else + Q_UNUSED(actual); + Q_UNUSED(expected); return "Cannot launch diff. Please check allmocs.json and allmocs_baseline.json on disk."; #endif }; - QVERIFY2(actualOutput == expectedOutput, showPotentialDiff(actualOutput, expectedOutput).constData()); + QVERIFY2(*actualOutput == *expectedOutput, showPotentialDiff(*actualOutput, *expectedOutput).constData()); } void TestFwdProperties::setProp1(const FwdClass1 &v) @@ -4178,7 +4428,7 @@ void tst_Moc::qpropertyMembers() instance.publicProperty.setValue(100); QCOMPARE(prop.read(&instance).toInt(), 100); - QCOMPARE(publicPropertySpy.count(), 1); + QCOMPARE(publicPropertySpy.size(), 1); QCOMPARE(prop.metaType(), QMetaType(QMetaType::Int)); @@ -4319,6 +4569,140 @@ void tst_Moc::privateQPropertyShim() QCOMPARE(testObject.priv.testProperty2.value(), 42); } + +class BindableOnly : public QObject +{ + Q_OBJECT + Q_PROPERTY(int score BINDABLE scoreBindable READ default WRITE default) +public: + BindableOnly(QObject *parent = nullptr) + : QObject(parent) + , m_score(4) + {} + QBindable<int> scoreBindable() { return QBindable<int>(&m_score); } +private: + QProperty<int> m_score; +}; + +class BindableAndNotifyable : public QObject +{ + Q_OBJECT + Q_PROPERTY(int score BINDABLE scoreBindable NOTIFY scoreChanged READ default WRITE default) +public: + BindableAndNotifyable(QObject *parent = nullptr) + : QObject(parent) + , m_score(4) + {} + QBindable<int> scoreBindable() { return QBindable<int>(&m_score); } +signals: + void scoreChanged(); +private: + QProperty<int> m_score; +}; + +void tst_Moc::readWriteThroughBindable() +{ + { + BindableOnly o; + QCOMPARE(o.scoreBindable().value(), 4); + QCOMPARE(o.property("score").toInt(), 4); + o.scoreBindable().setValue(5); + QCOMPARE(o.scoreBindable().value(), 5); + QCOMPARE(o.property("score").toInt(), 5); + const QMetaObject *mo = o.metaObject(); + const int i = mo->indexOfProperty("score"); + QVERIFY(i > 0); + QMetaProperty p = mo->property(i); + QCOMPARE(p.name(), "score"); + QVERIFY(p.isValid()); + QVERIFY(p.isWritable()); + QCOMPARE(p.read(&o), 5); + QVERIFY(o.setProperty("score", 6)); + QCOMPARE(o.property("score").toInt(), 6); + QVERIFY(p.write(&o, 7)); + QCOMPARE(p.read(&o), 7); + } + { + BindableAndNotifyable o; + QCOMPARE(o.scoreBindable().value(), 4); + QCOMPARE(o.property("score").toInt(), 4); + o.scoreBindable().setValue(5); + QCOMPARE(o.scoreBindable().value(), 5); + QCOMPARE(o.property("score").toInt(), 5); + const QMetaObject *mo = o.metaObject(); + const int i = mo->indexOfProperty("score"); + QVERIFY(i > 0); + QMetaProperty p = mo->property(i); + QCOMPARE(p.name(), "score"); + QVERIFY(p.isValid()); + QVERIFY(p.isWritable()); + QCOMPARE(p.read(&o), 5); + QVERIFY(o.setProperty("score", 6)); + QCOMPARE(o.property("score").toInt(), 6); + QVERIFY(p.write(&o, 7)); + QCOMPARE(p.read(&o), 7); + } +} + +struct WithInvokableCtor +{ + Q_GADGET + Q_PROPERTY(int thing MEMBER m_thing CONSTANT FINAL) +public: + WithInvokableCtor() = default; + Q_INVOKABLE WithInvokableCtor(int theThing) : m_thing(theThing) {} + + int m_thing = 10; +}; + +void tst_Moc::invokableCtors() +{ + const QMetaType metaType = QMetaType::fromType<WithInvokableCtor>(); + Q_ASSERT(metaType.sizeOf() > 0); + const QMetaObject *metaObject = metaType.metaObject(); + QVERIFY(metaObject); + + QCOMPARE(metaObject->constructorCount(), 1); + WithInvokableCtor *result = nullptr; + const auto guard = qScopeGuard([&]() { delete result; }); + int argument = 17; + void *a[] = { &result, &argument }; + metaObject->static_metacall(QMetaObject::CreateInstance, 0, a); + QVERIFY(result); + QCOMPARE(result->m_thing, 17); + + // Call dtor so that we're left with "uninitialized" memory. + WithInvokableCtor result2; + result2.~WithInvokableCtor(); + + void *b[] = { &result2, &argument }; + metaObject->static_metacall(QMetaObject::ConstructInPlace, 0, b); + QCOMPARE(result2.m_thing, 17); +} + +void tst_Moc::virtualInlineTaggedSlot() +{ + auto mo = TagTest::staticMetaObject; + auto idx = mo.indexOfMethod("pamOpen(int)"); + auto method = mo.method(idx); + QVERIFY(method.isValid()); // fails! + QCOMPARE(method.tag(), "Q_NOREPLY"); + idx = mo.indexOfMethod("test()"); + method = mo.method(idx); + QVERIFY(method.isValid()); + QCOMPARE(method.tag(), "Q_NOREPLY"); + QCOMPARE(method.returnMetaType(), QMetaType::fromType<int>()); +} + +void tst_Moc::tokenStartingWithNumber() +{ + auto *mo = &TokenStartingWithNumber::staticMetaObject; + int index = mo->indexOfEnumerator("FooItems"); + QMetaEnum metaEnum = mo->enumerator(index); + QVERIFY(metaEnum.isValid()); + QCOMPARE(metaEnum.keyCount(), 3); +} + QTEST_MAIN(tst_Moc) // the generated code must compile with QT_NO_KEYWORDS |