/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of Qt for Python. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "testaddfunction.h" #include #include "testutil.h" #include #include #include #include #include void TestAddFunction::testParsingFuncNameAndConstness() { // generic test... const char sig1[] = "func(type1, const type2, const type3* const)"; QString errorMessage; auto f1 = AddedFunction::createAddedFunction(QLatin1String(sig1), QLatin1String("void"), &errorMessage); QVERIFY2(!f1.isNull(), qPrintable(errorMessage)); QCOMPARE(f1->name(), QLatin1String("func")); QCOMPARE(f1->arguments().count(), 3); TypeInfo retval = f1->returnType(); QCOMPARE(retval.qualifiedName(), QStringList{QLatin1String("void")}); QCOMPARE(retval.indirections(), 0); QCOMPARE(retval.isConstant(), false); QCOMPARE(retval.referenceType(), NoReference); // test with a ugly template as argument and other ugly stuff const char sig2[] = " _fu__nc_ ( type1, const type2, const Abc * > * *@my_name@, const type3* const ) const "; auto f2 = AddedFunction::createAddedFunction(QLatin1String(sig2), QLatin1String("const Abc * > * *"), &errorMessage); QVERIFY2(!f2.isNull(), qPrintable(errorMessage)); QCOMPARE(f2->name(), QLatin1String("_fu__nc_")); const auto &args = f2->arguments(); QCOMPARE(args.count(), 4); retval = f2->returnType(); QCOMPARE(retval.qualifiedName(), QStringList{QLatin1String("Abc")}); QCOMPARE(retval.instantiations().size(), 2); QCOMPARE(retval.toString(), QLatin1String("const Abc*>**")); QCOMPARE(retval.indirections(), 2); QCOMPARE(retval.isConstant(), true); QCOMPARE(retval.referenceType(), NoReference); QVERIFY(args.at(0).name.isEmpty()); QVERIFY(args.at(1).name.isEmpty()); QCOMPARE(args.at(2).name, QLatin1String("my_name")); auto arg2Type = args.at(2).typeInfo; QCOMPARE(arg2Type.qualifiedName(), QStringList{QLatin1String("Abc")}); QCOMPARE(arg2Type.instantiations().size(), 2); QCOMPARE(arg2Type.toString(), QLatin1String("const Abc*>**")); QCOMPARE(arg2Type.indirections(), 2); QCOMPARE(arg2Type.isConstant(), true); QCOMPARE(arg2Type.referenceType(), NoReference); QVERIFY(args.at(3).name.isEmpty()); // function with no args. const char sig3[] = "func()"; auto f3 = AddedFunction::createAddedFunction(QLatin1String(sig3), QLatin1String("void"), &errorMessage); QVERIFY2(!f3.isNull(), qPrintable(errorMessage)); QCOMPARE(f3->name(), QLatin1String("func")); QCOMPARE(f3->arguments().count(), 0); // const call operator const char sig4[] = "operator()(int)const"; auto f4 = AddedFunction::createAddedFunction(QLatin1String(sig4), QLatin1String("int"), &errorMessage); QVERIFY2(!f4.isNull(), qPrintable(errorMessage)); QCOMPARE(f4->name(), QLatin1String("operator()")); QCOMPARE(f4->arguments().count(), 1); QVERIFY(f4->isConstant()); } void TestAddFunction::testAddFunction() { const char cppCode[] = R"CPP( struct B {}; struct A { void a(int); };)CPP"; const char xmlCode[] = R"XML( )XML"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); TypeDatabase* typeDb = TypeDatabase::instance(); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); QVERIFY(classA); QCOMPARE(classA->functions().count(), 5); // default ctor, default copy ctor, func a() and the added functions auto addedFunc = classA->findFunction(QLatin1String("b")); QVERIFY(addedFunc); QCOMPARE(addedFunc->visibility(), AbstractMetaFunction::Protected); QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::NormalFunction); QVERIFY(addedFunc->isUserAdded()); QCOMPARE(addedFunc->ownerClass(), classA); QCOMPARE(addedFunc->implementingClass(), classA); QCOMPARE(addedFunc->declaringClass(), classA); QVERIFY(!addedFunc->isVirtual()); QVERIFY(!addedFunc->isSignal()); QVERIFY(!addedFunc->isSlot()); QVERIFY(!addedFunc->isStatic()); AbstractMetaType returnType = addedFunc->type(); QCOMPARE(returnType.typeEntry(), typeDb->findPrimitiveType(QLatin1String("int"))); const AbstractMetaArgumentList &args = addedFunc->arguments(); QCOMPARE(args.count(), 3); QCOMPARE(args.at(0).type().typeEntry(), returnType.typeEntry()); QCOMPARE(args.at(1).defaultValueExpression(), QLatin1String("4.6")); QCOMPARE(args.at(2).type().typeEntry(), typeDb->findType(QLatin1String("B"))); auto addedCallOperator = classA->findFunction(QLatin1String("operator()")); QVERIFY(addedCallOperator); } void TestAddFunction::testAddFunctionConstructor() { const char cppCode[] = "struct A { A() {} };\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); QVERIFY(classA); QCOMPARE(classA->functions().count(), 3); // default and added ctors const auto addedFunc = classA->functions().constLast(); QCOMPARE(addedFunc->visibility(), AbstractMetaFunction::Public); QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::ConstructorFunction); QCOMPARE(addedFunc->arguments().size(), 1); QVERIFY(addedFunc->isUserAdded()); QVERIFY(addedFunc->isVoid()); } void TestAddFunction::testAddFunctionTagDefaultValues() { const char cppCode[] = "struct A {};\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); QVERIFY(classA); QCOMPARE(classA->functions().count(), 3); // default ctor, default copy ctor and the added function const auto addedFunc = classA->functions().constLast(); QCOMPARE(addedFunc->visibility(), AbstractMetaFunction::Public); QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::NormalFunction); QVERIFY(addedFunc->isUserAdded()); QVERIFY(addedFunc->isVoid()); } void TestAddFunction::testAddFunctionCodeSnippets() { const char cppCode[] = "struct A {};\n"; const char xmlCode[] = "\ \n\ \n\ \n\ Hi!, I am the code.\n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); QVERIFY(classA); const auto addedFunc = classA->functions().constLast(); QVERIFY(addedFunc->hasInjectedCode()); } void TestAddFunction::testAddFunctionWithoutParenteses() { const char sig1[] = "func"; QString errorMessage; auto f1 = AddedFunction::createAddedFunction(QLatin1String(sig1), QLatin1String("void"), &errorMessage); QVERIFY2(!f1.isNull(), qPrintable(errorMessage)); QCOMPARE(f1->name(), QLatin1String("func")); QCOMPARE(f1->arguments().count(), 0); QCOMPARE(f1->isConstant(), false); const char cppCode[] = "struct A {};\n"; const char xmlCode[] = "\ \n\ \n\ \n\ Hi!, I am the code.\n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); QVERIFY(classA); const auto addedFunc = classA->findFunction(QLatin1String("func")); QVERIFY(!addedFunc.isNull()); QVERIFY(addedFunc->hasInjectedCode()); QCOMPARE(addedFunc->injectedCodeSnips(TypeSystem::CodeSnipPositionAny, TypeSystem::TargetLangCode).count(), 1); } void TestAddFunction::testAddFunctionWithDefaultArgs() { const char sig1[] = "func"; QString errorMessage; auto f1 = AddedFunction::createAddedFunction(QLatin1String(sig1), QLatin1String("void"), &errorMessage); QVERIFY2(!f1.isNull(), qPrintable(errorMessage)); QCOMPARE(f1->name(), QLatin1String("func")); QCOMPARE(f1->arguments().count(), 0); QCOMPARE(f1->isConstant(), false); const char cppCode[] = "struct A { };\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); QVERIFY(classA); const auto addedFunc = classA->findFunction(QLatin1String("func")); QVERIFY(!addedFunc.isNull()); const AbstractMetaArgument &arg = addedFunc->arguments().at(1); QCOMPARE(arg.defaultValueExpression(), QLatin1String("2")); } void TestAddFunction::testAddFunctionAtModuleLevel() { const char cppCode[] = "struct A { };\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ custom_code();\n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); QVERIFY(classA); TypeDatabase* typeDb = TypeDatabase::instance(); AddedFunctionList addedFuncs = typeDb->findGlobalUserFunctions(QLatin1String("func")); QCOMPARE(addedFuncs.size(), 1); const FunctionModificationList mods = addedFuncs.constFirst()->modifications; QCOMPARE(mods.size(), 1); QVERIFY(mods.constFirst().isCodeInjection()); CodeSnip snip = mods.constFirst().snips().constFirst(); QCOMPARE(snip.code().trimmed(), QLatin1String("custom_code();")); } void TestAddFunction::testAddFunctionWithVarargs() { const char sig1[] = "func(int,char,...)"; QString errorMessage; auto f1 = AddedFunction::createAddedFunction(QLatin1String(sig1), QLatin1String("void"), &errorMessage); QVERIFY2(!f1.isNull(), qPrintable(errorMessage)); QCOMPARE(f1->name(), QLatin1String("func")); QCOMPARE(f1->arguments().count(), 3); QVERIFY(!f1->isConstant()); const char cppCode[] = "struct A {};\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); QVERIFY(classA); const auto addedFunc = classA->findFunction(QLatin1String("func")); QVERIFY(!addedFunc.isNull()); const AbstractMetaArgument &arg = addedFunc->arguments().constLast(); QVERIFY(arg.type().isVarargs()); QVERIFY(arg.type().typeEntry()->isVarargs()); } void TestAddFunction::testAddStaticFunction() { const char cppCode[] = "struct A { };\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ custom_code();\n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); const AbstractMetaClass *classA = AbstractMetaClass::findClass(classes, QLatin1String("A")); QVERIFY(classA); const auto addedFunc = classA->findFunction(QLatin1String("func")); QVERIFY(!addedFunc.isNull()); QVERIFY(addedFunc->isStatic()); } void TestAddFunction::testAddGlobalFunction() { const char cppCode[] = "struct A { };struct B {};\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ custom_code();\n\ \n\ \n\ custom_code();\n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); const auto globalFuncs = builder->globalFunctions(); QCOMPARE(globalFuncs.count(), 2); const AbstractMetaClass *classB = AbstractMetaClass::findClass(builder->classes(), QLatin1String("B")); QVERIFY(classB); QVERIFY(!classB->findFunction(QLatin1String("globalFunc"))); QVERIFY(!classB->findFunction(QLatin1String("globalFunc2"))); QVERIFY(!globalFuncs[0]->injectedCodeSnips().isEmpty()); QVERIFY(!globalFuncs[1]->injectedCodeSnips().isEmpty()); } void TestAddFunction::testAddFunctionWithApiVersion() { const char cppCode[] = ""; const char xmlCode[] = "\ \n\ \n\ \n\ custom_code();\n\ \n\ \n\ custom_code();\n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode, true, QLatin1String("0.1"))); QVERIFY(!builder.isNull()); const auto globalFuncs = builder->globalFunctions(); QCOMPARE(globalFuncs.count(), 1); } void TestAddFunction::testModifyAddedFunction() { const char cppCode[] = "class Foo { };\n"; const char xmlCode[] = R"( custom_code(); )"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); AbstractMetaClass* foo = AbstractMetaClass::findClass(classes, QLatin1String("Foo")); const auto method = foo->findFunction(QLatin1String("method")); QVERIFY(!method.isNull()); QCOMPARE(method->arguments().size(), 2); const AbstractMetaArgument &arg = method->arguments().at(1); QCOMPARE(arg.defaultValueExpression(), QLatin1String("0")); QCOMPARE(arg.name(), QLatin1String("varName")); QCOMPARE(method->argumentName(2), QLatin1String("varName")); } void TestAddFunction::testAddFunctionOnTypedef() { const char cppCode[] = "template class Foo { }; typedef Foo FooInt;\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ \n\ custom_code();\n\ \n\ \n\ custom_code();\n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClassList classes = builder->classes(); AbstractMetaClass* foo = AbstractMetaClass::findClass(classes, QLatin1String("FooInt")); QVERIFY(foo); QVERIFY(foo->hasNonPrivateConstructor()); const auto &lst = foo->queryFunctions(AbstractMetaClass::Constructors); for (const auto &f : lst) QVERIFY(f->signature().startsWith(f->name())); QCOMPARE(lst.size(), 2); const auto method = foo->findFunction(QLatin1String("method")); QVERIFY(!method.isNull()); } void TestAddFunction::testAddFunctionWithTemplateArg() { const char cppCode[] = "template class Foo { };\n"; const char xmlCode[] = "\ \n\ \n\ \n\ \n\ \n"; QScopedPointer builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); QCOMPARE(builder->globalFunctions().size(), 1); const auto func = builder->globalFunctions().constFirst(); const AbstractMetaArgument &arg = func->arguments().constFirst(); QCOMPARE(arg.type().instantiations().count(), 1); } // Test splitting of parameter lists. Q_DECLARE_METATYPE(AddedFunctionParser::Argument) using Arguments = AddedFunctionParser::Arguments; void TestAddFunction::testAddFunctionTypeParser_data() { QTest::addColumn("parameterList"); QTest::addColumn("expected"); QTest::newRow("empty") << QString() << Arguments{}; QTest::newRow("1-arg") << QString::fromLatin1("int @a@=42") << Arguments{{QLatin1String("int"), QLatin1String("a"), QLatin1String("42")}}; QTest::newRow("2-args") << QString::fromLatin1("double @d@, int @a@=42") << Arguments{{QLatin1String("double"), QLatin1String("d"), {}}, {QLatin1String("int"), QLatin1String("a"), QLatin1String("42")}}; QTest::newRow("template-var_args") << QString::fromLatin1("const QList &@list@ = QList{1,2}, int @b@=5, ...") << Arguments{{QLatin1String("const QList &"), QLatin1String("list"), QLatin1String("QList{1,2}")}, {QLatin1String("int"), QLatin1String("b"), QLatin1String("5")}, {QLatin1String("..."), {}, {}}}; } void TestAddFunction::testAddFunctionTypeParser() { QFETCH(QString, parameterList); QFETCH(Arguments, expected); const auto actual = AddedFunctionParser::splitParameters(parameterList); QCOMPARE(actual, expected); } QTEST_APPLESS_MAIN(TestAddFunction)