aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp')
-rw-r--r--sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp522
1 files changed, 522 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp b/sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp
new file mode 100644
index 000000000..a891e1e28
--- /dev/null
+++ b/sources/shiboken6/ApiExtractor/tests/testaddfunction.cpp
@@ -0,0 +1,522 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "testaddfunction.h"
+#include "testutil.h"
+#include <abstractmetaargument.h>
+#include <abstractmetafunction.h>
+#include <abstractmetalang.h>
+#include <abstractmetatype.h>
+#include <codesnip.h>
+#include <addedfunction.h>
+#include <addedfunction_p.h>
+#include <complextypeentry.h>
+#include <primitivetypeentry.h>
+
+#include <qtcompat.h>
+
+#include <QtTest/QTest>
+
+using namespace Qt::StringLiterals;
+
+static constexpr auto voidT = "void"_L1;
+
+void TestAddFunction::testParsingFuncNameAndConstness()
+{
+ // generic test...
+ static constexpr auto sig1 = "func(type1, const type2, const type3* const)"_L1;
+ QString errorMessage;
+ auto f1 = AddedFunction::createAddedFunction(sig1, voidT, &errorMessage);
+ QVERIFY2(f1, qPrintable(errorMessage));
+ QCOMPARE(f1->name(), u"func");
+ QCOMPARE(f1->arguments().size(), 3);
+ TypeInfo retval = f1->returnType();
+ QCOMPARE(retval.qualifiedName(), QStringList{voidT});
+ QCOMPARE(retval.indirections(), 0);
+ QCOMPARE(retval.isConstant(), false);
+ QCOMPARE(retval.referenceType(), NoReference);
+
+ // test with a ugly template as argument and other ugly stuff
+ static constexpr auto sig2 =
+ " _fu__nc_ ( type1, const type2, const Abc<int& , C<char*> * >"
+ " * *@my_name@, const type3* const ) const "_L1;
+ auto f2 = AddedFunction::createAddedFunction(sig2,
+ u"const Abc<int& , C<char*> * > * *"_s,
+ &errorMessage);
+ QVERIFY2(f2, qPrintable(errorMessage));
+ QCOMPARE(f2->name(), u"_fu__nc_");
+ const auto &args = f2->arguments();
+ QCOMPARE(args.size(), 4);
+ retval = f2->returnType();
+ QCOMPARE(retval.qualifiedName(), QStringList{u"Abc"_s});
+ QCOMPARE(retval.instantiations().size(), 2);
+ QCOMPARE(retval.toString(), u"const Abc<int&, C<char*>*>**");
+ 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, u"my_name");
+ auto arg2Type = args.at(2).typeInfo;
+ QCOMPARE(arg2Type.qualifiedName(), QStringList{u"Abc"_s});
+ QCOMPARE(arg2Type.instantiations().size(), 2);
+ QCOMPARE(arg2Type.toString(), u"const Abc<int&, C<char*>*>**");
+ QCOMPARE(arg2Type.indirections(), 2);
+ QCOMPARE(arg2Type.isConstant(), true);
+ QCOMPARE(arg2Type.referenceType(), NoReference);
+
+ QVERIFY(args.at(3).name.isEmpty());
+
+ // function with no args.
+ auto f3 = AddedFunction::createAddedFunction("func()"_L1, voidT, &errorMessage);
+ QVERIFY2(f3, qPrintable(errorMessage));
+ QCOMPARE(f3->name(), u"func");
+ QCOMPARE(f3->arguments().size(), 0);
+
+ // const call operator
+ auto f4 = AddedFunction::createAddedFunction("operator()(int)const"_L1,
+ "int"_L1, &errorMessage);
+ QVERIFY2(f4, qPrintable(errorMessage));
+ QCOMPARE(f4->name(), u"operator()");
+ QCOMPARE(f4->arguments().size(), 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(
+<typesystem package='Foo'>
+ <primitive-type name='int'/>
+ <primitive-type name='float'/>
+ <value-type name='B'/>
+ <value-type name='A'>
+ <add-function signature='b(int, float = 4.6, const B&amp;)' return-type='int' access='protected'/>
+ <add-function signature='operator()(int)' return-type='int' access='public'/>
+ </value-type>
+</typesystem>)XML";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ auto *typeDb = TypeDatabase::instance();
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ // default ctor, default copy ctor, func a() and the added functions
+ QCOMPARE(classA->functions().size(), 5);
+
+ auto addedFunc = classA->findFunction("b");
+ QVERIFY(addedFunc);
+ QCOMPARE(addedFunc->access(), Access::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(u"int"_s));
+ const AbstractMetaArgumentList &args = addedFunc->arguments();
+ QCOMPARE(args.size(), 3);
+ QCOMPARE(args.at(0).type().typeEntry(), returnType.typeEntry());
+ QCOMPARE(args.at(1).defaultValueExpression(), u"4.6");
+ QCOMPARE(args.at(2).type().typeEntry(), typeDb->findType(u"B"_s));
+
+ auto addedCallOperator = classA->findFunction("operator()");
+ QVERIFY(addedCallOperator);
+}
+
+void TestAddFunction::testAddFunctionConstructor()
+{
+ const char cppCode[] = "struct A { A() {} };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'>\n\
+ <add-function signature='A(int)'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ QCOMPARE(classA->functions().size(), 3); // default and added ctors
+ const auto addedFunc = classA->functions().constLast();
+ QCOMPARE(addedFunc->access(), Access::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[] = "\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func()'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ // default ctor, default copy ctor and the added function
+ QCOMPARE(classA->functions().size(), 3);
+ const auto addedFunc = classA->functions().constLast();
+ QCOMPARE(addedFunc->access(), Access::Public);
+ QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::NormalFunction);
+ QVERIFY(addedFunc->isUserAdded());
+ QVERIFY(addedFunc->isVoid());
+}
+
+void TestAddFunction::testAddFunctionCodeSnippets()
+{
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func()'>\n\
+ <inject-code class='target' position='end'>Hi!, I am the code.</inject-code>\n\
+ </add-function>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->functions().constLast();
+ QVERIFY(addedFunc->hasInjectedCode());
+}
+
+void TestAddFunction::testAddFunctionWithoutParenteses()
+{
+ static constexpr auto sig1 = "func"_L1;
+ QString errorMessage;
+ auto f1 = AddedFunction::createAddedFunction(sig1, voidT, &errorMessage);
+ QVERIFY2(f1, qPrintable(errorMessage));
+ QCOMPARE(f1->name(), u"func");
+ QCOMPARE(f1->arguments().size(), 0);
+ QCOMPARE(f1->isConstant(), false);
+
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func'>\n\
+ <inject-code class='target' position='end'>Hi!, I am the code.</inject-code>\n\
+ </add-function>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->findFunction(sig1);
+ QVERIFY(addedFunc);
+ QVERIFY(addedFunc->hasInjectedCode());
+ const auto snips = addedFunc->injectedCodeSnips(TypeSystem::CodeSnipPositionAny,
+ TypeSystem::TargetLangCode);
+ QCOMPARE(snips.size(), 1);
+}
+
+void TestAddFunction::testAddFunctionWithDefaultArgs()
+{
+ static constexpr auto sig1 = "func"_L1;
+ QString errorMessage;
+ auto f1 = AddedFunction::createAddedFunction(sig1, voidT, &errorMessage);
+ QVERIFY2(f1, qPrintable(errorMessage));
+ QCOMPARE(f1->name(), u"func");
+ QCOMPARE(f1->arguments().size(), 0);
+ QCOMPARE(f1->isConstant(), false);
+
+ const char cppCode[] = "struct A { };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func(int, int)'>\n\
+ <modify-argument index='2'>\n\
+ <replace-default-expression with='2'/>\n\
+ </modify-argument>\n\
+ </add-function>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->findFunction(sig1);
+ QVERIFY(addedFunc);
+ const AbstractMetaArgument &arg = addedFunc->arguments().at(1);
+ QCOMPARE(arg.defaultValueExpression(), u"2");
+}
+
+void TestAddFunction::testAddFunctionAtModuleLevel()
+{
+ const char cppCode[] = "struct A { };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'/>\n\
+ <add-function signature='func(int, int)'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+
+ auto *typeDb = TypeDatabase::instance();
+
+ AddedFunctionList addedFuncs = typeDb->findGlobalUserFunctions(u"func"_s);
+
+ QCOMPARE(addedFuncs.size(), 1);
+
+ auto &mods = addedFuncs.constFirst()->modifications();
+
+ QCOMPARE(mods.size(), 1);
+ QVERIFY(mods.constFirst().isCodeInjection());
+ CodeSnip snip = mods.constFirst().snips().constFirst();
+ QCOMPARE(snip.code().trimmed(), u"custom_code();");
+}
+
+void TestAddFunction::testAddFunctionWithVarargs()
+{
+ QString errorMessage;
+ auto f1 = AddedFunction::createAddedFunction("func(int,char,...)"_L1, voidT,
+ &errorMessage);
+ QVERIFY2(f1, qPrintable(errorMessage));
+ QCOMPARE(f1->name(), u"func");
+ QCOMPARE(f1->arguments().size(), 3);
+ QVERIFY(!f1->isConstant());
+
+ const char cppCode[] = "struct A {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <primitive-type name='char'/>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func(int,char,...)'/>\n\
+ </value-type>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->findFunction("func");
+ QVERIFY(addedFunc);
+ 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[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'>\n\
+ <add-function signature='func(int, int)' static='yes'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ </value-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto classA = AbstractMetaClass::findClass(classes, "A");
+ QVERIFY(classA);
+ const auto addedFunc = classA->findFunction("func");
+ QVERIFY(addedFunc);
+ QVERIFY(addedFunc->isStatic());
+}
+
+void TestAddFunction::testAddGlobalFunction()
+{
+ const char cppCode[] = "struct A { };struct B {};\n";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <value-type name='A'/>\n\
+ <add-function signature='globalFunc(int, int)' static='yes'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ <add-function signature='globalFunc2(int, int)' static='yes'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ <value-type name='B'/>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ const auto globalFuncs = builder->globalFunctions();
+ QCOMPARE(globalFuncs.size(), 2);
+ const auto classB = AbstractMetaClass::findClass(builder->classes(), "B");
+ QVERIFY(classB);
+ QVERIFY(!classB->findFunction("globalFunc"));
+ QVERIFY(!classB->findFunction("globalFunc2"));
+ QVERIFY(!globalFuncs[0]->injectedCodeSnips().isEmpty());
+ QVERIFY(!globalFuncs[1]->injectedCodeSnips().isEmpty());
+}
+
+void TestAddFunction::testAddFunctionWithApiVersion()
+{
+ const char cppCode[] = "";
+ const char xmlCode[] = "\
+ <typesystem package='Foo'>\n\
+ <primitive-type name='int'/>\n\
+ <add-function signature='globalFunc(int, int)' static='yes' since='1.3'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ <add-function signature='globalFunc2(int, int)' static='yes' since='0.1'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode,
+ true, u"0.1"_s));
+ QVERIFY(builder);
+ const auto globalFuncs = builder->globalFunctions();
+ QCOMPARE(globalFuncs.size(), 1);
+}
+
+void TestAddFunction::testModifyAddedFunction()
+{
+ const char cppCode[] = "class Foo { };\n";
+ const char xmlCode[] = R"(
+<typesystem package='Package'>
+ <primitive-type name='float'/>
+ <primitive-type name='int'/>
+ <value-type name='Foo'>
+ <add-function signature='method(float, int)'>
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>
+ <modify-argument index='2' rename='varName'>
+ <replace-default-expression with='0'/>
+ </modify-argument>
+ </add-function>
+ </value-type>
+</typesystem>
+)";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto foo = AbstractMetaClass::findClass(classes, "Foo");
+ const auto method = foo->findFunction("method");
+ QVERIFY(method);
+ QCOMPARE(method->arguments().size(), 2);
+ const AbstractMetaArgument &arg = method->arguments().at(1);
+ QCOMPARE(arg.defaultValueExpression(), u"0");
+ QCOMPARE(arg.name(), u"varName");
+ QCOMPARE(method->argumentName(2), u"varName");
+}
+
+void TestAddFunction::testAddFunctionOnTypedef()
+{
+ const char cppCode[] = "template<class T> class Foo { }; typedef Foo<int> FooInt;\n";
+ const char xmlCode[] = "\
+ <typesystem package='Package'>\n\
+ <value-type name='FooInt'>\n\
+ <add-function signature='FooInt(PySequence)'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ <add-function signature='method()'>\n\
+ <inject-code class='target' position='beginning'>custom_code();</inject-code>\n\
+ </add-function>\n\
+ </value-type>\n\
+ </typesystem>\n";
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ AbstractMetaClassList classes = builder->classes();
+ const auto foo = AbstractMetaClass::findClass(classes, "FooInt");
+ QVERIFY(foo);
+ QVERIFY(foo->hasNonPrivateConstructor());
+ const auto &lst = foo->queryFunctions(FunctionQueryOption::AnyConstructor);
+ for (const auto &f : lst)
+ QVERIFY(f->signature().startsWith(f->name()));
+ QCOMPARE(lst.size(), 2);
+ const auto method = foo->findFunction("method");
+ QVERIFY(method);
+}
+
+void TestAddFunction::testAddFunctionWithTemplateArg()
+{
+ const char cppCode[] = "template<class T> class Foo { };\n";
+ const char xmlCode[] = "\
+ <typesystem package='Package'>\n\
+ <primitive-type name='int'/>\n\
+ <container-type name='Foo' type='list'/>\n\
+ <add-function signature='func(Foo&lt;int>)'/>\n\
+ </typesystem>\n";
+
+ QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode));
+ QVERIFY(builder);
+ QCOMPARE(builder->globalFunctions().size(), 1);
+ const auto func = builder->globalFunctions().constFirst();
+ const AbstractMetaArgument &arg = func->arguments().constFirst();
+ QCOMPARE(arg.type().instantiations().size(), 1);
+}
+
+// Test splitting of <add-function> parameter lists.
+
+Q_DECLARE_METATYPE(AddedFunctionParser::Argument)
+
+using Arguments = AddedFunctionParser::Arguments;
+
+void TestAddFunction::testAddFunctionTypeParser_data()
+{
+ QTest::addColumn<QString>("parameterList");
+ QTest::addColumn<Arguments>("expected");
+
+ QTest::newRow("empty")
+ << QString() << Arguments{};
+
+ QTest::newRow("1-arg")
+ << QString::fromLatin1("int @a@=42")
+ << Arguments{{u"int"_s, u"a"_s, u"42"_s}};
+
+ QTest::newRow("2-args")
+ << QString::fromLatin1("double @d@, int @a@=42")
+ << Arguments{{u"double"_s, u"d"_s, {}},
+ {u"int"_s, u"a"_s, u"42"_s}};
+
+ QTest::newRow("template-var_args")
+ << QString::fromLatin1("const QList<X,Y> &@list@ = QList<X,Y>{1,2}, int @b@=5, ...")
+ << Arguments{{u"const QList<X,Y> &"_s, u"list"_s, u"QList<X,Y>{1,2}"_s},
+ {u"int"_s, u"b"_s, u"5"_s},
+ {u"..."_s, {}, {}}};
+}
+
+void TestAddFunction::testAddFunctionTypeParser()
+{
+
+ QFETCH(QString, parameterList);
+ QFETCH(Arguments, expected);
+
+ const auto actual = AddedFunctionParser::splitParameters(parameterList);
+ QCOMPARE(actual, expected);
+}
+
+QTEST_APPLESS_MAIN(TestAddFunction)