diff options
Diffstat (limited to 'tests/auto/tools/moc/tst_moc.cpp')
-rw-r--r-- | tests/auto/tools/moc/tst_moc.cpp | 265 |
1 files changed, 201 insertions, 64 deletions
diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index 6a2962d609..d24dfa11f7 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -1,15 +1,16 @@ // 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 WITH Qt-GPL-exception-1.0 +// 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> @@ -62,6 +63,10 @@ #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") @@ -75,12 +80,48 @@ const char *string_hash_hash = STRING_HASH_HASH("baz"); 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 { @@ -188,6 +229,27 @@ namespace TestQNamespace { Q_FLAG_NS(TestFlag2) } +namespace TestSameEnumNamespace { + Q_NAMESPACE + + 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) @@ -218,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 {}; @@ -602,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 @@ -611,6 +695,7 @@ public: Q_ENUM(TestEnum) }; +#endif class PropertyUseClass : public QObject { @@ -774,6 +859,7 @@ private slots: void optionsFileError_data(); void optionsFileError(); void testQNamespace(); + void testNestedQNamespace(); void cxx17Namespaces(); void cxxAttributes(); void mocJsonOutput(); @@ -785,6 +871,8 @@ private slots: void privateQPropertyShim(); void readWriteThroughBindable(); void invokableCtors(); + void virtualInlineTaggedSlot(); + void tokenStartingWithNumber(); signals: void sigWithUnsignedArg(unsigned foo); @@ -822,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); @@ -836,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()); @@ -874,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" << "." @@ -888,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 @@ -946,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" << ".." @@ -960,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 @@ -1238,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. @@ -1256,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 @@ -1361,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 @@ -1397,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 @@ -1418,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 @@ -1438,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 @@ -1458,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 @@ -1504,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 @@ -1528,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(); @@ -1546,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()")); } } @@ -1960,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" << "." @@ -2182,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") @@ -2324,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 };") @@ -2371,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 };") @@ -2501,10 +2587,10 @@ void tst_Moc::cxx11Enums() const QMetaType metaType = metaEnum.metaType(); const bool isUnsigned = metaType.flags() & QMetaType::IsUnsignedEnumeration; if (isTyped) { - QCOMPARE(metaType.sizeOf(), sizeof(char)); + QCOMPARE(size_t(metaType.sizeOf()), sizeof(char)); QCOMPARE(isUnsigned, !std::is_signed_v<char>); } else if (isScoped) { - QCOMPARE(metaType.sizeOf(), sizeof(int)); + QCOMPARE(size_t(metaType.sizeOf()), sizeof(int)); QCOMPARE(isUnsigned, !std::is_signed_v<int>); } else { // underlying type is implementation defined @@ -3702,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 @@ -3856,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; @@ -3867,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 @@ -4058,6 +4143,28 @@ 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(), 5); @@ -4166,9 +4273,12 @@ QT_WARNING_POP 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()); }; @@ -4184,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) @@ -4214,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) @@ -4566,6 +4680,29 @@ void tst_Moc::invokableCtors() 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 |