diff options
Diffstat (limited to 'sources/shiboken6/ApiExtractor/tests')
77 files changed, 7185 insertions, 0 deletions
diff --git a/sources/shiboken6/ApiExtractor/tests/CMakeLists.txt b/sources/shiboken6/ApiExtractor/tests/CMakeLists.txt new file mode 100644 index 000000000..76c014fbb --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/CMakeLists.txt @@ -0,0 +1,66 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +set(CMAKE_AUTORCC ON) + +macro(declare_test testname) + # gone: qt4_automoc("${testname}.cpp") + set(SOURCES "${testname}.cpp") + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${testname}.h") + list(APPEND SOURCES "${testname}.h") + endif () + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${testname}.qrc") + list(APPEND SOURCES "${testname}.qrc") + endif () + + add_executable(${testname} ${SOURCES}) + target_include_directories(${testname} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${apiextractor_SOURCE_DIR} + ) + target_link_libraries(${testname} PRIVATE apiextractor Qt::Test) + add_test(${testname} ${testname}) + if (INSTALL_TESTS) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${testname} + DESTINATION share/apiextractor${apiextractor_SUFFIX}/tests) + endif() +endmacro(declare_test testname) + +declare_test(testabstractmetaclass) +declare_test(testabstractmetatype) +declare_test(testaddfunction) +declare_test(testarrayargument) +declare_test(testcodeinjection) +declare_test(testcontainer) +declare_test(testconversionoperator) +declare_test(testconversionruletag) +declare_test(testctorinformation) +declare_test(testdroptypeentries) +declare_test(testdtorinformation) +declare_test(testenum) +declare_test(testextrainclude) +declare_test(testfunctiontag) +declare_test(testimplicitconversions) +declare_test(testinserttemplate) +declare_test(testmodifyfunction) +declare_test(testmultipleinheritance) +declare_test(testnamespace) +declare_test(testnestedtypes) +declare_test(testnumericaltypedef) +declare_test(testprimitivetypetag) +declare_test(testrefcounttag) +declare_test(testreferencetopointer) +declare_test(testremovefield) +declare_test(testremoveimplconv) +declare_test(testremoveoperatormethod) +declare_test(testresolvetype) +declare_test(testreverseoperators) +declare_test(testtemplates) +declare_test(testtoposort) +declare_test(testvaluetypedefaultctortag) +declare_test(testvoidarg) +declare_test(testtyperevision) +if (NOT DISABLE_DOCSTRINGS) + declare_test(testmodifydocumentation) +endif() + diff --git a/sources/shiboken6/ApiExtractor/tests/a.xml b/sources/shiboken6/ApiExtractor/tests/a.xml new file mode 100644 index 000000000..3c09d3800 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/a.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" ?> +<!-- Sample for testModifyDocumentation --> +<WebXML> + <document> + <class name="A"> + <description>oi + <brief>Brief description</brief> + <para>Paragraph number 1</para> + <para>Paragraph number 2</para> + <para>Paragraph number 3</para> + </description> + </class> + </document> +</WebXML> diff --git a/sources/shiboken6/ApiExtractor/tests/injectedcode.txt b/sources/shiboken6/ApiExtractor/tests/injectedcode.txt new file mode 100644 index 000000000..872898810 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/injectedcode.txt @@ -0,0 +1,5 @@ +// Bla +// @snippet label +code line +// @snippet label +// Bla diff --git a/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp new file mode 100644 index 000000000..4b5da0c3a --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.cpp @@ -0,0 +1,778 @@ +// 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 "testabstractmetaclass.h" +#include "abstractmetabuilder.h" +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <usingmember.h> +#include <typesystem.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestAbstractMetaClass::testClassName() +{ + const char cppCode[] = "class ClassName {};"; + const char xmlCode[] = R"(<typesystem package="Foo"> + <value-type name="ClassName"/> +</typesystem>)"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + QCOMPARE(classes[0]->name(), u"ClassName"); +} + +void TestAbstractMetaClass::testClassNameUnderNamespace() +{ + const char cppCode[] = "namespace Namespace { class ClassName {}; }\n"; + const char xmlCode[] = R"XML( + <typesystem package="Foo"> + <namespace-type name="Namespace"> + <value-type name="ClassName"/> + </namespace-type> + </typesystem>)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); // 1 namespace + 1 class + if (classes.constFirst()->name() != u"ClassName") + qSwap(classes[0], classes[1]); + + QCOMPARE(classes[0]->name(), u"ClassName"); + QCOMPARE(classes[0]->qualifiedCppName(), u"Namespace::ClassName"); + QCOMPARE(classes[1]->name(), u"Namespace"); + QVERIFY(classes[1]->isNamespace()); + + // Check ctors info + QVERIFY(classes[0]->hasConstructors()); + QCOMPARE(classes[0]->functions().size(), 2); // default ctor + copy ctor + + auto ctors = classes[0]->queryFunctions(FunctionQueryOption::AnyConstructor); + QCOMPARE(ctors.size(), 2); + if (ctors.constFirst()->minimalSignature() != u"ClassName()") + qSwap(ctors[0], ctors[1]); + + QCOMPARE(ctors[0]->arguments().size(), 0); + QCOMPARE(ctors[0]->minimalSignature(), u"ClassName()"); + QCOMPARE(ctors[1]->arguments().size(), 1); + QCOMPARE(ctors[1]->minimalSignature(), u"ClassName(Namespace::ClassName)"); + + QVERIFY(!classes[0]->hasPrivateDestructor()); + QVERIFY(classes[0]->isCopyConstructible()); // implicit default copy ctor + + // This method is buggy and nobody wants to fix it or needs it fixed :-/ + // QVERIFY(classes[0]->hasNonPrivateConstructor()); +} + +static AbstractMetaFunctionCList virtualFunctions(const AbstractMetaClassCPtr &c) +{ + AbstractMetaFunctionCList result; + const auto &functions = c->functions(); + for (const auto &f : functions) { + if (f->isVirtual()) + result.append(f); + } + return result; +} + +void TestAbstractMetaClass::testVirtualMethods() +{ + const char cppCode[] =R"CPP( +class A { +public: + virtual int pureVirtual() const = 0; +}; +class B : public A {}; +class C : public B { +public: + int pureVirtual() const override { return 0; } +}; +class F final : public C { +public: + int pureVirtual() const final { return 1; } +}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package="Foo"> + <primitive-type name='int'/> + <object-type name='A'/> + <object-type name='B'/> + <object-type name='C'/> + <object-type name='F'/> +</typesystem> +)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 4); + const auto a = AbstractMetaClass::findClass(classes, "A"); + const auto b = AbstractMetaClass::findClass(classes, "B"); + const auto c = AbstractMetaClass::findClass(classes, "C"); + const auto f = AbstractMetaClass::findClass(classes, "F"); + QVERIFY(f); + + QCOMPARE(a->baseClass(), nullptr); + QCOMPARE(b->baseClass(), a); + QCOMPARE(c->baseClass(), b); + QCOMPARE(f->baseClass(), c); + + QCOMPARE(a->functions().size(), 2); // default ctor + the pure virtual method + QCOMPARE(b->functions().size(), 2); + QCOMPARE(c->functions().size(), 2); + QCOMPARE(f->functions().size(), 2); + QVERIFY(f->attributes().testFlag(AbstractMetaClass::FinalCppClass)); + + // implementing class, ownclass, declaringclass + const auto ctorA = a->queryFunctions(FunctionQueryOption::Constructors).constFirst(); + const auto ctorB = b->queryFunctions(FunctionQueryOption::Constructors).constFirst(); + const auto ctorC = c->queryFunctions(FunctionQueryOption::Constructors).constFirst(); + QVERIFY(ctorA->isConstructor()); + QVERIFY(!ctorA->isVirtual()); + QVERIFY(ctorB->isConstructor()); + QVERIFY(!ctorB->isVirtual()); + QVERIFY(ctorC->isConstructor()); + QVERIFY(!ctorC->isVirtual()); + QCOMPARE(ctorA->implementingClass(), a); + QCOMPARE(ctorA->ownerClass(), a); + QCOMPARE(ctorA->declaringClass(), a); + + const auto virtualFunctionsA = virtualFunctions(a); + const auto virtualFunctionsB = virtualFunctions(b); + const auto virtualFunctionsC = virtualFunctions(c); + const auto virtualFunctionsF = virtualFunctions(f); + QCOMPARE(virtualFunctionsA.size(), 1); // Add a pureVirtualMethods method !? + QCOMPARE(virtualFunctionsB.size(), 1); + QCOMPARE(virtualFunctionsC.size(), 1); + QCOMPARE(virtualFunctionsF.size(), 1); + + const auto funcA = virtualFunctionsA.constFirst(); + const auto funcB = virtualFunctionsB.constFirst(); + const auto funcC = virtualFunctionsC.constFirst(); + const auto funcF = virtualFunctionsF.constFirst(); + + QCOMPARE(funcA->ownerClass(), a); + QVERIFY(funcC->isVirtual()); + QCOMPARE(funcB->ownerClass(), b); + QCOMPARE(funcC->ownerClass(), c); + QVERIFY(funcC->cppAttributes().testFlag(FunctionAttribute::Override)); + QVERIFY(funcF->cppAttributes().testFlag(FunctionAttribute::Final)); + + QCOMPARE(funcA->declaringClass(), a); + QCOMPARE(funcB->declaringClass(), a); + QCOMPARE(funcC->declaringClass(), a); + + // The next two tests could return null, because it makes more sense. + // But we have too many code written relying on this behaviour where + // implementingClass is equals to declaringClass on pure virtual functions + QCOMPARE(funcA->implementingClass(), a); + QCOMPARE(funcB->implementingClass(), a); + QCOMPARE(funcC->implementingClass(), c); +} + +void TestAbstractMetaClass::testVirtualBase() +{ + const char cppCode[] =R"CPP( +class Base { +public: + virtual ~Base() = default; +}; +class Derived : public Base {}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package="Foo"> + <object-type name='Base'/> + <object-type name='Derived'/> +</typesystem> +)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto base = AbstractMetaClass::findClass(classes, "Base"); + QVERIFY(base); + QVERIFY(base->isPolymorphic()); + const auto derived = AbstractMetaClass::findClass(classes, "Derived"); + QVERIFY(derived); + QVERIFY(derived->isPolymorphic()); +} + +void TestAbstractMetaClass::testDefaultValues() +{ + const char cppCode[] = "\ + struct A {\n\ + class B {};\n\ + void method(B b = B());\n\ + };\n"; + const char xmlCode[] = R"XML( + <typesystem package="Foo"> + <value-type name='A'> + <value-type name='B'/> + </value-type> + </typesystem>)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + const auto candidates = classA->queryFunctionsByName(u"method"_s); + QCOMPARE(candidates.size(), 1); + const auto &method = candidates.constFirst(); + const AbstractMetaArgument &arg = method->arguments().constFirst(); + QCOMPARE(arg.defaultValueExpression(), arg.originalDefaultValueExpression()); +} + +void TestAbstractMetaClass::testModifiedDefaultValues() +{ + const char cppCode[] = "\ + struct A {\n\ + class B {};\n\ + void method(B b = B());\n\ + };\n"; + const char xmlCode[] = R"XML( + <typesystem package="Foo"> + <value-type name='A'> + <modify-function signature='method(A::B)'> + <modify-argument index='1'> + <replace-default-expression with='Hello'/> + </modify-argument> + </modify-function> + <value-type name='B'/> + </value-type> + </typesystem>)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + const auto methodMatches = classA->queryFunctionsByName(u"method"_s); + QCOMPARE(methodMatches.size(), 1); + const auto method = methodMatches.constFirst(); + const AbstractMetaArgument &arg = method->arguments().constFirst(); + QCOMPARE(arg.defaultValueExpression(), u"Hello"); + QCOMPARE(arg.originalDefaultValueExpression(), u"A::B()"); +} + +void TestAbstractMetaClass::testInnerClassOfAPolymorphicOne() +{ + const char cppCode[] = "\ + struct A {\n\ + class B {};\n\ + virtual void method();\n\ + };\n"; + const char xmlCode[] = R"XML( + <typesystem package="Foo"> + <object-type name='A'> + <value-type name='B'/> + </object-type> + </typesystem>)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + QVERIFY(classA->isPolymorphic()); + const auto classB = AbstractMetaClass::findClass(classes, "A::B"); + QVERIFY(classB); + QVERIFY(!classB->isPolymorphic()); +} + +void TestAbstractMetaClass::testForwardDeclaredInnerClass() +{ + const char cppCode[] ="\ + class A {\n\ + class B;\n\ + };\n\ + class A::B {\n\ + public:\n\ + void foo();\n\ + };\n"; + const char xmlCode[] = R"XML( + <typesystem package="Foo"> + <value-type name='A'> + <value-type name='B'/> + </value-type> + </typesystem>)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + const auto classB = AbstractMetaClass::findClass(classes, "A::B"); + QVERIFY(classB); + const auto fooF = classB->findFunction("foo"); + QVERIFY(fooF); +} + +void TestAbstractMetaClass::testSpecialFunctions() +{ + const char cppCode[] ="\ + struct A {\n\ + A();\n\ + A(const A&);\n\ + A &operator=(const A&);\n\ + };\n\ + struct B {\n\ + B();\n\ + B(const B &);\n\ + B &operator=(B);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <object-type name='A'/>\n\ + <object-type name='B'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + auto ctors = classA->queryFunctions(FunctionQueryOption::AnyConstructor); + QCOMPARE(ctors.size(), 2); + QCOMPARE(ctors.constFirst()->functionType(), AbstractMetaFunction::ConstructorFunction); + QCOMPARE(ctors.at(1)->functionType(), AbstractMetaFunction::CopyConstructorFunction); + auto assigmentOps = classA->queryFunctionsByName(u"operator="_s); + QCOMPARE(assigmentOps.size(), 1); + QCOMPARE(assigmentOps.constFirst()->functionType(), + AbstractMetaFunction::AssignmentOperatorFunction); + + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classB); + ctors = classB->queryFunctions(FunctionQueryOption::AnyConstructor); + QCOMPARE(ctors.size(), 2); + QCOMPARE(ctors.constFirst()->functionType(), AbstractMetaFunction::ConstructorFunction); + QCOMPARE(ctors.at(1)->functionType(), AbstractMetaFunction::CopyConstructorFunction); + assigmentOps = classA->queryFunctionsByName(u"operator="_s); + QCOMPARE(assigmentOps.size(), 1); + QCOMPARE(assigmentOps.constFirst()->functionType(), AbstractMetaFunction::AssignmentOperatorFunction); +} + +void TestAbstractMetaClass::testClassDefaultConstructors() +{ + const char cppCode[] = "\ + struct A {};\n\ + \n\ + struct B {\n\ + B();\n\ + private: \n\ + B(const B&);\n\ + };\n\ + \n\ + struct C {\n\ + C(const C&);\n\ + };\n\ + \n\ + struct D {\n\ + private: \n\ + D(const D&);\n\ + };\n\ + \n\ + struct E {\n\ + private: \n\ + ~E();\n\ + };\n\ + \n\ + struct F {\n\ + F(int, int);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='int'/>\n\ + <value-type name='A'/>\n\ + <object-type name='B'/>\n\ + <value-type name='C'/>\n\ + <object-type name='D'/>\n\ + <object-type name='E'/>\n\ + <value-type name='F'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 6); + + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + QCOMPARE(classA->functions().size(), 2); + + auto ctors = classA->queryFunctions(FunctionQueryOption::AnyConstructor); + QCOMPARE(ctors.size(), 2); + if (ctors.constFirst()->minimalSignature() != u"A()") + qSwap(ctors[0], ctors[1]); + + QCOMPARE(ctors[0]->arguments().size(), 0); + QCOMPARE(ctors[0]->minimalSignature(), u"A()"); + QCOMPARE(ctors[1]->arguments().size(), 1); + QCOMPARE(ctors[1]->minimalSignature(), u"A(A)"); + + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classB); + QCOMPARE(classB->functions().size(), 2); + QCOMPARE(classB->functions().constFirst()->minimalSignature(), u"B()"); + + const auto classC = AbstractMetaClass::findClass(classes, "C"); + QVERIFY(classC); + QCOMPARE(classC->functions().size(), 1); + QCOMPARE(classC->functions().constFirst()->minimalSignature(), u"C(C)"); + + const auto classD = AbstractMetaClass::findClass(classes, "D"); + QVERIFY(classD); + QCOMPARE(classD->functions().size(), 1); + QCOMPARE(classD->functions().constFirst()->minimalSignature(), u"D(D)"); + QVERIFY(classD->functions().constFirst()->isPrivate()); + + const auto classE = AbstractMetaClass::findClass(classes, "E"); + QVERIFY(classE); + QVERIFY(classE->hasPrivateDestructor()); + QCOMPARE(classE->functions().size(), 0); + + const auto classF = AbstractMetaClass::findClass(classes, "F"); + QVERIFY(classF); + + ctors = classF->queryFunctions(FunctionQueryOption::AnyConstructor); + QCOMPARE(ctors.size(), 2); + if (ctors.constFirst()->minimalSignature() != u"F(int,int)") + qSwap(ctors[0], ctors[1]); + + QCOMPARE(ctors[0]->arguments().size(), 2); + QCOMPARE(ctors[0]->minimalSignature(), u"F(int,int)"); + QCOMPARE(ctors[1]->arguments().size(), 1); + QCOMPARE(ctors[1]->minimalSignature(), u"F(F)"); +} + +void TestAbstractMetaClass::testClassInheritedDefaultConstructors() +{ + const char cppCode[] = "\ + struct A {\n\ + A();\n\ + private: \n\ + A(const A&);\n\ + };\n\ + struct B : public A {};\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <object-type name='A'/>\n\ + <object-type name='B'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + + auto ctors = classA->queryFunctions(FunctionQueryOption::AnyConstructor); + QCOMPARE(ctors.size(), 2); + if (ctors.constFirst()->minimalSignature() != u"A()") + qSwap(ctors[0], ctors[1]); + + QCOMPARE(ctors[0]->arguments().size(), 0); + QCOMPARE(ctors[0]->minimalSignature(), u"A()"); + QCOMPARE(ctors[1]->arguments().size(), 1); + QCOMPARE(ctors[1]->minimalSignature(), u"A(A)"); + QVERIFY(ctors[1]->isPrivate()); + + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classB); + + ctors = classB->queryFunctions(FunctionQueryOption::Constructors); + QCOMPARE(ctors.size(), 1); + QCOMPARE(ctors.constFirst()->arguments().size(), 0); + QCOMPARE(ctors.constFirst()->minimalSignature(), u"B()"); +} + +void TestAbstractMetaClass::testAbstractClassDefaultConstructors() +{ + const char cppCode[] = "\ + struct A {\n\ + virtual void method() = 0;\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <object-type name='A'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + + const auto ctors = classA->queryFunctions(FunctionQueryOption::Constructors); + QCOMPARE(ctors.size(), 1); + QCOMPARE(ctors.constFirst()->arguments().size(), 0); + QCOMPARE(ctors.constFirst()->minimalSignature(), u"A()"); +} + +void TestAbstractMetaClass::testObjectTypesMustNotHaveCopyConstructors() +{ + const char cppCode[] = "struct A {};\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <object-type name='A'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + + const auto ctors = classA->queryFunctions(FunctionQueryOption::Constructors); + QCOMPARE(ctors.size(), 1); + QCOMPARE(ctors.constFirst()->arguments().size(), 0); + QCOMPARE(ctors.constFirst()->minimalSignature(), u"A()"); +} + +void TestAbstractMetaClass::testIsPolymorphic() +{ + const char cppCode[] = "\ + class A\n\ + {\n\ + public:\n\ + A();\n\ + inline bool abc() const { return false; }\n\ + };\n\ + \n\ + class B : public A\n\ + {\n\ + public:\n\ + B();\n\ + inline bool abc() const { return false; }\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='bool'/>\n\ + <value-type name='A'/>\n\ + <value-type name='B'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto b = AbstractMetaClass::findClass(classes, "A"); + + QVERIFY(!b->isPolymorphic()); + const auto a = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(!a->isPolymorphic()); +} + +void TestAbstractMetaClass::testClassTypedefedBaseClass() +{ + const char cppCode[] =R"CPP( +class Base { +}; + +using BaseAlias1 = Base; +using BaseAlias2 = BaseAlias1; + +class Derived : public BaseAlias2 { +}; +)CPP"; + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <object-type name='Base'/> + <object-type name='Derived'/> +</typesystem> +)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto base = AbstractMetaClass::findClass(classes, "Base"); + QVERIFY(base); + const auto derived = AbstractMetaClass::findClass(classes, "Derived"); + QVERIFY(derived); + QCOMPARE(derived->baseClasses().value(0), base); +} + +void TestAbstractMetaClass::testFreeOperators_data() +{ + QTest::addColumn<QByteArray>("code"); + + const QByteArray classHeader(R"CPP( +class Value +{ +public: + Value(int v) : m_value(v) {} + int value() const { return m_value; } +)CPP"); + + const QByteArray classFooter(R"CPP( +private: + int m_value; +}; +)CPP"); + + const QByteArray multOperatorSignature("Value operator*(const Value &v1, const Value &v2)"); + const QByteArray multOperatorBody("{ return Value(v1.value() * v2.value()); }"); + const QByteArray multOperator = multOperatorSignature + '\n' + multOperatorBody; + + QTest::newRow("free") + << (classHeader + classFooter + "\ninline " + multOperator); + QTest::newRow("free-friend-declared") + << (classHeader + "\n friend " + multOperatorSignature + ";\n" + classFooter + + "\ninline " + multOperator); + QTest::newRow("hidden friend") + << (classHeader + " friend inline " + multOperator + classFooter); +}; + +void TestAbstractMetaClass::testFreeOperators() +{ + QFETCH(QByteArray, code); + const char xmlCode[] = R"XML( + <typesystem package="Foo"> + <primitive-type name="int"/> + <value-type name="Value"/> + </typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(code.constData(), xmlCode)); + QVERIFY(builder); + const auto classes = builder->classes(); + QCOMPARE(classes.size(), 1); + QVERIFY(classes.constFirst()->hasArithmeticOperatorOverload()); + FunctionQueryOptions opts(FunctionQueryOption::OperatorOverloads); + QCOMPARE(classes.constFirst()->queryFunctions(opts).size(), 1); +} + +void TestAbstractMetaClass::testUsingMembers() +{ + const char cppCode[] =R"CPP( +class Base { +public: + explicit Base(int); + +protected: + void member(); +}; + +class Derived : public Base { +public: + using Base::Base; + using Base::member; +}; +)CPP"; + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <object-type name='Base'/> + <object-type name='Derived'/> +</typesystem> +)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto base = AbstractMetaClass::findClass(classes, "Base"); + QVERIFY(base); + const auto derived = AbstractMetaClass::findClass(classes, "Derived"); + QVERIFY(derived); + const auto usingMembers = derived->usingMembers(); + QCOMPARE(usingMembers.size(), 2); + for (const auto &um : usingMembers) { + QCOMPARE(um.access, Access::Public); + QCOMPARE(um.baseClass, base); + QVERIFY(um.memberName == u"Base" || um.memberName == u"member"); + } +} + +void TestAbstractMetaClass::testUsingTemplateMembers_data() +{ + const QByteArray cppCode(R"CPP( +struct Value { + int value = 0; +}; + +template <class T> class List { +public: + List(); + void append(const T &t); +}; + +class ValueList : public List<Value> { +public: + void append(const Value &v1, const Value &v2); +)CPP"); + + QTest::addColumn<QByteArray>("code"); + QTest::newRow("simple") + << (cppCode + "using List::append;\n};\n"); + QTest::newRow("with-template-params") + << (cppCode + "using List<Value>::append;\n};\n"); +} + +void TestAbstractMetaClass::testUsingTemplateMembers() +{ + QFETCH(QByteArray, code); + + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <value-type name='Value'/> + <container-type name='List' type='list'/> + <value-type name='ValueList'/> +</typesystem> +)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(code.constData(), xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto valueList = AbstractMetaClass::findClass(classes, "ValueList"); + QVERIFY(valueList); + auto list = valueList->templateBaseClass(); + QVERIFY(valueList->isUsingMember(list, u"append"_s, Access::Public)); + QCOMPARE(valueList->queryFunctionsByName(u"append"_s).size(), 2); +} + +void TestAbstractMetaClass::testGenerateFunctions() +{ + const char cppCode[] = R"CPP( +class TestClass { +public: + TestClass(); + + void alpha(int); + void beta(int); + void beta(double); + void gamma(int); +}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <object-type name='TestClass' generate-functions='beta(double);gamma'/> +</typesystem> +)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto tc = AbstractMetaClass::findClass(classes, "TestClass"); + // Verify that the constructor and 2 functions are generated. + const auto &functions = tc->functions(); + QCOMPARE(functions.size(), 5); + const auto generateCount = + std::count_if(functions.cbegin(), functions.cend(), + [](const auto &af) { return af->generateBinding(); }); + QCOMPARE(generateCount, 3); +} + +QTEST_APPLESS_MAIN(TestAbstractMetaClass) diff --git a/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h new file mode 100644 index 000000000..a6bd2bf06 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testabstractmetaclass.h @@ -0,0 +1,38 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTABSTRACTMETACLASS_H +#define TESTABSTRACTMETACLASS_H + +#include <QtCore/QObject> + +class AbstractMetaBuilder; + +class TestAbstractMetaClass : public QObject +{ + Q_OBJECT +private slots: + void testClassName(); + void testClassNameUnderNamespace(); + void testVirtualMethods(); + void testVirtualBase(); + void testDefaultValues(); + void testModifiedDefaultValues(); + void testInnerClassOfAPolymorphicOne(); + void testForwardDeclaredInnerClass(); + void testSpecialFunctions(); + void testClassDefaultConstructors(); + void testClassInheritedDefaultConstructors(); + void testAbstractClassDefaultConstructors(); + void testObjectTypesMustNotHaveCopyConstructors(); + void testIsPolymorphic(); + void testClassTypedefedBaseClass(); + void testFreeOperators_data(); + void testFreeOperators(); + void testUsingMembers(); + void testUsingTemplateMembers_data(); + void testUsingTemplateMembers(); + void testGenerateFunctions(); +}; + +#endif // TESTABSTRACTMETACLASS_H diff --git a/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.cpp b/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.cpp new file mode 100644 index 000000000..2c320c874 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.cpp @@ -0,0 +1,229 @@ +// 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 "testabstractmetatype.h" +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <typesystem.h> +#include <parser/codemodel.h> +#include <typeparser.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestAbstractMetaType::parsing_data() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("output"); + QTest::newRow("primitive") + << QString::fromLatin1("int") << QString::fromLatin1("int"); + QTest::newRow("ref") + << QString::fromLatin1("int &") << QString::fromLatin1("int&"); + QTest::newRow("pointer") + << QString::fromLatin1("int **") << QString::fromLatin1("int**"); + QTest::newRow("const ref") + << QString::fromLatin1("const int &") << QString::fromLatin1("const int&"); + QTest::newRow("const pointer") + << QString::fromLatin1("const int **") << QString::fromLatin1("const int**"); + QTest::newRow("const pointer const") + << QString::fromLatin1("const int *const*") << QString::fromLatin1("const int*const*"); +} + +void TestAbstractMetaType::parsing() +{ + QFETCH(QString, input); + QFETCH(QString, output); + QString errorMessage; + const TypeInfo ti = TypeParser::parse(input, &errorMessage); + QVERIFY2(errorMessage.isEmpty(), qPrintable(errorMessage)); + const QString actual = ti.toString(); + QCOMPARE(actual, output); +} + +void TestAbstractMetaType::testConstCharPtrType() +{ + const char cppCode[] = "const char* justAtest();\n"; + const char xmlCode[] = "<typesystem package=\"Foo\">\n\ + <primitive-type name='char'/>\n\ + <function signature='justAtest()' />\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + QCOMPARE(builder->globalFunctions().size(), 1); + const auto func = builder->globalFunctions().constFirst(); + AbstractMetaType rtype = func->type(); + // Test properties of const char* + QVERIFY(!rtype.isVoid()); + QCOMPARE(rtype.package(), u"Foo"); + QCOMPARE(rtype.name(), u"char"); + QVERIFY(rtype.isConstant()); + QVERIFY(!rtype.isArray()); + QVERIFY(!rtype.isContainer()); + QVERIFY(!rtype.isObject()); + QVERIFY(!rtype.isPrimitive()); // const char* differs from char, so it's not considered a primitive type by apiextractor + QVERIFY(rtype.isNativePointer()); + QCOMPARE(rtype.referenceType(), NoReference); + QVERIFY(!rtype.isValue()); + QVERIFY(!rtype.isValuePointer()); +} + +void TestAbstractMetaType::testApiVersionSupported() +{ + const char cppCode[] = "class foo {}; class foo2 {};\n\ + void justAtest(); void justAtest3();\n"; + const char xmlCode[] = "<typesystem package='Foo'>\n\ + <value-type name='foo' since='0.1'/>\n\ + <value-type name='foo2' since='1.0'/>\n\ + <value-type name='foo3' since='1.1'/>\n\ + <function signature='justAtest()' since='0.1'/>\n\ + <function signature='justAtest2()' since='1.1'/>\n\ + <function signature='justAtest3()'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + false, u"1.0"_s)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + + + QCOMPARE(builder->globalFunctions().size(), 2); +} + + +void TestAbstractMetaType::testApiVersionNotSupported() +{ + const char cppCode[] = "class object {};\n"; + const char xmlCode[] = "<typesystem package='Foo'>\n\ + <value-type name='object' since='0.1'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, u"0.1"_s)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); +} + +void TestAbstractMetaType::testCharType() +{ + const char cppCode[] = "char justAtest(); class A {};\n"; + const char xmlCode[] = "<typesystem package=\"Foo\">\n\ + <primitive-type name='char'/>\n\ + <value-type name='A'/>\n\ + <function signature='justAtest()'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + QCOMPARE(classes.constFirst()->package(), u"Foo"); + + const auto functions = builder->globalFunctions(); + QCOMPARE(functions.size(), 1); + const auto func = functions.constFirst(); + AbstractMetaType rtype = func->type(); + // Test properties of const char* + QVERIFY(!rtype.isVoid()); + QCOMPARE(rtype.package(), u"Foo"); + QCOMPARE(rtype.name(), u"char"); + QVERIFY(!rtype.isConstant()); + QVERIFY(!rtype.isArray()); + QVERIFY(!rtype.isContainer()); + QVERIFY(!rtype.isObject()); + QVERIFY(rtype.isPrimitive()); + QVERIFY(!rtype.isNativePointer()); + QCOMPARE(rtype.referenceType(), NoReference); + QVERIFY(!rtype.isValue()); + QVERIFY(!rtype.isValuePointer()); +} + +void TestAbstractMetaType::testTypedef() +{ + const char cppCode[] = "\ + struct A {\n\ + void someMethod();\n\ + };\n\ + typedef A B;\n\ + typedef B C;\n"; + const char xmlCode[] = "<typesystem package=\"Foo\">\n\ + <value-type name='C' />\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + const auto c = AbstractMetaClass::findClass(classes, "C"); + QVERIFY(c); + QVERIFY(c->isTypeDef()); +} + +void TestAbstractMetaType::testTypedefWithTemplates() +{ + const char cppCode[] = "\ + template<typename T>\n\ + class A {};\n\ + \n\ + class B {};\n\ + typedef A<B> C;\n\ + \n\ + void func(C c);\n"; + const char xmlCode[] = "<typesystem package=\"Foo\">\n\ + <container-type name='A' type='list'/>\n\ + <value-type name='B' />\n\ + <function signature='func(A<B>)'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + const auto functions = builder->globalFunctions(); + QCOMPARE(functions.size(), 1); + const auto function = functions.constFirst(); + AbstractMetaArgumentList args = function->arguments(); + QCOMPARE(args.size(), 1); + const AbstractMetaArgument &arg = args.constFirst(); + AbstractMetaType metaType = arg.type(); + QCOMPARE(metaType.cppSignature(), u"A<B>"); +} + + +void TestAbstractMetaType::testObjectTypeUsedAsValue() +{ + const char cppCode[] = "\ + class A {\n\ + void method(A);\n\ + };\n"; + const char xmlCode[] = "<typesystem package='Foo'>\n\ + <object-type name='A'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + const auto overloads = classA->queryFunctionsByName(u"method"_s); + QCOMPARE(overloads.size(), 1); + const auto method = overloads.constFirst(); + QVERIFY(method); + AbstractMetaArgumentList args = method->arguments(); + QCOMPARE(args.size(), 1); + const AbstractMetaArgument &arg = args.constFirst(); + AbstractMetaType metaType = arg.type(); + QCOMPARE(metaType.cppSignature(), u"A"); + QVERIFY(metaType.isValue()); + QVERIFY(metaType.typeEntry()->isObject()); +} + +QTEST_APPLESS_MAIN(TestAbstractMetaType) diff --git a/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.h b/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.h new file mode 100644 index 000000000..fdcf0c787 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testabstractmetatype.h @@ -0,0 +1,24 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTABSTRACTMETATYPE_H +#define TESTABSTRACTMETATYPE_H + +#include <QtCore/QObject> + +class TestAbstractMetaType : public QObject +{ + Q_OBJECT +private slots: + void parsing_data(); + void parsing(); + void testConstCharPtrType(); + void testCharType(); + void testTypedef(); + void testTypedefWithTemplates(); + void testApiVersionSupported(); + void testApiVersionNotSupported(); + void testObjectTypeUsedAsValue(); +}; + +#endif 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&)' 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<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) diff --git a/sources/shiboken6/ApiExtractor/tests/testaddfunction.h b/sources/shiboken6/ApiExtractor/tests/testaddfunction.h new file mode 100644 index 000000000..77339609f --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testaddfunction.h @@ -0,0 +1,31 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTADDFUNCTION_H +#define TESTADDFUNCTION_H +#include <QtCore/QObject> + +class TestAddFunction : public QObject +{ + Q_OBJECT +private slots: + void testParsingFuncNameAndConstness(); + void testAddFunction(); + void testAddFunctionConstructor(); + void testAddFunctionTagDefaultValues(); + void testAddFunctionCodeSnippets(); + void testAddFunctionWithoutParenteses(); + void testAddFunctionWithDefaultArgs(); + void testAddFunctionAtModuleLevel(); + void testAddFunctionWithVarargs(); + void testAddStaticFunction(); + void testAddGlobalFunction(); + void testAddFunctionWithApiVersion(); + void testModifyAddedFunction(); + void testAddFunctionOnTypedef(); + void testAddFunctionWithTemplateArg(); + void testAddFunctionTypeParser_data(); + void testAddFunctionTypeParser(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testarrayargument.cpp b/sources/shiboken6/ApiExtractor/tests/testarrayargument.cpp new file mode 100644 index 000000000..6e1820bed --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testarrayargument.cpp @@ -0,0 +1,155 @@ +// 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 "testarrayargument.h" +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetaenum.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <primitivetypeentry.h> +#include <parser/enumvalue.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestArrayArgument::testArrayArgumentWithSizeDefinedByInteger() +{ + const char cppCode[] = "\ + struct A {\n\ + enum SomeEnum { Value0, Value1, NValues };\n\ + void method(double[3]);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='double'/>\n\ + <object-type name='A'>\n\ + <enum-type name='SomeEnum'/>\n\ + </object-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(!builder.isNull()); + const auto classA = AbstractMetaClass::findClass(builder->classes(), "A"); + QVERIFY(classA); + + const AbstractMetaArgument &arg = classA->functions().constLast()->arguments().constFirst(); + QVERIFY(arg.type().isArray()); + QCOMPARE(arg.type().arrayElementCount(), 3); + QCOMPARE(arg.type().arrayElementType()->name(), u"double"); +} + +static QString functionMinimalSignature(const AbstractMetaClassCPtr &c, const QString &name) +{ + const auto f = c->findFunction(name); + return f ? f->minimalSignature() : QString(); +} + +void TestArrayArgument::testArraySignature() +{ + const char cppCode[] ="\ + struct A {\n\ + void mi1(int arg[5]);\n\ + void mi1c(const int arg[5]);\n\ + void mi1cu(const int arg[]);\n\ + void mc1cu(const char arg[]);\n\ + void mc1cup(const char *arg[]);\n\ + void muc2(unsigned char *arg[2][3]);\n\ + void mc2c(const char *arg[5][6]);\n\ + void mc2cu(const char arg[][2]);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='char'/>\n\ + <primitive-type name='unsigned char'/>\n\ + <primitive-type name='int'/>\n\ + <object-type name='A'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(!builder.isNull()); + const auto classA = AbstractMetaClass::findClass(builder->classes(), "A"); + QCOMPARE(functionMinimalSignature(classA, u"mi1"_s), + u"mi1(int[5])"); + QCOMPARE(functionMinimalSignature(classA, u"mi1c"_s), + u"mi1c(const int[5])"); + QCOMPARE(functionMinimalSignature(classA, u"mi1cu"_s), + u"mi1cu(const int[])"); + QCOMPARE(functionMinimalSignature(classA, u"mc1cu"_s), + u"mc1cu(const char*)"); + QCOMPARE(functionMinimalSignature(classA, u"mc1cup"_s), + u"mc1cup(const char*[])"); + QCOMPARE(functionMinimalSignature(classA, u"muc2"_s), + u"muc2(unsigned char*[2][3])"); + QCOMPARE(functionMinimalSignature(classA, u"mc2c"_s), + u"mc2c(const char*[5][6])"); + QCOMPARE(functionMinimalSignature(classA, u"mc2cu"_s), + u"mc2cu(const char[][2])"); +} + +void TestArrayArgument::testArrayArgumentWithSizeDefinedByEnumValue() +{ + const char cppCode[] = "\ + struct A {\n\ + enum SomeEnum { Value0, Value1, NValues };\n\ + void method(double[NValues]);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='double'/>\n\ + <object-type name='A'>\n\ + <enum-type name='SomeEnum'/>\n\ + </object-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(!builder.isNull()); + AbstractMetaClassPtr classA = AbstractMetaClass::findClass(builder->classes(), "A"); + QVERIFY(classA); + + auto someEnum = classA->findEnum(u"SomeEnum"_s); + QVERIFY(someEnum.has_value()); + auto nvalues = classA->findEnumValue(u"NValues"_s); + QVERIFY(nvalues.has_value()); + + const AbstractMetaArgument &arg = classA->functions().constLast()->arguments().constFirst(); + QVERIFY(arg.type().isArray()); + QCOMPARE(arg.type().arrayElementCount(), nvalues->value().value()); + QCOMPARE(arg.type().arrayElementType()->name(), u"double"); +}; + +void TestArrayArgument::testArrayArgumentWithSizeDefinedByEnumValueFromGlobalEnum() +{ + const char cppCode[] = "\ + enum SomeEnum { Value0, Value1, NValues };\n\ + struct A {\n\ + void method(double[NValues]);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='double'/>\n\ + <enum-type name='SomeEnum'/>\n\ + <object-type name='A'>\n\ + </object-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + const auto classA = AbstractMetaClass::findClass(builder->classes(), "A"); + QVERIFY(classA); + + AbstractMetaEnum someEnum = builder->globalEnums().constFirst(); + auto nvalues = someEnum.findEnumValue(u"NValues"); + QVERIFY(nvalues.has_value()); + + const AbstractMetaArgument &arg = classA->functions().constLast()->arguments().constFirst(); + QVERIFY(arg.type().isArray()); + QCOMPARE(arg.type().arrayElementCount(), nvalues->value().value()); + QCOMPARE(arg.type().arrayElementType()->name(), u"double"); +}; + +QTEST_APPLESS_MAIN(TestArrayArgument) diff --git a/sources/shiboken6/ApiExtractor/tests/testarrayargument.h b/sources/shiboken6/ApiExtractor/tests/testarrayargument.h new file mode 100644 index 000000000..75ef0f792 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testarrayargument.h @@ -0,0 +1,18 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTARRAYARGUMENT_H +#define TESTARRAYARGUMENT_H +#include <QtCore/QObject> + +class TestArrayArgument : public QObject +{ + Q_OBJECT +private slots: + void testArrayArgumentWithSizeDefinedByInteger(); + void testArraySignature(); + void testArrayArgumentWithSizeDefinedByEnumValue(); + void testArrayArgumentWithSizeDefinedByEnumValueFromGlobalEnum(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testcodeinjection.cpp b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.cpp new file mode 100644 index 000000000..4829e6c33 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.cpp @@ -0,0 +1,164 @@ +// 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 "testcodeinjection.h" +#include "testutil.h" +#include <abstractmetalang.h> +#include <codesnip.h> +#include <modifications.h> +#include <textstream.h> +#include <complextypeentry.h> +#include <valuetypeentry.h> + +#include <qtcompat.h> + +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestCodeInjections::testReadFile_data() +{ + QTest::addColumn<QString>("filePath"); + QTest::addColumn<QString>("snippet"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("utf8") + << QString::fromLatin1(":/utf8code.txt") + << QString() + << QString::fromUtf8("\xC3\xA1\xC3\xA9\xC3\xAD\xC3\xB3\xC3\xBA"); + + QTest::newRow("snippet") + << QString::fromLatin1(":/injectedcode.txt") + << QString::fromLatin1("label") + << QString::fromLatin1("code line"); +} + +void TestCodeInjections::testReadFile() +{ + QFETCH(QString, filePath); + QFETCH(QString, snippet); + QFETCH(QString, expected); + + const char cppCode[] = "struct A {};\n"; + int argc = 0; + char *argv[] = {nullptr}; + QCoreApplication app(argc, argv); + + QString attribute = u"file='"_s + filePath + u'\''; + if (!snippet.isEmpty()) + attribute += u" snippet='"_s + snippet + u'\''; + + QString xmlCode = u"\ + <typesystem package=\"Foo\">\n\ + <value-type name='A'>\n\ + <conversion-rule class='target' "_s + attribute + u"/>\n\ + <inject-code class='target' "_s + attribute + u"/>\n\ + <value-type name='B'/>\n\ + </value-type>\n\ + </typesystem>\n"_s; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode.toLocal8Bit().constData())); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QCOMPARE(classA->typeEntry()->codeSnips().size(), 1); + QString code = classA->typeEntry()->codeSnips().constFirst().code(); + QVERIFY(code.indexOf(expected) != -1); + QVERIFY(classA->typeEntry()->isValue()); + auto vte = std::static_pointer_cast<const ValueTypeEntry>(classA->typeEntry()); + code = vte->targetConversionRule(); + QVERIFY(code.indexOf(expected) != -1); +} + +void TestCodeInjections::testInjectWithValidApiVersion() +{ + const char cppCode[] = "struct A {};\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <value-type name='A'>\n\ + <inject-code class='target' since='1.0'>\n\ + test Inject code\n\ + </inject-code>\n\ + </value-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, u"1.0"_s)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QCOMPARE(classA->typeEntry()->codeSnips().size(), 1); +} + +void TestCodeInjections::testInjectWithInvalidApiVersion() +{ + const char cppCode[] = "struct A {};\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <value-type name='A'>\n\ + <inject-code class='target' since='1.0'>\n\ + test Inject code\n\ + </inject-code>\n\ + </value-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, u"0.1"_s)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QCOMPARE(classA->typeEntry()->codeSnips().size(), 0); +} + +void TestCodeInjections::testTextStream() +{ + StringStream str(TextStream::Language::Cpp); + str << "void foo(int a, int b) {\n" << indent + << "if (a == b)\n" << indent << "return a;\n" << outdent + << "#if Q_OS_WIN\nprint()\n#endif\nreturn a + b;\n" << outdent + << "}\n\n// A table\n|" + << AlignedField("bla", 40, QTextStream::AlignRight) << "|\n|" + << AlignedField("bla", 40, QTextStream::AlignLeft) << "|\n|" + << AlignedField(QString(), 40, QTextStream::AlignLeft) << "|\n"; + str << "\n2nd table\n|" << AlignedField("bla", 3, QTextStream::AlignLeft) + << '|' << AlignedField(QString{}, 0, QTextStream::AlignLeft) << "|\n"; + +constexpr auto expected = R"(void foo(int a, int b) { + if (a == b) + return a; +#if Q_OS_WIN + print() +#endif + return a + b; +} + +// A table +| bla| +|bla | +| | + +2nd table +|bla|| +)"_L1; + + QCOMPARE(str.toString(), expected); +} + +void TestCodeInjections::testTextStreamRst() +{ + // Test that sphinx error: "Inline strong start-string without end-string." + // is avoided, that is, characters following a formatting end are escaped. + + StringStream str; + str << rstBold << "QObject" << rstBoldOff << "'s properties..." + << rstItalic << "some italic" << rstItalicOff << " followed by space."; + + static const char16_t expected[] = + uR"(**QObject**\'s properties...*some italic* followed by space.)"; + + QCOMPARE(str.toString(), expected); +} + +QTEST_APPLESS_MAIN(TestCodeInjections) diff --git a/sources/shiboken6/ApiExtractor/tests/testcodeinjection.h b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.h new file mode 100644 index 000000000..a164ea36e --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.h @@ -0,0 +1,23 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTCODEINJECTIONS_H +#define TESTCODEINJECTIONS_H + +#include <QtCore/QObject> + +class AbstractMetaBuilder; + +class TestCodeInjections : public QObject +{ + Q_OBJECT +private slots: + void testReadFile_data(); + void testReadFile(); + void testInjectWithValidApiVersion(); + void testInjectWithInvalidApiVersion(); + void testTextStream(); + void testTextStreamRst(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testcodeinjection.qrc b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.qrc new file mode 100644 index 000000000..fd7616bd2 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testcodeinjection.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource> + <file>utf8code.txt</file> + <file>injectedcode.txt</file> + </qresource> +</RCC> diff --git a/sources/shiboken6/ApiExtractor/tests/testcontainer.cpp b/sources/shiboken6/ApiExtractor/tests/testcontainer.cpp new file mode 100644 index 000000000..0bb72b3c1 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testcontainer.cpp @@ -0,0 +1,84 @@ +// 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 "testcontainer.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <complextypeentry.h> +#include <containertypeentry.h> + +void TestContainer::testContainerType() +{ + const char cppCode[] = "\ + namespace std {\n\ + template<class T>\n\ + class list {\n\ + T get(int x) { return 0; }\n\ + };\n\ + }\n\ + class A : public std::list<int> {\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <namespace-type name='std' generate='no' />\n\ + <container-type name='std::list' type='list' />\n\ + <object-type name='A'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + //search for class A + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + auto baseContainer = classA->typeEntry()->baseContainerType(); + QVERIFY(baseContainer); + QCOMPARE(reinterpret_cast<const ContainerTypeEntry*>(baseContainer.get())->containerKind(), + ContainerTypeEntry::ListContainer); +} + +void TestContainer::testListOfValueType() +{ + const char cppCode[] = "\ + namespace std {\n\ + template<class T>\n\ + class list {\n\ + T get(int x) { return 0; }\n\ + };\n\ + }\n\ + class ValueType {};\n\ + class A : public std::list<ValueType> {\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <namespace-type name='std' generate='no'/>\n\ + <container-type name='std::list' type='list'/>\n\ + <value-type name='ValueType'/>\n\ + <value-type name='A'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 3); + + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + QCOMPARE(classA->templateBaseClassInstantiations().size(), 1); + const AbstractMetaType templateInstanceType = + classA->templateBaseClassInstantiations().constFirst(); + + QCOMPARE(templateInstanceType.indirections(), 0); + QVERIFY(!templateInstanceType.typeEntry()->isObject()); + QVERIFY(templateInstanceType.typeEntry()->isValue()); + QCOMPARE(templateInstanceType.referenceType(), NoReference); + QVERIFY(!templateInstanceType.isObject()); + QVERIFY(!templateInstanceType.isValuePointer()); + QVERIFY(templateInstanceType.isValue()); +} + +QTEST_APPLESS_MAIN(TestContainer) + diff --git a/sources/shiboken6/ApiExtractor/tests/testcontainer.h b/sources/shiboken6/ApiExtractor/tests/testcontainer.h new file mode 100644 index 000000000..3fd23c3f0 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testcontainer.h @@ -0,0 +1,16 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTCONTAINER_H +#define TESTCONTAINER_H +#include <QtCore/QObject> + +class TestContainer : public QObject +{ + Q_OBJECT +private slots: + void testContainerType(); + void testListOfValueType(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testconversionoperator.cpp b/sources/shiboken6/ApiExtractor/tests/testconversionoperator.cpp new file mode 100644 index 000000000..8f2b277af --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testconversionoperator.cpp @@ -0,0 +1,178 @@ +// 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 "testconversionoperator.h" +#include "testutil.h" +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <typesystem.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestConversionOperator::testConversionOperator() +{ + const char cppCode[] = "\ + struct A {\n\ + };\n\ + struct B {\n\ + operator A() const;\n\ + };\n\ + struct C {\n\ + operator A() const;\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <value-type name='A'/>\n\ + <value-type name='B'/>\n\ + <value-type name='C'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + const auto classC = AbstractMetaClass::findClass(classes, "C"); + QVERIFY(classA); + QVERIFY(classB); + QVERIFY(classC); + QCOMPARE(classA->functions().size(), 2); + QCOMPARE(classB->functions().size(), 3); + QCOMPARE(classC->functions().size(), 3); + QCOMPARE(classA->externalConversionOperators().size(), 2); + + AbstractMetaFunctionCPtr convOp; + for (const auto &func : classB->functions()) { + if (func->isConversionOperator()) { + convOp = func; + break; + } + } + QVERIFY(convOp); + QVERIFY(classA->externalConversionOperators().contains(convOp)); +} + +void TestConversionOperator::testConversionOperatorOfDiscardedClass() +{ + const char cppCode[] = "\ + struct A {\n\ + };\n\ + struct B {\n\ + operator A() const;\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <value-type name='A' />\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->externalConversionOperators().size(), 0); +} + +void TestConversionOperator::testRemovedConversionOperator() +{ + const char cppCode[] = "\ + struct A {\n\ + };\n\ + struct B {\n\ + operator A() const;\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <value-type name='A' />\n\ + <value-type name='B'>\n\ + <modify-function signature='operator A() const' remove='all'/>\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"); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classA); + QVERIFY(classB); + QCOMPARE(classA->functions().size(), 2); + QCOMPARE(classB->functions().size(), 3); + QCOMPARE(classA->externalConversionOperators().size(), 0); + QCOMPARE(classA->implicitConversions().size(), 0); +} + +void TestConversionOperator::testConversionOperatorReturningReference() +{ + const char cppCode[] = "\ + struct A {};\n\ + struct B {\n\ + operator A&() const;\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <value-type name='A'/>\n\ + <value-type name='B'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classA); + QVERIFY(classB); + QCOMPARE(classA->functions().size(), 2); + QCOMPARE(classB->functions().size(), 3); + QCOMPARE(classA->externalConversionOperators().size(), 1); + QCOMPARE(classA->externalConversionOperators().constFirst()->type().cppSignature(), + u"A"); + QCOMPARE(classA->externalConversionOperators().constFirst()->ownerClass()->name(), + u"B"); + QCOMPARE(classA->implicitConversions().size(), 1); + QCOMPARE(classA->implicitConversions().constFirst()->type().cppSignature(), + u"A"); + QCOMPARE(classA->implicitConversions().constFirst()->ownerClass()->name(), + u"B"); +} + +void TestConversionOperator::testConversionOperatorReturningConstReference() +{ + const char cppCode[] = "\ + struct A {};\n\ + struct B {\n\ + operator const A&() const;\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <value-type name='A'/>\n\ + <value-type name='B'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classA); + QVERIFY(classB); + QCOMPARE(classA->functions().size(), 2); + QCOMPARE(classB->functions().size(), 3); + QCOMPARE(classA->externalConversionOperators().size(), 1); + QCOMPARE(classA->externalConversionOperators().constFirst()->type().cppSignature(), + u"A"_s); + QCOMPARE(classA->externalConversionOperators().constFirst()->ownerClass()->name(), + u"B"_s); + QCOMPARE(classA->implicitConversions().size(), 1); + QCOMPARE(classA->implicitConversions().constFirst()->type().cppSignature(), + u"A"_s); + QCOMPARE(classA->implicitConversions().constFirst()->ownerClass()->name(), + u"B"_s); +} + +QTEST_APPLESS_MAIN(TestConversionOperator) diff --git a/sources/shiboken6/ApiExtractor/tests/testconversionoperator.h b/sources/shiboken6/ApiExtractor/tests/testconversionoperator.h new file mode 100644 index 000000000..68288d240 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testconversionoperator.h @@ -0,0 +1,19 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTCONVERSIONOPERATOR_H +#define TESTCONVERSIONOPERATOR_H +#include <QtCore/QObject> + +class TestConversionOperator : public QObject +{ + Q_OBJECT +private slots: + void testConversionOperator(); + void testConversionOperatorOfDiscardedClass(); + void testRemovedConversionOperator(); + void testConversionOperatorReturningReference(); + void testConversionOperatorReturningConstReference(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testconversionruletag.cpp b/sources/shiboken6/ApiExtractor/tests/testconversionruletag.cpp new file mode 100644 index 000000000..b5efd92a6 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testconversionruletag.cpp @@ -0,0 +1,240 @@ +// 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 "testconversionruletag.h" +#include "testutil.h" +#include <abstractmetalang.h> +#include <complextypeentry.h> +#include <customconversion.h> +#include <primitivetypeentry.h> +#include <valuetypeentry.h> + +#include <qtcompat.h> + +#include <QtCore/QFile> +#include <QtCore/QTemporaryFile> +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestConversionRuleTag::testConversionRuleTagWithFile() +{ + // FIXME PYSIDE7 remove + // temp file used later + constexpr auto conversionData = "Hi! I'm a conversion rule."_L1; + QTemporaryFile file; + QVERIFY(file.open()); + QCOMPARE(file.write(conversionData.constData()), conversionData.size()); + file.close(); + + const char cppCode[] = "struct A {};\n"; + QString xmlCode = u"\ + <typesystem package='Foo'>\n\ + <value-type name='A'>\n\ + <conversion-rule class='target' file='"_s + file.fileName() + u"'/>\n\ + </value-type>\n\ + </typesystem>\n"_s; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode.toLocal8Bit().data())); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + const auto typeEntry = classA->typeEntry(); + QVERIFY(typeEntry->isValue()); + auto vte = std::static_pointer_cast<const ValueTypeEntry>(typeEntry); + QVERIFY(vte->hasTargetConversionRule()); + QCOMPARE(vte->targetConversionRule(), conversionData); +} + +void TestConversionRuleTag::testConversionRuleTagReplace() +{ + const char cppCode[] = "\ + struct A {\n\ + A();\n\ + A(const char*, int);\n\ + };\n\ + struct B {\n\ + A createA();\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='int'/>\n\ + <primitive-type name='char'/>\n\ + <primitive-type name='A'>\n\ + <conversion-rule>\n\ + <native-to-target>\n\ + DoThis();\n\ + return ConvertFromCppToPython(%IN);\n\ + </native-to-target>\n\ + <target-to-native>\n\ + <add-conversion type='TargetNone' check='%IN == Target_None'>\n\ + DoThat();\n\ + DoSomething();\n\ + %OUT = A();\n\ + </add-conversion>\n\ + <add-conversion type='B' check='CheckIfInputObjectIsB(%IN)'>\n\ + %OUT = %IN.createA();\n\ + </add-conversion>\n\ + <add-conversion type='String' check='String_Check(%IN)'>\n\ + %OUT = new A(String_AsString(%IN), String_GetSize(%IN));\n\ + </add-conversion>\n\ + </target-to-native>\n\ + </conversion-rule>\n\ + </primitive-type>\n\ + <value-type name='B'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + auto *typeDb = TypeDatabase::instance(); + auto typeA = typeDb->findPrimitiveType(u"A"_s); + QVERIFY(typeA); + + QVERIFY(typeA->hasCustomConversion()); + auto conversion = typeA->customConversion(); + + QCOMPARE(typeA, conversion->ownerType()); + QCOMPARE(conversion->nativeToTargetConversion().simplified(), + u"DoThis(); return ConvertFromCppToPython(%IN);"); + + QVERIFY(conversion->replaceOriginalTargetToNativeConversions()); + QVERIFY(conversion->hasTargetToNativeConversions()); + QCOMPARE(conversion->targetToNativeConversions().size(), 3); + + QVERIFY(!conversion->targetToNativeConversions().isEmpty()); + auto toNative = conversion->targetToNativeConversions().at(0); + QCOMPARE(toNative.sourceTypeName(), u"TargetNone"); + QVERIFY(toNative.isCustomType()); + QCOMPARE(toNative.sourceType(), nullptr); + QCOMPARE(toNative.sourceTypeCheck(), u"%IN == Target_None"); + QCOMPARE(toNative.conversion().simplified(), + u"DoThat(); DoSomething(); %OUT = A();"); + + QVERIFY(conversion->targetToNativeConversions().size() > 1); + toNative = conversion->targetToNativeConversions().at(1); + QCOMPARE(toNative.sourceTypeName(), u"B"); + QVERIFY(!toNative.isCustomType()); + auto typeB = typeDb->findType(u"B"_s); + QVERIFY(typeB); + QCOMPARE(toNative.sourceType(), typeB); + QCOMPARE(toNative.sourceTypeCheck(), u"CheckIfInputObjectIsB(%IN)"); + QCOMPARE(toNative.conversion().trimmed(), u"%OUT = %IN.createA();"); + + QVERIFY(conversion->targetToNativeConversions().size() > 2); + toNative = conversion->targetToNativeConversions().at(2); + QCOMPARE(toNative.sourceTypeName(), u"String"); + QVERIFY(toNative.isCustomType()); + QCOMPARE(toNative.sourceType(), nullptr); + QCOMPARE(toNative.sourceTypeCheck(), u"String_Check(%IN)"); + QCOMPARE(toNative.conversion().trimmed(), + u"%OUT = new A(String_AsString(%IN), String_GetSize(%IN));"); +} + +void TestConversionRuleTag::testConversionRuleTagAdd() +{ + const char cppCode[] = "\ + struct Date {\n\ + Date();\n\ + Date(int, int, int);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='int'/>\n\ + <value-type name='Date'>\n\ + <conversion-rule>\n\ + <target-to-native replace='no'>\n\ + <add-conversion type='TargetDate' check='TargetDate_Check(%IN)'>\n\ +if (!TargetDateTimeAPI) TargetDateTime_IMPORT;\n\ +%OUT = new Date(TargetDate_Day(%IN), TargetDate_Month(%IN), TargetDate_Year(%IN));\n\ + </add-conversion>\n\ + </target-to-native>\n\ + </conversion-rule>\n\ + </value-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + const auto classA = AbstractMetaClass::findClass(builder->classes(), "Date"); + QVERIFY(classA); + + QVERIFY(classA->typeEntry()->isValue()); + auto vte = std::static_pointer_cast<const ValueTypeEntry>(classA->typeEntry()); + QVERIFY(vte->hasCustomConversion()); + auto conversion = vte->customConversion(); + + QCOMPARE(conversion->nativeToTargetConversion(), QString()); + + QVERIFY(!conversion->replaceOriginalTargetToNativeConversions()); + QVERIFY(conversion->hasTargetToNativeConversions()); + QCOMPARE(conversion->targetToNativeConversions().size(), 1); + + QVERIFY(!conversion->targetToNativeConversions().isEmpty()); + const auto &toNative = conversion->targetToNativeConversions().constFirst(); + QCOMPARE(toNative.sourceTypeName(), u"TargetDate"); + QVERIFY(toNative.isCustomType()); + QCOMPARE(toNative.sourceType(), nullptr); + QCOMPARE(toNative.sourceTypeCheck(), u"TargetDate_Check(%IN)"); + QCOMPARE(toNative.conversion().trimmed(), + uR"(if (!TargetDateTimeAPI) TargetDateTime_IMPORT; +%OUT = new Date(TargetDate_Day(%IN), TargetDate_Month(%IN), TargetDate_Year(%IN));)"); +} + +void TestConversionRuleTag::testConversionRuleTagWithInsertTemplate() +{ + const char cppCode[] = "struct A {};"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='int'/>\n\ + <!-- single line -->\n\ + <template name='native_to_target'>return ConvertFromCppToPython(%IN);</template>\n\ + <!-- multi-line -->\n\ + <template name='target_to_native'>\n\ +%OUT = %IN.createA();\n\ + </template>\n\ + <primitive-type name='A'>\n\ + <conversion-rule>\n\ + <native-to-target>\n\ + <insert-template name='native_to_target'/>\n\ + </native-to-target>\n\ + <target-to-native>\n\ + <add-conversion type='TargetType'>\n\ + <insert-template name='target_to_native'/>\n\ + </add-conversion>\n\ + </target-to-native>\n\ + </conversion-rule>\n\ + </primitive-type>\n\ + </typesystem>\n"; + + const char nativeToTargetExpected[] = + "// TEMPLATE - native_to_target - START\n" + "return ConvertFromCppToPython(%IN);\n" + "// TEMPLATE - native_to_target - END"; + + const char targetToNativeExpected[] = + "// TEMPLATE - target_to_native - START\n" + "%OUT = %IN.createA();\n" + "// TEMPLATE - target_to_native - END"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + auto *typeDb = TypeDatabase::instance(); + auto typeA = typeDb->findPrimitiveType(u"A"_s); + QVERIFY(typeA); + + QVERIFY(typeA->hasCustomConversion()); + auto conversion = typeA->customConversion(); + + QCOMPARE(typeA, conversion->ownerType()); + QCOMPARE(conversion->nativeToTargetConversion().trimmed(), + QLatin1StringView(nativeToTargetExpected)); + + QVERIFY(conversion->hasTargetToNativeConversions()); + QCOMPARE(conversion->targetToNativeConversions().size(), 1); + + QVERIFY(!conversion->targetToNativeConversions().isEmpty()); + const auto &toNative = conversion->targetToNativeConversions().constFirst(); + QCOMPARE(toNative.conversion().trimmed(), + QLatin1StringView(targetToNativeExpected)); +} + +QTEST_APPLESS_MAIN(TestConversionRuleTag) diff --git a/sources/shiboken6/ApiExtractor/tests/testconversionruletag.h b/sources/shiboken6/ApiExtractor/tests/testconversionruletag.h new file mode 100644 index 000000000..64d496cc3 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testconversionruletag.h @@ -0,0 +1,19 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTCONVERSIONRULE_H +#define TESTCONVERSIONRULE_H + +#include <QtCore/QObject> + +class TestConversionRuleTag : public QObject +{ + Q_OBJECT +private slots: + void testConversionRuleTagWithFile(); + void testConversionRuleTagReplace(); + void testConversionRuleTagAdd(); + void testConversionRuleTagWithInsertTemplate(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testctorinformation.cpp b/sources/shiboken6/ApiExtractor/tests/testctorinformation.cpp new file mode 100644 index 000000000..c3a3ebef0 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testctorinformation.cpp @@ -0,0 +1,57 @@ +// 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 "testctorinformation.h" +#include "abstractmetabuilder.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetalang.h> +#include <typesystem.h> + +void TestCtorInformation::testCtorIsPrivate() +{ + const char cppCode[] = "class Control { public: Control() {} };\n\ + class Subject { private: Subject() {} };\n\ + class CtorLess { };\n"; + const char xmlCode[] = "<typesystem package='Foo'>\n\ + <value-type name='Control'/>\n\ + <object-type name='Subject'/>\n\ + <value-type name='CtorLess'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 3); + auto klass = AbstractMetaClass::findClass(classes, "Control"); + QVERIFY(klass); + QVERIFY(klass->hasNonPrivateConstructor()); + klass = AbstractMetaClass::findClass(classes, "Subject"); + QVERIFY(klass); + QVERIFY(!klass->hasNonPrivateConstructor()); + klass = AbstractMetaClass::findClass(classes, "CtorLess"); + QVERIFY(klass); + QVERIFY(klass->hasNonPrivateConstructor()); +} + +void TestCtorInformation::testHasNonPrivateCtor() +{ + const char cppCode[] = "template<typename T>\n\ + struct Base { Base(double) {} };\n\ + typedef Base<int> Derived;\n"; + const char xmlCode[] = "<typesystem package='Foo'>\n\ + <primitive-type name='int'/>\n\ + <primitive-type name='double'/>\n\ + <object-type name='Base' generate='no'/>\n\ + <object-type name='Derived'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto base = AbstractMetaClass::findClass(classes, "Base"); + QCOMPARE(base->hasNonPrivateConstructor(), true); + const auto derived = AbstractMetaClass::findClass(classes, "Derived"); + QCOMPARE(derived->hasNonPrivateConstructor(), true); +} + +QTEST_APPLESS_MAIN(TestCtorInformation) diff --git a/sources/shiboken6/ApiExtractor/tests/testctorinformation.h b/sources/shiboken6/ApiExtractor/tests/testctorinformation.h new file mode 100644 index 000000000..58f1648e4 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testctorinformation.h @@ -0,0 +1,19 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTCTORINFORMATION_H +#define TESTCTORINFORMATION_H + +#include <QtCore/QObject> + +class AbstractMetaBuilder; + +class TestCtorInformation: public QObject +{ + Q_OBJECT +private slots: + void testCtorIsPrivate(); + void testHasNonPrivateCtor(); +}; + +#endif // TESTCTORINFORMATION_H diff --git a/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.cpp b/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.cpp new file mode 100644 index 000000000..16f50e69d --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.cpp @@ -0,0 +1,228 @@ +// 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 "testdroptypeentries.h" +#include "testutil.h" +#include <abstractmetaenum.h> +#include <abstractmetalang.h> +#include <typesystem.h> +#include <conditionalstreamreader.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +static const char cppCode[] = "\ + struct ValueA {};\n\ + struct ValueB {};\n\ + struct ObjectA {};\n\ + struct ObjectB {};\n\ + namespace NamespaceA {\n\ + struct InnerClassA {};\n\ + namespace InnerNamespaceA {}\n\ + }\n\ + namespace NamespaceB {}\n\ + enum EnumA { Value0 };\n\ + enum EnumB { Value1 };\n\ + void funcA();\n\ + void funcB();\n"; + +static const char xmlCode[] = "\ +<typesystem package='Foo'>\n\ + <value-type name='ValueA'/>\n\ + <value-type name='ValueB'/>\n\ + <object-type name='ObjectA'/>\n\ + <object-type name='ObjectB'/>\n\ + <namespace-type name='NamespaceA'>\n\ + <value-type name='InnerClassA'/>\n\ + <namespace-type name='InnerNamespaceA'/>\n\ + </namespace-type>\n\ + <namespace-type name='NamespaceB'/>\n\ + <enum-type name='EnumA'/>\n\ + <enum-type name='EnumB'/>\n\ + <function signature='funcA()'/>\n\ + <function signature='funcB()'/>\n\ +</typesystem>\n"; + +void TestDropTypeEntries::testDropEntries() +{ + const QStringList droppedEntries{u"Foo.ValueB"_s, + u"ObjectB"_s, // Check whether module can be omitted + u"Foo.NamespaceA.InnerClassA"_s, + u"Foo.NamespaceB"_s, u"Foo.EnumB"_s, + u"Foo.funcB()"_s, + u"Foo.NamespaceA.InnerNamespaceA"_s}; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, + QString(), droppedEntries)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + QVERIFY(AbstractMetaClass::findClass(classes, "ValueA")); + QVERIFY(!AbstractMetaClass::findClass(classes, "ValueB")); + QVERIFY(AbstractMetaClass::findClass(classes, "ObjectA")); + QVERIFY(!AbstractMetaClass::findClass(classes, "ObjectB")); + QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceA")); + QVERIFY(!AbstractMetaClass::findClass(classes, "NamespaceA::InnerClassA")); + QVERIFY(!AbstractMetaClass::findClass(classes, "NamespaceB")); + + AbstractMetaEnumList globalEnums = builder->globalEnums(); + QCOMPARE(globalEnums.size(), 1); + QCOMPARE(globalEnums.constFirst().name(), u"EnumA"); + + auto *td = TypeDatabase::instance(); + QVERIFY(td->findType(u"funcA"_s)); + QVERIFY(!td->findType(u"funcB"_s)); +} + +void TestDropTypeEntries::testDontDropEntries() +{ + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + QVERIFY(AbstractMetaClass::findClass(classes, "ValueA")); + QVERIFY(AbstractMetaClass::findClass(classes, "ValueB")); + QVERIFY(AbstractMetaClass::findClass(classes, "ObjectA")); + QVERIFY(AbstractMetaClass::findClass(classes, "ObjectB")); + QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceA")); + QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceA::InnerClassA")); + QVERIFY(AbstractMetaClass::findClass(classes, "NamespaceB")); + + QCOMPARE(builder->globalEnums().size(), 2); + + auto *td = TypeDatabase::instance(); + QVERIFY(td->findType(u"funcA"_s)); + QVERIFY(td->findType(u"funcB"_s)); +} + +static const char cppCode2[] = "\ + struct ValueA {\n\ + void func();\n\ + };\n"; + +static const char xmlCode2[] = R"( +<typesystem package='Foo'> + <value-type name='ValueA'> + <modify-function signature='func()' remove='all'/> + </value-type> +</typesystem> +)"; + +void TestDropTypeEntries::testDropEntryWithChildTags() +{ + QStringList droppedEntries(u"Foo.ValueA"_s); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode2, xmlCode2, false, + QString(), droppedEntries)); + QVERIFY(builder); + QVERIFY(!AbstractMetaClass::findClass(builder->classes(), "ValueA")); +} + + +void TestDropTypeEntries::testDontDropEntryWithChildTags() +{ + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode2, xmlCode2, false)); + QVERIFY(builder); + QVERIFY(AbstractMetaClass::findClass(builder->classes(), "ValueA")); +} + +void TestDropTypeEntries::testConditionalParsing_data() +{ + const QString xml = R"(<?xml version="1.0" encoding="UTF-8"?> +<root> + <tag1>text</tag1> + <?if keyword1?> + <tag2>text</tag2> + <?if keyword2?> + <tag3>text</tag3> + <?endif?> + <?if keyword1 !keyword2?> + <tag4>text</tag4> + <?endif?> + <?endif?> + <tag5>text</tag5> + <?if !keyword99?> <!-- Exclusion only --> + <tag6>text</tag6> + <?endif?> +</root>)"_L1; + + constexpr auto root = "root"_L1; + constexpr auto tag1 = "tag1"_L1; + constexpr auto tag2 = "tag2"_L1; + constexpr auto tag3 = "tag3"_L1; + constexpr auto tag4 = "tag4"_L1; + constexpr auto tag5 = "tag5"_L1; + constexpr auto tag6 = "tag6"_L1; + constexpr auto keyword1 = "keyword1"_L1; + constexpr auto keyword2 = "keyword2"_L1; + + QTest::addColumn<QString>("xml"); + QTest::addColumn<QStringList>("keywords"); + QTest::addColumn<QStringList>("expectedTags"); + + QTest::newRow("no-keywords") + << xml << QStringList{} << QStringList{root, tag1, tag5, tag6}; + + QTest::newRow("skip-nested-condition") + << xml << QStringList{keyword1} + << QStringList{root, tag1, tag2, tag4, tag5, tag6}; + + QTest::newRow("both/check-not") + << xml << QStringList{keyword1, keyword2} + << QStringList{root, tag1, tag2, tag3, tag5, tag6}; +} + +// Parse XML and return a list of tags encountered +static QStringList parseXml(const QString &xml, const QStringList &keywords) +{ + QStringList tags; + ConditionalStreamReader reader(xml); + reader.setConditions(keywords); + while (!reader.atEnd()) { + auto t = reader.readNext(); + switch (t) { + case QXmlStreamReader::StartElement: + tags.append(reader.name().toString()); + break; + default: + break; + } + } + return tags; +} + +void TestDropTypeEntries::testConditionalParsing() +{ + QFETCH(QString, xml); + QFETCH(QStringList, keywords); + QFETCH(QStringList, expectedTags); + + const QStringList actualTags = parseXml(xml, keywords); + QCOMPARE(actualTags, expectedTags); +} + +void TestDropTypeEntries::testEntityParsing() +{ + const QString xml = R"(<?xml version="1.0" encoding="UTF-8"?> +<root> + <?entity testentity word1 word2?> + <text>bla &testentity;</text> +</root>)"_L1; + + QString actual; + ConditionalStreamReader reader(xml); + while (!reader.atEnd()) { + auto t = reader.readNext(); + switch (t) { + case QXmlStreamReader::Characters: + actual.append(reader.text()); + default: + break; + } + } + QVERIFY2(!reader.hasError(), qPrintable(reader.errorString())); + QCOMPARE(actual.trimmed(), u"bla word1 word2"); +} + +QTEST_APPLESS_MAIN(TestDropTypeEntries) diff --git a/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.h b/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.h new file mode 100644 index 000000000..98717bd21 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testdroptypeentries.h @@ -0,0 +1,22 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTDROPTYPEENTRIES_H +#define TESTDROPTYPEENTRIES_H + +#include <QtCore/QObject> + +class TestDropTypeEntries : public QObject +{ + Q_OBJECT + private slots: + void testDropEntries(); + void testDontDropEntries(); + void testDropEntryWithChildTags(); + void testDontDropEntryWithChildTags(); + void testConditionalParsing_data(); + void testConditionalParsing(); + void testEntityParsing(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testdtorinformation.cpp b/sources/shiboken6/ApiExtractor/tests/testdtorinformation.cpp new file mode 100644 index 000000000..2152d39de --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testdtorinformation.cpp @@ -0,0 +1,158 @@ +// 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 "testdtorinformation.h" +#include "abstractmetabuilder.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetalang.h> +#include <typesystem.h> + +void TestDtorInformation::testDtorIsPrivate() +{ + const char cppCode[] = R"(class Control { +public: + ~Control() {} +}; +class Subject { +private: + ~Subject() {} +}; +)"; + const char xmlCode[] = R"(<typesystem package="Foo"> + <value-type name="Control"/> + <value-type name="Subject"/> +</typesystem>)"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + auto klass = AbstractMetaClass::findClass(classes, "Control"); + QVERIFY(klass); + QVERIFY(!klass->hasPrivateDestructor()); + klass = AbstractMetaClass::findClass(classes, "Subject"); + QVERIFY(klass); + QVERIFY(klass->hasPrivateDestructor()); +} + +void TestDtorInformation::testDtorIsProtected() +{ + const char cppCode[] = R"(class Control { +public: + ~Control() {} +}; +class Subject { +protected: + ~Subject() {} +}; +)"; + const char xmlCode[] = R"(<typesystem package="Foo"> + <value-type name="Control"/> + <value-type name="Subject"/> +</typesystem>)"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + auto klass = AbstractMetaClass::findClass(classes, "Control"); + QVERIFY(klass); + QVERIFY(!klass->hasProtectedDestructor()); + klass = AbstractMetaClass::findClass(classes, "Subject"); + QVERIFY(klass); + QVERIFY(klass->hasProtectedDestructor()); +} + +void TestDtorInformation::testDtorIsVirtual() +{ + const char cppCode[] = R"(class Control { +public: + ~Control() {} +}; +class Subject { +protected: + virtual ~Subject() {} +}; +)"; + const char xmlCode[] = R"(<typesystem package="Foo"> + <value-type name="Control"/> + <value-type name="Subject"/> +</typesystem>)"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + auto klass = AbstractMetaClass::findClass(classes, "Control"); + QVERIFY(klass); + QVERIFY(!klass->hasVirtualDestructor()); + klass = AbstractMetaClass::findClass(classes, "Subject"); + QVERIFY(klass); + QVERIFY(klass->hasVirtualDestructor()); +} + +void TestDtorInformation::testDtorFromBaseIsVirtual() +{ + const char cppCode[] = R"CPP(class ControlBase { public: ~ControlBase() {} }; +class Control : public ControlBase {}; +class SubjectBase { public: virtual ~SubjectBase() {} }; +class Subject : public SubjectBase {}; +)CPP"; + const char xmlCode[] = R"XML(<typesystem package="Foo"><value-type name="ControlBase"/> +<value-type name="Control"/>" +<value-type name="SubjectBase"/>" +<value-type name="Subject"/> +</typesystem> +)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 4); + + auto klass = AbstractMetaClass::findClass(classes, "ControlBase"); + QVERIFY(klass); + QVERIFY(!klass->hasVirtualDestructor()); + klass = AbstractMetaClass::findClass(classes, "Control"); + QVERIFY(klass); + QVERIFY(!klass->hasVirtualDestructor()); + + klass = AbstractMetaClass::findClass(classes, "SubjectBase"); + QVERIFY(klass); + QVERIFY(klass->hasVirtualDestructor()); + klass = AbstractMetaClass::findClass(classes, "Subject"); + QVERIFY(klass); + QVERIFY(klass->hasVirtualDestructor()); +} + +void TestDtorInformation::testClassWithVirtualDtorIsPolymorphic() +{ + const char cppCode[] = R"(class Control { +public: + virtual ~Control() {} +}; +class Subject { +protected: + virtual ~Subject() {} +}; +)"; + const char xmlCode[] = R"(<typesystem package="Foo"> + <value-type name="Control"/> + <value-type name="Subject"/> +</typesystem>)"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + auto klass = AbstractMetaClass::findClass(classes, "Control"); + QVERIFY(klass); + QVERIFY(klass->isPolymorphic()); + klass = AbstractMetaClass::findClass(classes, "Subject"); + QVERIFY(klass); + QVERIFY(klass->isPolymorphic()); +} + +QTEST_APPLESS_MAIN(TestDtorInformation) + + diff --git a/sources/shiboken6/ApiExtractor/tests/testdtorinformation.h b/sources/shiboken6/ApiExtractor/tests/testdtorinformation.h new file mode 100644 index 000000000..0f8cb59b3 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testdtorinformation.h @@ -0,0 +1,22 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTDTORINFORMATION_H +#define TESTDTORINFORMATION_H + +#include <QtCore/QObject> + +class AbstractMetaBuilder; + +class TestDtorInformation: public QObject +{ + Q_OBJECT +private slots: + void testDtorIsPrivate(); + void testDtorIsProtected(); + void testDtorIsVirtual(); + void testDtorFromBaseIsVirtual(); + void testClassWithVirtualDtorIsPolymorphic(); +}; + +#endif // TESTDTORINFORMATION_H diff --git a/sources/shiboken6/ApiExtractor/tests/testenum.cpp b/sources/shiboken6/ApiExtractor/tests/testenum.cpp new file mode 100644 index 000000000..c7c2b8b3b --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testenum.cpp @@ -0,0 +1,577 @@ +// 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 "testenum.h" +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetaenum.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetabuilder_p.h> +#include <enumtypeentry.h> +#include <flagstypeentry.h> +#include <parser/enumvalue.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestEnum::testEnumCppSignature() +{ + const char cppCode[] = "\ + enum GlobalEnum { A, B };\n\ + \n\ + struct A {\n\ + enum ClassEnum { CA, CB };\n\ + void method(ClassEnum);\n\ + };\n\ + void func(A::ClassEnum);\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <enum-type name='GlobalEnum'/>\n\ + <value-type name='A'>\n\ + <enum-type name='ClassEnum'/>\n\ + </value-type>\n\ + <function signature='func(A::ClassEnum)'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + + AbstractMetaEnumList globalEnums = builder->globalEnums(); + QCOMPARE(globalEnums.size(), 1); + QCOMPARE(globalEnums.constFirst().name(), u"GlobalEnum"); + + // enum as parameter of a function + const auto functions = builder->globalFunctions(); + QCOMPARE(functions.size(), 1); + QCOMPARE(functions.constFirst()->arguments().size(), 1); + QCOMPARE(functions.constFirst()->arguments().constFirst().type().cppSignature(), + u"A::ClassEnum"); + + // enum as parameter of a method + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QCOMPARE(classA->enums().size(), 1); + const auto funcs = classA->queryFunctionsByName(u"method"_s); + QVERIFY(!funcs.isEmpty()); + const auto method = funcs.constFirst(); + AbstractMetaArgument arg = method->arguments().constFirst(); + QCOMPARE(arg.type().name(), u"ClassEnum"); + QCOMPARE(arg.type().cppSignature(), u"A::ClassEnum"); + QCOMPARE(functions.constFirst()->arguments().size(), 1); + arg = functions.constFirst()->arguments().constFirst(); + QCOMPARE(arg.type().name(), u"ClassEnum"); + QCOMPARE(arg.type().cppSignature(), u"A::ClassEnum"); + + AbstractMetaEnumList classEnums = classA->enums(); + QVERIFY(!classEnums.isEmpty()); + QCOMPARE(classEnums.constFirst().name(), u"ClassEnum"); + auto e = AbstractMetaClass::findEnumValue(classes, u"CA"_s); + QVERIFY(e.has_value()); + e = AbstractMetaClass::findEnumValue(classes, u"ClassEnum::CA"_s); + QVERIFY(e.has_value()); +} + +void TestEnum::testEnumWithApiVersion() +{ + const char cppCode[] = "\ + struct A {\n\ + enum ClassEnum { EnumA, EnumB };\n\ + enum ClassEnum2 { EnumC, EnumD };\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <value-type name='A'>\n\ + <enum-type name='ClassEnum' since='0.1'/>\n\ + <enum-type name='ClassEnum2' since='0.2'/>\n\ + </value-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, + true, u"0.1"_s)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + QCOMPARE(classes[0]->enums().size(), 1); +} + +void TestEnum::testAnonymousEnum() +{ + const char cppCode[] = "\ + enum { Global0, Global1 };\n\ + struct A {\n\ + enum { A0, A1 };\n\ + enum { isThis = true, isThat = false };\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <!-- Uses the first value of the enum to identify it. -->\n\ + <enum-type identified-by-value='Global0'/>\n\ + <value-type name='A'>\n\ + <!-- Uses the second value of the enum to identify it. -->\n\ + <enum-type identified-by-value='A1'/>\n\ + <enum-type identified-by-value='isThis'/>\n\ + </value-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + AbstractMetaEnumList globalEnums = builder->globalEnums(); + QCOMPARE(globalEnums.size(), 1); + QCOMPARE(globalEnums.constFirst().typeEntry()->qualifiedCppName(), + u"Global0"); + QVERIFY(globalEnums.constFirst().isAnonymous()); + + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + QCOMPARE(classes[0]->enums().size(), 2); + + auto anonEnumA1 = classes[0]->findEnum(u"A1"_s); + QVERIFY(anonEnumA1.has_value()); + QVERIFY(anonEnumA1->isAnonymous()); + QCOMPARE(anonEnumA1->typeEntry()->qualifiedCppName(), u"A::A1"); + + AbstractMetaEnumValue enumValueA0 = anonEnumA1->values().constFirst(); + QCOMPARE(enumValueA0.name(), u"A0"); + QCOMPARE(enumValueA0.value().value(), 0); + QCOMPARE(enumValueA0.stringValue(), QString()); + + AbstractMetaEnumValue enumValueA1 = anonEnumA1->values().constLast(); + QCOMPARE(enumValueA1.name(), u"A1"); + QCOMPARE(enumValueA1.value().value(), 1); + QCOMPARE(enumValueA1.stringValue(), QString()); + + auto anonEnumIsThis = classes[0]->findEnum(u"isThis"_s); + QVERIFY(anonEnumIsThis.has_value()); + QVERIFY(anonEnumIsThis->isAnonymous()); + QCOMPARE(anonEnumIsThis->typeEntry()->qualifiedCppName(), u"A::isThis"); + + AbstractMetaEnumValue enumValueIsThis = anonEnumIsThis->values().constFirst(); + QCOMPARE(enumValueIsThis.name(), u"isThis"); + QCOMPARE(enumValueIsThis.value().value(), static_cast<int>(true)); + QCOMPARE(enumValueIsThis.stringValue(), u"true"); + + AbstractMetaEnumValue enumValueIsThat = anonEnumIsThis->values().constLast(); + QCOMPARE(enumValueIsThat.name(), u"isThat"); + QCOMPARE(enumValueIsThat.value().value(), static_cast<int>(false)); + QCOMPARE(enumValueIsThat.stringValue(), u"false"); +} + +void TestEnum::testGlobalEnums() +{ + const char cppCode[] = "\ + enum EnumA { A0, A1 };\n\ + enum EnumB { B0 = 2, B1 = 0x4 };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <enum-type name='EnumA'/>\n\ + <enum-type name='EnumB'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + AbstractMetaEnumList globalEnums = builder->globalEnums(); + QCOMPARE(globalEnums.size(), 2); + + AbstractMetaEnum enumA = globalEnums.constFirst(); + QCOMPARE(enumA.typeEntry()->qualifiedCppName(), u"EnumA"); + + AbstractMetaEnumValue enumValueA0 = enumA.values().constFirst(); + QCOMPARE(enumValueA0.name(), u"A0"); + QCOMPARE(enumValueA0.value().value(), 0); + QCOMPARE(enumValueA0.stringValue(), QString()); + + AbstractMetaEnumValue enumValueA1 = enumA.values().constLast(); + QCOMPARE(enumValueA1.name(), u"A1"); + QCOMPARE(enumValueA1.value().value(), 1); + QCOMPARE(enumValueA1.stringValue(), QString()); + + AbstractMetaEnum enumB = globalEnums.constLast(); + QCOMPARE(enumB.typeEntry()->qualifiedCppName(), u"EnumB"); + + AbstractMetaEnumValue enumValueB0 = enumB.values().constFirst(); + QCOMPARE(enumValueB0.name(), u"B0"); + QCOMPARE(enumValueB0.value().value(), 2); + QCOMPARE(enumValueB0.stringValue(), u"2"); + + AbstractMetaEnumValue enumValueB1 = enumB.values().constLast(); + QCOMPARE(enumValueB1.name(), u"B1"); + QCOMPARE(enumValueB1.value().value(), 4); + QCOMPARE(enumValueB1.stringValue(), u"0x4"); +} + +void TestEnum::testEnumValueFromNeighbourEnum() +{ + const char cppCode[] = "\ + namespace A {\n\ + enum EnumA { ValueA0, ValueA1 };\n\ + enum EnumB { ValueB0 = A::ValueA1, ValueB1 = ValueA0 };\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <namespace-type name='A'>\n\ + <enum-type name='EnumA'/>\n\ + <enum-type name='EnumB'/>\n\ + </namespace-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + QCOMPARE(classes[0]->enums().size(), 2); + + auto enumA = classes[0]->findEnum(u"EnumA"_s); + QVERIFY(enumA.has_value()); + QCOMPARE(enumA->typeEntry()->qualifiedCppName(), u"A::EnumA"); + + AbstractMetaEnumValue enumValueA0 = enumA->values().constFirst(); + QCOMPARE(enumValueA0.name(), u"ValueA0"); + QCOMPARE(enumValueA0.value().value(), 0); + QCOMPARE(enumValueA0.stringValue(), QString()); + + AbstractMetaEnumValue enumValueA1 = enumA->values().constLast(); + QCOMPARE(enumValueA1.name(), u"ValueA1"); + QCOMPARE(enumValueA1.value().value(), 1); + QCOMPARE(enumValueA1.stringValue(), QString()); + + auto enumB = classes[0]->findEnum(u"EnumB"_s); + QVERIFY(enumB.has_value()); + QCOMPARE(enumB->typeEntry()->qualifiedCppName(), u"A::EnumB"); + + AbstractMetaEnumValue enumValueB0 = enumB->values().constFirst(); + QCOMPARE(enumValueB0.name(), u"ValueB0"); + QCOMPARE(enumValueB0.value().value(), 1); + QCOMPARE(enumValueB0.stringValue(), u"A::ValueA1"); + + AbstractMetaEnumValue enumValueB1 = enumB->values().constLast(); + QCOMPARE(enumValueB1.name(), u"ValueB1"); + QCOMPARE(enumValueB1.value().value(), 0); + QCOMPARE(enumValueB1.stringValue(), u"ValueA0"); +} + +void TestEnum::testEnumValueFromExpression() +{ + const char cppCode[] = "\ + struct A {\n\ + enum EnumA : unsigned {\n\ + ValueA0 = 3u,\n\ + ValueA1 = ~3u,\n\ + ValueA2 = 0xffffffff,\n\ + ValueA3 = 0xf0,\n\ + ValueA4 = 8 |ValueA3,\n\ + ValueA5 = ValueA3|32,\n\ + ValueA6 = ValueA3 >> 1,\n\ + ValueA7 = ValueA3 << 1\n\ + };\n\ + enum EnumB : int {\n\ + ValueB0 = ~3,\n\ + };\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <value-type name='A'>\n\ + <enum-type name='EnumA'/>\n\ + <enum-type name='EnumB'/>\n\ + </value-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + AbstractMetaClassPtr classA = AbstractMetaClass::findClass(builder->classes(), "A"); + QVERIFY(classA); + + auto enumA = classA->findEnum(u"EnumA"_s); + QVERIFY(enumA.has_value()); + QVERIFY(!enumA->isSigned()); + QCOMPARE(enumA->typeEntry()->qualifiedCppName(), u"A::EnumA"); + + AbstractMetaEnumValue valueA0 = enumA->values().at(0); + QCOMPARE(valueA0.name(), u"ValueA0"); + QCOMPARE(valueA0.stringValue(), u"3u"); + QCOMPARE(valueA0.value().unsignedValue(), 3u); + + AbstractMetaEnumValue valueA1 = enumA->values().at(1); + QCOMPARE(valueA1.name(), u"ValueA1"); + QCOMPARE(valueA1.stringValue(), u"~3u"); + QCOMPARE(valueA1.value().unsignedValue(), ~3u); + + AbstractMetaEnumValue valueA2 = enumA->values().at(2); + QCOMPARE(valueA2.name(), u"ValueA2"); + QCOMPARE(valueA2.stringValue(), u"0xffffffff"); + QCOMPARE(valueA2.value().unsignedValue(), 0xffffffffu); + + AbstractMetaEnumValue valueA3 = enumA->values().at(3); + QCOMPARE(valueA3.name(), u"ValueA3"); + QCOMPARE(valueA3.stringValue(), u"0xf0"); + QCOMPARE(valueA3.value().unsignedValue(), 0xf0u); + + AbstractMetaEnumValue valueA4 = enumA->values().at(4); + QCOMPARE(valueA4.name(), u"ValueA4"); + QCOMPARE(valueA4.stringValue(), u"8 |ValueA3"); + QCOMPARE(valueA4.value().unsignedValue(), 8|0xf0u); + + AbstractMetaEnumValue valueA5 = enumA->values().at(5); + QCOMPARE(valueA5.name(), u"ValueA5"); + QCOMPARE(valueA5.stringValue(), u"ValueA3|32"); + QCOMPARE(valueA5.value().unsignedValue(), 0xf0u|32); + + AbstractMetaEnumValue valueA6 = enumA->values().at(6); + QCOMPARE(valueA6.name(), u"ValueA6"); + QCOMPARE(valueA6.stringValue(), u"ValueA3 >> 1"); + QCOMPARE(valueA6.value().unsignedValue(), 0xf0u >> 1); + + AbstractMetaEnumValue valueA7 = enumA->values().at(7); + QCOMPARE(valueA7.name(), u"ValueA7"); + QCOMPARE(valueA7.stringValue(), u"ValueA3 << 1"); + QCOMPARE(valueA7.value().unsignedValue(), 0xf0u << 1); + + const auto enumB = classA->findEnum(u"EnumB"_s); + QVERIFY(enumB.has_value()); + QVERIFY(enumB->isSigned()); + QCOMPARE(enumB->typeEntry()->qualifiedCppName(), u"A::EnumB"); + QCOMPARE(enumB->values().size(), 1); + const AbstractMetaEnumValue valueB0 = enumB->values().at(0); + QCOMPARE(valueB0.name(), u"ValueB0"); + QCOMPARE(valueB0.stringValue(), u"~3"); + QCOMPARE(valueB0.value().value(), ~3); +} + +void TestEnum::testPrivateEnum() +{ + const char cppCode[] = "\ + class A {\n\ + private:\n\ + enum PrivateEnum { Priv0 = 0x0f, Priv1 = 0xf0 };\n\ + public:\n\ + enum PublicEnum { Pub0 = Priv0, Pub1 = A::Priv1 };\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <value-type name='A'>\n\ + <enum-type name='PublicEnum'/>\n\ + </value-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + const auto classA = AbstractMetaClass::findClass(builder->classes(), "A"); + QVERIFY(classA); + QCOMPARE(classA->enums().size(), 2); + + auto privateEnum = classA->findEnum(u"PrivateEnum"_s); + QVERIFY(privateEnum.has_value()); + QVERIFY(privateEnum->isPrivate()); + QCOMPARE(privateEnum->typeEntry()->qualifiedCppName(), u"A::PrivateEnum"); + + auto publicEnum = classA->findEnum(u"PublicEnum"_s); + QVERIFY(publicEnum.has_value()); + QCOMPARE(publicEnum->typeEntry()->qualifiedCppName(), u"A::PublicEnum"); + + AbstractMetaEnumValue pub0 = publicEnum->values().constFirst(); + QCOMPARE(pub0.name(), u"Pub0"); + QCOMPARE(pub0.value().value(), 0x0f); + QCOMPARE(pub0.stringValue(), u"Priv0"); + + AbstractMetaEnumValue pub1 = publicEnum->values().constLast(); + QCOMPARE(pub1.name(), u"Pub1"); + QCOMPARE(pub1.value().value(), 0xf0); + QCOMPARE(pub1.stringValue(), u"A::Priv1"); +} + +void TestEnum::testTypedefEnum() +{ + const char cppCode[] = "\ + typedef enum EnumA {\n\ + A0,\n\ + A1,\n\ + } EnumA;\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <enum-type name='EnumA'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + AbstractMetaEnumList globalEnums = builder->globalEnums(); + QCOMPARE(globalEnums.size(), 1); + + AbstractMetaEnum enumA = globalEnums.constFirst(); + QCOMPARE(enumA.typeEntry()->qualifiedCppName(), u"EnumA"); + + AbstractMetaEnumValue enumValueA0 = enumA.values().constFirst(); + QCOMPARE(enumValueA0.name(), u"A0"); + QCOMPARE(enumValueA0.value().value(), 0); + QCOMPARE(enumValueA0.stringValue(), u""); + + AbstractMetaEnumValue enumValueA1 = enumA.values().constLast(); + QCOMPARE(enumValueA1.name(), u"A1"); + QCOMPARE(enumValueA1.value().value(), 1); + QCOMPARE(enumValueA1.stringValue(), QString()); +} + +// Helper classes and functions for testing enum default value fixing. +// Put the AbstractMetaBuilder into test fixture struct to avoid having +// to re-parse for each data row. + +struct EnumDefaultValuesFixture +{ + std::shared_ptr<AbstractMetaBuilder> builder; + + AbstractMetaType globalEnum; + AbstractMetaType testEnum; + AbstractMetaType testOptions; +}; + +Q_DECLARE_METATYPE(EnumDefaultValuesFixture) +Q_DECLARE_METATYPE(AbstractMetaType) + +static int populateDefaultValuesFixture(EnumDefaultValuesFixture *fixture) +{ + static const char cppCode[] =R"( +enum GlobalEnum { GE1, GE2 }; +namespace Test1 +{ +namespace Test2 +{ + enum Enum1 { E1, E2 }; + enum Option { O1, O2 }; +} // namespace Test2 +} // namespace Test1 +)"; + static const char xmlCode[] = R"( +<typesystem package="Foo"> + <enum-type name='GlobalEnum'/> + <namespace-type name='Test1'> + <namespace-type name='Test2'> + <enum-type name='Enum1'/> + <enum-type name='Option' flags='Options'/> + </namespace-type> + </namespace-type> +</typesystem> +)"; + + fixture->builder.reset(TestUtil::parse(cppCode, xmlCode, false)); + if (!fixture->builder) + return -1; + + const auto globalEnums = fixture->builder->globalEnums(); + if (globalEnums.size() != 1) + return -2; + + fixture->globalEnum = AbstractMetaType(globalEnums.constFirst().typeEntry()); + fixture->globalEnum.decideUsagePattern(); + + AbstractMetaClassCPtr testNamespace; + for (const auto &c : fixture->builder->classes()) { + if (c->name() == u"Test2") { + testNamespace = c; + break; + } + } + if (!testNamespace) + return -3; + + const auto namespaceEnums = testNamespace->enums(); + if (namespaceEnums.size() != 2) + return -4; + QList<EnumTypeEntryCPtr > enumTypeEntries{ + std::static_pointer_cast<const EnumTypeEntry>(namespaceEnums.at(0).typeEntry()), + std::static_pointer_cast<const EnumTypeEntry>(namespaceEnums.at(1).typeEntry())}; + if (enumTypeEntries.constFirst()->flags()) + std::swap(enumTypeEntries[0], enumTypeEntries[1]); + fixture->testEnum = AbstractMetaType(enumTypeEntries.at(0)); + fixture->testEnum.decideUsagePattern(); + fixture->testOptions = AbstractMetaType(enumTypeEntries.at(1)->flags()); + fixture->testOptions.decideUsagePattern(); + return 0; +} + +void TestEnum::testEnumDefaultValues_data() +{ + EnumDefaultValuesFixture fixture; + const int setupOk = populateDefaultValuesFixture(&fixture); + + QTest::addColumn<EnumDefaultValuesFixture>("fixture"); + QTest::addColumn<int>("setupOk"); // To verify setup + QTest::addColumn<AbstractMetaType>("metaType"); // Type and parameters for fixup + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("expected"); + + // Global should just remain unmodified + QTest::newRow("global") << fixture << setupOk + << fixture.globalEnum << "GE1" << "GE1"; + QTest::newRow("global-int") << fixture << setupOk + << fixture.globalEnum << "42" << "42"; + QTest::newRow("global-hex-int") << fixture << setupOk + << fixture.globalEnum << "0x10" << "0x10"; + QTest::newRow("global-int-cast") << fixture << setupOk + << fixture.globalEnum << "GlobalEnum(-1)" << "GlobalEnum(-1)"; + + // Namespaced enum as number should remain unmodified + QTest::newRow("namespace-enum-int") << fixture << setupOk + << fixture.testEnum << "42" << "42"; + QTest::newRow("namespace-enum-hex-int") << fixture << setupOk + << fixture.testEnum << "0x10" << "0x10"; + // Partial qualification of namespaced enum + QTest::newRow("namespace-enum-qualified") << fixture << setupOk + << fixture.testEnum << "Enum1::E1" << "Test1::Test2::Enum1::E1"; + // Unqualified namespaced enums + QTest::newRow("namespace-enum-unqualified") << fixture << setupOk + << fixture.testEnum << "E1" << "Test1::Test2::Enum1::E1"; + // Namespaced enums cast from int should be qualified by scope + QTest::newRow("namespace-enum-int-cast") << fixture << setupOk + << fixture.testEnum << "Enum1(-1)" << "Test1::Test2::Enum1(-1)"; + + // Namespaced option as number should remain unmodified + QTest::newRow("namespace-option-int") << fixture << setupOk + << fixture.testOptions << "0x10" << "0x10"; + QTest::newRow("namespace-option-expression") << fixture << setupOk + << fixture.testOptions << "0x10 | 0x20" << "0x10 | 0x20"; + QTest::newRow("namespace-option-expression1") << fixture << setupOk + << fixture.testOptions << "0x10 | Test1::Test2::Option::O1" + << "0x10 | Test1::Test2::Option::O1"; + QTest::newRow("namespace-option-expression2") << fixture << setupOk + << fixture.testOptions << "0x10 | O1" << "0x10 | Test1::Test2::Option::O1"; + // Complicated expressions - should remain unmodified + QTest::newRow("namespace-option-expression-paren") << fixture << setupOk + << fixture.testOptions << "0x10 | (0x20 | 0x40 | O1)" + << "0x10 | (0x20 | 0x40 | O1)"; + + // Option: Cast Enum from int should be qualified + QTest::newRow("namespace-option-int-cast") << fixture << setupOk + << fixture.testOptions << "Option(0x10)" << "Test1::Test2::Option(0x10)"; + // Option: Cast Flags from int should be qualified + QTest::newRow("namespace-options-int-cast") << fixture << setupOk + << fixture.testOptions << "Options(0x10 | 0x20)" << "Test1::Test2::Options(0x10 | 0x20)"; + QTest::newRow("namespace-option-cast-expression1") << fixture << setupOk + << fixture.testOptions << "Test1::Test2::Options(0x10 | Test1::Test2::Option::O1)" + << "Test1::Test2::Options(0x10 | Test1::Test2::Option::O1)"; + QTest::newRow("namespace-option-cast-expression2") << fixture << setupOk + << fixture.testOptions << "Options(0x10 | O1)" + << "Test1::Test2::Options(0x10 | Test1::Test2::Option::O1)"; +} + +void TestEnum::testEnumDefaultValues() +{ + QFETCH(EnumDefaultValuesFixture, fixture); + QFETCH(int, setupOk); + QFETCH(AbstractMetaType, metaType); + QFETCH(QString, input); + QFETCH(QString, expected); + QCOMPARE(setupOk, 0); + const QString actual = fixture.builder->fixEnumDefault(metaType, input); + QCOMPARE(actual, expected); +} + +QTEST_APPLESS_MAIN(TestEnum) diff --git a/sources/shiboken6/ApiExtractor/tests/testenum.h b/sources/shiboken6/ApiExtractor/tests/testenum.h new file mode 100644 index 000000000..452755490 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testenum.h @@ -0,0 +1,25 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTENUM_H +#define TESTENUM_H + +#include <QtCore/QObject> + +class TestEnum : public QObject +{ + Q_OBJECT +private slots: + void testEnumCppSignature(); + void testEnumWithApiVersion(); + void testAnonymousEnum(); + void testGlobalEnums(); + void testEnumValueFromNeighbourEnum(); + void testEnumValueFromExpression(); + void testPrivateEnum(); + void testTypedefEnum(); + void testEnumDefaultValues_data(); + void testEnumDefaultValues(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testextrainclude.cpp b/sources/shiboken6/ApiExtractor/tests/testextrainclude.cpp new file mode 100644 index 000000000..fcc409a42 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testextrainclude.cpp @@ -0,0 +1,62 @@ +// 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 "testextrainclude.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetalang.h> +#include <complextypeentry.h> +#include <typesystemtypeentry.h> + +void TestExtraInclude::testClassExtraInclude() +{ + const char cppCode[] = "struct A {};\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <value-type name='A'>\n\ + <extra-includes>\n\ + <include file-name='header.h' location='global'/>\n\ + </extra-includes>\n\ + </value-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + + QList<Include> includes = classA->typeEntry()->extraIncludes(); + QCOMPARE(includes.size(), 1); + QCOMPARE(includes.constFirst().name(), u"header.h"); +} + +void TestExtraInclude::testGlobalExtraIncludes() +{ + const char cppCode[] = "struct A {};\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <extra-includes>\n\ + <include file-name='header1.h' location='global'/>\n\ + <include file-name='header2.h' location='global'/>\n\ + </extra-includes>\n\ + <value-type name='A'/>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QVERIFY(AbstractMetaClass::findClass(classes, "A")); + + auto *td = TypeDatabase::instance(); + TypeSystemTypeEntryCPtr module = td->defaultTypeSystemType(); + QVERIFY(module); + QCOMPARE(module->name(), u"Foo"); + + QList<Include> includes = module->extraIncludes(); + QCOMPARE(includes.size(), 2); + QCOMPARE(includes.constFirst().name(), u"header1.h"); + QCOMPARE(includes.constLast().name(), u"header2.h"); +} + +QTEST_APPLESS_MAIN(TestExtraInclude) diff --git a/sources/shiboken6/ApiExtractor/tests/testextrainclude.h b/sources/shiboken6/ApiExtractor/tests/testextrainclude.h new file mode 100644 index 000000000..6bcb57993 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testextrainclude.h @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTEXTRAINCLUDE_H +#define TESTEXTRAINCLUDE_H + +#include <QtCore/QObject> + +class TestExtraInclude : public QObject +{ + Q_OBJECT + private slots: + void testClassExtraInclude(); + void testGlobalExtraIncludes(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testfunctiontag.cpp b/sources/shiboken6/ApiExtractor/tests/testfunctiontag.cpp new file mode 100644 index 000000000..18eaf5774 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testfunctiontag.cpp @@ -0,0 +1,79 @@ +// 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 "testfunctiontag.h" +#include "testutil.h" +#include <abstractmetafunction.h> +#include <modifications.h> +#include <typesystem.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestFunctionTag::testFunctionTagForSpecificSignature() +{ + const char cppCode[] = "void globalFunction(int); void globalFunction(float); void dummy();\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <primitive-type name='int'/>\n\ + <primitive-type name='float'/>\n\ + <function signature='globalFunction(int)'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + TypeEntryCPtr func = TypeDatabase::instance()->findType(u"globalFunction"_s); + QVERIFY(func); + QCOMPARE(builder->globalFunctions().size(), 1); +} + +void TestFunctionTag::testFunctionTagForAllSignatures() +{ + const char cppCode[] = "void globalFunction(int); void globalFunction(float); void dummy();\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <primitive-type name='int'/>\n\ + <primitive-type name='float'/>\n\ + <function signature='globalFunction(int)'/>\n\ + <function signature='globalFunction(float)'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + TypeEntryCPtr func = TypeDatabase::instance()->findType(u"globalFunction"_s); + QVERIFY(func); + QCOMPARE(builder->globalFunctions().size(), 2); +} + +void TestFunctionTag::testRenameGlobalFunction() +{ + const char cppCode[] = "void global_function_with_ugly_name();\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <function signature='global_function_with_ugly_name()' rename='smooth'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + TypeEntryCPtr func = TypeDatabase::instance()->findType(u"global_function_with_ugly_name"_s); + QVERIFY(func); + + QCOMPARE(builder->globalFunctions().size(), 1); + const auto metaFunc = builder->globalFunctions().constFirst(); + + QVERIFY(metaFunc); + QCOMPARE(metaFunc->modifications().size(), 1); + QVERIFY(metaFunc->modifications().constFirst().isRenameModifier()); + QCOMPARE(metaFunc->modifications().constFirst().renamedToName(), + u"smooth"); + + QCOMPARE(metaFunc->name(), u"smooth"); + QCOMPARE(metaFunc->originalName(), u"global_function_with_ugly_name"); + QCOMPARE(metaFunc->minimalSignature(), u"global_function_with_ugly_name()"); +} + +QTEST_APPLESS_MAIN(TestFunctionTag) + diff --git a/sources/shiboken6/ApiExtractor/tests/testfunctiontag.h b/sources/shiboken6/ApiExtractor/tests/testfunctiontag.h new file mode 100644 index 000000000..7c60cb4e0 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testfunctiontag.h @@ -0,0 +1,18 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTFUNCTIONTAG_H +#define TESTFUNCTIONTAG_H + +#include <QtCore/QObject> + +class TestFunctionTag : public QObject +{ + Q_OBJECT +private slots: + void testFunctionTagForSpecificSignature(); + void testFunctionTagForAllSignatures(); + void testRenameGlobalFunction(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.cpp b/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.cpp new file mode 100644 index 000000000..899d00ad4 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.cpp @@ -0,0 +1,142 @@ +// 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 "testimplicitconversions.h" +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <complextypeentry.h> +#include <QtTest/QTest> + +void TestImplicitConversions::testWithPrivateCtors() +{ + const char cppCode[] = "\ + class B;\n\ + class C;\n\ + class A {\n\ + A(const B&);\n\ + public:\n\ + A(const C&);\n\ + };\n\ + class B {};\n\ + class C {};\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <value-type name='A'/>\n\ + <value-type name='B'/>\n\ + <value-type name='C'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 3); + + const auto classA = AbstractMetaClass::findClass(classes, "A"); + const auto classC = AbstractMetaClass::findClass(classes, "C"); + const auto implicitConvs = classA->implicitConversions(); + QCOMPARE(implicitConvs.size(), 1); + QCOMPARE(implicitConvs.constFirst()->arguments().constFirst().type().typeEntry(), + classC->typeEntry()); +} + +void TestImplicitConversions::testWithModifiedVisibility() +{ + const char cppCode[] = "\ + class B;\n\ + class A {\n\ + public:\n\ + A(const B&);\n\ + };\n\ + class B {};\n"; + const char xmlCode[] = R"( +<typesystem package='Foo'> + <value-type name='A'> + <modify-function signature='A(const B&)' access='private'/> + </value-type> + <value-type name='B'/> +</typesystem> +)"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + const auto implicitConvs = classA->implicitConversions(); + QCOMPARE(implicitConvs.size(), 1); + QCOMPARE(implicitConvs.constFirst()->arguments().constFirst().type().typeEntry(), + classB->typeEntry()); +} + + +void TestImplicitConversions::testWithAddedCtor() +{ + const char cppCode[] = "\ + class B;\n\ + class A {\n\ + public:\n\ + A(const B&);\n\ + };\n\ + class B {};\n\ + class C {};\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <custom-type name='TARGETLANGTYPE'/>\n\ + <value-type name='A'>\n\ + <add-function signature='A(const C&)'/>\n\ + </value-type>\n\ + <value-type name='B'>\n\ + <add-function signature='B(TARGETLANGTYPE*)'/>\n\ + </value-type>\n\ + <value-type name='C'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 3); + + const auto classA = AbstractMetaClass::findClass(classes, "A"); + auto implicitConvs = classA->implicitConversions(); + QCOMPARE(implicitConvs.size(), 2); + + // Added constructors with custom types should never result in implicit converters. + const auto classB = AbstractMetaClass::findClass(classes, "B"); + implicitConvs = classB->implicitConversions(); + QCOMPARE(implicitConvs.size(), 0); +} + +void TestImplicitConversions::testWithExternalConversionOperator() +{ + const char cppCode[] = "\ + class A {};\n\ + struct B {\n\ + operator A() const;\n\ + };\n"; + const char xmlCode[] = "\n\ + <typesystem package='Foo'>\n\ + <value-type name='A'/>\n\ + <value-type name='B'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + const auto implicitConvs = classA->implicitConversions(); + QCOMPARE(implicitConvs.size(), 1); + const auto &externalConvOps = classA->externalConversionOperators(); + QCOMPARE(externalConvOps.size(), 1); + + AbstractMetaFunctionCPtr convOp; + for (const auto &func : classB->functions()) { + if (func->isConversionOperator()) + convOp = func; + } + QVERIFY(convOp); + QCOMPARE(implicitConvs.constFirst(), convOp); +} + +QTEST_APPLESS_MAIN(TestImplicitConversions) diff --git a/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.h b/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.h new file mode 100644 index 000000000..e0678c5f5 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testimplicitconversions.h @@ -0,0 +1,21 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTIMPLICITCONVERSIONS_H +#define TESTIMPLICITCONVERSIONS_H + +#include <QtCore/QObject> + +class AbstractMetaBuilder; + +class TestImplicitConversions : public QObject +{ + Q_OBJECT +private slots: + void testWithPrivateCtors(); + void testWithModifiedVisibility(); + void testWithAddedCtor(); + void testWithExternalConversionOperator(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testinserttemplate.cpp b/sources/shiboken6/ApiExtractor/tests/testinserttemplate.cpp new file mode 100644 index 000000000..23cf0f9ea --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testinserttemplate.cpp @@ -0,0 +1,63 @@ +// 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 "testinserttemplate.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetalang.h> +#include <codesnip.h> +#include <modifications.h> +#include <complextypeentry.h> +#include <typesystemtypeentry.h> + +void TestInsertTemplate::testInsertTemplateOnClassInjectCode() +{ + const char cppCode[] = "struct A{};\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <template name='code_template'>\n\ + code template content\n\ + </template>\n\ + <value-type name='A'>\n\ + <inject-code class='native'>\n\ + <insert-template name='code_template'/>\n\ + </inject-code>\n\ + </value-type>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + QCOMPARE(classA->typeEntry()->codeSnips().size(), 1); + QString code = classA->typeEntry()->codeSnips().constFirst().code(); + QVERIFY(code.contains(u"code template content")); +} + +void TestInsertTemplate::testInsertTemplateOnModuleInjectCode() +{ + const char cppCode[] = ""; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <template name='code_template'>\n\ + code template content\n\ + </template>\n\ + <inject-code class='native'>\n\ + <insert-template name='code_template'/>\n\ + </inject-code>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QVERIFY(classes.isEmpty()); + + TypeSystemTypeEntryCPtr module = TypeDatabase::instance()->defaultTypeSystemType(); + QVERIFY(module); + QCOMPARE(module->name(), u"Foo"); + QCOMPARE(module->codeSnips().size(), 1); + QString code = module->codeSnips().constFirst().code().trimmed(); + QVERIFY(code.contains(u"code template content")); +} + +QTEST_APPLESS_MAIN(TestInsertTemplate) diff --git a/sources/shiboken6/ApiExtractor/tests/testinserttemplate.h b/sources/shiboken6/ApiExtractor/tests/testinserttemplate.h new file mode 100644 index 000000000..f4f67abc0 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testinserttemplate.h @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTINSERTTEMPLATE_H +#define TESTINSERTTEMPLATE_H + +#include <QtCore/QObject> + +class TestInsertTemplate : public QObject +{ + Q_OBJECT + private slots: + void testInsertTemplateOnClassInjectCode(); + void testInsertTemplateOnModuleInjectCode(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp new file mode 100644 index 000000000..9cf2e0cc7 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.cpp @@ -0,0 +1,117 @@ +// 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 "testmodifydocumentation.h" +#include "testutil.h" +#include <abstractmetalang.h> +#include <abstractmetafunction.h> +#include <documentation.h> +#include <modifications.h> +#include <complextypeentry.h> +#include <qtdocparser.h> + +#include <qtcompat.h> + +#include <QtCore/QCoreApplication> +#include <QtCore/QTemporaryDir> +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestModifyDocumentation::testModifyDocumentation() +{ + const char cppCode[] = "struct B { void b(); }; class A {};\n"; + const char xmlCode[] = +R"(<typesystem package="Foo"> + <value-type name='B'> + <modify-function signature='b()' remove='all'/> + </value-type> + <value-type name='A'> + <modify-documentation xpath='description/brief'><brief>Modified Brief</brief></modify-documentation> + <modify-documentation xpath='description/para[3]'><para>Some changed contents here</para></modify-documentation> + </value-type> +</typesystem> +)"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + const auto classA = AbstractMetaClass::findClass(builder->classes(), "A"); + QVERIFY(classA); + DocModificationList docMods = classA->typeEntry()->docModifications(); + QCOMPARE(docMods.size(), 2); + QCOMPARE(docMods[0].code().trimmed(), u"<brief>Modified Brief</brief>"); + QCOMPARE(docMods[0].signature(), QString()); + QCOMPARE(docMods[1].code().trimmed(), u"<para>Some changed contents here</para>"); + QCOMPARE(docMods[1].signature(), QString()); + + // Create a temporary directory for the documentation file since libxml2 + // cannot handle Qt resources. + QTemporaryDir tempDir(QDir::tempPath() + u"/shiboken_testmodifydocXXXXXX"_s); + QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString())); + constexpr auto docFileName = "a.xml"_L1; + QVERIFY(QFile::copy(u":/"_s + docFileName, tempDir.filePath(docFileName))); + + QtDocParser docParser; + docParser.setDocumentationDataDirectory(tempDir.path()); + docParser.fillDocumentation(classA); + + const Documentation &doc = classA->documentation(); + const QString actualDocSimplified = doc.detailed().simplified(); + const QString actualBriefSimplified = doc.brief().simplified(); + QVERIFY(!actualDocSimplified.isEmpty()); + +const char expectedDoc[] = +R"(<?xml version="1.0"?> +<description>oi +<para>Paragraph number 1</para> +<para>Paragraph number 2</para> +<para>Some changed contents here</para> +</description> +)"; + const QString expectedDocSimplified = QString::fromLatin1(expectedDoc).simplified(); + // Check whether the first modification worked. + QVERIFY(actualBriefSimplified.contains(u"Modified Brief")); + +#ifndef HAVE_LIBXSLT + // QtXmlPatterns is unable to handle para[3] in style sheets, + // this only works in its XPath search. + QEXPECT_FAIL("", "QtXmlPatterns cannot handle para[3] (QTBUG-66925)", Abort); +#endif + QCOMPARE(actualDocSimplified, expectedDocSimplified); +} + +void TestModifyDocumentation::testInjectAddedFunctionDocumentation() +{ + const char cppCode[] ="class A {};\n"; + const char xmlCode[] = R"XML( +<typesystem package="Foo"> + <value-type name='A'> + <add-function signature="foo(int@parameter_name@)"> + <inject-documentation format="target" mode="append"> + Injected documentation of added function foo. + </inject-documentation> + </add-function> + </value-type> +</typesystem> +)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + const auto classA = AbstractMetaClass::findClass(builder->classes(), "A"); + QVERIFY(classA); + const auto f = classA->findFunction("foo"); + QVERIFY(f); + QVERIFY(f->isUserAdded()); + auto docMods = f->addedFunctionDocModifications(); + QCOMPARE(docMods.size(), 1); + const QString code = docMods.constFirst().code(); + QVERIFY(code.contains(u"Injected documentation of added function foo.")); +} + +// We expand QTEST_MAIN macro but using QCoreApplication instead of QApplication +// because this test needs an event loop but can't use QApplication to avoid a crash +// on our ARMEL/FRAMANTLE buildbot +int main(int argc, char** argv) +{ + QCoreApplication app(argc, argv); + TestModifyDocumentation tc; + return QTest::qExec(&tc, argc, argv); +} diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.h b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.h new file mode 100644 index 000000000..c1cc8f480 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.h @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTMODIFYDOCUMENTATION_H +#define TESTMODIFYDOCUMENTATION_H + +#include <QtCore/QObject> + +class TestModifyDocumentation : public QObject +{ +Q_OBJECT +private slots: + void testModifyDocumentation(); + void testInjectAddedFunctionDocumentation(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.qrc b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.qrc new file mode 100644 index 000000000..76b1bfc61 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testmodifydocumentation.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource> + <file>a.xml</file> + </qresource> +</RCC> 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) diff --git a/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h new file mode 100644 index 000000000..8a4f5d826 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testmodifyfunction.h @@ -0,0 +1,26 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTABSTRACTMETACLASS_H +#define TESTABSTRACTMETACLASS_H + +#include <QtCore/QObject> + +class TestModifyFunction : public QObject +{ + Q_OBJECT + private slots: + void testOwnershipTransfer(); + void testWithApiVersion(); + void testAllowThread(); + void testRenameArgument_data(); + void testRenameArgument(); + void invalidateAfterUse(); + void testGlobalFunctionModification(); + void testScopedModifications_data(); + void testScopedModifications(); + void testSnakeCaseRenaming_data(); + void testSnakeCaseRenaming(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.cpp b/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.cpp new file mode 100644 index 000000000..1cf4c8e0f --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.cpp @@ -0,0 +1,50 @@ +// 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 "testmultipleinheritance.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <typesystem.h> + +void TestMultipleInheritance::testVirtualClass() +{ + const char cppCode[] = "\ + struct A {\n\ + virtual ~A();\n\ + virtual void theBug();\n\ + };\n\ + struct B {\n\ + virtual ~B();\n\ + };\n\ + struct C : A, B {\n\ + };\n\ + struct D : C {\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <object-type name='A' />\n\ + <object-type name='B' />\n\ + <object-type name='C' />\n\ + <object-type name='D' />\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 4); + + const auto classD = AbstractMetaClass::findClass(classes, "D"); + bool functionFound = false; + for (const auto &f : classD->functions()) { + if (f->name() == u"theBug") { + functionFound = true; + break; + } + } + QVERIFY(functionFound); + +} + +QTEST_APPLESS_MAIN(TestMultipleInheritance) diff --git a/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.h b/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.h new file mode 100644 index 000000000..ec9935305 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testmultipleinheritance.h @@ -0,0 +1,18 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTMULTIPLEINHERITANCE_H +#define TESTMULTIPLEINHERITANCE_H + +#include <QtCore/QObject> + +class AbstractMetaBuilder; + +class TestMultipleInheritance : public QObject +{ + Q_OBJECT + private slots: + void testVirtualClass(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testnamespace.cpp b/sources/shiboken6/ApiExtractor/tests/testnamespace.cpp new file mode 100644 index 000000000..3773e614a --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testnamespace.cpp @@ -0,0 +1,77 @@ +// 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 "testnamespace.h" +#include "testutil.h" +#include <abstractmetalang.h> +#include <abstractmetaenum.h> +#include <typesystem.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void NamespaceTest::testNamespaceMembers() +{ + const char cppCode[] = "\ + namespace Namespace\n\ + {\n\ + enum Option {\n\ + OpZero,\n\ + OpOne\n\ + };\n\ + void foo(Option opt);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <namespace-type name='Namespace'>\n\ + <enum-type name='Option' />\n\ + </namespace-type>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto ns = AbstractMetaClass::findClass(classes, "Namespace"); + QVERIFY(ns); + auto metaEnum = ns->findEnum(u"Option"_s); + QVERIFY(metaEnum.has_value()); + const auto func = ns->findFunction("foo"); + QVERIFY(func); +} + +void NamespaceTest::testNamespaceInnerClassMembers() +{ + const char cppCode[] = "\ + namespace OuterNamespace\n\ + {\n\ + namespace InnerNamespace {\n\ + struct SomeClass {\n\ + void method();\n\ + };\n\ + };\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <namespace-type name='OuterNamespace'>\n\ + <namespace-type name='InnerNamespace'>\n\ + <value-type name='SomeClass'/>\n\ + </namespace-type>\n\ + </namespace-type>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto ons = AbstractMetaClass::findClass(classes, "OuterNamespace"); + QVERIFY(ons); + const auto ins = AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace"); + QVERIFY(ins); + const auto sc = AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace::SomeClass"); + QVERIFY(sc); + const auto meth = sc->findFunction("method"); + QVERIFY(meth); +} + +QTEST_APPLESS_MAIN(NamespaceTest) + diff --git a/sources/shiboken6/ApiExtractor/tests/testnamespace.h b/sources/shiboken6/ApiExtractor/tests/testnamespace.h new file mode 100644 index 000000000..af46bdea3 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testnamespace.h @@ -0,0 +1,19 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTNAMESPACE_H +#define TESTNAMESPACE_H + +#include <QtCore/QObject> + +// The class is named 'NamespaceTest' to avoid clashes with Qt COIN using +// '-qtnamespace TestNamespace'. +class NamespaceTest : public QObject +{ + Q_OBJECT + private slots: + void testNamespaceMembers(); + void testNamespaceInnerClassMembers(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testnestedtypes.cpp b/sources/shiboken6/ApiExtractor/tests/testnestedtypes.cpp new file mode 100644 index 000000000..10ca1a0f6 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testnestedtypes.cpp @@ -0,0 +1,115 @@ +// 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 "testnestedtypes.h" +#include "testutil.h" +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <codesnip.h> +#include <modifications.h> +#include <complextypeentry.h> +#include <primitivetypeentry.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestNestedTypes::testNestedTypesModifications() +{ + const char cppCode[] = "\ + namespace OuterNamespace {\n\ + namespace InnerNamespace {\n\ + struct SomeClass {\n\ + void method() {}\n\ + };\n\ + };\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <namespace-type name='OuterNamespace'>\n\ + <namespace-type name='InnerNamespace'>\n\ + <inject-code class='native'>custom_code1();</inject-code>\n\ + <add-function signature='method()' return-type='OuterNamespace::InnerNamespace::SomeClass'>\n\ + <inject-code class='target'>custom_code2();</inject-code>\n\ + </add-function>\n\ + <object-type name='SomeClass' target-lang-name='RenamedSomeClass'>\n\ + <modify-function signature='method()' remove='all'/>\n\ + </object-type>\n\ + </namespace-type>\n\ + </namespace-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + + const auto ons = AbstractMetaClass::findClass(classes, "OuterNamespace"); + QVERIFY(ons); + + const auto ins = AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace"); + QVERIFY(ins); + QCOMPARE(ins->functions().size(), 1); + QCOMPARE(ins->typeEntry()->codeSnips().size(), 1); + CodeSnip snip = ins->typeEntry()->codeSnips().constFirst(); + QCOMPARE(snip.code().trimmed(), u"custom_code1();"); + + const auto addedFunc = ins->functions().constFirst(); + QVERIFY(addedFunc->isUserAdded()); + QCOMPARE(addedFunc->access(), Access::Public); + QCOMPARE(addedFunc->functionType(), AbstractMetaFunction::NormalFunction); + QCOMPARE(addedFunc->type().minimalSignature(), + u"OuterNamespace::InnerNamespace::SomeClass"); + + QCOMPARE(addedFunc->modifications().size(), 1); + QVERIFY(addedFunc->modifications().constFirst().isCodeInjection()); + snip = addedFunc->modifications().constFirst().snips().constFirst(); + QCOMPARE(snip.code().trimmed(), u"custom_code2();"); + + const auto sc = + AbstractMetaClass::findClass(classes, "OuterNamespace::InnerNamespace::SomeClass"); + QVERIFY(sc); + QCOMPARE(sc->functions().size(), 2); // default constructor and removed method + const auto removedFunc = sc->functions().constLast(); + QVERIFY(removedFunc->isModifiedRemoved()); +} + + +void TestNestedTypes::testDuplicationOfNestedTypes() +{ + const char cppCode[] = "\ + namespace Namespace {\n\ + class SomeClass {};\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <namespace-type name='Namespace'>\n\ + <value-type name='SomeClass'>\n\ + <add-function signature='createSomeClass(Namespace::SomeClass)'/>\n\ + </value-type>\n\ + </namespace-type>\n\ + </typesystem>\n"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 2); + const auto nspace = AbstractMetaClass::findClass(classes, "Namespace"); + QVERIFY(nspace); + const auto cls1 = AbstractMetaClass::findClass(classes, "SomeClass"); + QVERIFY(cls1); + const auto cls2 = AbstractMetaClass::findClass(classes, "Namespace::SomeClass"); + QVERIFY(cls2); + QCOMPARE(cls1, cls2); + QCOMPARE(cls1->name(), u"SomeClass"); + QCOMPARE(cls1->qualifiedCppName(), u"Namespace::SomeClass"); + + auto t1 = TypeDatabase::instance()->findType(u"Namespace::SomeClass"_s); + QVERIFY(t1); + auto t2 = TypeDatabase::instance()->findType(u"SomeClass"_s); + QVERIFY(!t2); +} + +QTEST_APPLESS_MAIN(TestNestedTypes) diff --git a/sources/shiboken6/ApiExtractor/tests/testnestedtypes.h b/sources/shiboken6/ApiExtractor/tests/testnestedtypes.h new file mode 100644 index 000000000..544ea05ab --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testnestedtypes.h @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTNESTEDTYPES_H +#define TESTNESTEDTYPES_H + +#include <QtCore/QObject> + +class TestNestedTypes : public QObject +{ + Q_OBJECT +private slots: + void testNestedTypesModifications(); + void testDuplicationOfNestedTypes(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.cpp b/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.cpp new file mode 100644 index 000000000..9eef7ec47 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.cpp @@ -0,0 +1,90 @@ +// 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 "testnumericaltypedef.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <typesystem.h> + +void TestNumericalTypedef::testNumericalTypedef() +{ + const char cppCode[] = "\ + typedef double real;\n\ + void funcDouble(double);\n\ + void funcReal(real);\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='double'/>\n\ + <primitive-type name='real'/>\n\ + <function signature='funcDouble(double)'/>\n\ + <function signature='funcReal(real)'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + QCOMPARE(builder->globalFunctions().size(), 2); + auto funcDouble = builder->globalFunctions().constFirst(); + auto funcReal = builder->globalFunctions().constLast(); + QVERIFY(funcReal); + + if (funcDouble->name() == u"funcReal") + std::swap(funcDouble, funcReal); + + QCOMPARE(funcDouble->minimalSignature(), u"funcDouble(double)"); + QCOMPARE(funcReal->minimalSignature(), u"funcReal(real)"); + + const AbstractMetaType doubleType = funcDouble->arguments().constFirst().type(); + QCOMPARE(doubleType.cppSignature(), u"double"); + QVERIFY(doubleType.isPrimitive()); + QVERIFY(isCppPrimitive(doubleType.typeEntry())); + + const AbstractMetaType realType = funcReal->arguments().constFirst().type(); + QCOMPARE(realType.cppSignature(), u"real"); + QVERIFY(realType.isPrimitive()); + QVERIFY(isCppPrimitive(realType.typeEntry())); +} + +void TestNumericalTypedef::testUnsignedNumericalTypedef() +{ + const char cppCode[] = "\ + typedef unsigned short custom_ushort;\n\ + void funcUnsignedShort(unsigned short);\n\ + void funcUShort(custom_ushort);\n"; + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='short'/>\n\ + <primitive-type name='unsigned short'/>\n\ + <primitive-type name='custom_ushort'/>\n\ + <function signature='funcUnsignedShort(unsigned short)'/>\n\ + <function signature='funcUShort(custom_ushort)'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + QCOMPARE(builder->globalFunctions().size(), 2); + auto funcUnsignedShort = builder->globalFunctions().constFirst(); + auto funcUShort = builder->globalFunctions().constLast(); + + if (funcUnsignedShort->name() == u"funcUShort") + std::swap(funcUnsignedShort, funcUShort); + + QCOMPARE(funcUnsignedShort->minimalSignature(), u"funcUnsignedShort(unsigned short)"); + QCOMPARE(funcUShort->minimalSignature(), u"funcUShort(custom_ushort)"); + + const AbstractMetaType unsignedShortType = funcUnsignedShort->arguments().constFirst().type(); + QCOMPARE(unsignedShortType.cppSignature(), u"unsigned short"); + QVERIFY(unsignedShortType.isPrimitive()); + QVERIFY(isCppPrimitive(unsignedShortType.typeEntry())); + + const AbstractMetaType ushortType = funcUShort->arguments().constFirst().type(); + QCOMPARE(ushortType.cppSignature(), u"custom_ushort"); + QVERIFY(ushortType.isPrimitive()); + QVERIFY(isCppPrimitive(ushortType.typeEntry())); +} + +QTEST_APPLESS_MAIN(TestNumericalTypedef) + diff --git a/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.h b/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.h new file mode 100644 index 000000000..32f549836 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testnumericaltypedef.h @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTNUMERICALTYPEDEF_H +#define TESTNUMERICALTYPEDEF_H + +#include <QtCore/QObject> + +class TestNumericalTypedef : public QObject +{ + Q_OBJECT + private slots: + void testNumericalTypedef(); + void testUnsignedNumericalTypedef(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.cpp b/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.cpp new file mode 100644 index 000000000..99cced09d --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.cpp @@ -0,0 +1,40 @@ +// 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 "testprimitivetypetag.h" +#include "testutil.h" +#include <abstractmetalang.h> +#include <primitivetypeentry.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestPrimitiveTypeTag::testPrimitiveTypeDefaultConstructor() +{ + const char cppCode[] = "\ + struct A {};\n\ + struct B {};\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <primitive-type name='A' default-constructor='A()'/>\n\ + <object-type name='B'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classB); + + auto typeEntry = TypeDatabase::instance()->findPrimitiveType(u"A"_s); + QVERIFY(typeEntry); + QVERIFY(typeEntry->hasDefaultConstructor()); + QCOMPARE(typeEntry->defaultConstructor(), u"A()"); +} + +QTEST_APPLESS_MAIN(TestPrimitiveTypeTag) + diff --git a/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.h b/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.h new file mode 100644 index 000000000..3a0e05138 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testprimitivetypetag.h @@ -0,0 +1,16 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTPRIMITIVETYPETAG_H +#define TESTPRIMITIVETYPETAG_H + +#include <QtCore/QObject> + +class TestPrimitiveTypeTag : public QObject +{ + Q_OBJECT + private slots: + void testPrimitiveTypeDefaultConstructor(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testrefcounttag.cpp b/sources/shiboken6/ApiExtractor/tests/testrefcounttag.cpp new file mode 100644 index 000000000..f2e261624 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testrefcounttag.cpp @@ -0,0 +1,84 @@ +// 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 "testrefcounttag.h" +#include "testutil.h" +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <modifications.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestRefCountTag::testReferenceCountTag() +{ + const char cppCode[] = "\ + struct A {};\n\ + struct B {\n\ + void keepObject(B* b);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <object-type name='A'/>\n\ + <object-type name='B'>\n\ + <modify-function signature='keepObject(B*)'>\n\ + <modify-argument index='1'>\n\ + <reference-count action='add'/>\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("keepObject"); + QVERIFY(func); + const auto refCount = + func->modifications().constFirst().argument_mods().constFirst().referenceCounts().constFirst(); + QCOMPARE(refCount.action, ReferenceCount::Add); +} + +void TestRefCountTag::testWithApiVersion() +{ + const char cppCode[] = "\ + struct A {};\n\ + struct B {\n\ + void keepObject(B*, B*);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <object-type name='A'/>\n\ + <object-type name='B'>\n\ + <modify-function signature='keepObject(B*, B*)'>\n\ + <modify-argument index='1' since='0.1'>\n\ + <reference-count action='add'/>\n\ + </modify-argument>\n\ + <modify-argument index='2' since='0.2'>\n\ + <reference-count action='add'/>\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"); + const auto func = classB->findFunction("keepObject"); + QVERIFY(func); + const auto refCount = + func->modifications().constFirst().argument_mods().constFirst().referenceCounts().constFirst(); + QCOMPARE(refCount.action, ReferenceCount::Add); + + QCOMPARE(func->modifications().size(), 1); +} + + +QTEST_APPLESS_MAIN(TestRefCountTag) + + diff --git a/sources/shiboken6/ApiExtractor/tests/testrefcounttag.h b/sources/shiboken6/ApiExtractor/tests/testrefcounttag.h new file mode 100644 index 000000000..6093c6f7b --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testrefcounttag.h @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTREFCOUNTTAG_H +#define TESTREFCOUNTTAG_H + +#include <QtCore/QObject> + +class TestRefCountTag : public QObject +{ + Q_OBJECT + private slots: + void testReferenceCountTag(); + void testWithApiVersion(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.cpp b/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.cpp new file mode 100644 index 000000000..ae85c5a86 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.cpp @@ -0,0 +1,37 @@ +// 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 "testreferencetopointer.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <typesystem.h> + +void TestReferenceToPointer::testReferenceToPointerArgument() +{ + const char cppCode[] = "\ + struct A {};\n\ + struct B {\n\ + void dummy(A*&);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <object-type name='A'/>\n\ + <object-type name='B'/>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classB); + const auto func = classB->findFunction("dummy"); + QVERIFY(func); + QCOMPARE(func->arguments().constFirst().type().minimalSignature(), u"A*&"); +} + +QTEST_APPLESS_MAIN(TestReferenceToPointer) + + diff --git a/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.h b/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.h new file mode 100644 index 000000000..2a7b34807 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testreferencetopointer.h @@ -0,0 +1,16 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTREFERENCETOPOINTER_H +#define TESTREFERENCETOPOINTER_H + +#include <QtCore/QObject> + +class TestReferenceToPointer : public QObject +{ + Q_OBJECT + private slots: + void testReferenceToPointerArgument(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testremovefield.cpp b/sources/shiboken6/ApiExtractor/tests/testremovefield.cpp new file mode 100644 index 000000000..2cc82071b --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testremovefield.cpp @@ -0,0 +1,76 @@ +// 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 "testremovefield.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetafield.h> +#include <abstractmetafunction.h> +#include <abstractmetatype.h> +#include <abstractmetalang.h> +#include <typesystem.h> + +using namespace Qt::StringLiterals; + +void TestRemoveField::testRemoveField() +{ + const char cppCode[] = "\ + struct A {\n\ + int fieldA;\n\ + int fieldB;\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <primitive-type name='int'/>\n\ + <value-type name='A'>\n\ + <modify-field name='fieldB' remove='all'/>\n\ + </value-type>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + QCOMPARE(classA->fields().size(), 1); + const AbstractMetaField &fieldA = classA->fields().constFirst(); + QCOMPARE(fieldA.name(), u"fieldA"); +} + +// Verify that 'static constexpr' fields are seen as static/const and +// appear fully qualified for function parameter default values. +void TestRemoveField::testConstExprField() +{ + const char cppCode[] = R"( +struct A { + static constexpr int constExprField = 44; + + void f(int iParam=constExprField); +}; +)"; + + const char xmlCode[] = R"( +<typesystem package="Foo"> + <value-type name='A'/> +</typesystem> +)"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + const auto &fields = classA->fields(); + QCOMPARE(fields.size(), 1); + QVERIFY(fields.constFirst().isStatic()); + QVERIFY(fields.constFirst().type().isConstant()); + const auto function = classA->findFunction("f"_L1); + QVERIFY(function); + const auto &arguments = function->arguments(); + QCOMPARE(arguments.size(), 1); + QCOMPARE(arguments.constFirst().defaultValueExpression(), "A::constExprField"_L1); +} + +QTEST_APPLESS_MAIN(TestRemoveField) + + diff --git a/sources/shiboken6/ApiExtractor/tests/testremovefield.h b/sources/shiboken6/ApiExtractor/tests/testremovefield.h new file mode 100644 index 000000000..05912d99e --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testremovefield.h @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTREMOVEFIELD_H +#define TESTREMOVEFIELD_H + +#include <QtCore/QObject> + +class TestRemoveField : public QObject +{ + Q_OBJECT + private slots: + void testRemoveField(); + void testConstExprField(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.cpp b/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.cpp new file mode 100644 index 000000000..87e318e95 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.cpp @@ -0,0 +1,48 @@ +// 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 "testremoveimplconv.h" +#include "testutil.h" +#include <QtTest/QTest> +#include <abstractmetaargument.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <complextypeentry.h> + +// When a constructor able to trigger implicity conversions is removed +// it should not appear in the implicity conversion list. +void TestRemoveImplConv::testRemoveImplConv() +{ + const char cppCode[] = "\ + struct A {};\n\ + struct B {};\n\ + struct C {\n\ + C(const A&);\n\ + C(const B&);\n\ + };\n"; + const char xmlCode[] = "\ + <typesystem package=\"Foo\">\n\ + <value-type name='A'/>\n\ + <value-type name='B'/>\n\ + <value-type name='C'>\n\ + <modify-function signature='C(const A&)' remove='all'/>\n\ + </value-type>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 3); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classB); + const auto classC = AbstractMetaClass::findClass(classes, "C"); + QVERIFY(classC); + const auto implConv = classC->implicitConversions(); + QCOMPARE(implConv.size(), 1); + QCOMPARE(implConv.constFirst()->arguments().constFirst().type().typeEntry(), + classB->typeEntry()); +} + +QTEST_APPLESS_MAIN(TestRemoveImplConv) diff --git a/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.h b/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.h new file mode 100644 index 000000000..d11d30633 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testremoveimplconv.h @@ -0,0 +1,16 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTREMOVEIMPLCONV_H +#define TESTREMOVEIMPLCONV_H + +#include <QtCore/QObject> + +class TestRemoveImplConv : public QObject +{ +Q_OBJECT +private slots: + void testRemoveImplConv(); +}; + +#endif // TESTREMOVEIMPLCONV_H diff --git a/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.cpp b/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.cpp new file mode 100644 index 000000000..17a069b5e --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.cpp @@ -0,0 +1,98 @@ +// 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 "testremoveoperatormethod.h" +#include "testutil.h" +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <typesystem.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestRemoveOperatorMethod::testRemoveOperatorMethod() +{ + const char cppCode[] = R"(#include <cstdint> + +struct Char {}; +struct ByteArray {}; +struct String {}; + +struct A { + A& operator>>(char&); + A& operator>>(char*); + A& operator>>(short&); + A& operator>>(unsigned short&); + A& operator>>(int&); + A& operator>>(unsigned int&); + A& operator>>(int64_t&); + A& operator>>(uint64_t&); + A& operator>>(float&); + A& operator>>(double&); + A& operator>>(Char&); + A& operator>>(ByteArray&); + A& operator>>(String&); +}; +)"; + + const char xmlCode[] = "\ + <typesystem package='Foo'>\n\ + <primitive-type name='char'/>\n\ + <primitive-type name='short'/>\n\ + <primitive-type name='unsigned short'/>\n\ + <primitive-type name='int'/>\n\ + <primitive-type name='unsigned int'/>\n\ + <primitive-type name='int64_t'/>\n\ + <primitive-type name='uint64_t'/>\n\ + <primitive-type name='float'/>\n\ + <primitive-type name='double'/>\n\ + <primitive-type name='Char'/>\n\ + <primitive-type name='String'/>\n\ + <value-type name='ByteArray'/>\n\ + <object-type name='A'>\n\ + <modify-function signature='operator>>(char&)' remove='all'/>\n\ + <modify-function signature='operator>>(char*)' remove='all'/>\n\ + <modify-function signature='operator>>(short&)' remove='all'/>\n\ + <modify-function signature='operator>>(unsigned short&)' remove='all'/>\n\ + <modify-function signature='operator>>(int&)' remove='all'/>\n\ + <modify-function signature='operator>>(unsigned int&)' remove='all'/>\n\ + <modify-function signature='operator>>(int64_t&)' remove='all'/>\n\ + <modify-function signature='operator>>(uint64_t&)' remove='all'/>\n\ + <modify-function signature='operator>>(float&)' remove='all'/>\n\ + <modify-function signature='operator>>(double&)' remove='all'/>\n\ + <modify-function signature='operator>>(Char&)' remove='all'/>\n\ + <modify-function signature='operator>>(String&)' remove='all'/>\n\ + </object-type>\n\ + </typesystem>\n"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + QCOMPARE(classA->functions().size(), 14); + QStringList removedSignatures; + removedSignatures.append(u"operator>>(char&)"_s); + removedSignatures.append(u"operator>>(char*)"_s); + removedSignatures.append(u"operator>>(short&)"_s); + removedSignatures.append(u"operator>>(unsigned short&)"_s); + removedSignatures.append(u"operator>>(int&)"_s); + removedSignatures.append(u"operator>>(unsigned int&)"_s); + removedSignatures.append(u"operator>>(int64_t&)"_s); + removedSignatures.append(u"operator>>(uint64_t&)"_s); + removedSignatures.append(u"operator>>(float&)"_s); + removedSignatures.append(u"operator>>(double&)"_s); + removedSignatures.append(u"operator>>(Char&)"_s); + removedSignatures.append(u"operator>>(String&)"_s); + auto notRemoved = classA->functions().size(); + for (const auto &f : classA->functions()) { + QCOMPARE(f->isModifiedRemoved(), bool(removedSignatures.contains(f->minimalSignature()))); + notRemoved -= int(f->isModifiedRemoved()); + } + QCOMPARE(notRemoved, 2); +} + +QTEST_APPLESS_MAIN(TestRemoveOperatorMethod) + diff --git a/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.h b/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.h new file mode 100644 index 000000000..6ec335e0c --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testremoveoperatormethod.h @@ -0,0 +1,16 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTREMOVEOPERATORMETHOD_H +#define TESTREMOVEOPERATORMETHOD_H + +#include <QtCore/QObject> + +class TestRemoveOperatorMethod : public QObject +{ + Q_OBJECT + private slots: + void testRemoveOperatorMethod(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testresolvetype.cpp b/sources/shiboken6/ApiExtractor/tests/testresolvetype.cpp new file mode 100644 index 000000000..67ebcc606 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testresolvetype.cpp @@ -0,0 +1,281 @@ +// 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 "testresolvetype.h" +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetaenum.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <complextypeentry.h> +#include <enumtypeentry.h> +#include <primitivetypeentry.h> +#include <typedatabase.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestResolveType::initTestCase() +{ + // For enum lookup in testFixDefaultArguments() + AbstractMetaBuilder::setCodeModelTestMode(true); +} + +void TestResolveType::testResolveReturnTypeFromParentScope() +{ + const char cppCode[] = "\n\ + namespace A {\n\ + struct B {\n\ + struct C {};\n\ + };\n\ + struct D : public B::C {\n\ + C* foo = 0;\n\ + C* method();\n\ + };\n\ + };"; + const char xmlCode[] = R"XML( + <typesystem package='Foo'> + <namespace-type name='A'> + <value-type name='B'> + <value-type name='C'/> + </value-type> + <value-type name='D'/> + </namespace-type> + </typesystem>)XML"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classD = AbstractMetaClass::findClass(classes, "A::D"); + QVERIFY(classD); + const auto meth = classD->findFunction("method"); + QVERIFY(meth); + QVERIFY(meth); +} + +// Helper classes and functions for testing default value fixing. +// Put the AbstractMetaBuilder into test fixture struct to avoid having +// to re-parse for each data row. + +struct DefaultValuesFixture +{ + std::shared_ptr<AbstractMetaBuilder> builder; + + AbstractMetaType intType; + AbstractMetaType stringType; + AbstractMetaType classType; + AbstractMetaType listType; + AbstractMetaType enumType; + AbstractMetaClassCPtr klass{}; +}; + +Q_DECLARE_METATYPE(DefaultValuesFixture) +Q_DECLARE_METATYPE(AbstractMetaType) + +static int populateDefaultValuesFixture(DefaultValuesFixture *fixture) +{ + static const char cppCode[] =R"( +#include <string> +#include <list> + +namespace Namespace { +class Test +{ +public: + enum Enum { enumValue1, enumValue2 }; + + explicit Test(int x = INT_FIELD_1); + explicit Test(const std::string &t = std::string(CHAR_FIELD_1)); + + static void listFunc(std::list<Test> list = std::list<Test>()); + + static const int INT_FIELD_1 = 42; + static const char *CHAR_FIELD_1; + static const Enum DefaultValue = enumValue1; +}; +} // Namespace +)"; + static const char xmlCode[] = R"( +<typesystem package="Foo"> + <namespace-type name='Namespace'> + <value-type name='Test'> + <enum-type name='Enum'/> + </value-type> + </namespace-type> + <container-type name="std::list" type="list"/> +</typesystem> +)"; + + fixture->builder.reset(TestUtil::parse(cppCode, xmlCode, false)); + if (!fixture->builder) + return -1; + + for (const auto &klass : fixture->builder->classes()) { + if (klass->name() == u"Test") { + fixture->klass = klass; + break; + } + } + if (!fixture->klass) + return -2; + + fixture->classType = AbstractMetaType(fixture->klass->typeEntry()); + fixture->classType.decideUsagePattern(); + + for (const auto &f : fixture->klass->findFunctions("Test")) { + if (f->functionType() == AbstractMetaFunction::ConstructorFunction + && f->arguments().size() == 1) { + const auto type = f->arguments().constFirst().type(); + if (type.name() == u"int") + fixture->intType = type; + else + fixture->stringType = type; + } + } + if (fixture->intType.isVoid() || fixture->stringType.isVoid()) + return -3; + + auto listFunc = fixture->klass->findFunction("listFunc"); + if (!listFunc || listFunc->arguments().size() != 1) + return -3; + fixture->listType = listFunc->arguments().constFirst().type(); + + fixture->enumType = AbstractMetaType(fixture->klass->enums().constFirst().typeEntry()); + fixture->enumType.decideUsagePattern(); + + return 0; +} + +void TestResolveType::testFixDefaultArguments_data() +{ + DefaultValuesFixture fixture; + const int setupOk = populateDefaultValuesFixture(&fixture); + + QTest::addColumn<DefaultValuesFixture>("fixture"); + QTest::addColumn<int>("setupOk"); // To verify setup + QTest::addColumn<AbstractMetaType>("metaType"); // Type and parameters for fixup + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("int") << fixture << setupOk + << fixture.intType << "1" << "1"; + QTest::newRow("int-macro") << fixture << setupOk + << fixture.intType << "GL_MACRO" << "GL_MACRO"; + QTest::newRow("int-enum") << fixture << setupOk + << fixture.intType << "enumValue1" << "Namespace::Test::Enum::enumValue1"; + + // Test expansion of container types + QString expected = u"std::list<Namespace::Test>()"_s; + QTest::newRow("list") + << fixture << setupOk << fixture.listType + << expected << expected; + QTest::newRow("partially qualified list") + << fixture << setupOk << fixture.listType + << "std::list<Test>()" << expected; + + // Test field expansion + expected = u"Namespace::Test::INT_FIELD_1"_s; + QTest::newRow("qualified class field") + << fixture << setupOk << fixture.intType + << expected << expected; + QTest::newRow("partially qualified class field") + << fixture << setupOk << fixture.intType + << "Test::INT_FIELD_1" << expected; + QTest::newRow("unqualified class field") + << fixture << setupOk << fixture.intType + << "INT_FIELD_1" << expected; + + // Test field expansion when constructing some class + expected = u"QLatin1String(Namespace::Test::CHAR_FIELD_1)"_s; + QTest::newRow("class from qualified class field") + << fixture << setupOk << fixture.classType + << expected << expected; + QTest::newRow("class from partially qualified class field") + << fixture << setupOk << fixture.classType + << "QLatin1String(Test::CHAR_FIELD_1)" << expected; + QTest::newRow("class from unqualified class field") + << fixture << setupOk << fixture.classType + << "QLatin1String(CHAR_FIELD_1)" << expected; + + // Test field expansion when constructing class itself + expected = u"Namespace::Test(Namespace::Test::CHAR_FIELD_1)"_s; + QTest::newRow("self from qualified class field") + << fixture << setupOk << fixture.classType + << expected << expected; + QTest::newRow("self from partially qualified class field") + << fixture << setupOk << fixture.classType + << "Test(Test::CHAR_FIELD_1)" << expected; + QTest::newRow("self from unqualified class field") + << fixture << setupOk << fixture.classType + << "Test(CHAR_FIELD_1)" << expected; + + // Test enum expansion when constructing class itself + expected = u"Namespace::Test(Namespace::Test::Enum::enumValue1)"_s; + QTest::newRow("self from qualified enum") + << fixture << setupOk << fixture.classType + << expected << expected; + QTest::newRow("self from enum") + << fixture << setupOk << fixture.classType + << "Test(enumValue1)" << expected; + + // Don't qualify fields to "Test::Enum::DefaultValue" + QTest::newRow("enum from static field") + << fixture << setupOk << fixture.enumType + << "DefaultValue" << u"Namespace::Test::DefaultValue"_s; +} + +void TestResolveType::testFixDefaultArguments() +{ + QFETCH(DefaultValuesFixture, fixture); + QFETCH(int, setupOk); + QFETCH(AbstractMetaType, metaType); + QFETCH(QString, input); + QFETCH(QString, expected); + QCOMPARE(setupOk, 0); + const QString actual = fixture.builder->fixDefaultValue(input, metaType, fixture.klass); + QCOMPARE(actual, expected); +} + +// Verify that the typedefs of the C++ 11 integer types (int32_t, ...) +// are seen by the C++ parser, otherwise they are handled as unknown +// primitive types, causing invalid code to be generated. +// (see BuilderPrivate::visitHeader(), +// sources/shiboken6/ApiExtractor/clangparser/clangbuilder.cpp). +void TestResolveType::testCppTypes() +{ + static const char cppCode[] =R"( +#include <cstdint> + +class Test +{ +public: + explicit Test(int32_t v); +}; +)"; + static const char xmlCode[] = R"( +<typesystem package="Foo"> + <value-type name='Test'/> + <primitive-type name='int32_t'/> +</typesystem> +)"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto testClass = AbstractMetaClass::findClass(classes, "Test"); + QVERIFY(testClass); + + auto *tdb = TypeDatabase::instance(); + auto int32TEntry = tdb->findType(u"int32_t"_s); + QVERIFY2(int32TEntry, "int32_t not found"); + QVERIFY(int32TEntry->isPrimitive()); + auto int32T = std::static_pointer_cast<const PrimitiveTypeEntry>(int32TEntry); + auto basicType = basicReferencedTypeEntry(int32T); + QVERIFY2(basicType != int32T, + "Typedef for int32_t not found. Check the system include paths."); +} + +QTEST_APPLESS_MAIN(TestResolveType) diff --git a/sources/shiboken6/ApiExtractor/tests/testresolvetype.h b/sources/shiboken6/ApiExtractor/tests/testresolvetype.h new file mode 100644 index 000000000..a07855eab --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testresolvetype.h @@ -0,0 +1,21 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTRESOLVETYPE_H +#define TESTRESOLVETYPE_H + +#include <QtCore/QObject> + +class TestResolveType : public QObject +{ + Q_OBJECT + private slots: + void initTestCase(); + + void testResolveReturnTypeFromParentScope(); + void testFixDefaultArguments_data(); + void testFixDefaultArguments(); + void testCppTypes(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp new file mode 100644 index 000000000..f4eecff2c --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.cpp @@ -0,0 +1,129 @@ +// 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 "testreverseoperators.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <typesystem.h> +#include <clangparser/compilersupport.h> + +#include <algorithm> + +void TestReverseOperators::testReverseSum() +{ + const char cppCode[] = "struct A {\n\ + A& operator+(int);\n\ + };\n\ + A& operator+(int, const A&);"; + const char xmlCode[] = "\n\ + <typesystem package=\"Foo\">\n\ + <primitive-type name='int' />\n\ + <value-type name='A' />\n\ + </typesystem>"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + QCOMPARE(classA->functions().size(), 4); + + AbstractMetaFunctionCPtr reverseOp; + AbstractMetaFunctionCPtr normalOp; + for (const auto &func : classA->functions()) { + if (func->name() == u"operator+") { + if (func->isReverseOperator()) + reverseOp = func; + else + normalOp = func; + } + } + + QVERIFY(normalOp); + QVERIFY(!normalOp->isReverseOperator()); + QCOMPARE(normalOp->arguments().size(), 1); + QVERIFY(reverseOp); + QVERIFY(reverseOp->isReverseOperator()); + QCOMPARE(reverseOp->arguments().size(), 1); +} + +void TestReverseOperators::testReverseSumWithAmbiguity() +{ + const char cppCode[] = "\n\ + struct A { A operator+(int); };\n\ + A operator+(int, const A&);\n\ + struct B {};\n\ + B operator+(const A&, const B&);\n\ + B operator+(const B&, const A&);\n\ + "; + const char xmlCode[] = "\n\ + <typesystem package=\"Foo\">\n\ + <primitive-type name='int' />\n\ + <value-type name='A' />\n\ + <value-type name='B' />\n\ + </typesystem>"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + QCOMPARE(classA->functions().size(), 4); + + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classB); + QCOMPARE(classB->functions().size(), 4); + + AbstractMetaFunctionCPtr reverseOp; + AbstractMetaFunctionCPtr normalOp; + for (const auto &func : classB->functions()) { + if (func->name() == u"operator+") { + if (func->isReverseOperator()) + reverseOp = func; + else + normalOp = func; + } + } + QVERIFY(normalOp); + QVERIFY(!normalOp->isReverseOperator()); + QCOMPARE(normalOp->arguments().size(), 1); + QCOMPARE(normalOp->minimalSignature(), u"operator+(B,A)"); + QVERIFY(reverseOp); + QVERIFY(reverseOp->isReverseOperator()); + QCOMPARE(reverseOp->arguments().size(), 1); + QCOMPARE(reverseOp->minimalSignature(), u"operator+(A,B)"); +} + +void TestReverseOperators::testSpaceshipOperator() +{ + const char cppCode[] = R"( + class Test { + public: + explicit Test(int v); + int operator<=>(const Test &rhs) const = default; + };)"; + const char xmlCode[] = R"( + <typesystem package="Foo"> + <value-type name='Test'/> + </typesystem>)"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false, + {}, {}, LanguageLevel::Cpp20)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + const auto testClass = AbstractMetaClass::findClass(classes, "Test"); + QVERIFY(testClass); + const auto &functions = testClass->functions(); + // 6 operators should be synthesized + const auto count = std::count_if(functions.cbegin(), functions.cend(), + [](const AbstractMetaFunctionCPtr &f) { + return f->isComparisonOperator(); + }); + QCOMPARE(count, 6); +} + +QTEST_APPLESS_MAIN(TestReverseOperators) + diff --git a/sources/shiboken6/ApiExtractor/tests/testreverseoperators.h b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.h new file mode 100644 index 000000000..fb8d97c97 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testreverseoperators.h @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTREVERSEOPERATORS_H +#define TESTREVERSEOPERATORS_H +#include <QtCore/QObject> + +class TestReverseOperators : public QObject +{ + Q_OBJECT +private slots: + void testReverseSum(); + void testReverseSumWithAmbiguity(); + void testSpaceshipOperator(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testtemplates.cpp b/sources/shiboken6/ApiExtractor/tests/testtemplates.cpp new file mode 100644 index 000000000..ea37c6255 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testtemplates.cpp @@ -0,0 +1,628 @@ +// 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 "testtemplates.h" +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetafield.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <abstractmetatype.h> +#include <complextypeentry.h> +#include <containertypeentry.h> + +#include <qtcompat.h> + +#include <QtCore/QTemporaryFile> +#include <QtCore/QTextStream> +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestTemplates::testTemplateWithNamespace() +{ + const char cppCode[] = R"CPP( +template<typename T> struct QList {}; +struct Url { + void name(); +}; +namespace Internet { + struct Url{}; + struct Bookmarks { + QList<Url> list(); + }; +}; +)CPP"; + + const char xmlCode0[] = R"XML( +<typesystem package='Package.Network'> + <value-type name='Url'/> +</typesystem>)XML"; + + QTemporaryFile file; + QVERIFY(file.open()); + file.write(xmlCode0); + file.close(); + + QString xmlCode1 = QString::fromLatin1(R"XML( +<typesystem package='Package.Internet'> + <load-typesystem name='%1' generate='no'/> + <container-type name='QList' type='list'/> + <namespace-type name='Internet' generate='no'> + <value-type name='Url'/> + <value-type name='Bookmarks'/> + </namespace-type> +</typesystem>)XML").arg(file.fileName()); + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, qPrintable(xmlCode1), false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + + const auto classB = AbstractMetaClass::findClass(classes, "Bookmarks"); + QVERIFY(classB); + const auto func = classB->findFunction("list"); + QVERIFY(func); + AbstractMetaType funcType = func->type(); + QVERIFY(!funcType.isVoid()); + QCOMPARE(funcType.cppSignature(), u"QList<Internet::Url>"); +} + +void TestTemplates::testTemplateOnContainers() +{ + const char cppCode[] = R"CPP( +struct Base {}; +template<typename T> struct QList {}; +namespace Namespace { + enum SomeEnum { E1, E2 }; + template<SomeEnum type> struct A { + A<type> foo(const QList<A<type> >& a); + }; + typedef A<E1> B; +} +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package="Package"> + <container-type name='QList' type='list'/> + <namespace-type name='Namespace'> + <enum-type name='SomeEnum'/> + <object-type name='A' generate='no'/> + <object-type name='B'/> + </namespace-type> + <object-type name='Base'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classB); + QVERIFY(!classB->baseClass()); + QVERIFY(classB->baseClassName().isEmpty()); + const auto func = classB->findFunction("foo"); + QVERIFY(func); + AbstractMetaType argType = func->arguments().constFirst().type(); + QCOMPARE(argType.instantiations().size(), 1); + QCOMPARE(argType.typeEntry()->qualifiedCppName(), u"QList"); + + const AbstractMetaType &instance1 = argType.instantiations().constFirst(); + QCOMPARE(instance1.instantiations().size(), 1); + QCOMPARE(instance1.typeEntry()->qualifiedCppName(), u"Namespace::A"); + + const AbstractMetaType &instance2 = instance1.instantiations().constFirst(); + QCOMPARE(instance2.instantiations().size(), 0); + QCOMPARE(instance2.typeEntry()->qualifiedCppName(), u"Namespace::E1"); +} + +void TestTemplates::testTemplateValueAsArgument() +{ + const char cppCode[] = R"CPP( +template<typename T> struct List {}; +void func(List<int> arg) {} +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <primitive-type name='int'/> + <container-type name='List' type='list'/> + <function signature='func(List<int>)'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + const auto globalFuncs = builder->globalFunctions(); + QCOMPARE(globalFuncs.size(), 1); + + const auto func = globalFuncs.constFirst(); + QCOMPARE(func->minimalSignature(), u"func(List<int>)"); + QCOMPARE(func->arguments().constFirst().type().cppSignature(), + u"List<int>"); +} + +void TestTemplates::testTemplatePointerAsArgument() +{ + const char cppCode[] = R"CPP( +template<typename T> struct List {}; +void func(List<int>* arg) {} +)CPP"; + + const char xmlCode[] = R"XML( + <typesystem package='Package'> + <primitive-type name='int'/> + <container-type name='List' type='list'/> + <function signature='func(List<int>*)'/> + </typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaFunctionCList globalFuncs = builder->globalFunctions(); + QCOMPARE(globalFuncs.size(), 1); + + const auto func = globalFuncs.constFirst(); + QCOMPARE(func->minimalSignature(), u"func(List<int>*)"); + QCOMPARE(func->arguments().constFirst().type().cppSignature(), + u"List<int> *"); +} + +void TestTemplates::testTemplateReferenceAsArgument() +{ + const char cppCode[] = R"CPP( +template<typename T> struct List {}; +void func(List<int>& arg) {} + )CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <primitive-type name='int'/> + <container-type name='List' type='list'/> + <function signature='func(List<int>&)'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + const auto globalFuncs = builder->globalFunctions(); + QCOMPARE(globalFuncs.size(), 1); + + const auto func = globalFuncs.constFirst(); + QCOMPARE(func->minimalSignature(), u"func(List<int>&)"); + QCOMPARE(func->arguments().constFirst().type().cppSignature(), + u"List<int> &"); +} + +void TestTemplates::testTemplateParameterFixup() +{ + const char cppCode[] = R"CPP( +template<typename T> +struct List { + struct Iterator {}; + void append(List l); + void erase(List::Iterator it); +}; +)CPP"; + + const char xmlCode[] = R"XML( + <typesystem package='Package'> + <container-type name='List' type='list'> + <value-type name='Iterator'/> + </container-type> + </typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + const AbstractMetaClassList templates = builder->templates(); + + QCOMPARE(templates.size(), 1); + AbstractMetaClassCPtr list = templates.constFirst(); + // Verify that the parameter of "void append(List l)" gets fixed to "List<T>" + const auto append = list->findFunction("append"); + QVERIFY(append); + QCOMPARE(append->arguments().size(), 1); + QCOMPARE(append->arguments().at(0).type().cppSignature(), u"List<T>"); + // Verify that the parameter of "void erase(Iterator)" is not modified + const auto erase = list->findFunction("erase"); + QVERIFY(erase); + QCOMPARE(erase->arguments().size(), 1); + QCOMPARE(erase->arguments().at(0).type().cppSignature(), u"List::Iterator"); +} + +void TestTemplates::testInheritanceFromContainterTemplate() +{ + const char cppCode[] = R"CPP( +template<typename T> +struct ListContainer { + inline void push_front(const T& t); + inline T& front(); +}; +struct FooBar {}; +struct FooBars : public ListContainer<FooBar> {}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <container-type name='ListContainer' type='list'/> + <value-type name='FooBar'/> + <value-type name='FooBars'> + <modify-function signature='push_front(FooBar)' remove='all'/> + <modify-function signature='front()' remove='all'/> + </value-type> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + AbstractMetaClassList templates = builder->templates(); + QCOMPARE(classes.size(), 2); + QCOMPARE(templates.size(), 1); + + const auto foobars = AbstractMetaClass::findClass(classes, "FooBars"); + QCOMPARE(foobars->functions().size(), 4); + + AbstractMetaClassCPtr lc = templates.constFirst(); + QCOMPARE(lc->functions().size(), 2); +} + +void TestTemplates::testTemplateInheritanceMixedWithForwardDeclaration() +{ + const char cppCode[] = R"CPP( +enum SomeEnum { E1, E2 }; +template<SomeEnum type> struct Future; +template<SomeEnum type> +struct A { + A(); + void method(); + friend struct Future<type>; +}; +typedef A<E1> B; +template<SomeEnum type> struct Future {}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <enum-type name='SomeEnum'/> + <value-type name='A' generate='no'/> + <value-type name='B'/> + <value-type name='Future' generate='no'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classB); + QVERIFY(!classB->baseClass()); + QVERIFY(classB->baseClassName().isEmpty()); + // 3 functions: simple constructor, copy constructor and "method()". + QCOMPARE(classB->functions().size(), 3); +} + +void TestTemplates::testTemplateInheritanceMixedWithNamespaceAndForwardDeclaration() +{ + const char cppCode[] = R"CPP( +namespace Namespace { +enum SomeEnum { E1, E2 }; +template<SomeEnum type> struct Future; +template<SomeEnum type> +struct A { + A(); + void method(); + friend struct Future<type>; +}; +typedef A<E1> B; +template<SomeEnum type> struct Future {}; +}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <namespace-type name='Namespace'> + <enum-type name='SomeEnum'/> + <value-type name='A' generate='no'/> + <value-type name='B'/> + <value-type name='Future' generate='no'/> + </namespace-type> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + + const auto classB = AbstractMetaClass::findClass(classes, "Namespace::B"); + QVERIFY(classB); + QVERIFY(!classB->baseClass()); + QVERIFY(classB->baseClassName().isEmpty()); + // 3 functions: simple constructor, copy constructor and "method()". + QCOMPARE(classB->functions().size(), 3); +} + +void TestTemplates::testTypedefOfInstantiationOfTemplateClass() +{ + const char cppCode[] = R"CPP( +namespace NSpace { +enum ClassType { + TypeOne +}; +template<ClassType CLASS_TYPE> +struct BaseTemplateClass { + inline ClassType getClassType() const { return CLASS_TYPE; } +}; +typedef BaseTemplateClass<TypeOne> TypeOneClass; +} +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Package'> + <namespace-type name='NSpace'> + <enum-type name='ClassType'/> + <object-type name='BaseTemplateClass' generate='no'/> + <object-type name='TypeOneClass'/> + </namespace-type> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 3); + + const auto base = AbstractMetaClass::findClass(classes, "BaseTemplateClass"); + QVERIFY(base); + const auto one = AbstractMetaClass::findClass(classes, "TypeOneClass"); + QVERIFY(one); + QCOMPARE(one->templateBaseClass(), base); + QCOMPARE(one->functions().size(), base->functions().size()); + QVERIFY(one->isTypeDef()); + auto oneType = one->typeEntry(); + auto baseType = base->typeEntry(); + QCOMPARE(oneType->baseContainerType(), baseType); + QCOMPARE(one->baseClassNames(), QStringList(u"NSpace::BaseTemplateClass<NSpace::TypeOne>"_s)); + + QVERIFY(one->hasTemplateBaseClassInstantiations()); + AbstractMetaTypeList instantiations = one->templateBaseClassInstantiations(); + QCOMPARE(instantiations.size(), 1); + const AbstractMetaType &inst = instantiations.constFirst(); + QVERIFY(!inst.isEnum()); + QVERIFY(!inst.typeEntry()->isEnum()); + QVERIFY(inst.typeEntry()->isEnumValue()); + QCOMPARE(inst.cppSignature(), u"NSpace::TypeOne"); +} + +void TestTemplates::testContainerTypeIncompleteArgument() +{ + const char cppCode[] = R"CPP( +template<typename T> +class Vector { + void method(const Vector& vector); + Vector otherMethod(); +}; +template <typename T> +void Vector<T>::method(const Vector<T>& vector) {} +template <typename T> +Vector<T> Vector<T>::otherMethod() { return Vector<T>(); } +typedef Vector<int> IntVector; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <container-type name='Vector' type='vector'/> + <value-type name='IntVector'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + QCOMPARE(classes.size(), 1); + + const auto vector = AbstractMetaClass::findClass(classes, "IntVector"); + QVERIFY(vector); + auto baseContainer = vector->typeEntry()->baseContainerType(); + QVERIFY(baseContainer); + QCOMPARE(reinterpret_cast<const ContainerTypeEntry*>(baseContainer.get())->containerKind(), + ContainerTypeEntry::ListContainer); + QCOMPARE(vector->functions().size(), 4); + + const auto method = vector->findFunction("method"); + QVERIFY(method); + QCOMPARE(method->signature(), u"method(const Vector<int> & vector)"); + + const auto otherMethod = vector->findFunction("otherMethod"); + QVERIFY(otherMethod); + QCOMPARE(otherMethod->signature(), u"otherMethod()"); + QVERIFY(!otherMethod->type().isVoid()); + QCOMPARE(otherMethod->type().cppSignature(), u"Vector<int>"); +} + +void TestTemplates::testNonTypeTemplates() +{ + // PYSIDe-1296, functions with non type templates parameters. + const char cppCode[] = R"CPP( +template <class T, int Size> +class Array { + T array[Size]; +}; + +Array<int, 2> foo(); + +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <container-type name='Array' type='vector'/> + <function signature="foo()"/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true)); + QVERIFY(builder); + auto functions = builder->globalFunctions(); + QCOMPARE(functions.size(), 1); + auto foo = functions.constFirst(); + QCOMPARE(foo->name(), u"foo"); + QCOMPARE(foo->type().name(), u"Array"); +} + +// Perform checks on template inheritance; a typedef of a template class +// should result in rewritten types. +void TestTemplates::testTemplateTypeDefs_data() +{ + QTest::addColumn<QString>("cpp"); + QTest::addColumn<QString>("xml"); + + const char optionalClassDef[] = R"CPP( +template<class T> // Some value type similar to std::optional +class Optional { +public: + T value() const { return m_value; } + operator bool() const { return m_success; } + + T m_value; + bool m_success = false; +}; +)CPP"; + + const char xmlPrefix[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <primitive-type name='bool'/> +)XML"; + + const char xmlOptionalDecl[] = "<value-type name='Optional' generate='no'/>\n"; + const char xmlOptionalIntDecl[] = "<value-type name='IntOptional'/>\n"; + const char xmlPostFix[] = "</typesystem>\n"; + + // Flat, global namespace + QString cpp; + QTextStream(&cpp) << optionalClassDef + << "typedef Optional<int> IntOptional;\n"; + QString xml; + QTextStream(&xml) << xmlPrefix << xmlOptionalDecl << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Optional<int>'/>" + << xmlPostFix; + QTest::newRow("global-namespace") + << cpp << xml; + + // Typedef from namespace Std + cpp.clear(); + QTextStream(&cpp) << "namespace Std {\n" << optionalClassDef << "}\n" + << "typedef Std::Optional<int> IntOptional;\n"; + xml.clear(); + QTextStream(&xml) << xmlPrefix + << "<namespace-type name='Std'>\n" << xmlOptionalDecl + << "</namespace-type>\n" << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Std::Optional<int>'/>" + << xmlPostFix; + QTest::newRow("namespace-Std") + << cpp << xml; + + // Typedef from nested class + cpp.clear(); + QTextStream(&cpp) << "class Outer {\npublic:\n" << optionalClassDef << "\n};\n" + << "typedef Outer::Optional<int> IntOptional;\n"; + xml.clear(); + QTextStream(&xml) << xmlPrefix + << "<object-type name='Outer'>\n" << xmlOptionalDecl + << "</object-type>\n" << xmlOptionalIntDecl + << "<typedef-type name='XmlIntOptional' source='Outer::Optional<int>'/>" + << xmlPostFix; + QTest::newRow("nested-class") + << cpp << xml; +} + +void TestTemplates::testTemplateTypeDefs() +{ + QFETCH(QString, cpp); + QFETCH(QString, xml); + + const QByteArray cppBa = cpp.toLocal8Bit(); + const QByteArray xmlBa = xml.toLocal8Bit(); + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppBa.constData(), xmlBa.constData(), true)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + + const auto optional = AbstractMetaClass::findClass(classes, "Optional"); + QVERIFY(optional); + + // Find the typedef'ed class + const auto optionalInt = AbstractMetaClass::findClass(classes, "IntOptional"); + QVERIFY(optionalInt); + QCOMPARE(optionalInt->templateBaseClass(), optional); + + // Find the class typedef'ed in the typesystem XML + const auto xmlOptionalInt = AbstractMetaClass::findClass(classes, "XmlIntOptional"); + QVERIFY(xmlOptionalInt); + QCOMPARE(xmlOptionalInt->templateBaseClass(), optional); + + // Check whether the value() method now has an 'int' return + const auto valueMethod = optionalInt->findFunction("value"); + QVERIFY(valueMethod); + QCOMPARE(valueMethod->type().cppSignature(), u"int"); + + // ditto for typesystem XML + const auto xmlValueMethod = xmlOptionalInt->findFunction("value"); + QVERIFY(xmlValueMethod); + QCOMPARE(xmlValueMethod->type().cppSignature(), u"int"); + + // Check whether the m_value field is of type 'int' + const auto valueField = optionalInt->findField(u"m_value"); + QVERIFY(valueField.has_value()); + QCOMPARE(valueField->type().cppSignature(), u"int"); + + // ditto for typesystem XML + const auto xmlValueField = + xmlOptionalInt->findField(u"m_value"); + QVERIFY(xmlValueField.has_value()); + QCOMPARE(xmlValueField->type().cppSignature(), u"int"); +} + +void TestTemplates::testTemplateTypeAliases() +{ + // Model Qt 6's "template<typename T> using QList = QVector<T>" + const char cppCode[] = R"CPP( +template<typename T> +class Container1 { }; + +template<typename T> +using Container2 = Container1<T>; + +class Test +{ +public: + Container2<int> m_intContainer; +}; + +class Derived : public Container2<int> +{ +public: +}; +)CPP"; + + const char xmlCode[] = R"XML( +<typesystem package='Foo'> + <primitive-type name='int'/> + <value-type name='Container1'/> + <value-type name='Derived'/> + <object-type name='Test'/> +</typesystem>)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + const auto testClass = AbstractMetaClass::findClass(classes, "Test"); + QVERIFY(testClass); + + auto fields = testClass->fields(); + QCOMPARE(fields.size(), 1); + auto fieldType = testClass->fields().at(0).type(); + QCOMPARE(fieldType.name(), u"Container1"); + QCOMPARE(fieldType.instantiations().size(), 1); + + const auto derived = AbstractMetaClass::findClass(classes, "Derived"); + QVERIFY(derived); + auto base = derived->templateBaseClass(); + QVERIFY(base); + QCOMPARE(base->name(), u"Container1"); +} + +QTEST_APPLESS_MAIN(TestTemplates) diff --git a/sources/shiboken6/ApiExtractor/tests/testtemplates.h b/sources/shiboken6/ApiExtractor/tests/testtemplates.h new file mode 100644 index 000000000..36800f723 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testtemplates.h @@ -0,0 +1,30 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTTEMPLATES_H +#define TESTTEMPLATES_H + +#include <QtCore/QObject> + +class TestTemplates : public QObject +{ + Q_OBJECT +private slots: + void testTemplateOnContainers(); + void testTemplateWithNamespace(); + void testTemplateValueAsArgument(); + void testTemplatePointerAsArgument(); + void testTemplateReferenceAsArgument(); + void testTemplateParameterFixup(); + void testInheritanceFromContainterTemplate(); + void testTemplateInheritanceMixedWithForwardDeclaration(); + void testTemplateInheritanceMixedWithNamespaceAndForwardDeclaration(); + void testTypedefOfInstantiationOfTemplateClass(); + void testContainerTypeIncompleteArgument(); + void testNonTypeTemplates(); + void testTemplateTypeDefs_data(); + void testTemplateTypeDefs(); + void testTemplateTypeAliases(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testtoposort.cpp b/sources/shiboken6/ApiExtractor/tests/testtoposort.cpp new file mode 100644 index 000000000..50cefcfe9 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testtoposort.cpp @@ -0,0 +1,61 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "testtoposort.h" +#include "graph.h" + +#include <QtTest/QTest> +#include <QtCore/QDebug> + +using IntGraph = Graph<int>; + +Q_DECLARE_METATYPE(IntGraph) + +using IntList = QList<int>; + +void TestTopoSort::testTopoSort_data() +{ + QTest::addColumn<IntGraph>("graph"); + QTest::addColumn<bool>("expectedValid"); + QTest::addColumn<IntList>("expectedOrder"); + + const int nodes1[] = {0, 1, 2}; + IntGraph g(std::begin(nodes1), std::end(nodes1)); + g.addEdge(1, 2); + g.addEdge(0, 1); + IntList expected = {0, 1, 2}; + QTest::newRow("DAG") << g << true << expected; + + const int nodes2[] = {0, 1}; + g.clear(); + g.setNodes(std::begin(nodes2), std::end(nodes2)); + expected = {1, 0}; + QTest::newRow("No edges") << g << true << expected; + + g.clear(); + g.setNodes(std::begin(nodes1), std::end(nodes1)); + g.addEdge(0, 1); + g.addEdge(1, 2); + g.addEdge(2, 0); + expected.clear(); + QTest::newRow("Cyclic") << g << false << expected; +} + +void TestTopoSort::testTopoSort() +{ + QFETCH(IntGraph, graph); + QFETCH(bool, expectedValid); + QFETCH(IntList, expectedOrder); + + const auto result = graph.topologicalSort(); + QCOMPARE(result.isValid(), expectedValid); + if (expectedValid) { + QCOMPARE(result.result, expectedOrder); + QVERIFY(result.cyclic.isEmpty()); + } else { + QVERIFY(!result.cyclic.isEmpty()); + } +} + +QTEST_APPLESS_MAIN(TestTopoSort) + diff --git a/sources/shiboken6/ApiExtractor/tests/testtoposort.h b/sources/shiboken6/ApiExtractor/tests/testtoposort.h new file mode 100644 index 000000000..4271d6a0e --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testtoposort.h @@ -0,0 +1,17 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTTOPOSORT_H +#define TESTTOPOSORT_H + +#include <QtCore/QObject> + +class TestTopoSort : public QObject +{ +Q_OBJECT +private slots: + void testTopoSort_data(); + void testTopoSort(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testtyperevision.cpp b/sources/shiboken6/ApiExtractor/tests/testtyperevision.cpp new file mode 100644 index 000000000..72dae8cc5 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testtyperevision.cpp @@ -0,0 +1,92 @@ +// 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 "testtyperevision.h" +#include "testutil.h" +#include <abstractmetaenum.h> +#include <abstractmetalang.h> +#include <complextypeentry.h> +#include <enumtypeentry.h> +#include <flagstypeentry.h> +#include <typedatabase.h> + +#include <qtcompat.h> + +#include <QtTest/QTest> + +using namespace Qt::StringLiterals; + +void TestTypeRevision::testRevisionAttr() +{ + const char cppCode[] = "class Rev_0 {};" + "class Rev_1 {};" + "class Rev_2 { public: enum Rev_3 { X }; enum Rev_5 { Y }; };"; + const char xmlCode[] = "<typesystem package=\"Foo\">" + "<value-type name=\"Rev_0\"/>" + "<value-type name=\"Rev_1\" revision=\"1\"/>" + "<object-type name=\"Rev_2\" revision=\"2\">" + " <enum-type name=\"Rev_3\" revision=\"3\" flags=\"Flag_4\" flags-revision=\"4\" />" + " <enum-type name=\"Rev_5\" revision=\"5\" flags=\"Flag_5\" />" + "</object-type>" + "</typesystem>"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); + QVERIFY(builder); + AbstractMetaClassList classes = builder->classes(); + const auto rev0 = AbstractMetaClass::findClass(classes, "Rev_0"); + QCOMPARE(rev0->typeEntry()->revision(), 0); + + const auto rev1 = AbstractMetaClass::findClass(classes, "Rev_1"); + QCOMPARE(rev1->typeEntry()->revision(), 1); + + const auto rev2 = AbstractMetaClass::findClass(classes, "Rev_2"); + QCOMPARE(rev2->typeEntry()->revision(), 2); + + auto rev3 = rev2->findEnum(u"Rev_3"_s); + QVERIFY(rev3.has_value()); + QCOMPARE(rev3->typeEntry()->revision(), 3); + auto rev4 = rev3->typeEntry()->flags(); + QCOMPARE(rev4->revision(), 4); + auto rev5 = rev2->findEnum(u"Rev_5"_s); + QVERIFY(rev5.has_value()); + EnumTypeEntryCPtr revEnumTypeEntry = rev5->typeEntry(); + QCOMPARE(revEnumTypeEntry->revision(), 5); + QCOMPARE(revEnumTypeEntry->flags()->revision(), 5); +} + + +void TestTypeRevision::testVersion_data() +{ + QTest::addColumn<QString>("version"); + QTest::addColumn<int>("expectedClassCount"); + + QTest::newRow("none") << QString() << 2; + QTest::newRow("1.0") << QString::fromLatin1("1.0") << 1; // Bar20 excluded + QTest::newRow("2.0") << QString::fromLatin1("2.0") << 2; + QTest::newRow("3.0") << QString::fromLatin1("3.0") << 1; // Bar excluded by "until" +} + +void TestTypeRevision::testVersion() +{ + QFETCH(QString, version); + QFETCH(int, expectedClassCount); + + const char cppCode[] = R"CPP( +class Bar {}; +class Bar20 {}; +)CPP"; + const char xmlCode[] = R"XML( +<typesystem package="Foo"> + <value-type name="Bar" until="2.0"/> + <value-type name="Bar20" since="2.0"/> +</typesystem> +)XML"; + + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, true, version)); + QVERIFY(builder); + + QCOMPARE(builder->classes().size(), expectedClassCount); +} + +QTEST_APPLESS_MAIN(TestTypeRevision) + + diff --git a/sources/shiboken6/ApiExtractor/tests/testtyperevision.h b/sources/shiboken6/ApiExtractor/tests/testtyperevision.h new file mode 100644 index 000000000..84af839d2 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testtyperevision.h @@ -0,0 +1,19 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTTYPEREVISION_H +#define TESTTYPEREVISION_H + +#include <QtCore/QObject> + +class TestTypeRevision : public QObject +{ + Q_OBJECT + +private slots: + void testRevisionAttr(); + void testVersion_data(); + void testVersion(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testutil.h b/sources/shiboken6/ApiExtractor/tests/testutil.h new file mode 100644 index 000000000..dc4e3b2da --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testutil.h @@ -0,0 +1,65 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTUTIL_H +#define TESTUTIL_H +#include <QtCore/QBuffer> +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QTemporaryFile> +#include "abstractmetabuilder.h" +#include "reporthandler.h" +#include "typedatabase.h" + +#include <exception> +#include <memory> + +namespace TestUtil +{ + static AbstractMetaBuilder *parse(const char *cppCode, const char *xmlCode, + bool silent = true, + const QString &apiVersion = {}, + const QStringList &dropTypeEntries = {}, + LanguageLevel languageLevel = LanguageLevel::Default) + { + ReportHandler::setSilent(silent); + ReportHandler::startTimer(); + auto *td = TypeDatabase::instance(true); + if (apiVersion.isEmpty()) + TypeDatabase::clearApiVersions(); + else if (!TypeDatabase::setApiVersion(QLatin1StringView("*"), apiVersion)) + return nullptr; + td->setDropTypeEntries(dropTypeEntries); + QBuffer buffer; + // parse typesystem + buffer.setData(xmlCode); + if (!buffer.open(QIODevice::ReadOnly)) + return nullptr; + if (!td->parseFile(&buffer)) + return nullptr; + buffer.close(); + // parse C++ code + QTemporaryFile tempSource(QDir::tempPath() + QLatin1StringView("/st_XXXXXX_main.cpp")); + if (!tempSource.open()) { + qWarning().noquote().nospace() << "Creation of temporary file failed: " + << tempSource.errorString(); + return nullptr; + } + QByteArrayList arguments; + arguments.append(QFile::encodeName(tempSource.fileName())); + tempSource.write(cppCode, qint64(strlen(cppCode))); + tempSource.close(); + + auto builder = std::make_unique<AbstractMetaBuilder>(); + try { + if (!builder->build(arguments, {}, true, languageLevel)) + return nullptr; + } catch (const std::exception &e) { + qWarning("%s", e.what()); + return nullptr; + } + return builder.release(); + } +} // namespace TestUtil + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.cpp b/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.cpp new file mode 100644 index 000000000..98e30eac2 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.cpp @@ -0,0 +1,39 @@ +// 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 "testvaluetypedefaultctortag.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetalang.h> +#include <complextypeentry.h> + +void TestValueTypeDefaultCtorTag::testValueTypeDefaultCtorTagArgument() +{ + const char cppCode[] = "\n\ + struct A {\n\ + A(int,int);\n\ + };\n\ + struct B {};\n\ + "; + const char xmlCode[] = "\n\ + <typesystem package='Foo'>\n\ + <primitive-type name='int' />\n\ + <value-type name='A' default-constructor='A(0, 0)' />\n\ + <value-type name='B' />\n\ + </typesystem>"; + QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode, false)); + QVERIFY(builder); + + AbstractMetaClassList classes = builder->classes(); + + const auto classA = AbstractMetaClass::findClass(classes, "A"); + QVERIFY(classA); + QVERIFY(classA->typeEntry()->hasDefaultConstructor()); + QCOMPARE(classA->typeEntry()->defaultConstructor(), u"A(0, 0)"); + + const auto classB = AbstractMetaClass::findClass(classes, "B"); + QVERIFY(classB); + QVERIFY(!classB->typeEntry()->hasDefaultConstructor()); +} + +QTEST_APPLESS_MAIN(TestValueTypeDefaultCtorTag) diff --git a/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.h b/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.h new file mode 100644 index 000000000..192c07c1d --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testvaluetypedefaultctortag.h @@ -0,0 +1,16 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTVALUETYPEDEFAULTCTORTAG_H +#define TESTVALUETYPEDEFAULTCTORTAG_H + +#include <QtCore/QObject> + +class TestValueTypeDefaultCtorTag : public QObject +{ + Q_OBJECT + private slots: + void testValueTypeDefaultCtorTagArgument(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/testvoidarg.cpp b/sources/shiboken6/ApiExtractor/tests/testvoidarg.cpp new file mode 100644 index 000000000..a600181a5 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testvoidarg.cpp @@ -0,0 +1,67 @@ +// 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 "testvoidarg.h" +#include <QtTest/QTest> +#include "testutil.h" +#include <abstractmetaargument.h> +#include <abstractmetafunction.h> +#include <abstractmetalang.h> +#include <typesystem.h> + +void TestVoidArg::testVoidParsedFunction() +{ + const char cppCode[] = "struct A { void a(void); };"; + const char xmlCode[] = "\n\ + <typesystem package=\"Foo\">\n\ + <value-type name='A'/>\n\ + </typesystem>"; + 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("a"); + QVERIFY(addedFunc); + QCOMPARE(addedFunc->arguments().size(), 0); +} + +void TestVoidArg::testVoidAddedFunction() +{ + const char cppCode[] = "struct A { };"; + const char xmlCode[] = "\n\ + <typesystem package=\"Foo\">\n\ + <value-type name='A' >\n\ + <add-function signature=\"a(void)\"/>\n\ + </value-type>\n\ + </typesystem>"; + 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("a"); + QVERIFY(addedFunc); + QCOMPARE(addedFunc->arguments().size(), 0); + +} + +void TestVoidArg::testVoidPointerParsedFunction() +{ + const char cppCode[] = "struct A { void a(void*); };"; + const char xmlCode[] = "\n\ + <typesystem package=\"Foo\">\n\ + <value-type name='A' />\n\ + </typesystem>"; + 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("a"); + QVERIFY(addedFunc); + QCOMPARE(addedFunc->arguments().size(), 1); + +} + +QTEST_APPLESS_MAIN(TestVoidArg) diff --git a/sources/shiboken6/ApiExtractor/tests/testvoidarg.h b/sources/shiboken6/ApiExtractor/tests/testvoidarg.h new file mode 100644 index 000000000..191b9cfb2 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/testvoidarg.h @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef TESTVOIDARG_H +#define TESTVOIDARG_H +#include <QtCore/QObject> + +class TestVoidArg : public QObject +{ + Q_OBJECT +private slots: + void testVoidParsedFunction(); + void testVoidPointerParsedFunction(); + void testVoidAddedFunction(); +}; + +#endif diff --git a/sources/shiboken6/ApiExtractor/tests/utf8code.txt b/sources/shiboken6/ApiExtractor/tests/utf8code.txt new file mode 100644 index 000000000..6d5fa9dcf --- /dev/null +++ b/sources/shiboken6/ApiExtractor/tests/utf8code.txt @@ -0,0 +1 @@ +áéíóú
\ No newline at end of file |