diff options
Diffstat (limited to 'sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp')
-rw-r--r-- | sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp new file mode 100644 index 000000000..a7d40f70a --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.cpp @@ -0,0 +1,480 @@ +// 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 "testmodifyfunction.h" +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetabuilder_p.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <modifications.h> +#include <typesystem.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestModifyFunction::testRenameArgument_data() +{ + QTest::addColumn<QByteArray>("pattern"); + QTest::newRow("fixed_string") << QByteArrayLiteral("method(int)"); + QTest::newRow("regular_expression") << QByteArrayLiteral("^method.*"); +} + +void TestModifyFunction::testRenameArgument() +{ + QFETCH(QByteArray, pattern); + + const char cppCode[] = "\ + struct A {\n\ + void method(int=0);\n\ + };\n"; + const char xmlCode1[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='int'/>\n\ + <object-type name='A'>\n\ + <modify-function signature='"; + const char xmlCode2[] = R"('> + <modify-argument index='1' rename='otherArg'/> + </modify-function> + </object-type> + </typesystem> +)"; + + const QByteArray xmlCode = QByteArray(xmlCode1) + pattern + QByteArray(xmlCode2); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode.constData(), false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + const auto func = classA->findFunction("method"); + QVERIFY(func); + + QCOMPARE(func->argumentName(1), u"otherArg"); +} + +void TestModifyFunction::testOwnershipTransfer() +{ + const char cppCode[] = "\ + struct A {};\n\ + struct B {\n\ + virtual A* method();\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <object-type name='A' />\n\ + <object-type name='B'>\n\ + <modify-function signature='method()'>\n\ + <modify-argument index='return'>\n\ + <define-ownership owner='c++'/>\n\ + </modify-argument>\n\ + </modify-function>\n\ + </object-type>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + const auto func = classB->findFunction("method"); + QVERIFY(func); + + QCOMPARE(func->argumentTargetOwnership(func->ownerClass(), 0), + TypeSystem::CppOwnership); +} + + +void TestModifyFunction::invalidateAfterUse() +{ + const char cppCode[] = "\ + struct A {\n\ + virtual void call(int *a);\n\ + };\n\ + struct B : A {\n\ + };\n\ + struct C : B {\n\ + virtual void call2(int *a);\n\ + };\n\ + struct D : C {\n\ + virtual void call2(int *a);\n\ + };\n\ + struct E : D {\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='int'/>\n\ + <object-type name='A'>\n\ + <modify-function signature='call(int*)'>\n\ + <modify-argument index='1' invalidate-after-use='true'/>\n\ + </modify-function>\n\ + </object-type>\n\ + <object-type name='B' />\n\ + <object-type name='C'>\n\ + <modify-function signature='call2(int*)'>\n\ + <modify-argument index='1' invalidate-after-use='true'/>\n\ + </modify-function>\n\ + </object-type>\n\ + <object-type name='D'>\n\ + <modify-function signature='call2(int*)'>\n\ + <modify-argument index='1' invalidate-after-use='true'/>\n\ + </modify-function>\n\ + </object-type>\n\ + <object-type name='E' />\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, u"0.1"_s)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + auto func = classB->findFunction("call"); + QCOMPARE(func->modifications().size(), 1); + QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); + QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); + + const auto classC = AbstractMetaClass::findClass(classes, "C"); + QVERIFY(classC); + func = classC->findFunction("call"); + QCOMPARE(func->modifications().size(), 1); + QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); + QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); + + func = classC->findFunction("call2"); + QCOMPARE(func->modifications().size(), 1); + QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); + QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); + + AbstractMetaClassCPtr classD = AbstractMetaClass::findClass(classes, "D"); + QVERIFY(classD); + func = classD->findFunction("call"); + QCOMPARE(func->modifications().size(), 1); + QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); + QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); + + func = classD->findFunction("call2"); + QCOMPARE(func->modifications().size(), 1); + QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); + QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); + + const auto classE = AbstractMetaClass::findClass(classes, "E"); + QVERIFY(classE); + func = classE->findFunction("call"); + QVERIFY(func); + QCOMPARE(func->modifications().size(), 1); + QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); + QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); + + func = classE->findFunction("call2"); + QVERIFY(func); + QCOMPARE(func->modifications().size(), 1); + QCOMPARE(func->modifications().at(0).argument_mods().size(), 1); + QVERIFY(func->modifications().at(0).argument_mods().at(0).resetAfterUse()); +} + +void TestModifyFunction::testWithApiVersion() +{ + const char cppCode[] = "\ + struct A {};\n\ + struct B {\n\ + virtual A* method();\n\ + virtual B* methodB();\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <object-type name='A' />\n\ + <object-type name='B'>\n\ + <modify-function signature='method()' since='0.1'>\n\ + <modify-argument index='return'>\n\ + <define-ownership owner='c++'/>\n\ + </modify-argument>\n\ + </modify-function>\n\ + <modify-function signature='methodB()' since='0.2'>\n\ + <modify-argument index='return'>\n\ + <define-ownership owner='c++'/>\n\ + </modify-argument>\n\ + </modify-function>\n\ + </object-type>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, u"0.1"_s)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + auto func = classB->findFunction("method"); + + auto returnOwnership = func->argumentTargetOwnership(func->ownerClass(), 0); + QCOMPARE(returnOwnership, TypeSystem::CppOwnership); + + func = classB->findFunction("methodB"); + returnOwnership = func->argumentTargetOwnership(func->ownerClass(), 0); + QVERIFY(returnOwnership != TypeSystem::CppOwnership); +} + +// Modifications on class/typesystem level are tested below +// in testScopedModifications(). +void TestModifyFunction::testAllowThread() +{ + const char cppCode[] =R"CPP(\ +struct A { + void f1(); + void f2(); + void f3(); + int getter1() const; + int getter2() const; +}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='A'> + <modify-function signature='f2()' allow-thread='auto'/> + <modify-function signature='f3()' allow-thread='no'/> + <modify-function signature='getter2()const' allow-thread='yes'/> + </object-type> +</typesystem> +)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, u"0.1"_s)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + + // Nothing specified, true + const auto f1 = classA->findFunction("f1"); + QVERIFY(f1); + QVERIFY(!f1->allowThread()); + + // 'auto' specified, should be false for nontrivial function + const auto f2 = classA->findFunction("f2"); + QVERIFY(f2); + QVERIFY(f2->allowThread()); + + // 'no' specified, should be false + const auto f3 = classA->findFunction("f3"); + QVERIFY(f3); + QVERIFY(!f3->allowThread()); + + // Nothing specified, should be false for simple getter + const auto getter1 = classA->findFunction("getter1"); + QVERIFY(getter1); + QVERIFY(!getter1->allowThread()); + + // Forced to true simple getter + const auto getter2 = classA->findFunction("getter2"); + QVERIFY(getter2); + QVERIFY(getter2->allowThread()); // Forced to true simple getter +} + +void TestModifyFunction::testGlobalFunctionModification() +{ + const char cppCode[] = "\ + struct A {};\n\ + void function(A* a = 0);\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='A'/>\n\ + <function signature='function(A*)'>\n\ + <modify-function signature='function(A*)'>\n\ + <modify-argument index='1'>\n\ + <replace-type modified-type='A'/>\n\ + <replace-default-expression with='A()'/>\n\ + </modify-argument>\n\ + </modify-function>\n\ + </function>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + QCOMPARE(builder->globalFunctions().size(), 1); + + auto *td = TypeDatabase::instance(); + FunctionModificationList mods = td->globalFunctionModifications({u"function(A*)"_s}); + QCOMPARE(mods.size(), 1); + const QList<ArgumentModification> &argMods = mods.constFirst().argument_mods(); + QCOMPARE(argMods.size(), 1); + ArgumentModification argMod = argMods.constFirst(); + QCOMPARE(argMod.replacedDefaultExpression(), u"A()"); + + QVERIFY(!builder->globalFunctions().isEmpty()); + const auto func = builder->globalFunctions().constFirst(); + QCOMPARE(func->arguments().size(), 1); + const AbstractMetaArgument &arg = func->arguments().constFirst(); + QCOMPARE(arg.type().cppSignature(), u"A *"); + QCOMPARE(arg.originalDefaultValueExpression(), u"0"); + QCOMPARE(arg.defaultValueExpression(), u"A()"); +} + +// Tests modifications of exception handling and allow-thread +// on various levels. +void TestModifyFunction::testScopedModifications_data() +{ + QTest::addColumn<QByteArray>("cppCode"); + QTest::addColumn<QByteArray>("xmlCode"); + QTest::addColumn<bool>("expectedGenerateUnspecified"); + QTest::addColumn<bool>("expectedGenerateNonThrowing"); + QTest::addColumn<bool>("expectedGenerateThrowing"); + QTest::addColumn<bool>("expectedAllowThread"); + + const QByteArray cppCode = R"CPP( +struct Base { +}; + +struct A : public Base { + void unspecified(); + void nonThrowing() noexcept; +# if __cplusplus >= 201703L // C++ 17 + void throwing() noexcept(false); +#else + void throwing() throw(int); +#endif +}; +)CPP"; + + // Default: Off + QTest::newRow("none") + << cppCode + << QByteArray(R"XML( +<typesystem package= 'Foo'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='A'/> +</typesystem>)XML") + << false << false << false // exception + << false; // allowthread + + // Modify one function + QTest::newRow("modify-function1") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='A'> + <modify-function signature='throwing()' exception-handling='auto-on'/> + </object-type> +</typesystem>)XML") + << false << false << true // exception + << false; // allowthread + + // Flip defaults by modifying functions + QTest::newRow("modify-function2") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='A'> + <modify-function signature='unspecified()' exception-handling='auto-on'/> + <modify-function signature='throwing()' exception-handling='no'/> + </object-type> +</typesystem>)XML") + << true << false << false // exception + << false; // allowthread + + // Activate on type system level + QTest::newRow("typesystem-on") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo' exception-handling='auto-on' allow-thread='no'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='A'/> +</typesystem>)XML") + << true << false << true // exception + << false; // allowthread + + // Activate on class level + QTest::newRow("class-on") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='A' exception-handling='auto-on' allow-thread='no'/> +</typesystem>)XML") + << true << false << true // exception + << false; // allowthread + + // Activate on base class level + QTest::newRow("baseclass-on") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='Base' exception-handling='auto-on' allow-thread='no'/> + <object-type name='A'/> +</typesystem>)XML") + << true << false << true // exception + << false; // allowthread + + // Override value on class level + QTest::newRow("override-class-on") + << cppCode + << QByteArray(R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='A' exception-handling='auto-on'> + <modify-function signature='throwing()' exception-handling='no'/> + </object-type> +</typesystem>)XML") + << true << false << false // exception + << false; // allowthread +} + +void TestModifyFunction::testScopedModifications() +{ + QFETCH(QByteArray, cppCode); + QFETCH(QByteArray, xmlCode); + QFETCH(bool, expectedGenerateUnspecified); + QFETCH(bool, expectedGenerateNonThrowing); + QFETCH(bool, expectedGenerateThrowing); + QFETCH(bool, expectedAllowThread); + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode.constData(), xmlCode.constData(), false)); + QVERIFY(builder); + + const auto classA = AbstractMetaClass::findClass(builder->classes(), "A"); + QVERIFY(classA); + + auto f = classA->findFunction("unspecified"); + QVERIFY(f); + QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Unknown); + QCOMPARE(f->generateExceptionHandling(), expectedGenerateUnspecified); + QCOMPARE(f->allowThread(), expectedAllowThread); + + f = classA->findFunction("nonThrowing"); + QVERIFY(f); + QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::NoExcept); + QCOMPARE(f->generateExceptionHandling(), expectedGenerateNonThrowing); + + f = classA->findFunction("throwing"); + QVERIFY(f); + QCOMPARE(f->exceptionSpecification(), ExceptionSpecification::Throws); + QCOMPARE(f->generateExceptionHandling(), expectedGenerateThrowing); +} + +void TestModifyFunction::testSnakeCaseRenaming_data() +{ + QTest::addColumn<QLatin1StringView>("name"); + QTest::addColumn<QLatin1StringView>("expected"); + QTest::newRow("s1") + << "snakeCaseFunc"_L1 << "snake_case_func"_L1; + QTest::newRow("s2") + << "SnakeCaseFunc"_L1 << "snake_case_func"_L1; + QTest::newRow("consecutive-uppercase") + << "snakeCAseFunc"_L1 << "snakeCAseFunc"_L1; +} + +void TestModifyFunction::testSnakeCaseRenaming() +{ + QFETCH(QLatin1StringView, name); + QFETCH(QLatin1StringView, expected); + + const QString actual = AbstractMetaBuilder::getSnakeCaseName(name); + QCOMPARE(actual, expected); +} + +QTEST_APPLESS_MAIN(TestModifyFunction) |