// Copyright (C) 2020 The Qt Company Ltd. // Copyright (C) 2020 Olivier Goffart // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include #include #include #include #include #include #include #include #include "using-namespaces.h" #include "assign-namespace.h" #include "no-keywords.h" #include "single_function_keyword.h" #include "backslash-newlines.h" #include "slots-with-void-template.h" #include "qinvokable.h" // msvc and friends crap out on it #if !defined(Q_CC_GNU) || defined(Q_OS_WIN) #define SKIP_NEWLINE_TEST #endif #if !defined(SKIP_NEWLINE_TEST) #include "os9-newlines.h" // msvc and friends crap out on this file too, // it seems to contain Mac 9 EOLs, and not windows EOLs. #include "win-newlines.h" #endif #include "escapes-in-string-literals.h" #include "cstyle-enums.h" #if defined(PARSE_BOOST) #include "parse-boost.h" #endif #include "cxx11-enums.h" #include "cxx11-final-classes.h" #include "cxx11-explicit-override-control.h" #include "cxx11-trailing-return.h" #include "parse-defines.h" #include "related-metaobjects-in-namespaces.h" #include "related-metaobjects-in-gadget.h" #include "related-metaobjects-name-conflict.h" #include "non-gadget-parent-class.h" #include "grand-parent-gadget-class.h" #include "namespace.h" #include "cxx17-namespaces.h" #include "cxx-attributes.h" #include "moc_include.h" #include "pointery_to_incomplete.h" #include "fwdclass1.h" #include "fwdclass2.h" #include "fwdclass3.h" #include "signal-with-default-arg.h" #include "qmlmacro.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 {} #endif 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 { Q_GADGET Q_CLASSINFO("key", "value") public: enum class TestGEnum1 { Key1 = 11, Key2 }; Q_ENUM(TestGEnum1) enum class TestGEnum2 { Key1 = 17, Key2 }; Q_ENUM(TestGEnum2) }; } namespace TestQNamespace { Q_NAMESPACE enum class TestEnum1 { Key1 = 11, Key2 }; Q_ENUM_NS(TestEnum1) enum class TestEnum2 { Key1 = 17, Key2 }; Q_ENUM_NS(TestEnum2) // try to dizzy moc by adding a struct in between struct TestGadget { Q_GADGET public: enum class TestGEnum1 { Key1 = 13, Key2 }; enum class TestGEnum2 { Key1 = 23, Key2 }; Q_ENUM(TestGEnum1) Q_ENUM(TestGEnum2) }; 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 class TestFlag1 { None = 0, Flag1 = 1, Flag2 = 2, Any = Flag1 | Flag2 }; Q_FLAG_NS(TestFlag1) enum class TestFlag2 { None = 0, Flag1 = 4, Flag2 = 8, Any = Flag1 | Flag2 }; Q_FLAG_NS(TestFlag2) } namespace TestExportNamespace { Q_NAMESPACE_EXPORT(TESTEXPORTMACRO) enum class MyEnum { Key1, Key2 }; Q_ENUM_NS(MyEnum) } QT_USE_NAMESPACE template struct QTBUG_31218 {}; struct QTBUG_31218_Derived : QTBUG_31218<-1<0> {}; #if defined(Q_MOC_RUN) class QTBUG_45790 : Bug() { }; #endif class CreatableGadget { Q_GADGET public: Q_INVOKABLE CreatableGadget() { CreatableGadget::qt_static_metacall((QObject*)this, QMetaObject::ReadProperty, -1, nullptr); } }; CreatableGadget creatableGadget; // Force the compiler to use the constructor struct MyStruct {}; struct MyStruct2 {}; struct SuperClass {}; // Try to avoid inserting for instance a comment with a quote between the following line and the Q_OBJECT // That will make the test give a false positive. const char* test_multiple_number_of_escapes = "\\\""; namespace MyNamespace { class TestSuperClass : public QObject { Q_OBJECT public: inline TestSuperClass() {} }; } namespace String { typedef QString Type; } namespace Int { typedef int Type; } typedef struct { int doNotConfuseMoc; } OldStyleCStruct; namespace { class GadgetInUnnamedNS { Q_GADGET Q_PROPERTY(int x READ x WRITE setX) Q_PROPERTY(int y READ y WRITE setY) public: explicit GadgetInUnnamedNS(int x, int y) : m_x(x), m_y(y) {} int x() const { return m_x; } int y() const { return m_y; } void setX(int x) { m_x = x; } void setY(int y) { m_y = y; } private: int m_x, m_y; }; class ObjectInUnnamedNS : public QObject { Q_OBJECT public: explicit ObjectInUnnamedNS(QObject *parent = nullptr) : QObject(parent) {} }; } class Sender : public QObject { Q_OBJECT public: void sendValue(const String::Type& value) { emit send(value); } void sendValue(const Int::Type& value) { emit send(value); } bool operator< ( const Sender & ) const { /* QTBUG-36834 */ return true;} signals: void send(const String::Type&); void send(const Int::Type&); }; class Receiver : public QObject { Q_OBJECT public: Receiver() : stringCallCount(0), intCallCount(0) {} int stringCallCount; int intCallCount; public slots: void receive(const String::Type&) { stringCallCount++; } void receive(const Int::Type&) { intCallCount++; } }; #define MACRO_WITH_POSSIBLE_COMPILER_SPECIFIC_ATTRIBUTES #define DONT_CONFUSE_MOC(klass) klass #define DONT_CONFUSE_MOC_EVEN_MORE(klass, dummy, dummy2) klass Q_DECLARE_METATYPE(MyStruct) Q_DECLARE_METATYPE(MyStruct*) namespace myNS { struct Points { Points() : p1(0xBEEF), p2(0xBABE) { } int p1, p2; }; } Q_DECLARE_METATYPE(myNS::Points) class TestClassinfoWithEscapes: public QObject { Q_OBJECT Q_CLASSINFO("escaped", "\"bar\"") Q_CLASSINFO("\"escaped\"", "foo") 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) { } }; #define CLASSINFO_VAARGS(...) Q_CLASSINFO("classinfo_va_args", #__VA_ARGS__) class TestClassinfoFromVaArgs : public QObject { Q_OBJECT CLASSINFO_VAARGS(a, b, c, d) }; #undef CLASSINFO_VAARGS struct ForwardDeclaredStruct; struct StructQObject : public QObject { Q_OBJECT public: void foo(struct ForwardDeclaredStruct *); }; QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wunused-variable") void StructQObject::foo(struct ForwardDeclaredStruct *) { struct Inner { bool field; }; Q_DECL_UNUSED_MEMBER struct Inner unusedVariable; } QT_WARNING_POP 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) { Q_OBJECT Q_CLASSINFO("help", QT_TR_NOOP("Opening this will let you configure something")) Q_PROPERTY(short int shortIntProperty READ shortIntProperty) Q_PROPERTY(unsigned short int unsignedShortIntProperty READ unsignedShortIntProperty) Q_PROPERTY(signed short int signedShortIntProperty READ signedShortIntProperty) Q_PROPERTY(long int longIntProperty READ longIntProperty) Q_PROPERTY(unsigned long int unsignedLongIntProperty READ unsignedLongIntProperty) Q_PROPERTY(signed long int signedLongIntProperty READ signedLongIntProperty) Q_PROPERTY(long double longDoubleProperty READ longDoubleProperty) Q_PROPERTY(myNS::Points points READ points WRITE setPoints) Q_CLASSINFO("Multi" "line", "" "This is a " "multiline Q_CLASSINFO" "") // a really really long string that we have to cut into pieces in the generated stringdata // table, otherwise msvc craps out Q_CLASSINFO("D-Bus Introspection", "" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \" name=\"com.trolltech.QtDBus.QtTypeName.In0\" />\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "") public: inline TestClass() {} private slots: inline void dummy1() MACRO_WITH_POSSIBLE_COMPILER_SPECIFIC_ATTRIBUTES {} inline void dummy2() MACRO_WITH_POSSIBLE_COMPILER_SPECIFIC_ATTRIBUTES const {} inline void dummy3() const MACRO_WITH_POSSIBLE_COMPILER_SPECIFIC_ATTRIBUTES {} void slotWithULongLong(unsigned long long) {} void slotWithULongLongP(unsigned long long*) {} void slotWithULong(unsigned long) {} void slotWithLongLong(long long) {} void slotWithLong(long) {} void slotWithColonColonType(::Int::Type) {} TestClass &slotWithReferenceReturnType() { return *this; } #if (0 && 1) || 1 void expressionEvaluationShortcut1() {} #endif #if (1 || 0) && 0 #else void expressionEvaluationShortcut2() {} #endif public slots: void slotWithArray(const double[3]) {} void slotWithNamedArray(const double namedArray[3]) { Q_UNUSED(namedArray); } void slotWithMultiArray(const double[3][4]) {} short int shortIntProperty() { return 0; } unsigned short int unsignedShortIntProperty() { return 0; } signed short int signedShortIntProperty() { return 0; } long int longIntProperty() { return 0; } unsigned long int unsignedLongIntProperty() { return 0; } signed long int signedLongIntProperty() { return 0; } long double longDoubleProperty() { return 0.0; } myNS::Points points() { return m_points; } void setPoints(myNS::Points points) { m_points = points; } signals: void signalWithArray(const double[3]); void signalWithNamedArray(const double namedArray[3]); void signalWithIterator(QList::iterator); void signalWithListPointer(QList*); //QTBUG-31002 private slots: // for tst_Moc::preprocessorConditionals #if 0 void invalidSlot() {} #else void slotInElse() {} #endif #if 1 void slotInIf() {} #else void invalidSlot() {} #endif #if 0 void invalidSlot() {} #elif 0 #else void slotInLastElse() {} #endif #if 0 void invalidSlot() {} #elif 1 void slotInElif() {} #else void invalidSlot() {} #endif friend class Receiver; // task #85783 signals: friend class Sender; // task #85783 #define MACRO_DEFINED #if !(defined MACRO_UNDEF || defined MACRO_DEFINED) || 1 void signalInIf1(); #else void doNotExist(); #endif #if !(!defined MACRO_UNDEF || !defined MACRO_DEFINED) && 1 void doNotExist(); #else void signalInIf2(); #endif #if !(!defined (MACRO_DEFINED) || !defined (MACRO_UNDEF)) && 1 void doNotExist(); #else void signalInIf3(); #endif # //QTBUG-22717 # /* */ # # \ // public slots: void const slotWithSillyConst() {} void slotTakingCRefViaTypedef(ObjectCRef o) { this->setObjectName(o.objectName()); } public: Q_INVOKABLE void const slotWithSillyConst2() {} Q_INVOKABLE QObject& myInvokableReturningRef() { return *this; } Q_INVOKABLE const QObject& myInvokableReturningConstRef() const { return *this; } // that one however should be fine public slots: void slotWithVoidStar(void *) {} private: myNS::Points m_points; #ifdef Q_MOC_RUN int xx = 11'11; // digit separator must not confuse moc (QTBUG-59351) int xx = 0b11'11; // digit separator in a binary literal must not confuse moc (QTBUG-75656) #endif private slots: inline virtual void blub1() {} virtual inline void blub2() {} }; 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 public: enum TestEnum { One, Two, Three }; Q_ENUM(TestEnum) }; #endif class PropertyUseClass : public QObject { Q_OBJECT Q_PROPERTY(PropertyTestClass::TestEnum foo READ foo) public: inline PropertyTestClass::TestEnum foo() const { return PropertyTestClass::One; } }; class EnumSourceClass : public QObject { Q_OBJECT public: enum TestEnum { Value = 37 }; Q_ENUM(TestEnum) }; class EnumUserClass : public QObject { Q_OBJECT public: Q_ENUMS(EnumSourceClass::TestEnum) }; class CtorTestClass : public QObject { Q_OBJECT public: Q_INVOKABLE CtorTestClass(QObject *parent = nullptr); CtorTestClass(int foo); inline Q_INVOKABLE CtorTestClass(const QString &str) { m_str = str; } QString m_str; protected: CtorTestClass(int foo, int bar, int baz); private: CtorTestClass(float, float) {} }; CtorTestClass::CtorTestClass(QObject *parent) : QObject(parent) {} CtorTestClass::CtorTestClass(int, int, int) {} class PrivatePropertyTest; class tst_Moc : public QObject { Q_OBJECT Q_PROPERTY(bool user1 READ user1 USER true ) Q_PROPERTY(bool user2 READ user2 USER false) Q_PROPERTY(QString member1 MEMBER sMember) Q_PROPERTY(QString member2 MEMBER sMember READ member2) Q_PROPERTY(QString member3 MEMBER sMember WRITE setMember3) Q_PROPERTY(QString member4 MEMBER sMember NOTIFY member4Changed) Q_PROPERTY(QString member5 MEMBER sMember NOTIFY member5Changed) Q_PROPERTY(QString member6 MEMBER sConst CONSTANT) Q_PROPERTY(QString sub1 MEMBER (sub.m_string)) Q_PROPERTY(QString sub2 READ (sub.string) WRITE (sub.setString)) public: inline tst_Moc() : sConst("const") {} private slots: void initTestCase(); void dontStripNamespaces(); void oldStyleCasts(); void warnOnExtraSignalSlotQualifiaction(); void uLongLong(); void inputFileNameWithDotsButNoExtension(); void userProperties(); void supportConstSignals(); void task87883(); void multilineComments(); void classinfoWithEscapes(); void classinfoFromVaArgs(); void trNoopInClassInfo(); void ppExpressionEvaluation(); void arrayArguments(); void preprocessorConditionals(); void blackslashNewlines(); void slotWithSillyConst(); void slotTakingCRefViaTypedef(); void testExtraData(); void testExtraDataForEnum(); void namespaceTypeProperty(); void slotsWithVoidTemplate(); void structQObject(); void namespacedFlags(); void warnOnMultipleInheritance(); void ignoreOptionClashes(); void forgottenQInterface(); void os9Newline(); void winNewline(); void escapesInStringLiterals(); void frameworkSearchPath(); void cstyleEnums(); void defineMacroViaCmdline(); void defineMacroViaForcedInclude(); void defineMacroViaForcedIncludeRelative(); void environmentIncludePaths_data(); void environmentIncludePaths(); void specifyMetaTagsFromCmdline(); void invokable(); void singleFunctionKeywordSignalAndSlot(); void templateGtGt(); void qprivateslots(); void qprivateproperties(); void warnOnPropertyWithoutREAD(); void constructors(); void typenameWithUnsigned(); void warnOnVirtualSignal(); void QTBUG5590_dummyProperty(); void QTBUG12260_defaultTemplate(); void notifyError(); void QTBUG17635_invokableAndProperty(); void revisions(); void warnings_data(); void warnings(); void privateClass(); void cxx11Enums_data(); void cxx11Enums(); void cxx11TrailingReturn(); void returnRefs(); void memberProperties_data(); void memberProperties(); void memberProperties2(); void privateSignalConnection(); void finalClasses_data(); void finalClasses(); void explicitOverrideControl_data(); void explicitOverrideControl(); void overloadedAddressOperator(); void autoPropertyMetaTypeRegistration(); void autoMethodArgumentMetaTypeRegistration(); void autoSignalSpyMetaTypeRegistration(); void parseDefines(); void preprocessorOnly(); void unterminatedFunctionMacro(); void QTBUG32933_relatedObjectsDontIncludeItself(); void writeEnumFromUnrelatedClass(); void relatedMetaObjectsWithinNamespaces(); void relatedMetaObjectsInGadget(); void relatedMetaObjectsNameConflict_data(); void relatedMetaObjectsNameConflict(); void strignLiteralsInMacroExtension(); void unnamedNamespaceObjectsAndGadgets(); void veryLongStringData(); void gadgetHierarchy(); void optionsFileError_data(); void optionsFileError(); void testQNamespace(); void cxx17Namespaces(); void cxxAttributes(); void mocJsonOutput(); void mocInclude(); void requiredProperties(); void qpropertyMembers(); void observerMetaCall(); void setQPRopertyBinding(); void privateQPropertyShim(); void readWriteThroughBindable(); void virtualInlineTaggedSlot(); signals: void sigWithUnsignedArg(unsigned foo); void sigWithSignedArg(signed foo); void sigWithConstSignedArg(const signed foo); void sigWithVolatileConstSignedArg(volatile const signed foo); void sigWithCustomType(const MyStruct); void constSignal1() const; void constSignal2(int arg) const; void member4Changed(); void member5Changed(const QString &newVal); void sigWithDefaultArg(int i = 12); private: bool user1() { return true; }; bool user2() { return false; }; template void revisions_T(); QString member2() const { return sMember; } void setMember3( const QString &sVal ) { sMember = sVal; } private: QString m_moc; QString m_sourceDirectory; QString qtIncludePath; class PrivateClass; QString sMember; const QString sConst; PrivatePropertyTest *pPPTest; struct { QString m_string; void setString(const QString &s) { m_string = s; } QString string() { return m_string; } } sub; }; void tst_Moc::initTestCase() { QString binpath = QLibraryInfo::path(QLibraryInfo::BinariesPath); QString qtpaths = QString("%1/qtpaths").arg(binpath); QString libexecPath = QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath); m_moc = QString("%1/moc").arg(libexecPath); const QString testHeader = QFINDTESTDATA("backslash-newlines.h"); QVERIFY(!testHeader.isEmpty()); m_sourceDirectory = QFileInfo(testHeader).absolutePath(); #if defined(Q_OS_UNIX) && QT_CONFIG(process) QProcess proc; proc.start(qtpaths, QStringList() << "-query" << "QT_INSTALL_HEADERS"); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QByteArray output = proc.readAllStandardOutput(); QVERIFY(!output.isEmpty()); QCOMPARE(proc.readAllStandardError(), QByteArray()); qtIncludePath = QString::fromLocal8Bit(output).trimmed(); QFileInfo fi(qtIncludePath); QVERIFY(fi.exists()); QVERIFY(fi.isDir()); #endif } void tst_Moc::dontStripNamespaces() { Sender sender; Receiver receiver; connect(&sender, SIGNAL(send(const String::Type &)), &receiver, SLOT(receive(const String::Type &))); connect(&sender, SIGNAL(send(const Int::Type &)), &receiver, SLOT(receive(const Int::Type &))); sender.sendValue(String::Type("Hello")); QCOMPARE(receiver.stringCallCount, 1); QCOMPARE(receiver.intCallCount, 0); sender.sendValue(Int::Type(42)); QCOMPARE(receiver.stringCallCount, 1); QCOMPARE(receiver.intCallCount, 1); } void tst_Moc::oldStyleCasts() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; proc.start(m_moc, QStringList(m_sourceDirectory + QStringLiteral("/oldstyle-casts.h"))); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QCOMPARE(proc.readAllStandardError(), QByteArray()); QStringList args; args << "-c" << "-x" << "c++" << "-Wold-style-cast" << "-I" << "." << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++1z" << "-"; proc.start("gcc", args); QVERIFY(proc.waitForStarted()); proc.write(mocOut); proc.closeWriteChannel(); QVERIFY(proc.waitForFinished()); QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString()); QCOMPARE(proc.exitCode(), 0); #else QSKIP("Only tested on linux/gcc"); #endif } void tst_Moc::warnOnExtraSignalSlotQualifiaction() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; const QString header = m_sourceDirectory + QStringLiteral("/extraqualification.h"); proc.start(m_moc, QStringList(header)); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + 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 } void tst_Moc::uLongLong() { TestClass tst; const QMetaObject *mobj = tst.metaObject(); int idx = mobj->indexOfSlot("slotWithULong(ulong)"); QVERIFY(idx != -1); idx = mobj->indexOfSlot("slotWithULongLong(unsigned long long)"); QVERIFY(idx != -1); idx = mobj->indexOfSlot("slotWithULongLong(qulonglong)"); QVERIFY(idx != -1); idx = mobj->indexOfSlot("slotWithULongLongP(qulonglong*)"); QVERIFY(idx != -1); idx = mobj->indexOfSlot("slotWithLong(long)"); QVERIFY(idx != -1); idx = mobj->indexOfSlot("slotWithLongLong(long long)"); QVERIFY(idx != -1); } void tst_Moc::inputFileNameWithDotsButNoExtension() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; proc.setWorkingDirectory(m_sourceDirectory + QStringLiteral("/task71021")); proc.start(m_moc, QStringList("../Header")); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QCOMPARE(proc.readAllStandardError(), QByteArray()); QStringList args; args << "-c" << "-x" << "c++" << "-I" << ".." << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++1z" << "-"; proc.start("gcc", args); QVERIFY(proc.waitForStarted()); proc.write(mocOut); proc.closeWriteChannel(); QVERIFY(proc.waitForFinished()); QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString()); QCOMPARE(proc.exitCode(), 0); #else QSKIP("Only tested on linux/gcc"); #endif } void tst_Moc::userProperties() { const QMetaObject *mobj = metaObject(); QMetaProperty property = mobj->property(mobj->indexOfProperty("user1")); QVERIFY(property.isValid()); QVERIFY(property.isUser()); property = mobj->property(mobj->indexOfProperty("user2")); QVERIFY(property.isValid()); QVERIFY(!property.isUser()); } void tst_Moc::supportConstSignals() { QSignalSpy spy1(this, SIGNAL(constSignal1())); QVERIFY(spy1.isEmpty()); emit constSignal1(); QCOMPARE(spy1.size(), 1); QSignalSpy spy2(this, SIGNAL(constSignal2(int))); QVERIFY(spy2.isEmpty()); emit constSignal2(42); QCOMPARE(spy2.size(), 1); QCOMPARE(spy2.at(0).at(0).toInt(), 42); } #include "task87883.h" void tst_Moc::task87883() { QVERIFY(Task87883::staticMetaObject.className()); } #include "c-comments.h" void tst_Moc::multilineComments() { QVERIFY(IfdefedClass::staticMetaObject.className()); } void tst_Moc::classinfoWithEscapes() { const QMetaObject *mobj = &TestClassinfoWithEscapes::staticMetaObject; QCOMPARE(mobj->methodCount() - mobj->methodOffset(), 1); 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)")); } void tst_Moc::classinfoFromVaArgs() { const QMetaObject *mobj = &TestClassinfoFromVaArgs::staticMetaObject; QCOMPARE(mobj->classInfoCount(), 1); QCOMPARE(mobj->classInfo(0).name(), "classinfo_va_args"); QCOMPARE(mobj->classInfo(0).value(), "a,b,c,d"); } void tst_Moc::trNoopInClassInfo() { TestClass t; const QMetaObject *mobj = t.metaObject(); QVERIFY(mobj); QCOMPARE(mobj->classInfoCount(), 3); QCOMPARE(mobj->indexOfClassInfo("help"), 0); QCOMPARE(QString(mobj->classInfo(0).value()), QString("Opening this will let you configure something")); } void tst_Moc::ppExpressionEvaluation() { TestClass tst; const QMetaObject *mobj = tst.metaObject(); int idx = mobj->indexOfSlot("expressionEvaluationShortcut1()"); QVERIFY(idx != -1); idx = mobj->indexOfSlot("expressionEvaluationShortcut2()"); QVERIFY(idx != -1); } void tst_Moc::arrayArguments() { TestClass tst; const QMetaObject *mobj = tst.metaObject(); QVERIFY(mobj->indexOfSlot("slotWithArray(const double[3])") != -1); QVERIFY(mobj->indexOfSlot("slotWithNamedArray(const double[3])") != -1); QVERIFY(mobj->indexOfSlot("slotWithMultiArray(const double[3][4])") != -1); QVERIFY(mobj->indexOfSignal("signalWithArray(const double[3])") != -1); QVERIFY(mobj->indexOfSignal("signalWithNamedArray(const double[3])") != -1); } void tst_Moc::preprocessorConditionals() { TestClass tst; const QMetaObject *mobj = tst.metaObject(); QVERIFY(mobj->indexOfSlot("slotInElse()") != -1); QVERIFY(mobj->indexOfSlot("slotInIf()") != -1); QVERIFY(mobj->indexOfSlot("slotInLastElse()") != -1); QVERIFY(mobj->indexOfSlot("slotInElif()") != -1); QVERIFY(mobj->indexOfSignal("signalInIf1()") != -1); QVERIFY(mobj->indexOfSignal("signalInIf2()") != -1); QVERIFY(mobj->indexOfSignal("signalInIf3()") != -1); QCOMPARE(mobj->indexOfSignal("doNotExist()"), -1); } void tst_Moc::blackslashNewlines() { BackslashNewlines tst; const QMetaObject *mobj = tst.metaObject(); QVERIFY(mobj->indexOfSlot("works()") != -1); QCOMPARE(mobj->indexOfSlot("buggy()"), -1); } void tst_Moc::slotWithSillyConst() { TestClass tst; const QMetaObject *mobj = tst.metaObject(); QVERIFY(mobj->indexOfSlot("slotWithSillyConst()") != -1); QVERIFY(mobj->indexOfMethod("slotWithSillyConst2()") != -1); 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; QCOMPARE(mobj->enumeratorCount(), 1); QCOMPARE(QByteArray(mobj->enumerator(0).name()), QByteArray("TestEnum")); mobj = &PropertyUseClass::staticMetaObject; const int idx = mobj->indexOfProperty("foo"); QVERIFY(idx != -1); const QMetaProperty prop = mobj->property(idx); QVERIFY(prop.isValid()); QVERIFY(prop.isEnumType()); const QMetaEnum en = prop.enumerator(); QCOMPARE(QByteArray(en.name()), QByteArray("TestEnum")); } // QTBUG-20639 - Accept non-local enums for QML signal/slot parameters. void tst_Moc::testExtraDataForEnum() { const QMetaObject *mobjSource = &EnumSourceClass::staticMetaObject; QCOMPARE(mobjSource->enumeratorCount(), 1); QCOMPARE(QByteArray(mobjSource->enumerator(0).name()), QByteArray("TestEnum")); const QMetaObject *mobjUser = &EnumUserClass::staticMetaObject; QCOMPARE(mobjUser->enumeratorCount(), 0); const auto *objects = mobjUser->d.relatedMetaObjects; QVERIFY(objects); QCOMPARE(objects[0], mobjSource); QVERIFY(!objects[1]); } void tst_Moc::namespaceTypeProperty() { qRegisterMetaType("myNS::Points"); TestClass tst; QByteArray ba = QByteArray("points"); QVariant v = tst.property(ba); QVERIFY(v.isValid()); myNS::Points p = qvariant_cast(v); QCOMPARE(p.p1, 0xBEEF); QCOMPARE(p.p2, 0xBABE); p.p1 = 0xCAFE; p.p2 = 0x1EE7; QVERIFY(tst.setProperty(ba, QVariant::fromValue(p))); myNS::Points pp = qvariant_cast(tst.property(ba)); QCOMPARE(p.p1, pp.p1); QCOMPARE(p.p2, pp.p2); } void tst_Moc::slotsWithVoidTemplate() { SlotsWithVoidTemplateTest test; QVERIFY(QObject::connect(&test, SIGNAL(myVoidSignal(void)), &test, SLOT(dummySlot(void)))); QVERIFY(QObject::connect(&test, SIGNAL(mySignal(const TestTemplate &)), &test, SLOT(anotherSlot(const TestTemplate &)))); QVERIFY(QObject::connect(&test, SIGNAL(myVoidSignal2()), &test, SLOT(dummySlot2()))); } void tst_Moc::structQObject() { StructQObject o; QCOMPARE(QByteArray(o.metaObject()->className()), QByteArray("StructQObject")); } #include "namespaced-flags.h" Q_DECLARE_METATYPE(QList); void tst_Moc::namespacedFlags() { Foo::Baz baz; Foo::Bar bar; bar.setFlags(Foo::Bar::Read | Foo::Bar::Write); QVERIFY(baz.flags() != bar.flags()); const QVariant v = bar.property("flags"); QVERIFY(v.isValid()); QVERIFY(baz.setProperty("flags", v)); QCOMPARE(baz.flags(), bar.flags()); QList l; l << baz.flags(); QVariant v2 = baz.setProperty("flagsList", QVariant::fromValue(l)); QCOMPARE(l, baz.flagsList()); QCOMPARE(l, qvariant_cast >(baz.property("flagsList"))); } void tst_Moc::warnOnMultipleInheritance() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; QStringList args; const QString header = m_sourceDirectory + QStringLiteral("/warn-on-multiple-qobject-subclasses.h"); args << "-I" << qtIncludePath + "/QtGui" << header; proc.start(m_moc, args); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + 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 } void tst_Moc::ignoreOptionClashes() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; QStringList args; const QString header = m_sourceDirectory + QStringLiteral("/interface-from-include.h"); const QString includeDir = m_sourceDirectory + "/Test.framework/Headers"; // given --ignore-option-clashes, -pthread should be ignored, but the -I path should not be. args << "--ignore-option-clashes" << "-pthread" << "-I" << includeDir << "-fno-builtin" << header; proc.start(m_moc, args); bool finished = proc.waitForFinished(); 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()); QByteArray mocOut = proc.readAllStandardOutput(); // If -pthread wasn't ignored, it was parsed as a prefix of "thread/", which breaks compilation. QStringList gccArgs; gccArgs << "-c" << "-x" << "c++" << "-I" << ".." << "-I" << qtIncludePath << "-I" << includeDir << "-o" << "/dev/null" << "-fPIC" << "-std=c++1z" << "-"; proc.start("gcc", gccArgs); QVERIFY(proc.waitForStarted()); proc.write(mocOut); proc.closeWriteChannel(); QVERIFY(proc.waitForFinished()); QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString()); #else QSKIP("Only tested on linux/gcc"); #endif } void tst_Moc::forgottenQInterface() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; QStringList args; const QString header = m_sourceDirectory + QStringLiteral("/forgotten-qinterface.h"); args << "-I" << qtIncludePath + "/QtCore" << header; proc.start(m_moc, args); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + 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 } void tst_Moc::os9Newline() { #if !defined(SKIP_NEWLINE_TEST) const QMetaObject &mo = Os9Newlines::staticMetaObject; QVERIFY(mo.indexOfSlot("testSlot()") != -1); QFile f(m_sourceDirectory + QStringLiteral("/os9-newlines.h")); QVERIFY(f.open(QIODevice::ReadOnly)); // no QIODevice::Text! QByteArray data = f.readAll(); f.close(); QVERIFY(!data.contains('\n')); QVERIFY(data.contains('\r')); #endif } void tst_Moc::winNewline() { #if !defined(SKIP_NEWLINE_TEST) const QMetaObject &mo = WinNewlines::staticMetaObject; QVERIFY(mo.indexOfSlot("testSlot()") != -1); QFile f(m_sourceDirectory + QStringLiteral("/win-newlines.h")); QVERIFY(f.open(QIODevice::ReadOnly)); // no QIODevice::Text! QByteArray data = f.readAll(); f.close(); for (int i = 0; i < data.size(); ++i) { if (data.at(i) == QLatin1Char('\r')) { QVERIFY(i < data.size() - 1); ++i; QCOMPARE(data.at(i), '\n'); } else { QVERIFY(data.at(i) != '\n'); } } #endif } void tst_Moc::escapesInStringLiterals() { const QMetaObject &mo = StringLiterals::staticMetaObject; QCOMPARE(mo.classInfoCount(), 3); int idx = mo.indexOfClassInfo("Test"); QVERIFY(idx != -1); QMetaClassInfo info = mo.classInfo(idx); QCOMPARE(QByteArray(info.value()), QByteArray("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x53")); QVERIFY(idx != -1); idx = mo.indexOfClassInfo("Test2"); info = mo.classInfo(idx); QCOMPARE(QByteArray(info.value()), QByteArray("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\123")); QVERIFY(idx != -1); idx = mo.indexOfClassInfo("Test3"); info = mo.classInfo(idx); QCOMPARE(QByteArray(info.value()), QByteArray("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\nb")); } void tst_Moc::frameworkSearchPath() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_UNIX) && QT_CONFIG(process) QStringList args; args << "-F" << m_sourceDirectory + QStringLiteral("/.") << m_sourceDirectory + QStringLiteral("/interface-from-framework.h") ; QProcess proc; proc.start(m_moc, args); bool finished = proc.waitForFinished(); 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()); #else QSKIP("Only tested/relevant on unixy platforms"); #endif } void tst_Moc::cstyleEnums() { const QMetaObject &obj = CStyleEnums::staticMetaObject; QCOMPARE(obj.enumeratorCount(), 2); QMetaEnum metaEnum = obj.enumerator(0); QCOMPARE(metaEnum.name(), "Baz"); QCOMPARE(metaEnum.keyCount(), 2); QCOMPARE(metaEnum.key(0), "Foo"); QCOMPARE(metaEnum.key(1), "Bar"); QMetaEnum metaEnum2 = obj.enumerator(1); QCOMPARE(metaEnum2.name(), "Baz2"); QCOMPARE(metaEnum2.keyCount(), 2); QCOMPARE(metaEnum2.key(0), "Foo2"); QCOMPARE(metaEnum2.key(1), "Bar2"); } void tst_Moc::templateGtGt() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; proc.start(m_moc, QStringList(m_sourceDirectory + QStringLiteral("/template-gtgt.h"))); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QVERIFY(mocWarning.isEmpty()); #else QSKIP("Only tested on unix/gcc"); #endif } void tst_Moc::defineMacroViaCmdline() { #if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; QStringList args; args << "-DFOO"; args << m_sourceDirectory + QStringLiteral("/macro-on-cmdline.h"); proc.start(m_moc, args); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QCOMPARE(proc.readAllStandardError(), QByteArray()); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else QSKIP("Only tested on unix/gcc"); #endif } void tst_Moc::defineMacroViaForcedInclude() { #if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; QStringList args; args << "--include" << m_sourceDirectory + QLatin1String("/subdir/extradefines.h"); args << m_sourceDirectory + QStringLiteral("/macro-on-cmdline.h"); proc.start(m_moc, args); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QCOMPARE(proc.readAllStandardError(), QByteArray()); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else QSKIP("Only tested on unix/gcc"); #endif } void tst_Moc::defineMacroViaForcedIncludeRelative() { #if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; QStringList args; args << "--include" << QStringLiteral("extradefines.h") << "-I" + m_sourceDirectory + "/subdir"; args << m_sourceDirectory + QStringLiteral("/macro-on-cmdline.h"); proc.start(m_moc, args); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QCOMPARE(proc.readAllStandardError(), QByteArray()); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else QSKIP("Only tested on unix/gcc"); #endif } void tst_Moc::environmentIncludePaths_data() { #if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QTest::addColumn("cmdline"); QTest::addColumn("varname"); QTest::newRow("INCLUDE") << "--compiler-flavor=msvc" << "INCLUDE"; QTest::newRow("CPATH1") << QString() << "CPATH"; QTest::newRow("CPATH2") << "--compiler-flavor=unix" << "CPATH"; QTest::newRow("CPLUS_INCLUDE_PATH1") << QString() << "CPLUS_INCLUDE_PATH"; QTest::newRow("CPLUS_INCLUDE_PATH2") << "--compiler-flavor=unix" << "CPLUS_INCLUDE_PATH"; #endif } void tst_Moc::environmentIncludePaths() { #if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QFETCH(QString, cmdline); QFETCH(QString, varname); QStringList args; if (!cmdline.isEmpty()) args << cmdline; args << "--include" << QStringLiteral("extradefines.h") << m_sourceDirectory + QStringLiteral("/macro-on-cmdline.h"); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.remove("INCLUDE"); env.remove("CPATH"); env.remove("CPLUS_INCLUDE_PATH"); env.insert(varname, m_sourceDirectory + "/subdir"); QProcess proc; proc.setProcessEnvironment(env); proc.start(m_moc, args); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QCOMPARE(proc.readAllStandardError(), QByteArray()); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); #else QSKIP("Only tested on unix/gcc"); #endif } // 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 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() { QStaticPlugin plugin(qt_plugin_instance, qt_plugin_query_metadata_v2); qRegisterStaticPluginFunction(plugin); } }; static StaticPluginInstance staticInstance; void tst_Moc::specifyMetaTagsFromCmdline() { foreach (const QStaticPlugin &plugin, QPluginLoader::staticPlugins()) { const QString iid = plugin.metaData().value(QLatin1String("IID")).toString(); if (iid == QLatin1String("test.meta.tags")) { const QJsonArray metaTagsUriList = plugin.metaData().value("uri").toArray(); QCOMPARE(metaTagsUriList.size(), 2); // The following uri-s are set in the pro file using // -Muri=com.company.app -Muri=com.company.app.private QCOMPARE(metaTagsUriList[0].toString(), QLatin1String("com.company.app")); QCOMPARE(metaTagsUriList[1].toString(), QLatin1String("com.company.app.private")); return; } } QFAIL("Could not find plugin with IID 'test.meta.tags'"); } void tst_Moc::invokable() { { const QMetaObject &mobj = InvokableBeforeReturnType::staticMetaObject; QCOMPARE(mobj.methodCount(), 6); QCOMPARE(mobj.method(5).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()")); } } void tst_Moc::singleFunctionKeywordSignalAndSlot() { { const QMetaObject &mobj = SingleFunctionKeywordBeforeReturnType::staticMetaObject; QCOMPARE(mobj.methodCount(), 7); QCOMPARE(mobj.method(5).methodSignature(), QByteArray("mySignal()")); QCOMPARE(mobj.method(6).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()")); } { const QMetaObject &mobj = SingleFunctionKeywordAfterInline::staticMetaObject; QCOMPARE(mobj.methodCount(), 7); QCOMPARE(mobj.method(5).methodSignature(), QByteArray("mySignal()")); QCOMPARE(mobj.method(6).methodSignature(), QByteArray("mySlot()")); } } #include "qprivateslots.h" void tst_Moc::qprivateslots() { TestQPrivateSlots tst; const QMetaObject *mobj = tst.metaObject(); QVERIFY(mobj->indexOfSlot("_q_privateslot()") != -1); QVERIFY(mobj->indexOfMethod("method1()") != -1); //tast204730 } class PrivatePropertyTest : public QObject { Q_OBJECT Q_PROPERTY(int foo READ foo WRITE setFoo) Q_PRIVATE_PROPERTY(d, int bar READ bar WRITE setBar) Q_PRIVATE_PROPERTY(PrivatePropertyTest::d, int plop READ plop WRITE setPlop) Q_PRIVATE_PROPERTY(PrivatePropertyTest::d_func(), int baz READ baz WRITE setBaz) Q_PRIVATE_PROPERTY(PrivatePropertyTest::d, QString blub MEMBER mBlub) Q_PRIVATE_PROPERTY(PrivatePropertyTest::d, QString blub2 MEMBER mBlub READ blub) Q_PRIVATE_PROPERTY(PrivatePropertyTest::d, QString blub3 MEMBER mBlub WRITE setBlub) Q_PRIVATE_PROPERTY(PrivatePropertyTest::d, QString blub4 MEMBER mBlub NOTIFY blub4Changed) Q_PRIVATE_PROPERTY(PrivatePropertyTest::d, QString blub5 MEMBER mBlub NOTIFY blub5Changed) Q_PRIVATE_PROPERTY(PrivatePropertyTest::d, QString blub6 MEMBER mConst CONSTANT) Q_PRIVATE_PROPERTY(PrivatePropertyTest::d, int zap READ zap WRITE setZap BINDABLE bindableZap) class MyDPointer { public: MyDPointer() : mConst("const"), mBar(0), mPlop(0) {} int bar() { return mBar ; } void setBar(int value) { mBar = value; } int plop() { return mPlop ; } void setPlop(int value) { mPlop = value; } int baz() { return mBaz ; } void setBaz(int value) { mBaz = value; } QString blub() const { return mBlub; } void setBlub(const QString &value) { mBlub = value; } int zap() { return mZap; } void setZap(int zap) { mZap = zap; } QBindable bindableZap() { return QBindable(&mZap); } QString mBlub; const QString mConst; private: int mBar; int mPlop; int mBaz; QProperty mZap; }; public: PrivatePropertyTest(QObject *parent = nullptr) : QObject(parent), mFoo(0), d (new MyDPointer) {} int foo() { return mFoo ; } void setFoo(int value) { mFoo = value; } MyDPointer *d_func() {return d.data();} const MyDPointer *d_func() const {return d.data();} signals: void blub4Changed(); void blub5Changed(const QString &newBlub); private: int mFoo; QScopedPointer d; }; void tst_Moc::qprivateproperties() { PrivatePropertyTest test; test.setProperty("foo", 1); QCOMPARE(test.property("foo"), QVariant::fromValue(1)); test.setProperty("bar", 2); QCOMPARE(test.property("bar"), QVariant::fromValue(2)); test.setProperty("plop", 3); QCOMPARE(test.property("plop"), QVariant::fromValue(3)); test.setProperty("baz", 4); QCOMPARE(test.property("baz"), QVariant::fromValue(4)); QMetaProperty zap = test.metaObject()->property(test.metaObject()->indexOfProperty("zap")); QVERIFY(zap.isValid()); QVERIFY(zap.isBindable()); auto zapBindable = zap.bindable(&test); QVERIFY(zapBindable.isBindable()); } void tst_Moc::warnOnPropertyWithoutREAD() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; const QString header = m_sourceDirectory + QStringLiteral("/warn-on-property-without-read.h"); proc.start(m_moc, QStringList(header)); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); QCOMPARE(mocWarning, header + 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 } void tst_Moc::constructors() { const QMetaObject *mo = &CtorTestClass::staticMetaObject; QCOMPARE(mo->constructorCount(), 3); { QMetaMethod mm = mo->constructor(0); QCOMPARE(mm.access(), QMetaMethod::Public); QCOMPARE(mm.methodType(), QMetaMethod::Constructor); QCOMPARE(mm.methodSignature(), QByteArray("CtorTestClass(QObject*)")); QCOMPARE(mm.typeName(), ""); QList paramNames = mm.parameterNames(); QCOMPARE(paramNames.size(), 1); QCOMPARE(paramNames.at(0), QByteArray("parent")); QList paramTypes = mm.parameterTypes(); QCOMPARE(paramTypes.size(), 1); QCOMPARE(paramTypes.at(0), QByteArray("QObject*")); } { QMetaMethod mm = mo->constructor(1); QCOMPARE(mm.access(), QMetaMethod::Public); QCOMPARE(mm.methodType(), QMetaMethod::Constructor); QCOMPARE(mm.methodSignature(), QByteArray("CtorTestClass()")); QCOMPARE(mm.typeName(), ""); QCOMPARE(mm.parameterNames().size(), 0); QCOMPARE(mm.parameterTypes().size(), 0); } { QMetaMethod mm = mo->constructor(2); QCOMPARE(mm.access(), QMetaMethod::Public); QCOMPARE(mm.methodType(), QMetaMethod::Constructor); QCOMPARE(mm.methodSignature(), QByteArray("CtorTestClass(QString)")); QCOMPARE(mm.typeName(), ""); QList paramNames = mm.parameterNames(); QCOMPARE(paramNames.size(), 1); QCOMPARE(paramNames.at(0), QByteArray("str")); QList paramTypes = mm.parameterTypes(); QCOMPARE(paramTypes.size(), 1); QCOMPARE(paramTypes.at(0), QByteArray("QString")); } QCOMPARE(mo->indexOfConstructor("CtorTestClass(QObject*)"), 0); QCOMPARE(mo->indexOfConstructor("CtorTestClass()"), 1); QCOMPARE(mo->indexOfConstructor("CtorTestClass(QString)"), 2); QCOMPARE(mo->indexOfConstructor("CtorTestClass2(QObject*)"), -1); QCOMPARE(mo->indexOfConstructor("CtorTestClass(float,float)"), -1); QScopedPointer o1(mo->newInstance()); QVERIFY(o1 != 0); QCOMPARE(o1->parent(), (QObject*)0); QVERIFY(qobject_cast(o1.data()) != 0); QObject *o2 = mo->newInstance(Q_ARG(QObject*, o1.data())); QVERIFY(o2 != 0); QCOMPARE(o2->parent(), o1.data()); QString str = QString::fromLatin1("hello"); QScopedPointer o3(mo->newInstance(Q_ARG(QString, str))); QVERIFY(o3 != 0); QCOMPARE(qobject_cast(o3.data())->m_str, str); { //explicit constructor QObject *o = QObject::staticMetaObject.newInstance(); QVERIFY(o); delete o; } } #include "task234909.h" #include "task240368.h" void tst_Moc::typenameWithUnsigned() { TypenameWithUnsigned tst; const QMetaObject *mobj = tst.metaObject(); QVERIFY(mobj->indexOfSlot("a(uint)") != -1); QVERIFY(mobj->indexOfSlot("b(uint)") != -1); QVERIFY(mobj->indexOfSlot("c(uint*)") != -1); QVERIFY(mobj->indexOfSlot("d(uint*)") != -1); QVERIFY(mobj->indexOfSlot("e(uint&)") != -1); QVERIFY(mobj->indexOfSlot("f(uint&)") != -1); QVERIFY(mobj->indexOfSlot("g(unsigned1)") != -1); QVERIFY(mobj->indexOfSlot("h(unsigned1)") != -1); QVERIFY(mobj->indexOfSlot("i(uint,unsigned1)") != -1); QVERIFY(mobj->indexOfSlot("j(unsigned1,uint)") != -1); QVERIFY(mobj->indexOfSlot("k(unsignedQImage)") != -1); QVERIFY(mobj->indexOfSlot("l(unsignedQImage)") != -1); } void tst_Moc::warnOnVirtualSignal() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; const QString header = m_sourceDirectory + QStringLiteral("/pure-virtual-signals.h"); proc.start(m_moc, QStringList(header)); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QString mocWarning = QString::fromLocal8Bit(proc.readAllStandardError()); 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 } class QTBUG5590_DummyObject: public QObject { Q_OBJECT Q_PROPERTY(bool dummy) }; class QTBUG5590_PropertyObject: public QTBUG5590_DummyObject { Q_OBJECT Q_PROPERTY(int value READ value WRITE setValue) Q_PROPERTY(int value2 READ value2 WRITE setValue2) public: QTBUG5590_PropertyObject() : m_value(85), m_value2(40) { } int value() const { return m_value; } void setValue(int value) { m_value = value; } int value2() const { return m_value2; } void setValue2(int value) { m_value2 = value; } private: int m_value, m_value2; }; void tst_Moc::QTBUG5590_dummyProperty() { QTBUG5590_PropertyObject o; QCOMPARE(o.property("value").toInt(), 85); QCOMPARE(o.property("value2").toInt(), 40); o.setProperty("value", 32); QCOMPARE(o.value(), 32); o.setProperty("value2", 82); QCOMPARE(o.value2(), 82); } QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wignored-qualifiers") QT_WARNING_DISABLE_GCC("-Wignored-qualifiers") class QTBUG7421_ReturnConstTemplate: public QObject { Q_OBJECT public slots: const QList returnConstTemplate1() { return QList(); } QList const returnConstTemplate2() { return QList(); } const int returnConstInt() { return 0; } const QString returnConstString(const QString s) { return s; } QString const returnConstString2( QString const s) { return s; } }; QT_WARNING_POP struct science_constant {}; struct science_const {}; struct constconst {}; struct const_ {}; class QTBUG9354_constInName: public QObject { Q_OBJECT public slots: void slotChooseScientificConst0(science_constant const &) {}; void foo(science_const const &) {}; void foo(constconst const &) {}; void foo(constconst *) {}; void foo(const_ *) {}; }; template class TestTemplate2 { }; class QTBUG11647_constInTemplateParameter : public QObject { Q_OBJECT public slots: void testSlot(TestTemplate2) {} void testSlot2(TestTemplate2) {} void testSlot3(TestTemplate2 const *, TestTemplate2< TestTemplate2 < void, int > , unsigned char *> > ) {} signals: void testSignal(TestTemplate2); }; class QTBUG12260_defaultTemplate_Object : public QObject { Q_OBJECT public slots: void doSomething(QHash = QHash() ) {} void doSomethingElse(QSharedPointer> 2)> > = QSharedPointer> 2)> >() ) {} void doAnotherThing(bool = (1 < 3), bool = (1 > 4)) {} void performSomething(QList> = QList>(8 < 1), QHash> = QHash>()) {} }; void tst_Moc::QTBUG12260_defaultTemplate() { QVERIFY(QTBUG12260_defaultTemplate_Object::staticMetaObject.indexOfSlot("doSomething(QHash)") != -1); QVERIFY(QTBUG12260_defaultTemplate_Object::staticMetaObject.indexOfSlot("doAnotherThing(bool,bool)") != -1); QVERIFY(QTBUG12260_defaultTemplate_Object::staticMetaObject.indexOfSlot("doSomethingElse(QSharedPointer>2)>>)") != -1); QVERIFY(QTBUG12260_defaultTemplate_Object::staticMetaObject.indexOfSlot("performSomething(QList>,QHash>)") != -1); } void tst_Moc::notifyError() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; const QString header = m_sourceDirectory + QStringLiteral("/error-on-wrong-notify.h"); proc.start(m_moc, QStringList(header)); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QCOMPARE(proc.exitStatus(), QProcess::NormalExit); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QCOMPARE(proc.readAllStandardError(), QByteArray()); QStringList args; args << "-c" << "-x" << "c++" << "-I" << "." << "-I" << qtIncludePath << "-o" << "/dev/null" << "-fPIC" << "-std=c++1z" << "-"; proc.start("gcc", args); QVERIFY(proc.waitForStarted()); proc.write(mocOut); proc.closeWriteChannel(); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 1); const QString gccOutput = QString::fromLocal8Bit(proc.readAllStandardError()); QVERIFY(gccOutput.contains(QLatin1String("error"))); QVERIFY(gccOutput.contains(QLatin1String("fooChanged"))); #else QSKIP("Only tested on linux/gcc"); #endif } class QTBUG_17635_InvokableAndProperty : public QObject { Q_OBJECT public: Q_PROPERTY(int numberOfEggs READ numberOfEggs) Q_PROPERTY(int numberOfChickens READ numberOfChickens) Q_INVOKABLE QString getEgg(int index) { Q_UNUSED(index); return QString::fromLatin1("Egg"); } Q_INVOKABLE QString getChicken(int index) { Q_UNUSED(index); return QString::fromLatin1("Chicken"); } int numberOfEggs() { return 2; } int numberOfChickens() { return 4; } }; void tst_Moc::QTBUG17635_invokableAndProperty() { //Moc used to fail parsing Q_INVOKABLE if they were dirrectly following a Q_PROPERTY; QTBUG_17635_InvokableAndProperty mc; QString val; QMetaObject::invokeMethod(&mc, "getEgg", Q_RETURN_ARG(QString, val), Q_ARG(int, 10)); QCOMPARE(val, QString::fromLatin1("Egg")); QMetaObject::invokeMethod(&mc, "getChicken", Q_RETURN_ARG(QString, val), Q_ARG(int, 10)); QCOMPARE(val, QString::fromLatin1("Chicken")); QVERIFY(mc.metaObject()->indexOfProperty("numberOfEggs") != -1); QVERIFY(mc.metaObject()->indexOfProperty("numberOfChickens") != -1); } // If changed, update VersionTestNotify below class VersionTest : public QObject { Q_OBJECT Q_PROPERTY(int prop1 READ foo) Q_PROPERTY(int prop2 READ foo REVISION 2) Q_PROPERTY(int prop514 READ foo REVISION(5, 14)) public: int foo() const { return 0; } Q_INVOKABLE void method1() {} Q_INVOKABLE Q_REVISION(4) void method2() {} Q_INVOKABLE Q_REVISION(6, 0) void method60() {} enum TestEnum { One, Two }; Q_ENUM(TestEnum); public slots: void slot1() {} Q_REVISION(3) void slot2() {} Q_REVISION(6, 1) void slot61() {} signals: void signal1(); Q_REVISION(5) void signal2(); Q_REVISION(6, 2) void signal62(); public slots Q_REVISION(6): void slot3() {} void slot4() {} public slots Q_REVISION(5, 12): void slot512() {} signals Q_REVISION(7): void signal3(); void signal4(); signals Q_REVISION(5, 15): void signal515(); }; // If changed, update VersionTest above class VersionTestNotify : public QObject { Q_OBJECT Q_PROPERTY(int prop1 READ foo NOTIFY fooChanged) Q_PROPERTY(int prop2 READ foo REVISION 2) Q_PROPERTY(int prop514 READ foo REVISION(5, 14)) public: int foo() const { return 0; } Q_INVOKABLE void method1() {} Q_INVOKABLE Q_REVISION(4) void method2() {} Q_INVOKABLE Q_REVISION(6, 0) void method60() {} enum TestEnum { One, Two }; Q_ENUM(TestEnum); public slots: void slot1() {} Q_REVISION(3) void slot2() {} Q_REVISION(6, 1) void slot61() {} signals: void fooChanged(); void signal1(); Q_REVISION(5) void signal2(); Q_REVISION(6, 2) void signal62(); public slots Q_REVISION(6): void slot3() {} void slot4() {} public slots Q_REVISION(5, 12): void slot512() {} signals Q_REVISION(7): void signal3(); void signal4(); signals Q_REVISION(5, 15): void signal515(); }; template void tst_Moc::revisions_T() { int idx = T::staticMetaObject.indexOfProperty("prop1"); QCOMPARE(T::staticMetaObject.property(idx).revision(), 0); idx = T::staticMetaObject.indexOfProperty("prop2"); QCOMPARE(T::staticMetaObject.property(idx).revision(), QTypeRevision::fromMinorVersion(2).toEncodedVersion()); idx = T::staticMetaObject.indexOfProperty("prop514"); QCOMPARE(T::staticMetaObject.property(idx).revision(), QTypeRevision::fromVersion(5, 14).toEncodedVersion()); idx = T::staticMetaObject.indexOfMethod("method1()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), 0); idx = T::staticMetaObject.indexOfMethod("method2()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromMinorVersion(4).toEncodedVersion()); idx = T::staticMetaObject.indexOfMethod("method60()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromVersion(6, 0).toEncodedVersion()); idx = T::staticMetaObject.indexOfSlot("slot1()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), 0); idx = T::staticMetaObject.indexOfSlot("slot2()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromMinorVersion(3).toEncodedVersion()); idx = T::staticMetaObject.indexOfSlot("slot61()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromVersion(6, 1).toEncodedVersion()); idx = T::staticMetaObject.indexOfSlot("slot3()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromMinorVersion(6).toEncodedVersion()); idx = T::staticMetaObject.indexOfSlot("slot4()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromMinorVersion(6).toEncodedVersion()); idx = T::staticMetaObject.indexOfSlot("slot512()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromVersion(5, 12).toEncodedVersion()); idx = T::staticMetaObject.indexOfSignal("signal1()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), 0); idx = T::staticMetaObject.indexOfSignal("signal2()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromMinorVersion(5).toEncodedVersion()); idx = T::staticMetaObject.indexOfSignal("signal62()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromVersion(6, 2).toEncodedVersion()); idx = T::staticMetaObject.indexOfSignal("signal3()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromMinorVersion(7).toEncodedVersion()); idx = T::staticMetaObject.indexOfSignal("signal4()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromMinorVersion(7).toEncodedVersion()); idx = T::staticMetaObject.indexOfSignal("signal515()"); QCOMPARE(T::staticMetaObject.method(idx).revision(), QTypeRevision::fromVersion(5, 15).toEncodedVersion()); idx = T::staticMetaObject.indexOfEnumerator("TestEnum"); QCOMPARE(T::staticMetaObject.enumerator(idx).keyCount(), 2); QCOMPARE(T::staticMetaObject.enumerator(idx).key(0), "One"); } // test using both class that has properties with and without NOTIFY signals void tst_Moc::revisions() { revisions_T(); revisions_T(); } void tst_Moc::warnings_data() { QTest::addColumn("input"); QTest::addColumn("args"); QTest::addColumn("exitCode"); QTest::addColumn("expectedStdOut"); QTest::addColumn("expectedStdErr"); // empty input should result in "no relevant classes" note QTest::newRow("No relevant classes") << QByteArray(" ") << QStringList() << 0 << QString() << QString("standard input:0:1: note: No relevant classes found. No output generated."); // passing "-nn" should suppress "no relevant classes" note QTest::newRow("-nn") << QByteArray(" ") << (QStringList() << "-nn") << 0 << QString() << QString(); // passing "-nw" should also suppress "no relevant classes" note QTest::newRow("-nw") << QByteArray(" ") << (QStringList() << "-nw") << 0 << QString() << QString(); // This should output a warning QTest::newRow("Invalid property warning") << QByteArray("class X : public QObject { Q_OBJECT Q_PROPERTY(int x) };") << QStringList() << 0 << QString("IGNORE_ALL_STDOUT") << QString("standard input:1:1: warning: Property declaration x has neither an associated QProperty<> member, nor a READ accessor function nor an associated MEMBER variable. The property will be invalid."); // This should output a warning QTest::newRow("Duplicate property warning") << QByteArray("class X : public QObject { Q_OBJECT Q_PROPERTY(int x READ x) Q_PROPERTY(int x READ y) };") << QStringList() << 0 << QString("IGNORE_ALL_STDOUT") << QString("standard input:1:1: warning: The property 'x' is defined multiple times in class X."); // Passing "-nn" should NOT suppress the warning QTest::newRow("Invalid property warning with -nn") << QByteArray("class X : public QObject { Q_OBJECT Q_PROPERTY(int x) };") << (QStringList() << "-nn") << 0 << QString("IGNORE_ALL_STDOUT") << QString("standard input:1:1: warning: Property declaration x has neither an associated QProperty<> member, nor a READ accessor function nor an associated MEMBER variable. The property will be invalid."); // Passing "-nw" should suppress the warning QTest::newRow("Invalid property warning with -nw") << QByteArray("class X : public QObject { Q_OBJECT Q_PROPERTY(int x) };") << (QStringList() << "-nw") << 0 << QString("IGNORE_ALL_STDOUT") << QString(); // This should output an error QTest::newRow("Does not inherit QObject") << QByteArray("class X { Q_OBJECT };") << QStringList() << 1 << QString() << QString("standard input:1:1: error: Class contains Q_OBJECT macro but does not inherit from QObject"); // "-nn" should not suppress the error QTest::newRow("Does not inherit QObject with -nn") << QByteArray("class X { Q_OBJECT };") << (QStringList() << "-nn") << 1 << QString() << QString("standard input:1:1: error: Class contains Q_OBJECT macro but does not inherit from QObject"); // "-nw" should not suppress the error QTest::newRow("Does not inherit QObject with -nw") << QByteArray("class X { Q_OBJECT };") << (QStringList() << "-nw") << 1 << QString() << QString("standard input:1:1: error: Class contains Q_OBJECT macro but does not inherit from QObject"); QTest::newRow("Warning on invalid macro") << QByteArray("#define Foo(a, b)\n class X : public QObject { Q_OBJECT }; \n Foo(a) \n Foo(a,b,c) \n") << QStringList() << 0 << QString("IGNORE_ALL_STDOUT") << QString(); QTest::newRow("Class declaration lacks Q_OBJECT macro.") << QByteArray("class X : public QObject \n { \n public slots: \n void foo() {} \n };") << QStringList() << 1 << QString() << QString("standard input:5:1: error: Class declaration lacks Q_OBJECT macro."); QTest::newRow("Namespace declaration lacks Q_NAMESPACE macro.") << QByteArray("namespace X {\nQ_CLASSINFO(\"key\",\"value\")\nenum class MyEnum {Key1 = 1}\nQ_ENUMS(MyEnum)\n}\n") << QStringList() << 1 << QString() << QString("standard input:1:1: error: Namespace declaration lacks Q_NAMESPACE macro."); QTest::newRow("Wrong Q_ENUM context.") << QByteArray("namespace X {\nQ_NAMESPACE\n\nenum class MyEnum {Key1 = 1}\nQ_ENUM(MyEnum)\n}\n") << QStringList() << 1 << QString() << QString("standard input:5:1: error: Q_ENUM can't be used in a Q_NAMESPACE, use Q_ENUM_NS instead"); QTest::newRow("Wrong Q_FLAG context.") << QByteArray("namespace X {\nQ_NAMESPACE\n\nenum class MyEnum {Key1 = 1}\nQ_FLAG(MyEnum)\n}\n") << QStringList() << 1 << QString() << QString("standard input:5:1: error: Q_FLAG can't be used in a Q_NAMESPACE, use Q_FLAG_NS instead"); QTest::newRow("Wrong Q_ENUM_NS context.") << QByteArray("class X {\nQ_GADGET\n\nenum class MyEnum {Key1 = 1}\nQ_ENUM_NS(MyEnum)\n};\n") << QStringList() << 1 << QString() << QString("standard input:5:1: error: Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead"); QTest::newRow("Wrong Q_FLAG_NS context.") << QByteArray("class X {\nQ_GADGET\n\nenum class MyEnum {Key1 = 1}\nQ_FLAG_NS(MyEnum)\n};\n") << QStringList() << 1 << QString() << QString("standard input:5:1: error: Q_FLAG_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_FLAG instead"); QTest::newRow("Invalid macro definition") << QByteArray("#define Foo(a, b, c) a b c #a #b #c a##b##c #d\n Foo(45, 42, 39);") << QStringList() << 1 << QString("IGNORE_ALL_STDOUT") << QString(":2:1: error: '#' is not followed by a macro parameter"); QTest::newRow("QTBUG-46210: crash on invalid macro invocation") << QByteArray("#define Foo(a, b, c) a b c #a #b #c a##b##c\n Foo(45);") << QStringList() << 1 << QString("IGNORE_ALL_STDOUT") << QString(":2:1: error: Macro invoked with too few parameters for a use of '#'"); QTest::newRow("QTBUG-54609: crash on invalid input") << QByteArray::fromBase64("EAkJCQkJbGFzcyBjbGFzcyBiYWkcV2kgTUEKcGYjZGVmaW5lIE1BKFEs/4D/FoQ=") << QStringList() << 1 << QString("IGNORE_ALL_STDOUT") << QString(":-1:1: error: Unexpected character in macro argument list."); QTest::newRow("Missing header warning") << QByteArray("class X : public QObject { Q_OBJECT };") << (QStringList() << QStringLiteral("--include") << QStringLiteral("doesnotexist.h")) << 0 << QString("IGNORE_ALL_STDOUT") << QStringLiteral("Warning: Failed to resolve include \"doesnotexist.h\" for moc file "); QTest::newRow("QTBUG-54815: Crash on invalid input") << QByteArray("class M{(})F<{}d000000000000000#0") << QStringList() << 0 << QString() << QString("standard input:1:1: note: No relevant classes found. No output generated."); QTest::newRow("Q_PLUGIN_METADATA: invalid file") << QByteArray("class X { \n Q_PLUGIN_METADATA(FILE \"does.not.exists\") \n };") << QStringList() << 1 << QString() << QString("standard input:2:1: error: Plugin Metadata file \"does.not.exists\" does not exist. Declaration will be ignored"); QTest::newRow("Auto-declared, missing trailing return") << QByteArray("class X { \n public slots: \n auto fun() { return 1; } };") << QStringList() << 1 << QString() << QString("standard input:3:1: error: Function declared with auto as return type but missing trailing return type. Return type deduction is not supported."); QTest::newRow("Auto-declared, volatile auto as trailing return type") << QByteArray("class X { \n public slots: \n auto fun() -> volatile auto { return 1; } };") << QStringList() << 1 << QString() << QString("standard input:3:1: error: Function declared with auto as return type but missing trailing return type. Return type deduction is not supported."); // We don't currently support the decltype keyword, so it's not the same error as above. // The test is just here to make sure this keeps generating an error until return type deduction // is supported. QTest::newRow("Auto-declared, decltype in trailing return type") << QByteArray("class X { \n public slots: \n auto fun() -> decltype(0+1) { return 1; } };") << QStringList() << 1 << 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 };") << QStringList() << 1 << QString() << QString("standard input:2:1: error: Plugin Metadata file \".\" could not be opened: file to open is a directory"); #endif } void tst_Moc::warnings() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif QFETCH(QByteArray, input); QFETCH(QStringList, args); QFETCH(int, exitCode); QFETCH(QString, expectedStdOut); QFETCH(QString, expectedStdErr); #ifdef Q_CC_MSVC // moc compiled with MSVC uses a different output format to match MSVC compiler style QRegularExpression lineNumberRe(":(-?\\d+):(\\d+)", QRegularExpression::InvertedGreedinessOption); expectedStdErr.replace(lineNumberRe, "(\\1:\\2)"); #endif #if QT_CONFIG(process) QProcess proc; QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("QT_MESSAGE_PATTERN", "no qDebug or qWarning please"); proc.setProcessEnvironment(env); proc.start(m_moc, args); QVERIFY(proc.waitForStarted()); QCOMPARE(proc.write(input), qint64(input.size())); proc.closeWriteChannel(); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), exitCode); QCOMPARE(proc.exitStatus(), QProcess::NormalExit); // magic value "IGNORE_ALL_STDOUT" ignores stdout if (expectedStdOut != "IGNORE_ALL_STDOUT") QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardOutput()).trimmed(), expectedStdOut); QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()).trimmed().remove('\r'), expectedStdErr); #else QSKIP("Only tested if QProcess is available"); #endif } class tst_Moc::PrivateClass : public QObject { Q_PROPERTY(int someProperty READ someSlot WRITE someSlot2) Q_OBJECT Q_SIGNALS: void someSignal(); public Q_SLOTS: int someSlot() { return 1; } void someSlot2(int) {} public: Q_INVOKABLE PrivateClass() {} }; void tst_Moc::privateClass() { QCOMPARE(PrivateClass::staticMetaObject.indexOfConstructor("PrivateClass()"), 0); QVERIFY(PrivateClass::staticMetaObject.indexOfSignal("someSignal()") > 0); } void tst_Moc::cxx11Enums_data() { QTest::addColumn("meta"); QTest::addColumn("typeName"); QTest::addColumn("enumName"); QTest::addColumn("prefix"); QTest::addColumn("isScoped"); 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; } void tst_Moc::cxx11Enums() { QFETCH(const QMetaObject *,meta); QCOMPARE(meta->enumeratorOffset(), 0); QFETCH(QByteArray, typeName); QFETCH(QByteArray, enumName); QFETCH(char, prefix); QFETCH(bool, isScoped); 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(); 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(meta->enumerator(idx).isScoped(), isScoped); } void tst_Moc::cxx11TrailingReturn() { CXX11TrailingReturn retClass; const QMetaObject *mobj = retClass.metaObject(); QVERIFY(mobj->indexOfSlot("fun()") != -1); QVERIFY(mobj->indexOfSlot("arguments(int,char)") != -1); QVERIFY(mobj->indexOfSlot("inlineFunc(int)") != -1); QVERIFY(mobj->indexOfSlot("constRefReturn()") != -1); QVERIFY(mobj->indexOfSlot("constConstRefReturn()") != -1); QVERIFY(mobj->indexOfSignal("trailingSignalReturn(int)") != -1); } void tst_Moc::returnRefs() { TestClass tst; const QMetaObject *mobj = tst.metaObject(); QVERIFY(mobj->indexOfMethod("myInvokableReturningRef()") != -1); QVERIFY(mobj->indexOfMethod("myInvokableReturningConstRef()") != -1); // Those two functions are copied from the qscriptextqobject test in qtscript // they used to cause miscompilation of the moc generated file. } void tst_Moc::memberProperties_data() { QTest::addColumn("object"); QTest::addColumn("property"); QTest::addColumn("signal"); QTest::addColumn("writeValue"); QTest::addColumn("expectedWriteResult"); QTest::addColumn("expectedReadResult"); pPPTest = new PrivatePropertyTest( this ); QTest::newRow("MEMBER property") << 0 << "member1" << "" << "abc" << true << "abc"; QTest::newRow("MEMBER property with READ function") << 0 << "member2" << "" << "def" << true << "def"; QTest::newRow("MEMBER property with WRITE function") << 0 << "member3" << "" << "ghi" << true << "ghi"; QTest::newRow("MEMBER property with NOTIFY") << 0 << "member4" << "member4Changed()" << "lmn" << true << "lmn"; QTest::newRow("MEMBER property with NOTIFY(value)") << 0 << "member5" << "member5Changed(const QString&)" << "opq" << true << "opq"; QTest::newRow("MEMBER property with CONSTANT") << 0 << "member6" << "" << "test" << false << "const"; QTest::newRow("private MEMBER property") << 1 << "blub" << "" << "abc" << true << "abc"; QTest::newRow("private MEMBER property with READ function") << 1 << "blub2" << "" << "def" << true << "def"; QTest::newRow("private MEMBER property with WRITE function") << 1 << "blub3" << "" << "ghi" << true << "ghi"; QTest::newRow("private MEMBER property with NOTIFY") << 1 << "blub4" << "blub4Changed()" << "jkl" << true << "jkl"; QTest::newRow("private MEMBER property with NOTIFY(value)") << 1 << "blub5" << "blub5Changed(const QString&)" << "mno" << true << "mno"; QTest::newRow("private MEMBER property with CONSTANT") << 1 << "blub6" << "" << "test" << false << "const"; QTest::newRow("sub1") << 0 << "sub1" << "" << "helloSub1" << true << "helloSub1"; QTest::newRow("sub2") << 0 << "sub2" << "" << "helloSub2" << true << "helloSub2"; } void tst_Moc::memberProperties() { QFETCH(int, object); QFETCH(QString, property); QFETCH(QString, signal); QFETCH(QString, writeValue); QFETCH(bool, expectedWriteResult); QFETCH(QString, expectedReadResult); QObject *pObj = (object == 0) ? this : static_cast(pPPTest); QString sSignalDeclaration; if (!signal.isEmpty()) sSignalDeclaration = QString(SIGNAL(%1)).arg(signal); else QTest::ignoreMessage(QtWarningMsg, "QSignalSpy: Not a valid signal, use the SIGNAL macro"); QSignalSpy notifySpy(pObj, sSignalDeclaration.toLatin1().constData()); int index = pObj->metaObject()->indexOfProperty(property.toLatin1().constData()); QVERIFY(index != -1); QMetaProperty prop = pObj->metaObject()->property(index); QCOMPARE(prop.write(pObj, writeValue), expectedWriteResult); QVariant readValue = prop.read(pObj); QCOMPARE(readValue.toString(), expectedReadResult); if (!signal.isEmpty()) { QCOMPARE(notifySpy.size(), 1); if (prop.notifySignal().parameterNames().size() > 0) { QList arguments = notifySpy.takeFirst(); QCOMPARE(arguments.size(), 1); QCOMPARE(arguments.at(0).toString(), expectedReadResult); } 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.size(), 0); } } //this used to fail to compile class ClassWithOneMember : public QObject { Q_PROPERTY(int member MEMBER member) Q_OBJECT public: int member; }; void tst_Moc::memberProperties2() { ClassWithOneMember o; o.member = 442; QCOMPARE(o.property("member").toInt(), 442); QVERIFY(o.setProperty("member", 6666)); QCOMPARE(o.member, 6666); } class SignalConnectionTester : public QObject { Q_OBJECT public: SignalConnectionTester(QObject *parent = nullptr) : QObject(parent), testPassed(false) { } public Q_SLOTS: void testSlot() { testPassed = true; } void testSlotWith1Arg(int i) { testPassed = i == 42; } void testSlotWith2Args(int i, const QString &s) { testPassed = i == 42 && s == "Hello"; } public: bool testPassed; }; class ClassWithPrivateSignals : public QObject { Q_OBJECT public: ClassWithPrivateSignals(QObject *parent = nullptr) : QObject(parent) { } void emitPrivateSignals() { emit privateSignal1(QPrivateSignal()); emit privateSignalWith1Arg(42, QPrivateSignal()); emit privateSignalWith2Args(42, "Hello", QPrivateSignal()); emit privateOverloadedSignal(QPrivateSignal()); emit privateOverloadedSignal(42, QPrivateSignal()); emit overloadedMaybePrivate(); emit overloadedMaybePrivate(42, QPrivateSignal()); } Q_SIGNALS: void privateSignal1(QPrivateSignal); void privateSignalWith1Arg(int arg1, QPrivateSignal); void privateSignalWith2Args(int arg1, const QString &arg2, QPrivateSignal); void privateOverloadedSignal(QPrivateSignal); void privateOverloadedSignal(int, QPrivateSignal); void overloadedMaybePrivate(); void overloadedMaybePrivate(int, QPrivateSignal); }; class SubClassFromPrivateSignals : public ClassWithPrivateSignals { Q_OBJECT public: SubClassFromPrivateSignals(QObject *parent = nullptr) : ClassWithPrivateSignals(parent) { } void emitProtectedSignals() { // Compile test: All of this intentionally does not compile: // emit privateSignal1(); // emit privateSignalWith1Arg(42); // emit privateSignalWith2Args(42, "Hello"); // // emit privateSignal1(QPrivateSignal()); // emit privateSignalWith1Arg(42, QPrivateSignal()); // emit privateSignalWith2Args(42, "Hello", QPrivateSignal()); // // emit privateSignal1(ClassWithPrivateSignals::QPrivateSignal()); // emit privateSignalWith1Arg(42, ClassWithPrivateSignals::QPrivateSignal()); // emit privateSignalWith2Args(42, "Hello", ClassWithPrivateSignals::QPrivateSignal()); // emit privateOverloadedSignal(); // emit privateOverloadedSignal(42); // emit overloadedMaybePrivate(); // emit overloadedMaybePrivate(42); } }; void tst_Moc::privateSignalConnection() { // Function pointer connects. Matching signals and slots { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignal1, &tester, &SignalConnectionTester::testSlot); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); tester.testPassed = false; QMetaObject::invokeMethod(&classWithPrivateSignals, "privateSignal1"); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignal1, &tester, &SignalConnectionTester::testSlot); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); tester.testPassed = false; QMetaObject::invokeMethod(&subClassFromPrivateSignals, "privateSignal1"); QVERIFY(tester.testPassed); } { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlotWith1Arg); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); tester.testPassed = false; QMetaObject::invokeMethod(&classWithPrivateSignals, "privateSignalWith1Arg", Q_ARG(int, 42)); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlotWith1Arg); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); tester.testPassed = false; QMetaObject::invokeMethod(&subClassFromPrivateSignals, "privateSignalWith1Arg", Q_ARG(int, 42)); QVERIFY(tester.testPassed); } { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignalWith2Args, &tester, &SignalConnectionTester::testSlotWith2Args); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); tester.testPassed = false; QMetaObject::invokeMethod(&classWithPrivateSignals, "privateSignalWith2Args", Q_ARG(int, 42), Q_ARG(QString, "Hello")); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignalWith2Args, &tester, &SignalConnectionTester::testSlotWith2Args); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); tester.testPassed = false; QMetaObject::invokeMethod(&subClassFromPrivateSignals, "privateSignalWith2Args", Q_ARG(int, 42), Q_ARG(QString, "Hello")); QVERIFY(tester.testPassed); } // String based connects. Matching signals and slots { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignal1()), &tester, SLOT(testSlot())); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignal1()), &tester, SLOT(testSlot())); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignalWith1Arg(int)), &tester, SLOT(testSlotWith1Arg(int))); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignalWith1Arg(int)), &tester, SLOT(testSlotWith1Arg(int))); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlotWith2Args(int,QString))); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlotWith2Args(int,QString))); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } // Function pointer connects. Decayed slot arguments { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlot); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlot); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlotWith1Arg); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlotWith1Arg); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlot); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlot); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } // String based connects. Decayed slot arguments { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignalWith1Arg(int)), &tester, SLOT(testSlot())); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignalWith1Arg(int)), &tester, SLOT(testSlot())); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlotWith1Arg(int))); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlotWith1Arg(int))); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlot())); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { SubClassFromPrivateSignals subClassFromPrivateSignals; SignalConnectionTester tester; QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlot())); QVERIFY(!tester.testPassed); subClassFromPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } // Overloaded private signals { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, SIGNAL(privateOverloadedSignal()), &tester, SLOT(testSlot())); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, SIGNAL(privateOverloadedSignal(int)), &tester, SLOT(testSlotWith1Arg(int))); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } // We can't use function pointer connections to private signals which are overloaded because we would have to cast in this case to: // static_cast(&ClassWithPrivateSignals::privateOverloadedSignal) // Which doesn't work as ClassWithPrivateSignals::QPrivateSignal is private. // Overload with either private or not private signals { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, SIGNAL(overloadedMaybePrivate()), &tester, SLOT(testSlot())); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, SIGNAL(privateOverloadedSignal(int)), &tester, SLOT(testSlotWith1Arg(int))); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } { ClassWithPrivateSignals classWithPrivateSignals; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals, static_cast(&ClassWithPrivateSignals::overloadedMaybePrivate), &tester, &SignalConnectionTester::testSlot); QVERIFY(!tester.testPassed); classWithPrivateSignals.emitPrivateSignals(); QVERIFY(tester.testPassed); } // We can't use function pointer connections to private signals which are overloaded because we would have to cast in this case to: // static_cast(&ClassWithPrivateSignals::overloadedMaybePrivate) // Which doesn't work as ClassWithPrivateSignals::QPrivateSignal is private. // Connecting from one private signal to another { ClassWithPrivateSignals classWithPrivateSignals1; ClassWithPrivateSignals classWithPrivateSignals2; SignalConnectionTester tester; QObject::connect(&classWithPrivateSignals1, &ClassWithPrivateSignals::privateSignal1, &classWithPrivateSignals2, &ClassWithPrivateSignals::privateSignal1); QObject::connect(&classWithPrivateSignals2, &ClassWithPrivateSignals::privateSignal1, &tester, &SignalConnectionTester::testSlot); QVERIFY(!tester.testPassed); classWithPrivateSignals1.emitPrivateSignals(); QVERIFY(tester.testPassed); } } void tst_Moc::finalClasses_data() { QTest::addColumn("className"); QTest::addColumn("expected"); QTest::newRow("FinalTestClassQt") << FinalTestClassQt::staticMetaObject.className() << "FinalTestClassQt"; QTest::newRow("ExportedFinalTestClassQt") << ExportedFinalTestClassQt::staticMetaObject.className() << "ExportedFinalTestClassQt"; QTest::newRow("ExportedFinalTestClassQtX") << ExportedFinalTestClassQtX::staticMetaObject.className() << "ExportedFinalTestClassQtX"; QTest::newRow("FinalTestClassCpp11") << FinalTestClassCpp11::staticMetaObject.className() << "FinalTestClassCpp11"; QTest::newRow("ExportedFinalTestClassCpp11") << ExportedFinalTestClassCpp11::staticMetaObject.className() << "ExportedFinalTestClassCpp11"; QTest::newRow("ExportedFinalTestClassCpp11X") << ExportedFinalTestClassCpp11X::staticMetaObject.className() << "ExportedFinalTestClassCpp11X"; QTest::newRow("SealedTestClass") << SealedTestClass::staticMetaObject.className() << "SealedTestClass"; QTest::newRow("ExportedSealedTestClass") << ExportedSealedTestClass::staticMetaObject.className() << "ExportedSealedTestClass"; QTest::newRow("ExportedSealedTestClassX") << ExportedSealedTestClassX::staticMetaObject.className() << "ExportedSealedTestClassX"; } void tst_Moc::finalClasses() { QFETCH(QString, className); QFETCH(QString, expected); QCOMPARE(className, expected); } void tst_Moc::explicitOverrideControl_data() { QTest::addColumn("mo"); #define ADD(x) QTest::newRow(#x) << &x::staticMetaObject ADD(ExplicitOverrideControlFinalQt); ADD(ExplicitOverrideControlFinalCxx11); ADD(ExplicitOverrideControlSealed); ADD(ExplicitOverrideControlOverrideQt); ADD(ExplicitOverrideControlOverrideCxx11); ADD(ExplicitOverrideControlFinalQtOverrideQt); ADD(ExplicitOverrideControlFinalCxx11OverrideCxx11); ADD(ExplicitOverrideControlSealedOverride); #undef ADD } void tst_Moc::explicitOverrideControl() { QFETCH(const QMetaObject*, mo); QVERIFY(mo); QCOMPARE(mo->indexOfMethod("pureSlot0()"), mo->methodOffset() + 0); QCOMPARE(mo->indexOfMethod("pureSlot1()"), mo->methodOffset() + 1); QCOMPARE(mo->indexOfMethod("pureSlot2()"), mo->methodOffset() + 2); QCOMPARE(mo->indexOfMethod("pureSlot3()"), mo->methodOffset() + 3); #if 0 // moc doesn't support volatile slots QCOMPARE(mo->indexOfMethod("pureSlot4()"), mo->methodOffset() + 4); QCOMPARE(mo->indexOfMethod("pureSlot5()"), mo->methodOffset() + 5); QCOMPARE(mo->indexOfMethod("pureSlot6()"), mo->methodOffset() + 6); QCOMPARE(mo->indexOfMethod("pureSlot7()"), mo->methodOffset() + 7); QCOMPARE(mo->indexOfMethod("pureSlot8()"), mo->methodOffset() + 8); QCOMPARE(mo->indexOfMethod("pureSlot9()"), mo->methodOffset() + 9); #endif } class OverloadedAddressOperator : public QObject { Q_OBJECT public: void* operator&() { return nullptr; } signals: void self(OverloadedAddressOperator&); public slots: void assertSelf(OverloadedAddressOperator &o) { QCOMPARE(std::addressof(o), this); testResult = (std::addressof(o) == this); } public: bool testResult = false; }; void tst_Moc::overloadedAddressOperator() { OverloadedAddressOperator o; OverloadedAddressOperator *p = std::addressof(o); QCOMPARE(&o, nullptr); QVERIFY(p); QObject::connect(p, &OverloadedAddressOperator::self, p, &OverloadedAddressOperator::assertSelf); emit o.self(o); QVERIFY(o.testResult); } class CustomQObject : public QObject { Q_OBJECT Q_ENUMS(Number) public: enum Number { Zero, One, Two }; explicit CustomQObject(QObject *parent = nullptr) : QObject(parent) { } }; Q_DECLARE_METATYPE(CustomQObject::Number) typedef CustomQObject* CustomQObjectStar; Q_DECLARE_METATYPE(CustomQObjectStar); namespace SomeNamespace { class NamespacedQObject : public QObject { Q_OBJECT public: explicit NamespacedQObject(QObject *parent = nullptr) : QObject(parent) { } }; struct NamespacedNonQObject {}; } Q_DECLARE_METATYPE(SomeNamespace::NamespacedNonQObject) // Need different types for the invokable method tests because otherwise the registration // done in the property test would interfere. class CustomQObject2 : public QObject { Q_OBJECT Q_ENUMS(Number) public: enum Number { Zero, One, Two }; explicit CustomQObject2(QObject *parent = nullptr) : QObject(parent) { } }; Q_DECLARE_METATYPE(CustomQObject2::Number) typedef CustomQObject2* CustomQObject2Star; Q_DECLARE_METATYPE(CustomQObject2Star); namespace SomeNamespace2 { class NamespacedQObject2 : public QObject { Q_OBJECT public: explicit NamespacedQObject2(QObject *parent = nullptr) : QObject(parent) { } }; struct NamespacedNonQObject2 {}; } Q_DECLARE_METATYPE(SomeNamespace2::NamespacedNonQObject2) struct CustomObject3 {}; struct CustomObject4 {}; struct CustomObject5 {}; struct CustomObject6 {}; struct CustomObject7 {}; struct CustomObject8 {}; struct CustomObject9 {}; struct CustomObject10 {}; struct CustomObject11 {}; struct CustomObject12 {}; Q_DECLARE_METATYPE(CustomObject3) Q_DECLARE_METATYPE(CustomObject4) Q_DECLARE_METATYPE(CustomObject5) Q_DECLARE_METATYPE(CustomObject6) Q_DECLARE_METATYPE(CustomObject7) Q_DECLARE_METATYPE(CustomObject8) Q_DECLARE_METATYPE(CustomObject9) Q_DECLARE_METATYPE(CustomObject10) Q_DECLARE_METATYPE(CustomObject11) Q_DECLARE_METATYPE(CustomObject12) class AutoRegistrationObject : public QObject { Q_OBJECT Q_PROPERTY(QObject* object READ object CONSTANT) Q_PROPERTY(CustomQObject* customObject READ customObject CONSTANT) Q_PROPERTY(QSharedPointer customObjectP READ customObjectP CONSTANT) Q_PROPERTY(QWeakPointer customObjectWP READ customObjectWP CONSTANT) Q_PROPERTY(QPointer customObjectTP READ customObjectTP CONSTANT) Q_PROPERTY(QList listInt READ listInt CONSTANT) Q_PROPERTY(QList listVariant READ listVariant CONSTANT) Q_PROPERTY(QList listObject READ listObject CONSTANT) Q_PROPERTY(QList> listListInt READ listListInt CONSTANT) Q_PROPERTY(QList> listListObject READ listListObject CONSTANT) Q_PROPERTY(CustomQObject::Number enumValue READ enumValue CONSTANT) Q_PROPERTY(CustomQObjectStar customObjectTypedef READ customObjectTypedef CONSTANT) Q_PROPERTY(SomeNamespace::NamespacedQObject* customObjectNamespaced READ customObjectNamespaced CONSTANT) Q_PROPERTY(SomeNamespace::NamespacedNonQObject customNonQObjectNamespaced READ customNonQObjectNamespaced CONSTANT) public: AutoRegistrationObject(QObject *parent = nullptr) : QObject(parent) { } QObject* object() const { return 0; } QSharedPointer customObjectP() const { return QSharedPointer(); } QWeakPointer customObjectWP() const { return QWeakPointer(); } QPointer customObjectTP() const { return QPointer(); } CustomQObject* customObject() const { return 0; } QList listInt() const { return QList(); } QList listVariant() const { return QList(); } QList listObject() const { return QList(); } QList> listListInt() const { return QList>(); } QList> listListObject() const { return QList>(); } CustomQObject::Number enumValue() const { return CustomQObject::Zero; } CustomQObjectStar customObjectTypedef() const { return 0; } SomeNamespace::NamespacedQObject* customObjectNamespaced() const { return 0; } SomeNamespace::NamespacedNonQObject customNonQObjectNamespaced() const { return SomeNamespace::NamespacedNonQObject(); } public slots: void objectSlot(QObject*) {} void customObjectSlot(CustomQObject2*) {} void sharedPointerSlot(QSharedPointer) {} void weakPointerSlot(QWeakPointer) {} void trackingPointerSlot(QPointer) {} void listIntSlot(QList) {} void listVariantSlot(QList) { } void listCustomObjectSlot(QList) {} void listListIntSlot(QList>) { } void listListCustomObjectSlot(QList>) { } void enumSlot(CustomQObject2::Number) {} void typedefSlot(CustomQObject2Star) {} void namespacedQObjectSlot(SomeNamespace2::NamespacedQObject2*) {} void namespacedNonQObjectSlot(SomeNamespace2::NamespacedNonQObject2) {} void bu1(int, CustomObject3) {} void bu2(CustomObject4, int) {} void bu3(CustomObject5, CustomObject6) {} void bu4(CustomObject7, int, CustomObject8) {} void bu5(int, CustomObject9, CustomObject10) {} void bu6(int, CustomObject11, int) {} // these can't be registered, but they should at least compile void ref1(int&) {} void ref2(QList&) {} void ref3(CustomQObject2&) {} void ref4(QSharedPointer&) {} signals: void someSignal(CustomObject12); }; void tst_Moc::autoPropertyMetaTypeRegistration() { AutoRegistrationObject aro; static const int numPropertiesUnderTest = 15; QList propertyMetaTypeIds; propertyMetaTypeIds.reserve(numPropertiesUnderTest); const QMetaObject *metaObject = aro.metaObject(); QCOMPARE(metaObject->propertyCount(), numPropertiesUnderTest); for (int i = 0; i < metaObject->propertyCount(); ++i) { const QMetaProperty prop = metaObject->property(i); propertyMetaTypeIds.append(prop.userType()); QVariant var = prop.read(&aro); QVERIFY(var.isValid()); } // Verify that QMetaProperty::userType gave us what we expected. QList expectedMetaTypeIds = QList() << QMetaType::QString // QObject::userType << QMetaType::QObjectStar // AutoRegistrationObject::object << qMetaTypeId() // etc. << qMetaTypeId>() << qMetaTypeId>() << qMetaTypeId>() << qMetaTypeId>() << qMetaTypeId>() << qMetaTypeId>() << qMetaTypeId>>() << qMetaTypeId>>() << qMetaTypeId() << qMetaTypeId() << qMetaTypeId() << qMetaTypeId(); QCOMPARE(propertyMetaTypeIds, expectedMetaTypeIds); } template struct DefaultConstructor { static inline T construct() { return T(); } }; template struct DefaultConstructor { static inline T* construct() { return 0; } }; void tst_Moc::autoMethodArgumentMetaTypeRegistration() { AutoRegistrationObject aro; QList methodArgMetaTypeIds; const QMetaObject *metaObject = aro.metaObject(); int i = metaObject->methodOffset(); // Start after QObject built-in slots; while (i < metaObject->methodCount()) { // Skip over signals so we start at the first slot. const QMetaMethod method = metaObject->method(i); if (method.methodType() == QMetaMethod::Signal) ++i; else break; } #define TYPE_LOOP(TYPE) \ { \ const QMetaMethod method = metaObject->method(i); \ for (int j = 0; j < method.parameterCount(); ++j) \ methodArgMetaTypeIds.append(method.parameterType(j)); \ QVERIFY(method.invoke(&aro, Q_ARG(TYPE, DefaultConstructor::construct()))); \ ++i; \ } #define FOR_EACH_SLOT_ARG_TYPE(F) \ F(QObject *) \ F(CustomQObject2 *) \ F(QSharedPointer) \ F(QWeakPointer) \ F(QPointer) \ F(QList) \ F(QList) \ F(QList) \ F(QList>) \ F(QList>) \ F(CustomQObject2::Number) \ F(CustomQObject2Star) \ F(SomeNamespace2::NamespacedQObject2 *) \ F(SomeNamespace2::NamespacedNonQObject2) // Note: mulit-arg slots are tested below. FOR_EACH_SLOT_ARG_TYPE(TYPE_LOOP) #undef TYPE_LOOP #undef FOR_EACH_SLOT_ARG_TYPE QList expectedMetaTypeIds = QList() << QMetaType::QObjectStar << qMetaTypeId() << qMetaTypeId>() << qMetaTypeId>() << qMetaTypeId>() << qMetaTypeId>() << qMetaTypeId>() << qMetaTypeId>() << qMetaTypeId>>() << qMetaTypeId>>() << qMetaTypeId() << qMetaTypeId() << qMetaTypeId() << qMetaTypeId(); QCOMPARE(methodArgMetaTypeIds, expectedMetaTypeIds); QList methodMultiArgMetaTypeIds; { const QMetaMethod method = metaObject->method(i); QCOMPARE(method.name(), QByteArray("bu1")); for (int j = 0; j < method.parameterCount(); ++j) methodMultiArgMetaTypeIds.append(method.parameterType(j)); QVERIFY(method.invoke(&aro, Q_ARG(int, 42), Q_ARG(CustomObject3, CustomObject3()))); ++i; } { const QMetaMethod method = metaObject->method(i); QCOMPARE(method.name(), QByteArray("bu2")); for (int j = 0; j < method.parameterCount(); ++j) methodMultiArgMetaTypeIds.append(method.parameterType(j)); QVERIFY(method.invoke(&aro, Q_ARG(CustomObject4, CustomObject4()), Q_ARG(int, 42))); ++i; } { const QMetaMethod method = metaObject->method(i); QCOMPARE(method.name(), QByteArray("bu3")); for (int j = 0; j < method.parameterCount(); ++j) methodMultiArgMetaTypeIds.append(method.parameterType(j)); QVERIFY(method.invoke(&aro, Q_ARG(CustomObject5, CustomObject5()), Q_ARG(CustomObject6, CustomObject6()))); ++i; } { const QMetaMethod method = metaObject->method(i); QCOMPARE(method.name(), QByteArray("bu4")); for (int j = 0; j < method.parameterCount(); ++j) methodMultiArgMetaTypeIds.append(method.parameterType(j)); QVERIFY(method.invoke(&aro, Q_ARG(CustomObject7, CustomObject7()), Q_ARG(int, 42), Q_ARG(CustomObject8, CustomObject8()))); ++i; } { const QMetaMethod method = metaObject->method(i); QCOMPARE(method.name(), QByteArray("bu5")); for (int j = 0; j < method.parameterCount(); ++j) methodMultiArgMetaTypeIds.append(method.parameterType(j)); QVERIFY(method.invoke(&aro, Q_ARG(int, 42), Q_ARG(CustomObject9, CustomObject9()), Q_ARG(CustomObject10, CustomObject10()))); ++i; } { const QMetaMethod method = metaObject->method(i); QCOMPARE(method.name(), QByteArray("bu6")); for (int j = 0; j < method.parameterCount(); ++j) methodMultiArgMetaTypeIds.append(method.parameterType(j)); QVERIFY(method.invoke(&aro, Q_ARG(int, 42), Q_ARG(CustomObject11, CustomObject11()), Q_ARG(int, 42))); ++i; } QList expectedMultiMetaTypeIds = QList() << QMetaType::Int << qMetaTypeId() << qMetaTypeId() << QMetaType::Int << qMetaTypeId() << qMetaTypeId() << qMetaTypeId() << QMetaType::Int << qMetaTypeId() << QMetaType::Int << qMetaTypeId() << qMetaTypeId() << QMetaType::Int << qMetaTypeId() << QMetaType::Int; QCOMPARE(methodMultiArgMetaTypeIds, expectedMultiMetaTypeIds); } void tst_Moc::autoSignalSpyMetaTypeRegistration() { AutoRegistrationObject aro; QList methodArgMetaTypeIds; const QMetaObject *metaObject = aro.metaObject(); int i = metaObject->indexOfSignal(QMetaObject::normalizedSignature("someSignal(CustomObject12)")); QVERIFY(i > 0); QCOMPARE(QMetaType::fromName("CustomObject12").id(), (int)QMetaType::UnknownType); QSignalSpy spy(&aro, SIGNAL(someSignal(CustomObject12))); QVERIFY(QMetaType::fromName("CustomObject12").id() != QMetaType::UnknownType); QCOMPARE(QMetaType::fromName("CustomObject12").id(), qMetaTypeId()); } void tst_Moc::parseDefines() { const QMetaObject *mo = &PD_NAMESPACE::PD_CLASSNAME::staticMetaObject; QCOMPARE(mo->className(), PD_SCOPED_STRING(PD_NAMESPACE, PD_CLASSNAME)); QVERIFY(mo->indexOfSlot("voidFunction()") != -1); int index = mo->indexOfSlot("stringMethod()"); QVERIFY(index != -1); QCOMPARE(mo->method(index).returnType(), int(QMetaType::QString)); index = mo->indexOfSlot("combined1()"); QVERIFY(index != -1); index = mo->indexOfSlot("combined2()"); QVERIFY(index != -1); index = mo->indexOfSlot("combined3()"); QVERIFY(index != -1); index = mo->indexOfSlot("combined4(int,int)"); QVERIFY(index != -1); index = mo->indexOfSlot("combined5()"); QVERIFY(index != -1); index = mo->indexOfSlot("combined6()"); QVERIFY(index != -1); index = mo->indexOfSlot("vararg1()"); QVERIFY(index != -1); index = mo->indexOfSlot("vararg2(int)"); QVERIFY(index != -1); index = mo->indexOfSlot("vararg3(int,int)"); QVERIFY(index != -1); index = mo->indexOfSlot("vararg4()"); QVERIFY(index != -1); index = mo->indexOfSlot("vararg5(int)"); QVERIFY(index != -1); index = mo->indexOfSlot("vararg6(int,int)"); QVERIFY(index != -1); index = mo->indexOfSlot("INNERFUNCTION(int)"); QVERIFY(index != -1); index = mo->indexOfSlot("inner_expanded(int)"); QVERIFY(index != -1); index = mo->indexOfSlot("expanded_method(int)"); QVERIFY(index != -1); index = mo->indexOfSlot("conditionSlot()"); QVERIFY(index != -1); int count = 0; for (int i = 0; i < mo->classInfoCount(); ++i) { QMetaClassInfo mci = mo->classInfo(i); if (!qstrcmp(mci.name(), "TestString")) { ++count; QVERIFY(!qstrcmp(mci.value(), "PD_CLASSNAME")); } if (!qstrcmp(mci.name(), "TestString2")) { ++count; QVERIFY(!qstrcmp(mci.value(), "ParseDefine")); } if (!qstrcmp(mci.name(), "TestString3")) { ++count; QVERIFY(!qstrcmp(mci.value(), "TestValue")); } } QCOMPARE(count, 3); index = mo->indexOfSlot("PD_DEFINE_ITSELF_SUFFIX(int)"); QVERIFY(index != -1); index = mo->indexOfSignal("cmdlineSignal(QMap)"); QVERIFY(index != -1); index = mo->indexOfSignal("signalQTBUG55853()"); QVERIFY(index != -1); } void tst_Moc::preprocessorOnly() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; proc.start(m_moc, QStringList() << "-E" << m_sourceDirectory + QStringLiteral("/pp-dollar-signs.h")); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 0); QByteArray mocOut = proc.readAllStandardOutput(); QVERIFY(!mocOut.isEmpty()); QCOMPARE(proc.readAllStandardError(), QByteArray()); QVERIFY(mocOut.contains("$$ = parser->createFoo()")); #else QSKIP("Only tested on unix/gcc"); #endif } void tst_Moc::unterminatedFunctionMacro() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if defined(Q_OS_UNIX) && defined(Q_CC_GNU) && QT_CONFIG(process) QProcess proc; proc.start(m_moc, QStringList() << "-E" << m_sourceDirectory + QStringLiteral("/unterminated-function-macro.h")); QVERIFY(proc.waitForFinished()); QCOMPARE(proc.exitCode(), 1); QCOMPARE(proc.readAllStandardOutput(), QByteArray()); QByteArray errorOutput = proc.readAllStandardError(); QVERIFY(!errorOutput.isEmpty()); QVERIFY(errorOutput.contains("missing ')' in macro usage")); #else QSKIP("Only tested on linux/gcc"); #endif } namespace QTBUG32933_relatedObjectsDontIncludeItself { namespace NS { class Obj : QObject { Q_OBJECT Q_PROPERTY(MyEnum p1 MEMBER member) Q_PROPERTY(Obj::MyEnum p2 MEMBER member) Q_PROPERTY(NS::Obj::MyEnum p3 MEMBER member) Q_PROPERTY(QTBUG32933_relatedObjectsDontIncludeItself::NS::Obj::MyEnum p4 MEMBER member) Q_ENUMS(MyEnum); public: enum MyEnum { Something, SomethingElse }; MyEnum member; }; } } void tst_Moc::QTBUG32933_relatedObjectsDontIncludeItself() { const QMetaObject *mo = &QTBUG32933_relatedObjectsDontIncludeItself::NS::Obj::staticMetaObject; const auto *objects = mo->d.relatedMetaObjects; // the related objects should be empty because the enums is in the same object. QVERIFY(!objects); } class UnrelatedClass : public QObject { Q_OBJECT Q_ENUMS(UnrelatedEnum) public: enum UnrelatedEnum { UnrelatedInvalidValue = -1, UnrelatedValue = 42 }; }; // The presence of this macro used to confuse moc and prevent // UnrelatedClass from being listed in the related meta objects. Q_DECLARE_METATYPE(UnrelatedClass::UnrelatedEnum) class TestClassReferencingUnrelatedEnum : public QObject { Q_OBJECT Q_PROPERTY(UnrelatedClass::UnrelatedEnum enumProperty READ enumProperty WRITE setEnumProperty) public: TestClassReferencingUnrelatedEnum() : m_enumProperty(UnrelatedClass::UnrelatedInvalidValue) {} UnrelatedClass::UnrelatedEnum enumProperty() const { return m_enumProperty; } void setEnumProperty(UnrelatedClass::UnrelatedEnum arg) { m_enumProperty = arg; } private: UnrelatedClass::UnrelatedEnum m_enumProperty; }; void tst_Moc::writeEnumFromUnrelatedClass() { TestClassReferencingUnrelatedEnum obj; QString enumValueAsString("UnrelatedValue"); obj.setProperty("enumProperty", enumValueAsString); QCOMPARE(int(obj.enumProperty()), int(UnrelatedClass::UnrelatedValue)); } void tst_Moc::relatedMetaObjectsWithinNamespaces() { const QMetaObject *relatedMo = &QTBUG_2151::A::staticMetaObject; const QMetaObject *testMo = &QTBUG_2151::B::staticMetaObject; QVERIFY(testMo->d.relatedMetaObjects); QCOMPARE(testMo->d.relatedMetaObjects[0], relatedMo); } void tst_Moc::relatedMetaObjectsInGadget() { const QMetaObject *relatedMo = &QTBUG_35657::A::staticMetaObject; const QMetaObject *testMo = &QTBUG_35657::B::staticMetaObject; QVERIFY(testMo->d.relatedMetaObjects); QCOMPARE(testMo->d.relatedMetaObjects[0], relatedMo); } void tst_Moc::relatedMetaObjectsNameConflict_data() { typedef QList QMetaObjects; QTest::addColumn("dependingObject"); QTest::addColumn("relatedMetaObjects"); //NS1 const QMetaObject *n1gadget = &NS1::Gadget::staticMetaObject; const QMetaObject *n1object = &NS1::Object::staticMetaObject; const QMetaObject *n1nestedGadget = &NS1::Nested::Gadget::staticMetaObject; const QMetaObject *n1nestedObject = &NS1::Nested::Object::staticMetaObject; //N2 const QMetaObject *n2gadget = &NS2::Gadget::staticMetaObject; const QMetaObject *n2object = &NS2::Object::staticMetaObject; const QMetaObject *n2nestedGadget = &NS2::Nested::Gadget::staticMetaObject; const QMetaObject *n2nestedObject = &NS2::Nested::Object::staticMetaObject; QTest::newRow("N1::dependingObject") << &NS1::DependingObject::staticMetaObject << (QMetaObjects() << n1gadget << n1object); QTest::newRow("N2::dependingObject") << &NS2::DependingObject::staticMetaObject << (QMetaObjects() << n2gadget << n2object); QTest::newRow("N1::dependingNestedObject") << &NS1::DependingNestedObject::staticMetaObject << (QMetaObjects() << n1nestedObject); QTest::newRow("N2::dependingNestedObject") << &NS2::DependingNestedObject::staticMetaObject << (QMetaObjects() << n2nestedObject); QTest::newRow("N1::dependingNestedGadget") << &NS1::DependingNestedGadget::staticMetaObject << (QMetaObjects() << n1nestedGadget); QTest::newRow("N2::dependingNestedGadget") << &NS2::DependingNestedGadget::staticMetaObject << (QMetaObjects() << n2nestedGadget); } void tst_Moc::relatedMetaObjectsNameConflict() { typedef QList QMetaObjects; QFETCH(const QMetaObject*, dependingObject); QFETCH(QMetaObjects, relatedMetaObjects); // load all specified metaobjects int a set QSet dependency; const auto *i = dependingObject->d.relatedMetaObjects; while (*i) { dependency.insert(*i); ++i; } // check if all required metaobjects are specified foreach (const QMetaObject *mo, relatedMetaObjects) QVERIFY(dependency.contains(mo)); // check if no additional metaobjects ara specified QCOMPARE(dependency.size(), relatedMetaObjects.size()); } class StringLiteralsInMacroExtension: public QObject { Q_OBJECT #define Macro(F) F " " F Q_CLASSINFO(Macro("String"), Macro("Literal")) #undef Macro #define Macro(F) F Q_CLASSINFO("String" Macro("!"), "Literal" Macro("!")) Q_CLASSINFO(Macro("!") "String", Macro("!") "Literal") #undef Macro #define Macro "foo" Q_CLASSINFO("String" Macro, "Literal" Macro) Q_CLASSINFO(Macro "String", Macro "Literal") #undef Macro }; void tst_Moc::strignLiteralsInMacroExtension() { const QMetaObject *mobj = &StringLiteralsInMacroExtension::staticMetaObject; QCOMPARE(mobj->classInfoCount(), 5); QCOMPARE(mobj->classInfo(0).name(), "String String"); QCOMPARE(mobj->classInfo(0).value(), "Literal Literal"); QCOMPARE(mobj->classInfo(1).name(), "String!"); QCOMPARE(mobj->classInfo(1).value(), "Literal!"); QCOMPARE(mobj->classInfo(2).name(), "!String"); QCOMPARE(mobj->classInfo(2).value(), "!Literal"); QCOMPARE(mobj->classInfo(3).name(), "Stringfoo"); QCOMPARE(mobj->classInfo(3).value(), "Literalfoo"); QCOMPARE(mobj->classInfo(4).name(), "fooString"); QCOMPARE(mobj->classInfo(4).value(), "fooLiteral"); } class VeryLongStringData : public QObject { Q_OBJECT #define repeat2(V) V V #define repeat4(V) repeat2(V) repeat2(V) #define repeat8(V) repeat4(V) repeat4(V) #define repeat16(V) repeat8(V) repeat8(V) #define repeat32(V) repeat16(V) repeat16(V) #define repeat64(V) repeat32(V) repeat32(V) #define repeat128(V) repeat64(V) repeat64(V) #define repeat256(V) repeat128(V) repeat128(V) #define repeat512(V) repeat256(V) repeat256(V) #define repeat1024(V) repeat512(V) repeat512(V) #define repeat2048(V) repeat1024(V) repeat1024(V) #define repeat4096(V) repeat2048(V) repeat2048(V) #define repeat8192(V) repeat4096(V) repeat4096(V) #define repeat16384(V) repeat8192(V) repeat8192(V) #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")) Q_CLASSINFO(":", ")") #undef repeat2 #undef repeat4 #undef repeat8 #undef repeat16 #undef repeat32 #undef repeat64 #undef repeat128 #undef repeat256 #undef repeat512 #undef repeat1024 #undef repeat2048 #undef repeat4096 #undef repeat8192 #undef repeat16384 #undef repeat32768 #undef repeat65534 }; void tst_Moc::unnamedNamespaceObjectsAndGadgets() { // these just test very basic functionality of gadgets and objects // defined in unnamed namespaces. { GadgetInUnnamedNS gadget(21, 42); QCOMPARE(gadget.x(), 21); QCOMPARE(gadget.y(), 42); gadget.staticMetaObject.property(0).writeOnGadget(&gadget, 12); gadget.staticMetaObject.property(1).writeOnGadget(&gadget, 24); QCOMPARE(gadget.x(), 12); QCOMPARE(gadget.y(), 24); } { ObjectInUnnamedNS object; QObject *qObject = &object; QCOMPARE(static_cast(qObject), qobject_cast(qObject)); } } void tst_Moc::veryLongStringData() { const QMetaObject *mobj = &VeryLongStringData::staticMetaObject; 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(65534)); QCOMPARE(strlen(mobj->classInfo(startAt + 0).value()), static_cast(65534)); QCOMPARE(strlen(mobj->classInfo(startAt + 1).name()), static_cast(65534)); QCOMPARE(strlen(mobj->classInfo(startAt + 1).value()), static_cast(65534)); QCOMPARE(strlen(mobj->classInfo(startAt + 2).name()), static_cast(32768)); QCOMPARE(strlen(mobj->classInfo(startAt + 2).value()), static_cast(32768)); QCOMPARE(strlen(mobj->classInfo(startAt + 3).name()), static_cast(1)); QCOMPARE(strlen(mobj->classInfo(startAt + 3).value()), static_cast(1)); } void tst_Moc::gadgetHierarchy() { QCOMPARE(NonGadgetParent::Derived::staticMetaObject.superClass(), static_cast(nullptr)); QCOMPARE(GrandParentGadget::DerivedGadget::staticMetaObject.superClass(), &GrandParentGadget::BaseGadget::staticMetaObject); QCOMPARE(GrandParentGadget::CRTPDerivedGadget::staticMetaObject.superClass(), &GrandParentGadget::BaseGadget::staticMetaObject); } void tst_Moc::optionsFileError_data() { QTest::addColumn("optionsArgument"); QTest::newRow("no filename") << QStringLiteral("@"); QTest::newRow("nonexistent file") << QStringLiteral("@letshuntasnark"); } void tst_Moc::optionsFileError() { #ifdef MOC_CROSS_COMPILED QSKIP("Not tested when cross-compiled"); #endif #if QT_CONFIG(process) QFETCH(QString, optionsArgument); QProcess p; p.start(m_moc, QStringList(optionsArgument)); QVERIFY(p.waitForFinished()); QCOMPARE(p.exitCode(), 1); QVERIFY(p.readAllStandardOutput().isEmpty()); const QByteArray err = p.readAllStandardError(); QVERIFY(err.contains("moc: ")); QVERIFY(!err.contains("QCommandLineParser")); #endif } static void checkEnum(const QMetaEnum &enumerator, const QByteArray &name, const QList> &keys) { QCOMPARE(name, QByteArray{enumerator.name()}); QCOMPARE(keys.size(), enumerator.keyCount()); for (int i = 0; i < enumerator.keyCount(); ++i) { QCOMPARE(keys[i].first, QByteArray{enumerator.key(i)}); QCOMPARE(keys[i].second, enumerator.value(i)); } } class EnumFromNamespaceClass : public QObject { Q_OBJECT Q_PROPERTY(FooNamespace::Enum1 prop READ prop CONSTANT) public: FooNamespace::Enum1 prop() { return FooNamespace::Enum1::Key2; } }; void tst_Moc::testQNamespace() { QCOMPARE(TestQNamespace::staticMetaObject.enumeratorCount(), 4); 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", {{"None", 0}, {"Flag1", 1}, {"Flag2", 2}, {"Any", 1 | 2}}); checkEnum(TestQNamespace::staticMetaObject.enumerator(3), "TestFlag2", {{"None", 0}, {"Flag1", 4}, {"Flag2", 8}, {"Any", 4 | 8}}); QCOMPARE(TestQNamespace::TestGadget::staticMetaObject.enumeratorCount(), 2); checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(0), "TestGEnum1", {{"Key1", 13}, {"Key2", 14}}); checkEnum(TestQNamespace::TestGadget::staticMetaObject.enumerator(1), "TestGEnum2", {{"Key1", 23}, {"Key2", 24}}); QCOMPARE(TestQNamespace::TestGadgetExport::staticMetaObject.enumeratorCount(), 2); checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(0), "TestGeEnum1", {{"Key1", 20}, {"Key2", 21}}); checkEnum(TestQNamespace::TestGadgetExport::staticMetaObject.enumerator(1), "TestGeEnum2", {{"Key1", 23}, {"Key2", 24}}); QMetaEnum meta = QMetaEnum::fromType(); QVERIFY(meta.isValid()); QCOMPARE(meta.name(), "TestEnum1"); QCOMPARE(meta.enclosingMetaObject(), &TestQNamespace::staticMetaObject); QCOMPARE(meta.keyCount(), 2); QCOMPARE(TestExportNamespace::staticMetaObject.enumeratorCount(), 1); checkEnum(TestExportNamespace::staticMetaObject.enumerator(0), "MyEnum", {{"Key1", 0}, {"Key2", 1}}); QCOMPARE(FooNamespace::staticMetaObject.enumeratorCount(), 1); QCOMPARE(FooNamespace::FooNestedNamespace::staticMetaObject.enumeratorCount(), 2); QCOMPARE(FooNamespace::FooNestedNamespace::FooMoreNestedNamespace::staticMetaObject.enumeratorCount(), 1); EnumFromNamespaceClass obj; const QVariant prop = obj.property("prop"); QCOMPARE(prop.userType(), QMetaType::fromType().id()); QCOMPARE(prop.toInt(), int(FooNamespace::Enum1::Key2)); } void tst_Moc::cxx17Namespaces() { QCOMPARE(CXX17Namespace::A::B::C::D::staticMetaObject.className(), "CXX17Namespace::A::B::C::D"); QCOMPARE(CXX17Namespace::A::B::C::D::staticMetaObject.enumeratorCount(), 1); QCOMPARE(CXX17Namespace::A::B::C::D::staticMetaObject.enumerator(0).name(), "NamEn"); QCOMPARE(QMetaEnum::fromType().name(), "NamEn"); QCOMPARE(QMetaEnum::fromType().keyCount(), 1); QCOMPARE(QMetaEnum::fromType().value(0), 4); QCOMPARE(CXX17Namespace::A::B::C::D::ClassInNamespace::staticMetaObject.className(), "CXX17Namespace::A::B::C::D::ClassInNamespace"); QCOMPARE(CXX17Namespace::A::B::C::D::ClassInNamespace::staticMetaObject.enumeratorCount(), 1); QCOMPARE(CXX17Namespace::A::B::C::D::ClassInNamespace::staticMetaObject.enumerator(0).name(), "GadEn"); QCOMPARE(QMetaEnum::fromType().name(), "GadEn"); QCOMPARE(QMetaEnum::fromType().keyCount(), 1); QCOMPARE(QMetaEnum::fromType().value(0), 3); } 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); for (auto a: {"deprecatedSlot", "deprecatedSlot2", "deprecatedReason", "deprecatedReasonWithLBRACK", "deprecatedReasonWith2LBRACK", "deprecatedReasonWithRBRACK", "deprecatedReasonWith2RBRACK", "slotWithArguments" #if !defined(_MSC_VER) || _MSC_VER >= 1912 , "noreturnSlot", "noreturnSlot2", "returnInt", "noreturnDeprecatedSlot", "noreturnSlot3" #endif }) { QVERIFY(so.indexOfSlot(a) != 1); } QCOMPARE(TestQNamespaceDeprecated::staticMetaObject.enumeratorCount(), 2); checkEnum(TestQNamespaceDeprecated::staticMetaObject.enumerator(0), "TestEnum1", {{"Key1", 11}, {"Key2", 12}, {"Key3", 13}, {"Key4", 14}, {"Key5", 15}, {"Key6", 16}, {"Key7", 17}}); checkEnum(TestQNamespaceDeprecated::staticMetaObject.enumerator(1), "TestFlag1", {{"None", 0}, {"Flag1", 1}, {"Flag2", 2}, {"Flag3", 3}, {"Any", 1 | 2 | 3}}); QCOMPARE(TestQNamespaceDeprecated::TestGadget::staticMetaObject.enumeratorCount(), 1); checkEnum(TestQNamespaceDeprecated::TestGadget::staticMetaObject.enumerator(0), "TestGEnum1", {{"Key1", 13}, {"Key2", 14}, {"Key3", 15}}); QMetaEnum meta = QMetaEnum::fromType(); QVERIFY(meta.isValid()); QCOMPARE(meta.name(), "TestEnum1"); QCOMPARE(meta.enclosingMetaObject(), &TestQNamespaceDeprecated::staticMetaObject); QCOMPARE(meta.keyCount(), 7); } void tst_Moc::mocJsonOutput() { const auto readFile = [](const QString &fileName) { QFile f(fileName); f.open(QIODevice::ReadOnly); return QJsonDocument::fromJson(f.readAll()); }; QString actualFile = QStringLiteral(":/allmocs.json"); QString expectedFile = QStringLiteral(":/allmocs_baseline.json"); if (!QFile::exists(actualFile)) { // TODO: necessary with cmake as we cannot generate the qrc file soon enough auto const appDir = QCoreApplication::applicationDirPath(); actualFile = appDir + QDir::separator() + QLatin1String("./allmocs.json"); expectedFile = appDir + QDir::separator() + QLatin1String("./allmocs_baseline.json"); } QVERIFY2(QFile::exists(actualFile), qPrintable(actualFile)); QVERIFY2(QFile::exists(expectedFile), qPrintable(expectedFile)); QJsonDocument actualOutput = readFile(actualFile); QJsonDocument expectedOutput = readFile(expectedFile); const auto showPotentialDiff = [](const QJsonDocument &actual, const QJsonDocument &expected) -> QByteArray { #if defined(Q_OS_UNIX) QByteArray actualStr = actual.toJson(); QByteArray expectedStr = expected.toJson(); QTemporaryFile actualFile; if (!actualFile.open()) return "Error opening actual temp file"; actualFile.write(actualStr); actualFile.flush(); QTemporaryFile expectedFile; if (!expectedFile.open()) return "Error opening expected temp file"; expectedFile.write(expectedStr); expectedFile.flush(); QProcess diffProc; diffProc.setProgram("diff"); diffProc.setArguments(QStringList() << "-ub" << expectedFile.fileName() << actualFile.fileName()); diffProc.start(); if (!diffProc.waitForStarted()) return "Error waiting for diff process to start."; if (!diffProc.waitForFinished()) return "Error waiting for diff process to finish."; return diffProc.readAllStandardOutput(); #else return "Cannot launch diff. Please check allmocs.json and allmocs_baseline.json on disk."; #endif }; QVERIFY2(actualOutput == expectedOutput, showPotentialDiff(actualOutput, expectedOutput).constData()); } void TestFwdProperties::setProp1(const FwdClass1 &v) { prop1.reset(new FwdClass1(v)); } void TestFwdProperties::setProp2(const FwdClass2 &v) { prop2.reset(new FwdClass2(v)); } void TestFwdProperties::setProp3(const FwdClass3 &v) { prop3.reset(new FwdClass3(v)); } TestFwdProperties::~TestFwdProperties() {} Q_DECLARE_METATYPE(FwdClass1); void tst_Moc::mocInclude() { TestFwdProperties obj; obj.setProperty("prop1", QVariant::fromValue(FwdClass1 { 45 })); QCOMPARE(obj.prop1->x, 45); } class RequiredTest :public QObject { Q_OBJECT Q_PROPERTY(int required MEMBER m_required REQUIRED) Q_PROPERTY(int notRequired MEMBER m_notRequired) private: int m_required; int m_notRequired; }; void tst_Moc::requiredProperties() { QMetaObject mo = RequiredTest::staticMetaObject; QMetaProperty required = mo.property(mo.indexOfProperty("required")); QVERIFY(required.isValid()); QVERIFY(required.isRequired()); QMetaProperty notRequired = mo.property(mo.indexOfProperty("notRequired")); QVERIFY(notRequired.isValid()); QVERIFY(!notRequired.isRequired()); } class ClassWithQPropertyMembers : public QObject { Q_OBJECT Q_PROPERTY(int publicProperty MEMBER publicProperty BINDABLE bindablePublicProperty NOTIFY publicPropertyChanged) Q_PROPERTY(int privateExposedProperty MEMBER privateExposedProperty) public: signals: void publicPropertyChanged(); public: QBindable bindablePublicProperty() { return QBindable(&publicProperty); } Q_OBJECT_BINDABLE_PROPERTY(ClassWithQPropertyMembers, int, publicProperty, &ClassWithQPropertyMembers::publicPropertyChanged); QProperty notExposed; protected: QProperty protectedProperty; private: QProperty privateProperty; QProperty privateExposedProperty; }; void tst_Moc::qpropertyMembers() { const auto metaObject = &ClassWithQPropertyMembers::staticMetaObject; QCOMPARE(metaObject->propertyCount() - metaObject->superClass()->propertyCount(), 2); QCOMPARE(metaObject->indexOfProperty("notExposed"), -1); QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("publicProperty")); QVERIFY(prop.isValid()); QVERIFY(metaObject->property(metaObject->indexOfProperty("privateExposedProperty")).isValid()); ClassWithQPropertyMembers instance; prop.write(&instance, 42); QCOMPARE(instance.publicProperty.value(), 42); QSignalSpy publicPropertySpy(&instance, SIGNAL(publicPropertyChanged())); instance.publicProperty.setValue(100); QCOMPARE(prop.read(&instance).toInt(), 100); QCOMPARE(publicPropertySpy.size(), 1); QCOMPARE(prop.metaType(), QMetaType(QMetaType::Int)); QVERIFY(prop.notifySignal().isValid()); } void tst_Moc::observerMetaCall() { const auto metaObject = &ClassWithQPropertyMembers::staticMetaObject; QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("publicProperty")); QVERIFY(prop.isValid()); ClassWithQPropertyMembers instance; int observerCallCount = 0; auto observer = [&observerCallCount]() { ++observerCallCount; }; auto bindable = prop.bindable(&instance); QVERIFY(bindable.isBindable()); auto handler = bindable.onValueChanged(observer); QCOMPARE(observerCallCount, 0); instance.publicProperty.setValue(100); QCOMPARE(observerCallCount, 1); instance.publicProperty.setValue(101); QCOMPARE(observerCallCount, 2); } void tst_Moc::setQPRopertyBinding() { const auto metaObject = &ClassWithQPropertyMembers::staticMetaObject; QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("publicProperty")); QVERIFY(prop.isValid()); ClassWithQPropertyMembers instance; bool bindingCalled = false; auto binding = Qt::makePropertyBinding([&bindingCalled]() { bindingCalled = true; return 42; }); auto bindable = prop.bindable(&instance); QVERIFY(bindable.isBindable()); bindable.setBinding(binding); QCOMPARE(instance.publicProperty.value(), 42); QVERIFY(bindingCalled); // but now it should've been called :) } class ClassWithPrivateQPropertyShim :public QObject { Q_OBJECT public: Q_PROPERTY(int testProperty READ testProperty WRITE setTestProperty BINDABLE bindableTestProperty NOTIFY testPropertyChanged) Q_PROPERTY(int testProperty2 READ testProperty2 WRITE setTestProperty2 BINDABLE bindableTestProperty2) //Q_PROPERTY(d_func(), int, lazyTestProperty, setLazyTestProperty, NOTIFY lazyTestPropertyChanged) signals: void testPropertyChanged(); void lazyTestPropertyChanged(); public: int testProperty() const { return priv.testProperty; } void setTestProperty(int val) { priv.testProperty = val; } int testProperty2() const { return priv.testProperty2; } void setTestProperty2(int val) { priv.testProperty2 = val; } QBindable bindableTestProperty() { return QBindable(&priv.testProperty); } QBindable bindableTestProperty2() { return QBindable(&priv.testProperty2); } struct Private { Private(ClassWithPrivateQPropertyShim *pub) : q(pub) {} QBindingStorage bindingStorage; ClassWithPrivateQPropertyShim *q = nullptr; void onTestPropertyChanged() { q->testPropertyChanged(); } Q_OBJECT_BINDABLE_PROPERTY(Private, int, testProperty, &Private::onTestPropertyChanged); QProperty testProperty2; }; Private priv{this}; Private *d_func() { return &priv; } const Private *d_func() const { return &priv; } }; inline const QBindingStorage *qGetBindingStorage(const ClassWithPrivateQPropertyShim::Private *o) { return &o->bindingStorage; } inline QBindingStorage *qGetBindingStorage(ClassWithPrivateQPropertyShim::Private *o) { return &o->bindingStorage; } void tst_Moc::privateQPropertyShim() { ClassWithPrivateQPropertyShim testObject; { auto metaObject = &ClassWithPrivateQPropertyShim::staticMetaObject; QMetaProperty prop = metaObject->property(metaObject->indexOfProperty("testProperty")); QVERIFY(prop.isValid()); QVERIFY(prop.notifySignal().isValid()); } testObject.priv.testProperty.setValue(42); QCOMPARE(testObject.property("testProperty").toInt(), 42); // Behave like a QProperty QVERIFY(!testObject.bindableTestProperty().hasBinding()); testObject.bindableTestProperty().setBinding([]() { return 100; }); QCOMPARE(testObject.testProperty(), 100); QVERIFY(testObject.bindableTestProperty().hasBinding()); // Old style setter getters testObject.setTestProperty(400); QVERIFY(!testObject.bindableTestProperty().hasBinding()); QCOMPARE(testObject.testProperty(), 400); // moc generates correct code for plain QProperty in PIMPL testObject.setTestProperty2(42); 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 scoreBindable() { return QBindable(&m_score); } private: QProperty 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 scoreBindable() { return QBindable(&m_score); } signals: void scoreChanged(); private: QProperty 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); } } 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()); } QTEST_MAIN(tst_Moc) // the generated code must compile with QT_NO_KEYWORDS #undef signals #undef slots #undef emit #include "tst_moc.moc"