diff options
Diffstat (limited to 'sources/shiboken6/tests/samplebinding')
134 files changed, 11987 insertions, 0 deletions
diff --git a/sources/shiboken6/tests/samplebinding/CMakeLists.txt b/sources/shiboken6/tests/samplebinding/CMakeLists.txt new file mode 100644 index 000000000..fc812feb8 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/CMakeLists.txt @@ -0,0 +1,176 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +project(sample) + +set(sample_TYPESYSTEM +${CMAKE_CURRENT_SOURCE_DIR}/typesystem_sample.xml +) + +set(sample_SRC +${CMAKE_CURRENT_BINARY_DIR}/sample/abstractmodifications_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/abstract_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/arraymodifytest_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/base1_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/base2_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/base3_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/base4_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/base5_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/base6_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/blackbox_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/brush_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/bytearray_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/bucket_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/classwithfunctionpointer_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/collector_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/comparisontester_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/color_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/ctorconvrule_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/customoverloadsequence_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/cvlistuser_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/cvvaluetype_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/sbkdate_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/deleteddefaultctor_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/derived_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/derivedusingct_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/derived_someinnerclass_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/echo_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/event_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/expression_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/exceptiontest_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/friendofonlycopy_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/handleholder_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/implicitconv_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/implicitbase_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/implicittarget_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/intarray2_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/intarray3_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/intlist_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/sortedoverload_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/intwrapper_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/injectcode_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/listuser_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/mapuser_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/mderived1_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/mderived2_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/mderived3_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/mderived4_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/mderived5_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/modelindex_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/modifications_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/modifiedconstructor_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/noimplicitconversion_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/nondefaultctor_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/objectmodel_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/objecttype_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/objecttypebyvalue_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/objecttypeholder_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/objecttypederived_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/objecttypelayout_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/objecttypeptrlist_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/objecttypeoperators_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/objectview_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/objtypereference_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/oddbooluser_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/onlycopy_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/otherbase_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/overload_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/overload2_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/pairuser_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/pen_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/persistentmodelindex_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/photon_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/photon_base_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/photon_valueidentity_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/photon_valueduplicator_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/point_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/pointerholder_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/pointf_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/pointvaluelist_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/polygon_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/primitivestructpointerholder_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/privatector_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/privatedtor_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/protectedenumclass_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/protectednonpolymorphic_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/protectedpolymorphic_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/protectedpolymorphicdaughter_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/protectedpolymorphicgranddaughter_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/protectedproperty_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/protectedvirtualdestructor_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/rect_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/rectf_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/reference_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/referentmodelindex_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/toberenamedvalue_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/removednamespace1_objectoninvisiblenamespace_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/renameduser_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/sample_module_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/sample_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/sample_sample_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/samplenamespace_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/samplenamespace_ctparam_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/samplenamespace_inlinenamespace_classwithininlinenamespace_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/samplenamespace_someclass_someinnerclass_okthisisrecursiveenough_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/samplenamespace_someclass_someinnerclass_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/samplenamespace_someclass_someotherinnerclass_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/samplenamespace_someclass_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/samplenamespace_derivedfromnamespace_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/stdcomplex_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/simplefile_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/size_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/sizef_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/snakecasetest_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/snakecasederivedtest_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/sonofmderived1_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/spaceshipcomparisontester_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/str_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/strlist_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/time_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/templateptr_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/unremovednamespace_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/valuewithunitdoubleinch_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/valuewithunitdoublemillimeter_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/valuewithunituser_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/virtualdaughter_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/virtualdaughter2_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/virtualfinaldaughter_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/virtualdtor_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/virtualmethods_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/voidholder_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/valueandvirtual_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/filter_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/data_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/intersection_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/union_wrapper.cpp +) + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sample-binding.txt.in" + "${CMAKE_CURRENT_BINARY_DIR}/sample-binding.txt" @ONLY) + +shiboken_get_tool_shell_wrapper(shiboken tool_wrapper) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log" + BYPRODUCTS ${sample_SRC} + COMMAND + ${tool_wrapper} + $<TARGET_FILE:Shiboken6::shiboken6> + --project-file=${CMAKE_CURRENT_BINARY_DIR}/sample-binding.txt + ${GENERATOR_EXTRA_FLAGS} + DEPENDS ${sample_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h Shiboken6::shiboken6 + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Running generator for 'sample' test binding..." +) + +add_library(sample MODULE ${sample_SRC}) +target_include_directories(sample PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(sample PUBLIC libsample libshiboken) +set_property(TARGET sample PROPERTY PREFIX "") +set_property(TARGET sample PROPERTY OUTPUT_NAME "sample${PYTHON_EXTENSION_SUFFIX}") + +if(WIN32) + set_property(TARGET sample PROPERTY SUFFIX ".pyd") +endif() + +create_generator_target(sample) diff --git a/sources/shiboken6/tests/samplebinding/__del___test.py b/sources/shiboken6/tests/samplebinding/__del___test.py new file mode 100644 index 000000000..456886614 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/__del___test.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +import sample + +delCalled = False + + +class MyObject(sample.ObjectType): + def __del__(self): + global delCalled + delCalled = True + + +class TestDel(unittest.TestCase): + def testIt(self): + a = MyObject() + del a + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertTrue(delCalled) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/abstract_test.py b/sources/shiboken6/tests/samplebinding/abstract_test.py new file mode 100644 index 000000000..89e87be1d --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/abstract_test.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for Abstract class''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Abstract + + +class Incomplete(Abstract): + def __init__(self): + Abstract.__init__(self) + + +class Concrete(Abstract): + def __init__(self): + Abstract.__init__(self) + self.pure_virtual_called = False + self.unpure_virtual_called = False + + def pureVirtual(self): + self.pure_virtual_called = True + + def pureVirtualReturningVoidPtr(self): + return 42 + + def unpureVirtual(self): + self.unpure_virtual_called = True + + def virtualGettingAEnum(self, enum): + self.virtual_getting_enum = True + + +class AbstractTest(unittest.TestCase): + '''Test case for Abstract class''' + + def testAbstractPureVirtualMethodAvailability(self): + '''Test if Abstract class pure virtual method was properly wrapped.''' + self.assertTrue('pureVirtual' in dir(Abstract)) + + def testAbstractInstanciation(self): + '''Test if instanciation of an abstract class raises the correct exception.''' + self.assertRaises(NotImplementedError, Abstract) + + def testUnimplementedPureVirtualMethodCall(self): + '''Test if calling a pure virtual method raises the correct exception.''' + i = Incomplete() + self.assertRaises(NotImplementedError, i.pureVirtual) + + def testPureVirtualReturningVoidPtrReturnValue(self): + '''Test if a pure virtual method returning void ptr can be properly reimplemented''' + # Note that the semantics of reimplementing the pure virtual method in + # Python and calling it from C++ is undefined until it's decided how to + # cast the Python data types to void pointers + c = Concrete() + self.assertEqual(c.pureVirtualReturningVoidPtr(), 42) + + def testReimplementedVirtualMethodCall(self): + '''Test if a Python override of a virtual method is correctly called from C++.''' + c = Concrete() + c.callUnpureVirtual() + self.assertTrue(c.unpure_virtual_called) + + def testImplementedPureVirtualMethodCall(self): + '''Test if a Python override of a pure virtual method is correctly called from C++.''' + c = Concrete() + c.callPureVirtual() + self.assertTrue(c.pure_virtual_called) + + def testEnumParameterOnVirtualMethodCall(self): + '''testEnumParameterOnVirtualMethodCall''' + c = Concrete() + c.callVirtualGettingEnum(Abstract.Short) + self.assertTrue(c.virtual_getting_enum) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/addedfunction_test.py b/sources/shiboken6/tests/samplebinding/addedfunction_test.py new file mode 100644 index 000000000..0b5680143 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/addedfunction_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for added functions.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import SampleNamespace, ObjectType, Point + + +class TestAddedFunctionsWithSimilarTypes(unittest.TestCase): + '''Adds new signatures very similar to already existing ones.''' + + def testValueTypeReferenceAndValue(self): + '''In C++ we have "function(const ValueType&, double)", + in Python we add "function(ValueType)".''' + point = Point(10, 20) + multiplier = 4.0 + control = (point.x() + point.y()) * multiplier + self.assertEqual(SampleNamespace.passReferenceToValueType(point, multiplier), control) + control = point.x() + point.y() + self.assertEqual(SampleNamespace.passReferenceToValueType(point), control) + + def testObjectTypeReferenceAndPointer(self): + '''In C++ we have "function(const ObjectType&, int)", + in Python we add "function(ValueType)".''' + obj = ObjectType() + obj.setObjectName('sbrubbles') + multiplier = 3.0 + control = len(obj.objectName()) * multiplier + self.assertEqual(SampleNamespace.passReferenceToObjectType(obj, multiplier), control) + control = len(obj.objectName()) + self.assertEqual(SampleNamespace.passReferenceToObjectType(obj), control) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/addedfunction_with_container_args_test.py b/sources/shiboken6/tests/samplebinding/addedfunction_with_container_args_test.py new file mode 100644 index 000000000..2a739033b --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/addedfunction_with_container_args_test.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for added functions with nested and multi-argument container types.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import sum2d, sumproduct + + +class TestAddedFunctionsWithContainerArgs(unittest.TestCase): + '''Tests added functions with nested and multi-argument container types.''' + + def testNestedContainerType(self): + '''Test added function with single-argument containers.''' + values = [[1, 2], [3, 4, 5], [6]] + self.assertEqual(sum2d(values), 21) + + def testMultiArgContainerType(self): + '''Test added function with a two-argument container.''' + values = [(1, 2), (3, 4), (5, 6)] + self.assertEqual(sumproduct(values), 44) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/argumentmodifications_test.py b/sources/shiboken6/tests/samplebinding/argumentmodifications_test.py new file mode 100644 index 000000000..b0ca56a6d --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/argumentmodifications_test.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for method arguments modifications performed as described on typesystem.''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Modifications, Point + + +class ArgumentModificationsTest(unittest.TestCase): + '''Test cases for method arguments modifications performed as described on typesystem.''' + + def setUp(self): + self.mods = Modifications() + + def tearDown(self): + del self.mods + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + def testArgRemoval0(self): + '''Tests argument removal modifications on Modifications.argRemoval0.''' + # void [-> PyObject*] argRemoval0(int, bool, int = 123 [removed, new val = 321], int = 456) + # code-injection: returns tuple with received parameters plus removed ones + a0, a1, a2 = 1, True, 2 + self.assertEqual(self.mods.argRemoval0(a0, a1), (a0, a1, 321, 456)) + self.assertEqual(self.mods.argRemoval0(a0, a1, a2), (a0, a1, 321, a2)) + # the other wasn't modified + # void argRemoval0(int, bool, int, bool) + self.assertEqual(self.mods.argRemoval0(0, False, 0, False), None) + + def testArgRemoval1(self): + '''Tests argument removal modifications on Modifications.argRemoval1.''' + # void [-> PyObject*] argRemoval1(int, bool, Point = Point(1, 2) [removed], + # Point = Point(3, 4) [removed], int = 333) + # code-injection: returns tuple with received parameters plus removed ones + a0, a1, a2 = 1, True, 2 + self.assertEqual(self.mods.argRemoval1(a0, a1), (a0, a1, Point(1, 2), Point(3, 4), 333)) + self.assertEqual(self.mods.argRemoval1(a0, a1, a2), (a0, a1, Point(1, 2), Point(3, 4), a2)) + # the other wasn't modified + # void argRemoval1(int, bool, int, bool) + self.assertEqual(self.mods.argRemoval1(0, False, 0, False), None) + + def testArgRemoval2(self): + '''Tests argument removal modifications on Modifications.argRemoval2.''' + # void [-> PyObject*] argRemoval2(int, bool, Point = Point(1, 2) + # [removed], Point = Point(3, 4) [removed], int = 333) + # code-injection: returns tuple with received parameters plus removed ones + a0, a1, a2 = 1, True, 2 + self.assertEqual(self.mods.argRemoval2(a0, a1), (a0, a1, Point(1, 2), Point(3, 4), 333)) + self.assertEqual(self.mods.argRemoval2(a0, a1, a2), (a0, a1, Point(1, 2), Point(3, 4), a2)) + + def testArgRemoval3(self): + '''Tests argument removal modifications on Modifications.argRemoval3.''' + # void [-> PyObject*] argRemoval3(int, Point = Point(1, 2) [removed], + # bool = true, Point = Point(3, 4) [removed], int = 333) + # code-injection: returns tuple with received parameters plus removed ones + a0, a1, a2 = 1, True, 2 + self.assertEqual(self.mods.argRemoval3(a0), (a0, Point(1, 2), True, Point(3, 4), 333)) + self.assertEqual(self.mods.argRemoval3(a0, a1), (a0, Point(1, 2), a1, Point(3, 4), 333)) + self.assertEqual(self.mods.argRemoval3(a0, a1, a2), (a0, Point(1, 2), a1, Point(3, 4), a2)) + + def testArgRemoval4(self): + '''Tests argument removal modifications on Modifications.argRemoval4.''' + # void [-> PyObject*] argRemoval4(int, Point [removed, new val = Point(6, 9)], bool, + # Point = Point(3, 4) [removed], int = 333) + # code-injection: returns tuple with received parameters plus removed ones + a0, a1, a2 = 1, True, 2 + self.assertRaises(TypeError, self.mods.argRemoval4, a0) + self.assertEqual(self.mods.argRemoval4(a0, a1), (a0, Point(6, 9), a1, Point(3, 4), 333)) + self.assertEqual(self.mods.argRemoval4(a0, a1, a2), (a0, Point(6, 9), a1, Point(3, 4), a2)) + + def testArgRemoval5(self): + '''Tests argument removal modifications on Modifications.argRemoval5.''' + # void [-> PyObject*] argRemoval5(int [removed, new val = 100], bool, + # Point = Point(1, 2) [removed], + # Point = Point(3, 4) [removed], int = 333) + # code-injection: returns tuple with received parameters plus removed ones + a0, a1, a2 = True, 2, True + self.assertEqual(self.mods.argRemoval5(a0), (100, a0, Point(1, 2), Point(3, 4), 333)) + self.assertEqual(self.mods.argRemoval5(a0, a1), (100, a0, Point(1, 2), Point(3, 4), a1)) + # void [-> PyObject*] argRemoval5(int [removed, new val = 200], bool, int, bool) + # code-injection: returns tuple with received parameters plus removed ones + self.assertEqual(self.mods.argRemoval5(a0, a1, a2), (200, a0, a1, a2)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/array_numpy_test.py b/sources/shiboken6/tests/samplebinding/array_numpy_test.py new file mode 100644 index 000000000..0d73bca1c --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/array_numpy_test.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test case for NumPy Array types.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +import sample + +hasNumPy = False + +try: + import numpy + hasNumPy = True +except ImportError: + pass + + +class ArrayTester(unittest.TestCase): + '''Test case for NumPy arrays.''' + + def testIntArray(self): + intList = numpy.array([1, 2, 3, 4], dtype='int32') + self.assertEqual(sample.sumIntArray(intList), 10) + + def testDoubleArray(self): + doubleList = numpy.array([1, 2, 3, 4], dtype='double') + self.assertEqual(sample.sumDoubleArray(doubleList), 10) + + def testIntMatrix(self): + intMatrix = numpy.array([[1, 2, 3], [4, 5, 6]], dtype='int32') + self.assertEqual(sample.sumIntMatrix(intMatrix), 21) + + def testDoubleMatrix(self): + doubleMatrix = numpy.array([[1, 2, 3], [4, 5, 6]], dtype='double') + self.assertEqual(sample.sumDoubleMatrix(doubleMatrix), 21) + + +if __name__ == '__main__' and hasNumPy: + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/array_sequence_test.py b/sources/shiboken6/tests/samplebinding/array_sequence_test.py new file mode 100644 index 000000000..ad65d58db --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/array_sequence_test.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test case for Array types (PySequence).''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +import sample + + +class ArrayTester(unittest.TestCase): + '''Test case for arrays.''' + + def testIntArray(self): + intList = [1, 2, 3, 4] + self.assertEqual(sample.sumIntArray(intList), 10) + + def testIntArrayModified(self): + intList = [1, 2, 3, 4] + tester = sample.ArrayModifyTest() + self.assertEqual(tester.sumIntArray(4, intList), 10) + + def testDoubleArray(self): + doubleList = [1.2, 2.3, 3.4, 4.5] + self.assertEqual(sample.sumDoubleArray(doubleList), 11.4) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/bug_554_test.py b/sources/shiboken6/tests/samplebinding/bug_554_test.py new file mode 100644 index 000000000..a7e7a7210 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/bug_554_test.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Unit test for bug#554''' + +import os +import sys +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType + + +class Bug554: + def crash(self): + class Crasher(ObjectType): + pass + + +if __name__ == '__main__': + bug = Bug554() + bug.crash() diff --git a/sources/shiboken6/tests/samplebinding/bug_704_test.py b/sources/shiboken6/tests/samplebinding/bug_704_test.py new file mode 100644 index 000000000..c470fe723 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/bug_704_test.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType + + +class NewStyle(object): + def name(self): + return "NewStyle" + + +def defineNewStyle(): + class MyObjectNew(ObjectType, NewStyle): + pass + + +class ObjectTypeTest(unittest.TestCase): + '''Test cases to avoid declaring Shiboken classes with multiple inheritance + from old style classes.''' + + def testObjectTypeNewStype(self): + defineNewStyle() + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/bytearray_test.py b/sources/shiboken6/tests/samplebinding/bytearray_test.py new file mode 100644 index 000000000..e51a899fa --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/bytearray_test.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import ByteArray + + +class ByteArrayBufferProtocolTest(unittest.TestCase): + '''Tests ByteArray implementation of Python buffer protocol.''' + + def testByteArrayBufferProtocol(self): + # Tests ByteArray implementation of Python buffer protocol using the Path().is_dir + # function with a unicode object or other object implementing the Python buffer protocol. + Path(str(ByteArray('/tmp'))).is_dir() + + +class ByteArrayConcatenationOperatorTest(unittest.TestCase): + '''Test cases for ByteArray concatenation with '+' operator.''' + + def testConcatByteArrayAndPythonString(self): + # Test concatenation of a ByteArray with a Python string, in this order. + ba = ByteArray('foo') + result = ba + '\x00bar' + self.assertEqual(type(result), ByteArray) + self.assertEqual(result, 'foo\x00bar') + + def testConcatPythonStringAndByteArray(self): + # Test concatenation of a Python string with a ByteArray, in this order. + concat_python_string_add_qbytearray_worked = True # noqa: F841 + ba = ByteArray('foo') + result = 'bar\x00' + ba + self.assertEqual(type(result), ByteArray) + self.assertEqual(result, 'bar\x00foo') + + +class ByteArrayOperatorEqual(unittest.TestCase): + '''TestCase for operator ByteArray == ByteArray.''' + + def testDefault(self): + # ByteArray() == ByteArray() + obj1 = ByteArray() + obj2 = ByteArray() + self.assertEqual(obj1, obj2) + + def testSimple(self): + # ByteArray(some_string) == ByteArray(some_string) + string = 'egg snakes' + self.assertEqual(ByteArray(string), ByteArray(string)) + + def testPyString(self): + # ByteArray(string) == string + string = 'my test string' + self.assertEqual(ByteArray(string), string) + + def testQString(self): + # ByteArray(string) == string + string = 'another test string' + self.assertEqual(ByteArray(string), string) + + +class ByteArrayOperatorAt(unittest.TestCase): + '''TestCase for operator ByteArray[]''' + + def testInRange(self): + # ByteArray[x] where x is a valid index. + string = 'abcdefgh' + obj = ByteArray(string) + for i in range(len(string)): + self.assertEqual(obj[i], bytes(string[i], "UTF8")) + + def testInRangeReverse(self): + # ByteArray[x] where x is a valid index (reverse order). + string = 'abcdefgh' + obj = ByteArray(string) + for i in range(len(string) - 1, 0, -1): + self.assertEqual(obj[i], bytes(string[i], "UTF8")) + + def testOutOfRange(self): + # ByteArray[x] where x is out of index. + string = '1234567' + obj = ByteArray(string) + self.assertRaises(IndexError, lambda: obj[len(string)]) + + def testNullStrings(self): + ba = ByteArray('\x00') + self.assertEqual(ba.at(0), '\x00') + self.assertEqual(ba[0], bytes('\x00', "UTF8")) + + +class ByteArrayOperatorLen(unittest.TestCase): + '''Test case for __len__ operator of ByteArray''' + + def testBasic(self): + '''ByteArray __len__''' + self.assertEqual(len(ByteArray()), 0) + self.assertEqual(len(ByteArray('')), 0) + self.assertEqual(len(ByteArray(' ')), 1) + self.assertEqual(len(ByteArray('yabadaba')), 8) + + +class ByteArrayAndPythonStr(unittest.TestCase): + '''Test case for __str__ operator of ByteArray''' + + def testStrOperator(self): + '''ByteArray __str__''' + self.assertEqual(ByteArray().__str__(), '') + self.assertEqual(ByteArray('').__str__(), '') + self.assertEqual(ByteArray('aaa').__str__(), 'aaa') + + def testPythonStrAndNull(self): + s1 = bytes('123\000321', "UTF8") + ba = ByteArray(s1) + s2 = ba.data() + self.assertEqual(s1, s2) + self.assertEqual(type(s1), type(s2)) + self.assertEqual(s1, ba) + self.assertNotEqual(type(s1), type(ba)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/child_return_test.py b/sources/shiboken6/tests/samplebinding/child_return_test.py new file mode 100644 index 000000000..f0ac70626 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/child_return_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''The BlackBox class has cases of ownership transference between C++ and Python.''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType + + +class ReturnOfChildTest(unittest.TestCase): + '''The BlackBox class has cases of ownership transference between C++ and Python.''' + + def testKillParentKeepingChild(self): + '''Ownership transference from Python to C++ and back again.''' + o1 = ObjectType.createWithChild() + child = o1.children()[0] + del o1 + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertRaises(RuntimeError, child.objectName) + + def testKillParentKeepingChild2(self): + '''Ownership transference from Python to C++ and back again.''' + o1 = ObjectType.createWithChild() + child = o1.findChild("child") + del o1 + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertRaises(RuntimeError, child.objectName) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/class_fields_test.py b/sources/shiboken6/tests/samplebinding/class_fields_test.py new file mode 100644 index 000000000..1eeb3d446 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/class_fields_test.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Simple test case for accessing the exposed C++ class fields.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Derived, Point, ObjectType + + +class TestAccessingCppFields(unittest.TestCase): + '''Simple test case for accessing the exposed C++ class fields.''' + + def testAccessingPrimitiveTypeField(self): + '''Reads and writes a primitive type (in this case an 'int') field.''' + d = Derived() + self.assertEqual(type(d.primitiveField), int) + + # attribution + old_value = d.primitiveField + new_value = 2255 + d.primitiveField = new_value + self.assertEqual(d.primitiveField, new_value) + self.assertNotEqual(d.primitiveField, old_value) + + # attribution with a convertible type + value = 1.2 + d.primitiveField = value + self.assertEqual(d.primitiveField, int(value)) + + # attribution with invalid type + self.assertRaises(TypeError, lambda: setattr(d, 'primitiveField', None)) + + def testAccessingRenamedFields(self): + '''Reads and writes a renamed field.''' + d = Derived() + self.assertEqual(type(d.renamedField), int) + old_value = d.renamedField + new_value = 2255 + d.renamedField = new_value + self.assertEqual(d.renamedField, new_value) + self.assertNotEqual(d.renamedField, old_value) + + def testAccessingReadOnlyFields(self): + '''Tests a read-only field.''' + d = Derived() + self.assertEqual(type(d.readOnlyField), int) + old_value = d.readOnlyField + try: + d.readOnlyField = 25555 + except AttributeError: + pass + self.assertEqual(d.readOnlyField, old_value) + + def testAccessingUsersPrimitiveTypeField(self): + '''Reads and writes an user's primitive type (in this case an 'Complex') field.''' + d = Derived() + self.assertEqual(type(d.userPrimitiveField), complex) + + # attribution + old_value = d.userPrimitiveField + new_value = complex(1.1, 2.2) + d.userPrimitiveField = new_value + self.assertEqual(d.userPrimitiveField, new_value) + self.assertNotEqual(d.userPrimitiveField, old_value) + + # attribution with invalid type + self.assertRaises(TypeError, lambda: setattr(d, 'userPrimitiveField', None)) + + def testAccessingValueTypeField(self): + '''Reads and writes a value type (in this case a 'Point') field.''' + d = Derived() + self.assertEqual(type(d.valueTypeField), Point) + + # attribution + old_value = d.valueTypeField # noqa: F841 + new_value = Point(-10, 537) + d.valueTypeField = new_value + self.assertEqual(d.valueTypeField, new_value) + + #object modify + d.valueTypeField.setX(10) + d.valueTypeField.setY(20) + self.assertEqual(d.valueTypeField.x(), 10) + self.assertEqual(d.valueTypeField.y(), 20) + + # attribution with invalid type + self.assertRaises(TypeError, lambda: setattr(d, 'valueTypeField', 123)) + + def testAccessingObjectTypeField(self): + '''Reads and writes a object type (in this case an 'ObjectType') field.''' + d = Derived() + + # attribution + old_value = d.objectTypeField + new_value = ObjectType() + d.objectTypeField = new_value + self.assertEqual(d.objectTypeField, new_value) + self.assertNotEqual(d.objectTypeField, old_value) + + # attribution with a convertible type + value = None + d.objectTypeField = value + self.assertEqual(d.objectTypeField, value) + + # attribution with invalid type + self.assertRaises(TypeError, lambda: setattr(d, 'objectTypeField', 123)) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testRefCountingAccessingObjectTypeField(self): + '''Accessing a object type field should respect the reference counting rules.''' + d = Derived() + + # attributing object to instance's field should increase its reference count + o1 = ObjectType() + refcount1 = sys.getrefcount(o1) + d.objectTypeField = o1 + self.assertEqual(d.objectTypeField, o1) + self.assertEqual(sys.getrefcount(d.objectTypeField), refcount1 + 1) + + # attributing a new object to instance's field should decrease the previous + # object's reference count + o2 = ObjectType() + refcount2 = sys.getrefcount(o2) + d.objectTypeField = o2 + self.assertEqual(d.objectTypeField, o2) + self.assertEqual(sys.getrefcount(o1), refcount1) + self.assertEqual(sys.getrefcount(d.objectTypeField), refcount2 + 1) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testRefCountingOfReferredObjectAfterDeletingReferrer(self): + '''Deleting the object referring to other object should decrease the + reference count of the referee.''' + d = Derived() + o = ObjectType() + refcount = sys.getrefcount(o) + d.objectTypeField = o + self.assertEqual(sys.getrefcount(o), refcount + 1) + del d + self.assertEqual(sys.getrefcount(o), refcount) + + def testStaticField(self): + self.assertEqual(Derived.staticPrimitiveField, 0) + + def testAccessingUnsignedIntBitField(self): + d = Derived() + + # attribution + old_value = d.bitField + new_value = 1 + d.bitField = new_value + self.assertEqual(d.bitField, new_value) + self.assertNotEqual(d.bitField, old_value) + + # attribution with a convertible type + value = 1.2 + d.bitField = value + self.assertEqual(d.bitField, int(value)) + + # attribution with invalid type + self.assertRaises(TypeError, lambda: setattr(d, 'bitField', None)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/collector_test.py b/sources/shiboken6/tests/samplebinding/collector_test.py new file mode 100644 index 000000000..4caebc62a --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/collector_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for Collector class' shift operators.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Collector, IntWrapper, ObjectType + + +class CollectorTest(unittest.TestCase): + '''Test cases for Collector class' shift operators.''' + + def testLShiftOperatorSingleUse(self): + '''Test case for using the Collector.__lshift__ operator just one time.''' + collector = Collector() + collector << 13 + self.assertEqual(collector.size(), 1) + self.assertEqual(collector.items(), [13]) + + def testLShiftOperatorMultipleUses(self): + '''Test case for using the Collector.__lshift__ operator many times in the same line.''' + collector = Collector() + collector << 2 << 3 << 5 << 7 << 11 + self.assertEqual(collector.size(), 5) + self.assertEqual(collector.items(), [2, 3, 5, 7, 11]) + + +class CollectorExternalOperator(unittest.TestCase): + '''Test cases for external operators of Collector''' + + def testLShiftExternal(self): + '''Collector external operator''' + collector = Collector() + collector << IntWrapper(5) + self.assertEqual(collector.size(), 1) + self.assertEqual(collector.items(), [5]) + + +class CollectorObjectType(unittest.TestCase): + '''Test cases for Collector ObjectType''' + + def testBasic(self): + '''Collector << ObjectType # greedy collector''' + collector = Collector() + obj = ObjectType() + collector << obj + self.assertEqual(collector.items()[0], obj.identifier()) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/complex_test.py b/sources/shiboken6/tests/samplebinding/complex_test.py new file mode 100644 index 000000000..454aff100 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/complex_test.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for Complex class''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +import sample +from sample import Point + + +class ComplexTest(unittest.TestCase): + '''Test case for conversions between C++ Complex class to Python complex class''' + + def testFunctionReturningComplexObject(self): + '''Test function returning a C++ Complex object.''' + cpx = sample.transmutePointIntoComplex(Point(5.0, 2.3)) + self.assertEqual(cpx, complex(5.0, 2.3)) + + def testFunctionReceivingComplexObjectAsArgument(self): + '''Test function returning a C++ Complex object.''' + pt = sample.transmuteComplexIntoPoint(complex(1.2, 3.4)) + # these assertions intentionally avoids to test the == operator, + # it should have its own test cases. + self.assertEqual(pt.x(), 1.2) + self.assertEqual(pt.y(), 3.4) + + def testComplexList(self): + '''Test list of C++ Complex objects conversion to a list of Python complex objects.''' + # the global function gimmeComplexList() is expected to return a list + # containing the following Complex values: [0j, 1.1+2.2j, 1.3+2.4j] + cpxlist = sample.gimmeComplexList() + self.assertEqual(cpxlist, [complex(), complex(1.1, 2.2), complex(1.3, 2.4)]) + + def testSumComplexPair(self): + '''Test sum of a tuple containing two complex objects.''' + cpx1 = complex(1.2, 3.4) + cpx2 = complex(5.6, 7.8) + self.assertEqual(sample.sumComplexPair((cpx1, cpx2)), cpx1 + cpx2) + + def testUsingTuples(self): + cpx1, cpx2 = (1.2, 3.4), (5.6, 7.8) + self.assertEqual(sample.sumComplexPair((cpx1, cpx2)), + sample.sumComplexPair((complex(*cpx1), complex(*cpx2)))) + cpx1, cpx2 = (1, 3), (5, 7) + self.assertEqual(sample.sumComplexPair((cpx1, cpx2)), + sample.sumComplexPair((complex(*cpx1), complex(*cpx2)))) + cpx1, cpx2 = (1.2, 3), (5.6, 7) + self.assertEqual(sample.sumComplexPair((cpx1, cpx2)), + sample.sumComplexPair((complex(*cpx1), complex(*cpx2)))) + cpx1, cpx2 = (1, 2, 3), (4, 5, 7) + self.assertRaises(TypeError, sample.sumComplexPair, (cpx1, cpx2)) + cpx1, cpx2 = ('1', '2'), ('4', '5') + self.assertRaises(TypeError, sample.sumComplexPair, (cpx1, cpx2)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/conversion_operator_test.py b/sources/shiboken6/tests/samplebinding/conversion_operator_test.py new file mode 100644 index 000000000..7e76245b1 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/conversion_operator_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for implicit conversion generated by conversion operator.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Time, StrList + + +class ConversionOperatorTest(unittest.TestCase): + '''Test cases for implicit conversion generated by conversion operator.''' + + def testConversionOperator(self): + '''Time defined an conversion operator for Str, so passing a Time object + to a method expecting a Str should work.''' + t = Time(1, 2, 3) + t_str = t.toString() + sl = StrList() + + # StrList.append expects a Str object. + sl.append(t) + + self.assertEqual(len(sl), 1) + self.assertEqual(sl[0], t_str) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/copy_test.py b/sources/shiboken6/tests/samplebinding/copy_test.py new file mode 100644 index 000000000..db539d1b9 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/copy_test.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for deep copy of objects''' + +import copy +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +try: + import cPickle as pickle +except ImportError: + import pickle + + +from sample import Point + + +class SimpleCopy(unittest.TestCase): + '''Simple copy of objects''' + + def testCopy(self): + point = Point(0.1, 2.4) + new_point = copy.copy(point) + + self.assertTrue(point is not new_point) + self.assertEqual(point, new_point) + + +class DeepCopy(unittest.TestCase): + '''Deep copy with shiboken objects''' + + def testDeepCopy(self): + '''Deep copy of value types''' + point = Point(3.1, 4.2) + new_point = copy.deepcopy([point])[0] + + self.assertTrue(point is not new_point) + self.assertEqual(point, new_point) + + +class PicklingTest(unittest.TestCase): + '''Support pickling''' + + def testSimple(self): + '''Simple pickling and unpickling''' + + point = Point(10.2, 43.5) + + data = pickle.dumps(point) + new_point = pickle.loads(data) + + self.assertEqual(point, new_point) + self.assertTrue(point is not new_point) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ctorconvrule_test.py b/sources/shiboken6/tests/samplebinding/ctorconvrule_test.py new file mode 100644 index 000000000..5e2695d72 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ctorconvrule_test.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for proper generation of constructor altered by conversion-rule tag.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import CtorConvRule + + +class TestCtorConvRule(unittest.TestCase): + '''Simple test case for CtorConvRule''' + + def testCtorConvRule(self): + '''Test CtorConvRule argument modification through conversion-rule tag.''' + value = 123 + obj = CtorConvRule(value) + self.assertEqual(obj.value(), value + 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/cyclic_test.py b/sources/shiboken6/tests/samplebinding/cyclic_test.py new file mode 100644 index 000000000..4e4ae2603 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/cyclic_test.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import ObjectType +from sample import ObjectView +from sample import ObjectModel + + +class ObjTest(unittest.TestCase): + + def test_cyclic_dependency_withParent(self): + """Create 2 objects with a cyclic dependency, so that they can + only be removed by the garbage collector, and then invoke the + garbage collector in a different thread. + """ + class CyclicChildObject(ObjectType): + def __init__(self, parent): + super(CyclicChildObject, self).__init__(parent) + self._parent = parent + + class CyclicObject(ObjectType): + def __init__(self): + super(CyclicObject, self).__init__() + CyclicChildObject(self) + + # turn off automatic garbage collection, to be able to trigger it + # at the 'right' time + gc.disable() + alive = lambda: sum(isinstance(o, CyclicObject) for o in gc.get_objects()) # noqa: E731 + + # + # first proof that the wizard is only destructed by the garbage + # collector + # + cycle = CyclicObject() + self.assertTrue(alive()) + del cycle + if not hasattr(sys, "pypy_version_info"): + # PYSIDE-535: the semantics of gc.enable/gc.disable is different for PyPy + self.assertTrue(alive()) + gc.collect() + self.assertFalse(alive()) + + def test_cyclic_dependency_withKeepRef(self): + """Create 2 objects with a cyclic dependency, so that they can + only be removed by the garbage collector, and then invoke the + garbage collector in a different thread. + """ + class CyclicChildObject(ObjectView): + def __init__(self, model): + super(CyclicChildObject, self).__init__(None) + self.setModel(model) + + class CyclicObject(ObjectModel): + def __init__(self): + super(CyclicObject, self).__init__() + self._view = CyclicChildObject(self) + + # turn off automatic garbage collection, to be able to trigger it + # at the 'right' time + gc.disable() + alive = lambda: sum(isinstance(o, CyclicObject) for o in gc.get_objects()) # noqa: E731 + + # + # first proof that the wizard is only destructed by the garbage + # collector + # + cycle = CyclicObject() + self.assertTrue(alive()) + del cycle + if not hasattr(sys, "pypy_version_info"): + # PYSIDE-535: the semantics of gc.enable/gc.disable is different for PyPy + self.assertTrue(alive()) + gc.collect() + self.assertFalse(alive()) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/date_test.py b/sources/shiboken6/tests/samplebinding/date_test.py new file mode 100644 index 000000000..2b6efcf18 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/date_test.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for python conversions types ''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from datetime import date + +from sample import SbkDate + + +class DateConversionTest(unittest.TestCase): + + def testConstructorWithDateObject(self): + pyDate = date(2010, 12, 12) + cDate = SbkDate(pyDate) + self.assertTrue(cDate.day(), pyDate.day) + self.assertTrue(cDate.month(), pyDate.month) + self.assertTrue(cDate.year(), pyDate.year) + + def testToPythonFunction(self): + cDate = SbkDate(2010, 12, 12) + pyDate = cDate.toPython() + self.assertTrue(cDate.day(), pyDate.day) + self.assertTrue(cDate.month(), pyDate.month) + self.assertTrue(cDate.year(), pyDate.year) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/decisor_test.py b/sources/shiboken6/tests/samplebinding/decisor_test.py new file mode 100644 index 000000000..0d39c5f96 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/decisor_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for the method overload decisor.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import SampleNamespace, Point, ObjectType, ObjectModel + + +class DecisorTest(unittest.TestCase): + '''Test cases for the method overload decisor.''' + + def testCallWithInvalidParametersSideA(self): + '''Call a method missing with the last argument missing. + This can trigger the bug #262, which means using an argument + not provided by the user.''' + pt = Point() + # This exception may move from a TypeError to a ValueError. + self.assertRaises((TypeError, ValueError), SampleNamespace.forceDecisorSideA, pt) + + def testCallWithInvalidParametersSideB(self): + '''Same as the previous test, but with an integer as first argument, + just to complicate things for the overload method decisor.''' + pt = Point() + # This exception may move from a TypeError to a ValueError. + self.assertRaises((TypeError, ValueError), SampleNamespace.forceDecisorSideB, 1, pt) + + def testDecideCallWithInheritance(self): + '''Call methods overloads that receive parent and inheritor classes' instances.''' + objecttype = ObjectType() + objectmodel = ObjectModel() + self.assertEqual(ObjectModel.receivesObjectTypeFamily(objecttype), + ObjectModel.ObjectTypeCalled) + self.assertNotEqual(ObjectModel.receivesObjectTypeFamily(objecttype), + ObjectModel.ObjectModelCalled) + self.assertEqual(ObjectModel.receivesObjectTypeFamily(objectmodel), + ObjectModel.ObjectModelCalled) + self.assertNotEqual(ObjectModel.receivesObjectTypeFamily(objectmodel), + ObjectModel.ObjectTypeCalled) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/delete_test.py b/sources/shiboken6/tests/samplebinding/delete_test.py new file mode 100644 index 000000000..57a792ae2 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/delete_test.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +import sample +from shiboken6 import Shiboken + + +class DeleteTest(unittest.TestCase): + def testNonCppWrapperClassDelete(self): + """Would segfault when shiboken.delete called on obj not created from Python.""" + obj = sample.ObjectType() + child = obj.createChild(None) + Shiboken.delete(child) + assert not Shiboken.isValid(child) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/deprecated_test.py b/sources/shiboken6/tests/samplebinding/deprecated_test.py new file mode 100644 index 000000000..c371df94f --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/deprecated_test.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest +import warnings + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType + + +class TestDeprecatedCall(unittest.TestCase): + def testCallWithError(self): + o = ObjectType() + warnings.simplefilter('error') + self.assertRaises(DeprecationWarning, o.deprecatedFunction) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/derived_test.py b/sources/shiboken6/tests/samplebinding/derived_test.py new file mode 100644 index 000000000..346f29136 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/derived_test.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for Derived class''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +import sample +from sample import Abstract, Derived, DerivedUsingCt, OverloadedFuncEnum + + +class Deviant(Derived): + def __init__(self): + Derived.__init__(self) + self.pure_virtual_called = False + self.unpure_virtual_called = False + + def pureVirtual(self): + self.pure_virtual_called = True + + def unpureVirtual(self): + self.unpure_virtual_called = True + + def className(self): + return 'Deviant' + + +class ImplementVirtualWithOutParameter(Derived): + def __init__(self, value): + super().__init__() + self._value = value + + def virtualWithOutParameter(self): + return self._value + + +class DerivedTest(unittest.TestCase): + '''Test case for Derived class''' + + def testParentClassMethodsAvailability(self): + '''Test if Derived class really inherits its methods from parent.''' + inherited_methods = set(['callPureVirtual', 'callUnpureVirtual', + 'id_', 'pureVirtual', 'unpureVirtual']) + self.assertTrue(inherited_methods.issubset(dir(Derived))) + + def testOtherOverloadedMethodCall(self): + '''Another test to check overloaded method calling, just to double check.''' + derived = Derived() + + result = derived.otherOverloaded(1, 2, True, 3.3) + self.assertEqual(type(result), Derived.OtherOverloadedFuncEnum) + self.assertEqual(result, sample.Derived.OtherOverloadedFunc_iibd) + + result = derived.otherOverloaded(1, 2.2) + self.assertEqual(type(result), Derived.OtherOverloadedFuncEnum) + self.assertEqual(result, Derived.OtherOverloadedFunc_id) + + def testOverloadedMethodCallWithDifferentNumericTypes(self): + '''Test if the correct overloaded method accepts a different numeric type as argument.''' + derived = Derived() + result = derived.overloaded(1.1, 2.2) + self.assertEqual(type(result), OverloadedFuncEnum) + + def testOverloadedMethodCallWithWrongNumberOfArguments(self): + '''Test if a call to an overloaded method with the wrong number of arguments + raises an exception.''' + derived = Derived() + self.assertRaises(TypeError, derived.otherOverloaded, 1, 2, True) + + def testReimplementedPureVirtualMethodCall(self): + '''Test if a Python override of a implemented pure virtual method is + correctly called from C++.''' + d = Deviant() + d.callPureVirtual() + self.assertTrue(d.pure_virtual_called) + + def testReimplementedVirtualMethodCall(self): + '''Test if a Python override of a reimplemented virtual method is + correctly called from C++.''' + d = Deviant() + d.callUnpureVirtual() + self.assertTrue(d.unpure_virtual_called) + + def testVirtualMethodCallString(self): + '''Test virtual method call returning string.''' + d = Derived() + self.assertEqual(d.className(), 'Derived') + self.assertEqual(d.getClassName(), 'Derived') + + def testReimplementedVirtualMethodCallReturningString(self): + '''Test if a Python override of a reimplemented virtual method is + correctly called from C++.''' + d = Deviant() + self.assertEqual(d.className(), 'Deviant') + self.assertEqual(d.getClassName(), 'Deviant') + + def testSingleArgument(self): + '''Test singleArgument call.''' + d = Derived() + self.assertTrue(d.singleArgument(False)) + self.assertTrue(not d.singleArgument(True)) + + def testMethodCallWithDefaultValue(self): + '''Test method call with default value.''' + d = Derived() + self.assertEqual(d.defaultValue(3), 3.1) + self.assertEqual(d.defaultValue(), 0.1) + + def testCallToMethodWithAbstractArgument(self): + '''Call to method that expects an Abstract argument.''' + objId = 123 + d = Derived(objId) + self.assertEqual(Abstract.getObjectId(d), objId) + + def testObjectCreationWithParentType(self): + '''Derived class creates an instance of itself in C++ and returns it as + a pointer to its ancestor Abstract.''' + obj = Derived.createObject() + self.assertEqual(type(obj), Derived) + + def testDerivedUsingCt(self): + '''Test whether a constructor of the base class declared by using works''' + obj = DerivedUsingCt(42) + self.assertEqual(obj.value(), 42) + + def testVirtualWithOutParameter(self): + d = Derived() + self.assertEqual(d.callVirtualWithOutParameter(), 42) + + d = ImplementVirtualWithOutParameter(1) + self.assertEqual(d.callVirtualWithOutParameter(), 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/duck_punching_test.py b/sources/shiboken6/tests/samplebinding/duck_punching_test.py new file mode 100644 index 000000000..aa21a0f7e --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/duck_punching_test.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for virtual methods.''' + +import os +import sys +import types +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import VirtualMethods, SimpleFile, Point + + +def MethodTypeCompat(func, instance): + return types.MethodType(func, instance) + + +class Duck(VirtualMethods): + def __init__(self): + VirtualMethods.__init__(self) + + +class Monkey(SimpleFile): + def __init__(self, filename): + SimpleFile.__init__(self, filename) + + +class DuckPunchingTest(unittest.TestCase): + '''Test case for duck punching (aka "monkey patching").''' + + def setUp(self): + self.multiplier = 2.0 + self.duck_method_called = False + self.call_counter = 0 + + def testMonkeyPatchOnVirtualMethod(self): + '''Injects new 'virtualMethod0' on a VirtualMethods instance and makes C++ call it.''' + vm = VirtualMethods() + pt, val, cpx, b = Point(1.1, 2.2), 4, complex(3.3, 4.4), True + + result1 = vm.virtualMethod0(pt, val, cpx, b) + result2 = vm.callVirtualMethod0(pt, val, cpx, b) + self.assertEqual(result1, result2) + self.assertEqual(result1, VirtualMethods.virtualMethod0(vm, pt, val, cpx, b)) + + def myVirtualMethod0(obj, pt, val, cpx, b): + self.duck_method_called = True + return VirtualMethods.virtualMethod0(obj, pt, val, cpx, b) * self.multiplier + vm.virtualMethod0 = MethodTypeCompat(myVirtualMethod0, vm) + + result1 = vm.callVirtualMethod0(pt, val, cpx, b) + self.assertTrue(self.duck_method_called) + + result2 = vm.virtualMethod0(pt, val, cpx, b) + self.assertEqual(result1, result2) + self.assertEqual(result1, + VirtualMethods.virtualMethod0(vm, pt, val, cpx, b) * self.multiplier) + + # This is done to decrease the refcount of the vm object + # allowing the object wrapper to be deleted before the + # BindingManager. This is useful when compiling Shiboken + # for debug, since the BindingManager destructor has an + # assert that checks if the wrapper mapper is empty. + vm.virtualMethod0 = None + + def testMonkeyPatchOnVirtualMethodWithInheritance(self): + '''Injects new 'virtualMethod0' on an object that inherits from + VirtualMethods and makes C++ call it.''' + duck = Duck() + pt, val, cpx, b = Point(1.1, 2.2), 4, complex(3.3, 4.4), True + + result1 = duck.virtualMethod0(pt, val, cpx, b) + result2 = duck.callVirtualMethod0(pt, val, cpx, b) + self.assertEqual(result1, result2) + self.assertEqual(result1, VirtualMethods.virtualMethod0(duck, pt, val, cpx, b)) + + def myVirtualMethod0(obj, pt, val, cpx, b): + self.duck_method_called = True + return VirtualMethods.virtualMethod0(obj, pt, val, cpx, b) * self.multiplier + duck.virtualMethod0 = MethodTypeCompat(myVirtualMethod0, duck) + + result1 = duck.callVirtualMethod0(pt, val, cpx, b) + self.assertTrue(self.duck_method_called) + + result2 = duck.virtualMethod0(pt, val, cpx, b) + self.assertEqual(result1, result2) + self.assertEqual(result1, + VirtualMethods.virtualMethod0(duck, pt, val, cpx, b) * self.multiplier) + + duck.virtualMethod0 = None + + def testMonkeyPatchOnMethodWithStaticAndNonStaticOverloads(self): + '''Injects new 'exists' on a SimpleFile instance and makes C++ call it.''' + simplefile = SimpleFile('foobar') + + # Static 'exists' + simplefile.exists('sbrubbles') + self.assertFalse(self.duck_method_called) + # Non-static 'exists' + simplefile.exists() + self.assertFalse(self.duck_method_called) + + def myExists(obj): + self.duck_method_called = True + return False + simplefile.exists = MethodTypeCompat(myExists, simplefile) + + # Static 'exists' was overridden by the monkey patch, which accepts 0 arguments + self.assertRaises(TypeError, simplefile.exists, 'sbrubbles') + # Monkey patched exists + simplefile.exists() + self.assertTrue(self.duck_method_called) + + simplefile.exists = None + + def testMonkeyPatchOnMethodWithStaticAndNonStaticOverloadsWithInheritance(self): + '''Injects new 'exists' on an object that inherits from SimpleFile and makes C++ call it.''' + monkey = Monkey('foobar') + + # Static 'exists' + monkey.exists('sbrubbles') + self.assertFalse(self.duck_method_called) + # Non-static 'exists' + monkey.exists() + self.assertFalse(self.duck_method_called) + + def myExists(obj): + self.duck_method_called = True + return False + monkey.exists = MethodTypeCompat(myExists, monkey) + + # Static 'exists' was overridden by the monkey patch, which accepts 0 arguments + self.assertRaises(TypeError, monkey.exists, 'sbrubbles') + # Monkey patched exists + monkey.exists() + self.assertTrue(self.duck_method_called) + + monkey.exists = None + + def testForInfiniteRecursion(self): + def myVirtualMethod0(obj, pt, val, cpx, b): + self.call_counter += 1 + return VirtualMethods.virtualMethod0(obj, pt, val, cpx, b) + vm = VirtualMethods() + vm.virtualMethod0 = MethodTypeCompat(myVirtualMethod0, vm) + pt, val, cpx, b = Point(1.1, 2.2), 4, complex(3.3, 4.4), True + vm.virtualMethod0(pt, val, cpx, b) + self.assertEqual(self.call_counter, 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/echo_test.py b/sources/shiboken6/tests/samplebinding/echo_test.py new file mode 100644 index 000000000..f1859260e --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/echo_test.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for <add-function> with const char* as argument''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Echo + + +class TestEcho(unittest.TestCase): + '''Simple test case for Echo.echo''' + + def testEcho(self): + '''Test function added with const char * as arg''' + x = 'Foobar' + y = Echo().echo(x) + self.assertEqual(x, y) + + def testCallOperator(self): + e = Echo() + self.assertEqual(e("Hello", 3), "Hello3") + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/enum_test.py b/sources/shiboken6/tests/samplebinding/enum_test.py new file mode 100644 index 000000000..276b8d894 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/enum_test.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for Python representation of C++ enums.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +# This is needed after the introduction of BUILD_DIR. + +import sample +from sample import SampleNamespace, ObjectType, Event + + +def createTempFile(): + import tempfile + return tempfile.SpooledTemporaryFile(mode='rw') + + +class EnumTest(unittest.TestCase): + '''Test case for Python representation of C++ enums.''' + + def testHashability(self): + self.assertEqual(hash(SampleNamespace.TwoIn), hash(SampleNamespace.TwoOut)) + self.assertNotEqual(hash(SampleNamespace.TwoIn), hash(SampleNamespace.OneIn)) + + def testEnumValuesInsideEnum(self): + '''Enum values should be accessible inside the enum as well as outside.''' + for value_name in SampleNamespace.Option.__members__: + enum_item1 = getattr(SampleNamespace.Option, value_name) + enum_item2 = getattr(SampleNamespace, value_name) + self.assertEqual(enum_item1, enum_item2) + + def testPassingIntegerOnEnumArgument(self): + '''Tries to use an integer in place of an enum argument.''' + self.assertRaises(TypeError, SampleNamespace.getNumber, 1) + + def testBuildingEnumFromIntegerValue(self): + '''Tries to build the proper enum using an integer.''' + SampleNamespace.getNumber(SampleNamespace.Option(1)) + + def testBuildingEnumWithDefaultValue(self): + '''Enum constructor with default value''' + enum = SampleNamespace.Option() + self.assertEqual(enum, SampleNamespace.None_) + + def testEnumConversionToAndFromPython(self): + '''Conversion of enum objects from Python to C++ back again.''' + enumout = SampleNamespace.enumInEnumOut(SampleNamespace.TwoIn) + self.assertTrue(enumout, SampleNamespace.TwoOut) + self.assertEqual(repr(enumout), repr(SampleNamespace.TwoOut)) + + def testEnumConstructorWithTooManyParameters(self): + '''Calling the constructor of non-extensible enum with the wrong number of parameters.''' + self.assertRaises((TypeError, ValueError), SampleNamespace.InValue, 13, 14) + + def testEnumConstructorWithNonNumberParameter(self): + '''Calling the constructor of non-extensible enum with a string.''' + self.assertRaises((TypeError, ValueError), SampleNamespace.InValue, '1') + + def testEnumItemAsDefaultValueToIntArgument(self): + '''Calls function with an enum item as default value to an int argument.''' + self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(), + SampleNamespace.ZeroIn) + self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(SampleNamespace.ZeroOut), # noqa E:501 + SampleNamespace.ZeroOut) + self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(123), 123) + + def testAnonymousGlobalEnums(self): + '''Checks availability of anonymous global enum items.''' + self.assertEqual(sample.AnonymousGlobalEnum_Value0, 0) + self.assertEqual(sample.AnonymousGlobalEnum_Value1, 1) + + def testAnonymousClassEnums(self): + '''Checks availability of anonymous class enum items.''' + self.assertEqual(SampleNamespace.AnonymousClassEnum_Value0, 0) + self.assertEqual(SampleNamespace.AnonymousClassEnum_Value1, 1) + + def testEnumClasses(self): + # C++ 11: values of enum classes need to be fully qualified to match C++ + sum = Event.EventTypeClass.Value1 + Event.EventTypeClass.Value2 + self.assertEqual(sum, 1) + + def testSetEnum(self): + event = Event(Event.ANY_EVENT) + self.assertEqual(event.eventType(), Event.ANY_EVENT) + event.setEventType(Event.BASIC_EVENT) + self.assertEqual(event.eventType(), Event.BASIC_EVENT) + event.setEventTypeByConstRef(Event.SOME_EVENT) + self.assertEqual(event.eventType(), Event.SOME_EVENT) + event.setEventTypeByConstPtr(Event.BASIC_EVENT) + self.assertEqual(event.eventType(), Event.BASIC_EVENT) + + def testEnumArgumentWithDefaultValue(self): + '''Option enumArgumentWithDefaultValue(Option opt = UnixTime);''' + self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(), SampleNamespace.UnixTime) + self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(SampleNamespace.RandomNumber), # noqa E:501 + SampleNamespace.RandomNumber) + + +class MyEvent(Event): + def __init__(self): + Event.__init__(self, Event.EventType(3)) + + +class OutOfBoundsTest(unittest.TestCase): + def testValue(self): + e = MyEvent() + self.assertEqual(repr(e.eventType()), "<EventType.ANY_EVENT: 3>") + + +class EnumOverloadTest(unittest.TestCase): + '''Test case for overloads involving enums''' + + def testWithInt(self): + '''Overload with Enums and ints with default value''' + o = ObjectType() + + self.assertEqual(o.callWithEnum('', Event.ANY_EVENT, 9), 81) + self.assertEqual(o.callWithEnum('', 9), 9) + + +class EnumOperators(unittest.TestCase): + '''Test case for operations on enums''' + + def testInequalitySameObject(self): + self.assertFalse(Event.ANY_EVENT != Event.ANY_EVENT) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/enumfromremovednamespace_test.py b/sources/shiboken6/tests/samplebinding/enumfromremovednamespace_test.py new file mode 100644 index 000000000..42ae23961 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/enumfromremovednamespace_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +import sample +from shiboken_test_helper import objectFullname + +from shibokensupport.signature import get_signature + + +class TestEnumFromRemovedNamespace(unittest.TestCase): + + def testNames(self): + # Test if invisible namespace does not appear on type name + self.assertEqual(objectFullname(sample.RemovedNamespace1_Enum), + "sample.RemovedNamespace1_Enum") + self.assertEqual(objectFullname(sample.ObjectOnInvisibleNamespace), + "sample.ObjectOnInvisibleNamespace") + + # Function arguments + signature = get_signature(sample.ObjectOnInvisibleNamespace.toInt) + self.assertEqual(objectFullname(signature.parameters['e'].annotation), + "sample.RemovedNamespace1_Enum") + signature = get_signature(sample.ObjectOnInvisibleNamespace.consume) + self.assertEqual(objectFullname(signature.parameters['other'].annotation), + "sample.ObjectOnInvisibleNamespace") + + def testGlobalFunctionFromRemovedNamespace(self): + self.assertEqual(sample.mathSum(1, 2), 3) + + def testEnumPromotedToUpperNamespace(self): + sample.UnremovedNamespace + sample.UnremovedNamespace.RemovedNamespace3_Enum + sample.UnremovedNamespace.RemovedNamespace3_Enum_Value0 + sample.UnremovedNamespace.RemovedNamespace3_AnonymousEnum_Value0 + + def testNestedFunctionFromRemovedNamespace(self): + self.assertEqual(sample.UnremovedNamespace.nestedMathSum(1, 2), 3) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/event_loop_call_virtual_test.py b/sources/shiboken6/tests/samplebinding/event_loop_call_virtual_test.py new file mode 100644 index 000000000..8e13d5d46 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/event_loop_call_virtual_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Simple event loop dispatcher test.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType, Event + + +class NoOverride(ObjectType): + + pass + + +class Override(ObjectType): + + def __init__(self): + ObjectType.__init__(self) + self.called = False + + def event(self, event): + self.called = True + return True + + +class TestEventLoop(unittest.TestCase): + + def testEventLoop(self): + '''Calling virtuals in a event loop''' + objs = [ObjectType(), NoOverride(), Override()] + + evaluated = ObjectType.processEvent(objs, + Event(Event.BASIC_EVENT)) + + self.assertEqual(evaluated, 3) + self.assertTrue(objs[2].called) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/event_loop_thread_test.py b/sources/shiboken6/tests/samplebinding/event_loop_thread_test.py new file mode 100644 index 000000000..8b854fca6 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/event_loop_thread_test.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +from random import random +import sys +import time +import threading +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType, Event + + +class Producer(ObjectType): + + def __init__(self): + ObjectType.__init__(self) + self.data = None + self.read = False + + def event(self, event): + self.data = random() + + while not self.read: + time.sleep(0.01) + + return True + + +class Collector(threading.Thread): + + def __init__(self, objects): + threading.Thread.__init__(self) + self.max_runs = len(objects) + self.objects = objects + self.data = [] + + def run(self): + i = 0 + while i < self.max_runs: + if self.objects[i].data is not None: + self.data.append(self.objects[i].data) + self.objects[i].read = True + i += 1 + time.sleep(0.01) + + +class TestEventLoopWithThread(unittest.TestCase): + '''Communication between a python thread and an simple + event loop in C++''' + + def testBasic(self): + '''Allowing threads and calling virtuals from C++''' + number = 10 + objs = [Producer() for x in range(number)] + thread = Collector(objs) + + thread.start() + + evaluated = ObjectType.processEvent(objs, + Event(Event.BASIC_EVENT)) + + thread.join() + + producer_data = [x.data for x in objs] + self.assertEqual(evaluated, number) + self.assertEqual(producer_data, thread.data) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/exception_test.py b/sources/shiboken6/tests/samplebinding/exception_test.py new file mode 100644 index 000000000..d9e6b377f --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/exception_test.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ExceptionTest + + +class CppExceptionTest(unittest.TestCase): + + def testVoid(self): + exceptionCount = 0 + et = ExceptionTest() + + et.voidThrowStdException(False) + + try: + et.voidThrowStdException(True) + except: # noqa: E722 + exceptionCount += 1 + + et.voidThrowInt(False) + + try: + et.voidThrowInt(True) + except: # noqa: E722 + exceptionCount += 1 + + self.assertEqual(exceptionCount, 2) + + def testReturnValue(self): + exceptionCount = 0 + et = ExceptionTest() + + result = et.intThrowStdException(False) + + try: + result = et.intThrowStdException(True) + except: # noqa: E722 + exceptionCount += 1 + + result = et.intThrowInt(False) + + try: + result = et.intThrowInt(True) # noqa: F841 + except: # noqa: E722 + exceptionCount += 1 + + self.assertEqual(exceptionCount, 2) + + def testModifications(self): + """PYSIDE-1995, test whether exceptions are propagated + when return ownership modifications are generated.""" + exceptionCount = 0 + try: + et = ExceptionTest.create(True) # noqa: F841 + except: # noqa: E722 + exceptionCount += 1 + self.assertEqual(exceptionCount, 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/filter_test.py b/sources/shiboken6/tests/samplebinding/filter_test.py new file mode 100644 index 000000000..df805093f --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/filter_test.py @@ -0,0 +1,29 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Data, Intersection, Union + + +class TestFilters(unittest.TestCase): + + def testAnd(self): + + f1 = Data(Data.Name, "joe") + f2 = Union() + + inter = f1 & f2 + + self.assertEqual(type(inter), Intersection) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/global.h b/sources/shiboken6/tests/samplebinding/global.h new file mode 100644 index 000000000..64806417a --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/global.h @@ -0,0 +1,73 @@ +// 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 "abstract.h" +#include "blackbox.h" +#include "bytearray.h" +#include "bucket.h" +#include "collector.h" +#include "complex.h" +#include "ctorconvrule.h" +#include "ctparam.h" +#include "cvlist.h" +#include "sbkdate.h" +#include "derived.h" +#include "derivedusingct.h" +#include "echo.h" +#include "exceptiontest.h" +#include "functions.h" +#include "implicitconv.h" +#include "nontypetemplate.h" +#include "overloadsort.h" +#include "handle.h" +#include "injectcode.h" +#include "list.h" +#include "listuser.h" +#include "mapuser.h" +#include "modelindex.h" +#include "modifications.h" +#include "modified_constructor.h" +#include "multiple_derived.h" +#include "noimplicitconversion.h" +#include "nondefaultctor.h" +#include "objectmodel.h" +#include "objecttype.h" +#include "objecttypebyvalue.h" +#include "objecttypeholder.h" +#include "objecttypelayout.h" +#include "objecttypeoperators.h" +#include "objectview.h" +#include "oddbool.h" +#include "onlycopy.h" +#include "overload.h" +#include "pairuser.h" +#include "pen.h" +#include "photon.h" +#include "point.h" +#include "pointf.h" +#include "pointerholder.h" +#include "polygon.h" +#include "privatector.h" +#include "privatedtor.h" +#include "protected.h" +#include "rect.h" +#include "reference.h" +#include "renaming.h" +#include "removednamespaces.h" +#include "sample.h" +#include "samplenamespace.h" +#include "stdcomplex.h" +#include "simplefile.h" +#include "size.h" +#include "snakecasetest.h" +#include "str.h" +#include "strlist.h" +#include "sometime.h" +#include "templateptr.h" +#include "transform.h" +#include "typesystypedef.h" +#include "virtualmethods.h" +#include "voidholder.h" +#include "valueandvirtual.h" +#include "expression.h" +#include "filter.h" diff --git a/sources/shiboken6/tests/samplebinding/handleholder_test.py b/sources/shiboken6/tests/samplebinding/handleholder_test.py new file mode 100644 index 000000000..af22328c5 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/handleholder_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +''' Test case for a class that holds a unknown handle object. + Test case for BUG #1105. +''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import HandleHolder + + +class HandleHolderTest(unittest.TestCase): + def testCreation(self): + holder = HandleHolder(HandleHolder.createHandle()) + holder2 = HandleHolder(HandleHolder.createHandle()) + self.assertEqual(holder.compare(holder2), False) + + def testTransfer(self): + holder = HandleHolder() + holder2 = HandleHolder(holder.handle()) + self.assertTrue(holder.compare(holder2)) + + def testUseDefinedType(self): + holder = HandleHolder(8) + holder2 = HandleHolder(holder.handle2()) + self.assertTrue(holder.compare2(holder2)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/hashabletype_test.py b/sources/shiboken6/tests/samplebinding/hashabletype_test.py new file mode 100644 index 000000000..c41f5cc06 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/hashabletype_test.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for __hash__''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType, Str + + +class HashableTest(unittest.TestCase): + + def testStrHash(self): + h = {} + s = Str("Hi") + h[s] = 2 + self.assertTrue(h.get(s), 2) + + def testObjectTypeHash(self): + h = {} + o = ObjectType() + h[o] = 2 + self.assertTrue(h.get(o), 2) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ignorederefop_test.py b/sources/shiboken6/tests/samplebinding/ignorederefop_test.py new file mode 100644 index 000000000..feb78d045 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ignorederefop_test.py @@ -0,0 +1,22 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import Reference + + +class TestLackOfDereferenceOperators (unittest.TestCase): + def testIf(self): + r = Reference() + self.assertFalse(hasattr(r, "__mul__")) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/implicitconv_numerical_test.py b/sources/shiboken6/tests/samplebinding/implicitconv_numerical_test.py new file mode 100644 index 000000000..081666281 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/implicitconv_numerical_test.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test case for inplicit converting C++ numeric types.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +import sys +import sample + +# Hardcode the limits of the underlying C-types depending on architecture and memory +# model (taking MSVC using LLP64 into account). +cIntMin = -2147483648 +cIntMax = 2147483647 +cLongMin = cIntMin +cLongMax = cIntMax +maxRepresentableInt = sys.maxsize +is64bitArchitecture = maxRepresentableInt > 2**32 +if is64bitArchitecture and sys.platform != 'win32': + cLongMin = -9223372036854775808 + cLongMax = 9223372036854775807 + + +class NumericTester(unittest.TestCase): + '''Helper class for numeric comparison testing''' + + def assertRaises(self, *args, **kwds): + if not hasattr(sys, "pypy_version_info"): + # PYSIDE-535: PyPy complains "Fatal RPython error: NotImplementedError" + return super().assertRaises(*args, **kwds) + + def check_value(self, source, expected, callback, desired_type=None): + result = callback(source) + self.assertEqual(result, expected) + + if desired_type: + self.assertEqual(type(result), desired_type) + + +class FloatImplicitConvert(NumericTester): + '''Test case for implicit converting C++ numeric types.''' + + def testFloatAsInt(self): + '''Float as Int''' + self.check_value(3.14, 3, sample.acceptInt, int) + self.assertRaises(OverflowError, sample.acceptInt, cIntMax + 400) + + def testFloatAsLong(self): + '''Float as Long''' + #C++ longs are python ints for us + self.check_value(3.14, 3, sample.acceptLong, int) + self.assertRaises(OverflowError, sample.acceptLong, cLongMax + 400) + + def testFloatAsUInt(self): + '''Float as unsigned Int''' + self.check_value(3.14, 3, sample.acceptUInt, int) + self.assertRaises(OverflowError, sample.acceptUInt, -3.14) + + def testFloatAsULong(self): + '''Float as unsigned Long''' + #FIXME Breaking with SystemError "bad argument to internal function" + self.check_value(3.14, 3, sample.acceptULong, int) + self.assertRaises(OverflowError, sample.acceptULong, -3.14) + + def testFloatAsDouble(self): + '''Float as double''' + self.check_value(3.14, 3.14, sample.acceptDouble, float) + + +class IntImplicitConvert(NumericTester): + '''Test case for implicit converting C++ numeric types.''' + + def testIntAsInt(self): + '''Int as Int''' + self.check_value(3, 3, sample.acceptInt, int) + + def testIntAsLong(self): + '''Int as Long''' + self.check_value(3, 3, sample.acceptLong, int) + + # cLongMax goes here as CPython implements int as a C long + self.check_value(cLongMax, cLongMax, sample.acceptLong, int) + self.check_value(cLongMin, cLongMin, sample.acceptLong, int) + + def testIntAsUInt(self): + '''Int as unsigned Int''' + self.check_value(3, 3, sample.acceptUInt, int) + self.assertRaises(OverflowError, sample.acceptUInt, -3) + + def testIntAsULong(self): + '''Int as unsigned Long''' + self.check_value(3, 3, sample.acceptULong, int) + self.assertRaises(OverflowError, sample.acceptULong, -3) + + def testFloatAsDouble(self): + '''Float as double''' + self.check_value(3.14, 3.14, sample.acceptDouble, float) + + +class LongImplicitConvert(NumericTester): + '''Test case for implicit converting C++ numeric types.''' + + def testLongAsInt(self): + '''Long as Int''' + self.check_value(24224, 24224, sample.acceptInt, int) + self.assertRaises(OverflowError, sample.acceptInt, cIntMax + 20) + + def testLongAsLong(self): + '''Long as Long''' + self.check_value(2405, 2405, sample.acceptLong, int) + self.assertRaises(OverflowError, sample.acceptLong, cLongMax + 20) + + def testLongAsUInt(self): + '''Long as unsigned Int''' + self.check_value(260, 260, sample.acceptUInt, int) + self.assertRaises(OverflowError, sample.acceptUInt, -42) + + def testLongAsULong(self): + '''Long as unsigned Long''' + self.check_value(128, 128, sample.acceptULong, int) + self.assertRaises(OverflowError, sample.acceptULong, -334) + + def testLongAsDouble(self): + '''Float as double''' + self.check_value(42, 42, sample.acceptDouble, float) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/implicitconv_test.py b/sources/shiboken6/tests/samplebinding/implicitconv_test.py new file mode 100644 index 000000000..ebafe0c52 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/implicitconv_test.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for implicit conversions''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ImplicitConv, ObjectType + + +class ImplicitConvTest(unittest.TestCase): + '''Test case for implicit conversions''' + + def testImplicitConversions(self): + '''Test if overloaded function call decisor takes implicit conversions into account.''' + ic = ImplicitConv.implicitConvCommon(ImplicitConv()) + self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorNone) + + ic = ImplicitConv.implicitConvCommon(3) + self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorOne) + self.assertEqual(ic.objId(), 3) + + ic = ImplicitConv.implicitConvCommon(ImplicitConv.CtorThree) + self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorThree) + + obj = ObjectType() + ic = ImplicitConv.implicitConvCommon(obj) + self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorObjectTypeReference) + + ic = ImplicitConv.implicitConvCommon(42.42) + self.assertEqual(ic.value(), 42.42) + + ic = ImplicitConv(None) + self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorPrimitiveType) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/inheritanceandscope_test.py b/sources/shiboken6/tests/samplebinding/inheritanceandscope_test.py new file mode 100644 index 000000000..28d62486a --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/inheritanceandscope_test.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for finding scope in cases involving inheritance.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import SampleNamespace + + +class ScopeAndInheritanceTest(unittest.TestCase): + '''Test cases for finding scope in cases involving inheritance.''' + + def testMethodCorrectlyWrapper(self): + '''A method returning a type declared in the scope of the method's + class parent must be found and the method correctly exported.''' + meth = getattr(SampleNamespace.DerivedFromNamespace, # noqa: F841 + 'methodReturningTypeFromParentScope') + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/injectcode_test.py b/sources/shiboken6/tests/samplebinding/injectcode_test.py new file mode 100644 index 000000000..f673a7807 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/injectcode_test.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for std::list container conversions''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import InjectCode + + +class MyInjectCode(InjectCode): + def __init__(self): + InjectCode.__init__(self) + self.multiplier = 2 + + def arrayMethod(self, values): + return self.multiplier * sum(values) + + +class InjectCodeTest(unittest.TestCase): + + @unittest.skipIf(hasattr(sys, "pypy_version_info"), + "PyPy type objects cannot be modified (yet) after creation") + def testTypeNativeBeginning_TypeTargetBeginning(self): + ic = InjectCode() + self.assertEqual(str(ic), "Hi! I'm the inject code dummy class.") + + def testFunctionTargetBeginning_FunctionTargetEnd(self): + ic = InjectCode() + ret = ic.simpleMethod1(2, 1) + self.assertEqual(ret, "4end") + ret = ic.simpleMethod1(4, 2) + self.assertEqual(ret, "7end") + + def testFunctionTargetBeginning(self): + ic = InjectCode() + ret = ic.simpleMethod2() + self.assertEqual(ret, "_end") + + def testArgsModification(self): + ic = InjectCode() + ret = ic.overloadedMethod(["1", "2", "3", "4"]) + self.assertEqual(ret, "1234") + ret = ic.overloadedMethod(2, 0.5) + self.assertEqual(ret, "2.5") + ret = ic.overloadedMethod(6, True) + self.assertEqual(ret, "6true") + + def testArgsModification2(self): + ic = InjectCode() + ret = ic.simpleMethod3(["1", "2", "3", "4"]) + self.assertEqual(ret, "1234") + + def testArgumentRemovalAndArgumentTypeModification(self): + '''A method has its first argument removed and the second modified.''' + ic = InjectCode() + values = (1, 2, 3, 4, 5) + result = ic.arrayMethod(values) + self.assertEqual(result, sum(values)) + + def testCallVirtualMethodWithArgumentRemovalAndArgumentTypeModification(self): + '''A virtual method has its first argument removed and the second modified.''' + ic = InjectCode() + values = (1, 2, 3, 4, 5) + result = ic.callArrayMethod(values) + self.assertEqual(result, sum(values)) + + def testCallReimplementedVirtualMethodWithArgumentRemovalAndArgumentTypeModification(self): + '''Calls a reimplemented virtual method that had its first argument removed + and the second modified.''' + ic = MyInjectCode() + values = (1, 2, 3, 4, 5) + result = ic.callArrayMethod(values) + self.assertEqual(result, ic.multiplier * sum(values)) + + def testUsageOfTypeSystemCheckVariableOnPrimitiveType(self): + '''When the sequence item is convertible to an integer -1 is returned, + or -2 if its not convertible.''' + ic = InjectCode() + values = (1, 2, 3, 4, '5', 6.7) + result = ic.arrayMethod(values) + + ints = [v for v in values if isinstance(v, int)] + floats = [-1 for v in values if isinstance(v, float)] + other = [-2 for v in values if not isinstance(v, int) and not isinstance(v, float)] + self.assertEqual(result, sum(ints + floats + other)) + + +class IntArrayTest(unittest.TestCase): + '''Test case for converting python sequence to int array''' + + def testBasic(self): + '''Shiboken::sequenceToIntArray - basic case''' + args = [1, 2, 3, 4] + ic = InjectCode() + self.assertEqual(sum(args) + len(args), ic.sumArrayAndLength(args)) + + def testEmpty(self): + '''Shiboken::sequenceToIntArray - empty sequence''' + args = [] + ic = InjectCode() + self.assertEqual(sum(args) + len(args), ic.sumArrayAndLength(args)) + + def testWithZero(self): + '''Shiboken::sequenceToIntArray - count only up to zero''' + args = [1, 2, 0, 3] + ic = InjectCode() + self.assertEqual(sum([1, 2]) + len([1, 2]), ic.sumArrayAndLength(args)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/innerclass_test.py b/sources/shiboken6/tests/samplebinding/innerclass_test.py new file mode 100644 index 000000000..721f33483 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/innerclass_test.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Derived + + +class TestInnerClass(unittest.TestCase): + def testInstaciate(self): + d = Derived.SomeInnerClass() # noqa: F841 + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/intlist_test.py b/sources/shiboken6/tests/samplebinding/intlist_test.py new file mode 100644 index 000000000..defa9ca71 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/intlist_test.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import IntList + + +class IntListTest(unittest.TestCase): + + def testAutoFunctionsToBaseList(self): + lst = IntList() + self.assertEqual(len(lst), 0) + lst.append(10) + self.assertEqual(lst[0], 10) + lst.append(20) + self.assertEqual(lst[1], 20) + lst.append(30) + self.assertEqual(lst[2], 30) + lst[1] = 25 + self.assertEqual(lst[0], 10) + self.assertEqual(lst[1], 25) + self.assertEqual(lst[2], 30) + self.assertEqual(len(lst), 3) + + def testIntListCtor_NoParams(self): + '''IntList constructor receives no parameter.''' + il = IntList() + self.assertEqual(len(il), 0) + self.assertEqual(il.constructorUsed(), IntList.NoParamsCtor) + + def testIntListCtor_int(self): + '''IntList constructor receives an integer.''' + value = 123 + il = IntList(value) + self.assertEqual(len(il), 1) + self.assertEqual(il[0], value) + self.assertEqual(il.constructorUsed(), IntList.IntCtor) + + def testIntListCtor_IntList(self): + '''IntList constructor receives an IntList object.''' + il1 = IntList(123) + il2 = IntList(il1) + self.assertEqual(len(il1), len(il2)) + for i in range(len(il1)): + self.assertEqual(il1[i], il2[i]) + self.assertEqual(il2.constructorUsed(), IntList.CopyCtor) + + def testIntListCtor_ListOfInts(self): + '''IntList constructor receives an integer list.''' + ints = [123, 456] + il = IntList(ints) + self.assertEqual(len(il), len(ints)) + for i in range(len(il)): + self.assertEqual(il[i], ints[i]) + self.assertEqual(il.constructorUsed(), IntList.ListOfIntCtor) + + def testIntListAttributeTypeCheck(self): + '''Attribute values to IntList.''' + il = IntList([0, 1, 2]) + self.assertEqual(len(il), 3) + il[0] = 123 + self.assertEqual(len(il), 3) + self.assertEqual(il[0], 123) + il[1] = 432.1 + self.assertEqual(len(il), 3) + self.assertEqual(il[1], int(432.1)) + self.assertRaises(TypeError, il.__setitem__, 2, '78') + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/intwrapper_test.py b/sources/shiboken6/tests/samplebinding/intwrapper_test.py new file mode 100644 index 000000000..d883adf47 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/intwrapper_test.py @@ -0,0 +1,39 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import IntWrapper + + +class IntWrapperTest(unittest.TestCase): + + def testOperators(self): + ten1 = IntWrapper(10) + ten2 = IntWrapper(10) + twenty = IntWrapper(20) + self.assertTrue(ten1 == ten2) + self.assertTrue(ten1 != twenty) + self.assertTrue(ten1 + ten2 == twenty) + self.assertTrue(ten1 - ten2 == IntWrapper(0)) + i = IntWrapper(ten1.toInt()) + i += ten2 + self.assertTrue(i == twenty) + i -= ten2 + self.assertTrue(i == ten1) + + def testAddPyMethodDef(self): + """Test of added free function (PYSIDE-1905).""" + i = IntWrapper(10) + self.assertEqual(i.add_ints(10, 20), 30) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/invalid_virtual_return_test.py b/sources/shiboken6/tests/samplebinding/invalid_virtual_return_test.py new file mode 100644 index 000000000..bb35b2bb1 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/invalid_virtual_return_test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test case for returning invalid types in a virtual function''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import ObjectModel, ObjectType, ObjectView + +import warnings + + +class MyObject(ObjectType): + pass + + +class ListModelWrong(ObjectModel): + + def __init__(self, parent=None): + ObjectModel.__init__(self, parent) + self.obj = 0 + + def data(self): + warnings.simplefilter('error') + # Shouldn't segfault. Must set TypeError + return self.obj + + +class ModelWrongReturnTest(unittest.TestCase): + + def testWrongTypeReturn(self): + model = ListModelWrong() + view = ObjectView(model) + self.assertRaises(RuntimeWarning, view.getRawModelData) # calls model.data() + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/keep_reference_test.py b/sources/shiboken6/tests/samplebinding/keep_reference_test.py new file mode 100644 index 000000000..10591fec6 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/keep_reference_test.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectModel, ObjectView + + +class TestKeepReference(unittest.TestCase): + '''Test case for objects that keep references to other object without + owning them (e.g. model/view relationships).''' + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testReferenceCounting(self): + '''Tests reference count of model-like object referred by view-like objects.''' + model1 = ObjectModel() + refcount1 = sys.getrefcount(model1) + view1 = ObjectView() + view1.setModel(model1) + self.assertEqual(sys.getrefcount(view1.model()), refcount1 + 1) + + view2 = ObjectView() + view2.setModel(model1) + self.assertEqual(sys.getrefcount(view2.model()), refcount1 + 2) + + model2 = ObjectModel() + view2.setModel(model2) + self.assertEqual(sys.getrefcount(view1.model()), refcount1 + 1) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testReferenceCountingWhenDeletingReferrer(self): + '''Tests reference count of model-like object referred by deceased view-like object.''' + model = ObjectModel() + refcount1 = sys.getrefcount(model) + view = ObjectView() + view.setModel(model) + self.assertEqual(sys.getrefcount(view.model()), refcount1 + 1) + + del view + self.assertEqual(sys.getrefcount(model), refcount1) + + def testReferreedObjectSurvivalAfterContextEnd(self): + '''Model-like object assigned to a view-like object must survive + after get out of context.''' + def createModelAndSetToView(view): + model = ObjectModel() + model.setObjectName('created model') + view.setModel(model) + view = ObjectView() + createModelAndSetToView(view) + model = view.model() # noqa: F841 + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/list_test.py b/sources/shiboken6/tests/samplebinding/list_test.py new file mode 100644 index 000000000..b668bfd90 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/list_test.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for std::list container conversions''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ListUser, Point, PointF + + +class ExtendedListUser(ListUser): + def __init__(self): + ListUser.__init__(self) + self.create_list_called = False + + def createList(self): + self.create_list_called = True + return [2, 3, 5, 7, 13] + + +class ListConversionTest(unittest.TestCase): + '''Test case for std::list container conversions''' + + def testReimplementedVirtualMethodCall(self): + '''Test if a Python override of a virtual method is correctly called from C++.''' + lu = ExtendedListUser() + lst = lu.callCreateList() + self.assertTrue(lu.create_list_called) + self.assertEqual(type(lst), list) + for item in lst: + self.assertEqual(type(item), int) + + def testPrimitiveConversionInsideContainer(self): + '''Test primitive type conversion inside conversible std::list container.''' + cpx0 = complex(1.2, 3.4) + cpx1 = complex(5.6, 7.8) + lst = ListUser.createComplexList(cpx0, cpx1) + self.assertEqual(type(lst), list) + for item in lst: + self.assertEqual(type(item), complex) + self.assertEqual(lst, [cpx0, cpx1]) + + def testSumListIntegers(self): + '''Test method that sums a list of integer values.''' + lu = ListUser() + lst = [3, 5, 7] + result = lu.sumList(lst) + self.assertEqual(result, sum(lst)) + + def testSumListFloats(self): + '''Test method that sums a list of float values.''' + lu = ListUser() + lst = [3.3, 4.4, 5.5] + result = lu.sumList(lst) + self.assertEqual(result, sum(lst)) + + def testConversionInBothDirections(self): + '''Test converting a list from Python to C++ and back again.''' + lu = ListUser() + lst = [3, 5, 7] + lu.setList(lst) + result = lu.getList() + self.assertEqual(result, lst) + + def testConversionInBothDirectionsWithSimilarContainer(self): + '''Test converting a tuple, instead of the expected list, + from Python to C++ and back again.''' + lu = ListUser() + lst = (3, 5, 7) + lu.setList(lst) + result = lu.getList() + self.assertNotEqual(result, lst) + self.assertEqual(result, list(lst)) + + def testConversionOfListOfObjectsPassedAsArgument(self): + '''Calls method with a Python list of wrapped objects to be converted to a C++ list.''' + mult = 3 + pts0 = (Point(1.0, 2.0), Point(3.3, 4.4), Point(5, 6)) + pts1 = (Point(1.0, 2.0), Point(3.3, 4.4), Point(5, 6)) + ListUser.multiplyPointList(pts1, mult) + for pt0, pt1 in zip(pts0, pts1): + self.assertEqual(pt0.x() * mult, pt1.x()) + self.assertEqual(pt0.y() * mult, pt1.y()) + + def testConversionOfInvalidLists(self): + mult = 3 + pts = (Point(1.0, 2.0), 3, Point(5, 6)) + self.assertRaises(TypeError, ListUser.multiplyPointList, pts, mult) + + def testOverloadMethodReceivingRelatedContainerTypes(self): + self.assertEqual(ListUser.ListOfPointF, ListUser.listOfPoints([PointF()])) + self.assertEqual(ListUser.ListOfPoint, ListUser.listOfPoints([Point()])) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/lock_test.py b/sources/shiboken6/tests/samplebinding/lock_test.py new file mode 100644 index 000000000..acd47634a --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/lock_test.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Simple test with a blocking C++ method that should allow python + threads to run.''' + +import os +import sys +import threading +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Bucket + + +class Unlocker(threading.Thread): + + def __init__(self, bucket): + threading.Thread.__init__(self) + self.bucket = bucket + + def run(self): + while not self.bucket.locked(): + pass + + self.bucket.unlock() + + +class MyBucket(Bucket): + + def __init__(self): + Bucket.__init__(self) + + def virtualBlockerMethod(self): + self.lock() + return True + + +class TestLockUnlock(unittest.TestCase): + + def testBasic(self): + '''Locking in C++ and releasing in a python thread''' + bucket = Bucket() + unlocker = Unlocker(bucket) + + unlocker.start() + bucket.lock() + unlocker.join() + + def testVirtualBlocker(self): + '''Same as the basic case but blocker method is a C++ virtual called from C++.''' + bucket = Bucket() + unlocker = Unlocker(bucket) + + unlocker.start() + result = bucket.callVirtualBlockerMethodButYouDontKnowThis() + unlocker.join() + self.assertTrue(result) + + def testReimplementedVirtualBlocker(self): + '''Same as the basic case but blocker method is a C++ virtual reimplemented + in Python and called from C++.''' + mybucket = MyBucket() + unlocker = Unlocker(mybucket) + + unlocker.start() + result = mybucket.callVirtualBlockerMethodButYouDontKnowThis() + unlocker.join() + self.assertTrue(result) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/map_test.py b/sources/shiboken6/tests/samplebinding/map_test.py new file mode 100644 index 000000000..fa99ad2e7 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/map_test.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for std::map container conversions''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import MapUser + + +class ExtendedMapUser(MapUser): + def __init__(self): + MapUser.__init__(self) + self.create_map_called = False + + def createMap(self): + self.create_map_called = True + return {'two': (complex(2.2, 2.2), 2), + 'three': (complex(3.3, 3.3), 3), + 'five': (complex(5.5, 5.5), 5), + 'seven': (complex(7.7, 7.7), 7)} + + +class MapConversionTest(unittest.TestCase): + '''Test case for std::map container conversions''' + + def testReimplementedVirtualMethodCall(self): + '''Test if a Python override of a virtual method is correctly called from C++.''' + mu = ExtendedMapUser() + map_ = mu.callCreateMap() + self.assertTrue(mu.create_map_called) + self.assertEqual(type(map_), dict) + for key, value in map_.items(): + self.assertEqual(type(key), str) + self.assertEqual(type(value[0]), complex) + self.assertEqual(type(value[1]), int) + + def testConversionInBothDirections(self): + '''Test converting a map from Python to C++ and back again.''' + mu = MapUser() + map_ = {'odds': [2, 4, 6], 'evens': [3, 5, 7], 'primes': [3, 4, 6]} + mu.setMap(map_) + result = mu.getMap() + self.assertEqual(result, map_) + + def testConversionMapIntKeyValueTypeValue(self): + '''C++ signature: MapUser::passMapIntValueType(const std::map<int, const ByteArray>&)''' + mu = MapUser() + map_ = {0: 'string'} + result = mu.passMapIntValueType(map_) + self.assertEqual(map_, result) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/metaclass_test.py b/sources/shiboken6/tests/samplebinding/metaclass_test.py new file mode 100644 index 000000000..4d7eeda96 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/metaclass_test.py @@ -0,0 +1,52 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Point + + +class MetaA(type): + pass + + +class A(object): + __metaclass__ = MetaA + + +MetaB = type(Point) +B = Point + + +class MetaC(MetaA, MetaB): + pass + + +class C(A, B): + __metaclass__ = MetaC + + +class D(C): + pass + + +class TestMetaClass(unittest.TestCase): + def testIt(self): + w1 = C() # works + w1.setX(1) + w1.setY(2) + + w2 = D() # should work! + w2.setX(3) + w2.setY(4) + + w3 = w1 + w2 + self.assertEqual(w3.x(), 4) + self.assertEqual(w3.y(), 6) diff --git a/sources/shiboken6/tests/samplebinding/mi_virtual_methods_test.py b/sources/shiboken6/tests/samplebinding/mi_virtual_methods_test.py new file mode 100644 index 000000000..8d324db59 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/mi_virtual_methods_test.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for virtual methods in multiple inheritance scenarios''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import VirtualMethods, ObjectType, Event + + +class ImplementsNone(ObjectType, VirtualMethods): + '''Implements no virtual methods''' + + def __init__(self): + ObjectType.__init__(self) + VirtualMethods.__init__(self) + + +class ImplementsBoth(ObjectType, VirtualMethods): + '''Implements ObjectType.event and VirtualMethods.sum1''' + + def __init__(self): + ObjectType.__init__(self) + VirtualMethods.__init__(self) + self.event_processed = False + + def event(self, event): + self.event_processed = True + return True + + def sum1(self, arg0, arg1, arg2): + return (arg0 + arg1 + arg2) * 2 + + +class CppVirtualTest(unittest.TestCase): + '''Virtual method defined in c++ called from C++''' + + def testCpp(self): + '''C++ calling C++ virtual method in multiple inheritance scenario''' + obj = ImplementsNone() + self.assertTrue(ObjectType.processEvent([obj], Event(Event.BASIC_EVENT))) + self.assertRaises(AttributeError, getattr, obj, 'event_processed') + + self.assertEqual(obj.callSum0(1, 2, 3), 6) + + +class PyVirtualTest(unittest.TestCase): + '''Virtual method reimplemented in python called from C++''' + + def testEvent(self): + '''C++ calling Python reimplementation of virtual in multiple inheritance''' + obj = ImplementsBoth() + self.assertTrue(ObjectType.processEvent([obj], Event(Event.BASIC_EVENT))) + self.assertTrue(obj.event_processed) + + self.assertEqual(obj.callSum1(1, 2, 3), 12) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/mixed_mi_test.py b/sources/shiboken6/tests/samplebinding/mixed_mi_test.py new file mode 100644 index 000000000..fa8481600 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/mixed_mi_test.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for multiple inheritance in mixed Python/C++ scenarios''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType + + +class Base(object): + '''Base Python class''' + + def __init__(self): + self.name = '' + + def pythonName(self): + return self.name + + def setPythonName(self, name): + self.name = name + + +class Child(Base, ObjectType): + '''Dummy class with mixed parents''' + + def __init__(self): + Base.__init__(self) + ObjectType.__init__(self) + + +class MixedInheritanceTest(unittest.TestCase): + + def testMixed(self): + '''Mixed Python/C++ multiple inheritance''' + obj = Child() + + obj.setObjectName('aaa') + self.assertEqual(obj.objectName(), 'aaa') + + obj.setPythonName('python') + self.assertEqual(obj.pythonName(), 'python') + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/modelindex_test.py b/sources/shiboken6/tests/samplebinding/modelindex_test.py new file mode 100644 index 000000000..e23503eff --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/modelindex_test.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ModelIndex, ReferentModelIndex, PersistentModelIndex + + +class TestCastOperator(unittest.TestCase): + + def testCastOperatorReturningValue(self): + index = PersistentModelIndex() + index.setValue(123) + self.assertEqual(index.value(), 123) + self.assertEqual(index.value(), ModelIndex.getValue(index)) + + def testCastOperatorReturningReference(self): + index = ReferentModelIndex() + index.setValue(123) + self.assertEqual(index.value(), 123) + self.assertEqual(index.value(), ModelIndex.getValue(index)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/modelview_test.py b/sources/shiboken6/tests/samplebinding/modelview_test.py new file mode 100644 index 000000000..b5663a04e --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/modelview_test.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test case for objects that keep references to other object without owning them + (e.g. model/view relationships).''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import ObjectModel, ObjectType, ObjectView + + +object_name = 'test object' + + +class MyObject(ObjectType): + pass + + +class ListModelKeepsReference(ObjectModel): + def __init__(self, parent=None): + ObjectModel.__init__(self, parent) + self.obj = MyObject() + self.obj.setObjectName(object_name) + + def data(self): + return self.obj + + +class ListModelDoesntKeepsReference(ObjectModel): + def data(self): + obj = MyObject() + obj.setObjectName(object_name) + return obj + + +class ModelViewTest(unittest.TestCase): + + def testListModelDoesntKeepsReference(self): + model = ListModelDoesntKeepsReference() + view = ObjectView(model) + obj = view.getRawModelData() + self.assertEqual(type(obj), MyObject) + self.assertEqual(obj.objectName(), object_name) + + def testListModelKeepsReference(self): + model = ListModelKeepsReference() + view = ObjectView(model) + obj = view.getRawModelData() + self.assertEqual(type(obj), MyObject) + self.assertEqual(obj.objectName(), object_name) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/modifications_test.py b/sources/shiboken6/tests/samplebinding/modifications_test.py new file mode 100644 index 000000000..dced14396 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/modifications_test.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for method modifications performed as described on type system. ''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Modifications, Point, ByteArray + + +class ExtModifications(Modifications): + def __init__(self): + Modifications.__init__(self) + self.multiplier = 3.0 + self.increment = 10.0 + + def name(self): + return 'ExtModifications' + + def differenceOfPointCoordinates(self, point): + ok, res = Modifications.differenceOfPointCoordinates(self, point) + return ok, res * self.multiplier + self.increment + + +class ModificationsTest(unittest.TestCase): + '''Test cases for method modifications performed as described on type system. ''' + + def setUp(self): + self.mods = Modifications() + + def tearDown(self): + del self.mods + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + def testRenamedMethodAvailability(self): + '''Test if Modification class really have renamed the 'className' + virtual method to 'name'.''' + self.assertTrue('className' not in dir(Modifications)) + self.assertTrue('name' in dir(Modifications)) + + def testReimplementationOfRenamedVirtualMethod(self): + '''Test if class inheriting from Modification class have the reimplementation + of renamed virtual method called.''' + em = ExtModifications() + self.assertEqual(self.mods.name(), 'Modifications') + self.assertEqual(em.name(), 'ExtModifications') + + def testRegularMethodRenaming(self): + '''Test if Modifications::cppMultiply was correctly renamed to calculateArea.''' + self.assertTrue('cppMultiply' not in dir(Modifications)) + self.assertTrue('calculateArea' in dir(Modifications)) + self.assertEqual(self.mods.calculateArea(3, 6), 3 * 6) + + def testRegularMethodRemoval(self): + '''Test if 'Modifications::exclusiveCppStuff' was removed from Python bindings.''' + self.assertTrue('exclusiveCppStuff' not in dir(Modifications)) + + def testArgumentRemoval(self): + '''Test if second argument of Modifications::doublePlus(int, int) was removed.''' + self.assertRaises(TypeError, self.mods.doublePlus, 3, 7) + self.assertEqual(self.mods.doublePlus(7), 14) + + def testDefaultValueRemoval(self): + '''Test if default value was removed from first argument of + Modifications::increment(int).''' + self.assertRaises(TypeError, self.mods.increment) + self.assertEqual(self.mods.increment(7), 8) + + def testDefaultValueReplacement(self): + '''Test if default values for both arguments of Modifications::power(int, int) + were modified.''' + # original default values: int power(int base = 1, int exponent = 0); + self.assertNotEqual(self.mods.power(4), 1) + # modified default values: int power(int base = 2, int exponent = 1); + self.assertEqual(self.mods.power(), 2) + self.assertEqual(self.mods.power(3), 3) + self.assertEqual(self.mods.power(5, 3), 5**3) + + def testSetNewDefaultValue(self): + '''Test if default value was correctly set to 10 for first argument of + Modifications::timesTen(int).''' + self.assertEqual(self.mods.timesTen(7), 70) + self.assertEqual(self.mods.timesTen(), 100) + + def testArgumentRemovalAndReturnTypeModificationWithTypesystemTemplates1(self): + '''Test modifications to method signature and return value using type + system templates (case 1).''' + result, ok = self.mods.pointToPair(Point(2, 5)) + self.assertEqual(type(ok), bool) + self.assertEqual(type(result), tuple) + self.assertEqual(len(result), 2) + self.assertEqual(type(result[0]), float) + self.assertEqual(type(result[1]), float) + self.assertEqual(result[0], 2.0) + self.assertEqual(result[1], 5.0) + + def testArgumentRemovalAndReturnTypeModificationWithTypesystemTemplates2(self): + '''Test modifications to method signature and return value using + type system templates (case 2).''' + result, ok = self.mods.multiplyPointCoordsPlusValue(Point(2, 5), 4.1) + self.assertEqual(type(ok), bool) + self.assertEqual(type(result), float) + self.assertEqual(result, 14.1) + + def testOverloadedMethodModifications(self): + '''Tests modifications to an overloaded method''' + # overloaded(int, bool[removed], int, double) + self.assertEqual(self.mods.overloaded(1, 2, 3.1), Modifications.Overloaded_ibid) + # overloaded(int, bool, int[removed,default=321], int) + self.assertEqual(self.mods.overloaded(1, True, 2), Modifications.Overloaded_ibii) + # the others weren't modified + self.assertEqual(self.mods.overloaded(1, True, 2, False), Modifications.Overloaded_ibib) + self.assertEqual(self.mods.overloaded(1, False, 2, Point(3, 4)), + Modifications.Overloaded_ibiP) + self.assertRaises(TypeError, self.mods.overloaded, 1, True, Point(2, 3), Point(4, 5)) + self.assertEqual(self.mods.over(1, True, Point(2, 3), Point(4, 5)), + Modifications.Overloaded_ibPP) + + def testPointArrayModification(self): + points = (Point(1, 1), Point(2, 2)) + summedPoint = Point(1, 1) + Point(2, 2) + self.assertEqual(self.mods.sumPointArray(points), summedPoint) + + def testTypeSystemVariableReplacementInFunctionModification(self): + ba = ByteArray('12345') + self.assertEqual(self.mods.getSize(ba), len(ba)) + self.assertEqual(self.mods.getSize(ba, 20), 20) + + def testNoNulPointerTag(self): + point = Point(12, 34) + self.assertEqual(self.mods.sumPointCoordinates(point), 12 + 34) + self.assertRaises(TypeError, self.mods.sumPointCoordinates, None) + + def testNonConversionRuleForArgumentWithDefaultValue(self): + status, obj = self.mods.nonConversionRuleForArgumentWithDefaultValue() + self.assertTrue(status) + self.assertEqual(obj, self.mods.getObject()) + self.assertEqual(obj.objectName(), 'MyObject') + + def testInjectCodeWithConversionVariableForUserPrimitive(self): + self.assertTrue(Modifications.invertBoolean(False)) + self.assertFalse(Modifications.invertBoolean(True)) + + def testConversionRuleForReturnType(self): + x, y = 11, 2 + diff = float(abs(x - y)) + point = Point(x, y) + + ok, res = self.mods.differenceOfPointCoordinates(point) + self.assertTrue(isinstance(ok, bool)) + self.assertTrue(isinstance(res, float)) + self.assertEqual(res, diff) + + ok, res = self.mods.callDifferenceOfPointCoordinates(point) + self.assertTrue(isinstance(ok, bool)) + self.assertTrue(isinstance(res, float)) + self.assertEqual(res, diff) + + ok, res = self.mods.differenceOfPointCoordinates(None) + self.assertTrue(isinstance(ok, bool)) + self.assertTrue(isinstance(res, float)) + self.assertEqual(res, 0.0) + + ok, res = self.mods.callDifferenceOfPointCoordinates(None) + self.assertTrue(isinstance(ok, bool)) + self.assertTrue(isinstance(res, float)) + self.assertEqual(res, 0.0) + + def testConversionRuleForReturnTypeOnExtendedClass(self): + x, y = 11, 2 + diff = float(abs(x - y)) + point = Point(x, y) + em = ExtModifications() + + ok, res = em.differenceOfPointCoordinates(point) + self.assertTrue(isinstance(ok, bool)) + self.assertTrue(isinstance(res, float)) + self.assertEqual(res, diff * em.multiplier + em.increment) + + ok, res = em.callDifferenceOfPointCoordinates(point) + self.assertTrue(isinstance(ok, bool)) + self.assertTrue(isinstance(res, float)) + self.assertEqual(res, diff * em.multiplier + em.increment) + + ok, res = em.differenceOfPointCoordinates(None) + self.assertTrue(isinstance(ok, bool)) + self.assertTrue(isinstance(res, float)) + self.assertEqual(res, em.increment) + + ok, res = em.callDifferenceOfPointCoordinates(None) + self.assertTrue(isinstance(ok, bool)) + self.assertTrue(isinstance(res, float)) + self.assertEqual(res, em.increment) + + def testDefaultValueModifications(self): + # PSYIDE-1095: setEnumValue() has the default value modified to + # calling defaultEnumValue() which returns Modifications.TestEnumValue2. + # This used to generated broken code since defaultEnumValue() was + # qualified by the enum scope. + modifications = Modifications() + modifications.setEnumValue() + self.assertEqual(modifications.enumValue(), Modifications.TestEnumValue2) + + def testSetGetAttro(self): + modifications = Modifications() + self.assertFalse(modifications.wasSetAttroCalled()) + setattr(modifications, 'Foo', 'Bar') + self.assertTrue(modifications.wasSetAttroCalled()) + self.assertEqual(getattr(modifications, 'Foo'), 'Bar') + self.assertTrue(modifications.wasGetAttroCalled()) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/modified_constructor_test.py b/sources/shiboken6/tests/samplebinding/modified_constructor_test.py new file mode 100644 index 000000000..9791a3491 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/modified_constructor_test.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Tests cases for ConstructorWithModifiedArgument class.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ModifiedConstructor + + +class ConstructorWithModifiedArgumentTest(unittest.TestCase): + '''Test cases for ConstructorWithModifiedArgument class.''' + + def testConstructorWithModifiedArgument(self): + sampleClass = ModifiedConstructor("10") + self.assertTrue(sampleClass.retrieveValue(), 10) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/modifiedvirtualmethods_test.py b/sources/shiboken6/tests/samplebinding/modifiedvirtualmethods_test.py new file mode 100644 index 000000000..dcb487f1a --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/modifiedvirtualmethods_test.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for modified virtual methods.''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import VirtualMethods, Str + + +class ExtendedVirtualMethods(VirtualMethods): + def __init__(self): + VirtualMethods.__init__(self) + self.name_called = False + self.sum0_called = False + self.sum1_called = False + self.sum2_called = False + self.sum3_called = False + self.sum4_called = False + self.sumThree_called = False + self.callMe_called = 0 + self.multiplier = 12345 + + def sum0(self, a0, a1, a2): + self.sum0_called = True + return VirtualMethods.sumThree(self, a0, a1, a2) * self.multiplier + + def sumThree(self, a0, a1, a2): + self.sumThree_called = True + return VirtualMethods.sumThree(self, a0, a1, a2) * self.multiplier + + def sum1(self, a0, a1, a2): + self.sum1_called = True + return VirtualMethods.sum1(self, a0, a1, a2) * self.multiplier + + def sum2(self, a0, a1): + self.sum2_called = True + return VirtualMethods.sum2(self, a0, a1) * self.multiplier + + def sum3(self, a0, a1): + self.sum3_called = True + return VirtualMethods.sum3(self, a0, a1) * self.multiplier + + def sum4(self, a0, a1): + self.sum4_called = True + return VirtualMethods.sum4(self, a0, a1) * self.multiplier + + def name(self): + self.name_called = True + return Str('ExtendedVirtualMethods') + + def callMe(self): + self.callMe_called += 1 + + def getMargins(self): + return tuple([m * 2 for m in VirtualMethods.getMargins(self)]) + + +class VirtualMethodsTest(unittest.TestCase): + '''Test case for modified virtual methods.''' + + def setUp(self): + self.vm = VirtualMethods() + self.evm = ExtendedVirtualMethods() + + def tearDown(self): + del self.vm + del self.evm + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + def testModifiedVirtualMethod0(self): + '''Renamed virtual method.''' + a0, a1, a2 = 2, 3, 5 + result0 = self.vm.callSum0(a0, a1, a2) + result1 = self.vm.sumThree(a0, a1, a2) + self.assertEqual(result0, a0 + a1 + a2) + self.assertEqual(result0, result1) + self.assertRaises(AttributeError, getattr, self.vm, 'sum0') + + def testReimplementedModifiedVirtualMethod0(self): + '''Override of a renamed virtual method.''' + a0, a1, a2 = 2, 3, 5 + result0 = self.vm.callSum0(a0, a1, a2) + result1 = self.vm.sumThree(a0, a1, a2) + result2 = self.evm.callSum0(a0, a1, a2) + self.assertEqual(result0, result1) + self.assertEqual(result0 * self.evm.multiplier, result2) + self.assertTrue(self.evm.sumThree_called) + self.assertFalse(self.evm.sum0_called) + + def testModifiedVirtualMethod1(self): + '''Virtual method with three arguments and the last one + changed to have the default value set to 1000.''' + a0, a1, a2 = 2, 3, 5 + result0 = self.vm.sum1(a0, a1) + self.assertEqual(result0, a0 + a1 + 1000) + result1 = self.vm.sum1(a0, a1, a2) + result2 = self.vm.callSum1(a0, a1, a2) + self.assertEqual(result1, result2) + + def testReimplementedModifiedVirtualMethod1(self): + '''Override of the virtual method with three arguments and + the last one changed to have the default value set to 1000.''' + a0, a1 = 2, 3 + result0 = self.vm.sum1(a0, a1) + result1 = self.evm.callSum1(a0, a1, 1000) + self.assertEqual(result0 * self.evm.multiplier, result1) + self.assertTrue(self.evm.sum1_called) + + def testModifiedVirtualMethod2(self): + '''Virtual method originally with three arguments, the last + one was removed and the default value set to 2000.''' + a0, a1 = 1, 2 + result0 = self.vm.sum2(a0, a1) + self.assertEqual(result0, a0 + a1 + 2000) + result1 = self.vm.sum2(a0, a1) + result2 = self.vm.callSum2(a0, a1, 2000) + self.assertEqual(result1, result2) + self.assertRaises(TypeError, self.vm.sum2, 1, 2, 3) + + def testReimplementedModifiedVirtualMethod2(self): + '''Override of the virtual method originally with three arguments, + the last one was removed and the default value set to 2000.''' + a0, a1 = 1, 2 + ignored = 54321 + result0 = self.vm.sum2(a0, a1) + result1 = self.evm.callSum2(a0, a1, ignored) + self.assertEqual(result0 * self.evm.multiplier, result1) + self.assertTrue(self.evm.sum2_called) + + def testModifiedVirtualMethod3(self): + '''Virtual method originally with three arguments have the second + one removed and replaced by custom code that replaces it by the sum + of the first and the last arguments.''' + a0, a1 = 1, 2 + result0 = self.vm.sum3(a0, a1) + self.assertEqual(result0, a0 + (a0 + a1) + a1) + result1 = self.vm.callSum3(a0, 10, a1) + self.assertNotEqual(result0, result1) + result2 = self.vm.callSum3(a0, a0 + a1, a1) + self.assertEqual(result0, result2) + self.assertRaises(TypeError, self.vm.sum3, 1, 2, 3) + + def testReimplementedModifiedVirtualMethod3(self): + '''Override of the virtual method originally with three arguments + have the second one removed and replaced by custom code that + replaces it by the sum of the first and the last arguments.''' + a0, a1 = 1, 2 + ignored = 54321 + result0 = self.vm.sum3(a0, a1) + result1 = self.evm.callSum3(a0, ignored, a1) + self.assertEqual(result0 * self.evm.multiplier, result1) + self.assertTrue(self.evm.sum3_called) + + def testModifiedVirtualMethod4(self): + '''Virtual method originally with three arguments, the + last one was removed and the default value set to 3000.''' + a0, a1 = 1, 2 + default_value = 3000 + result0 = self.vm.sum4(a0, a1) + self.assertEqual(result0, a0 + default_value + a1) + removed_arg_value = 100 + result1 = self.vm.callSum4(a0, removed_arg_value, a1) + self.assertEqual(result1, a0 + removed_arg_value + a1) + self.assertRaises(TypeError, self.vm.sum4, 1, 2, 3) + + def testReimplementedModifiedVirtualMethod4(self): + '''Override of the virtual method originally with three arguments, + the last one was removed and the default value set to 3000. + The method was modified with code injection on the binding override + (the one that receives calls from C++ with the original signature + and forwards it to Python overrides) that subtracts the value of the + second argument (removed in Python) from the value of the first + before sending them to Python.''' + a0, a1 = 1, 2 + removed_arg_value = 2011 + default_value = 3000 + result = self.evm.callSum4(a0, removed_arg_value, a1) + self.assertEqual(result, + (a0 - removed_arg_value + a1 + default_value) * self.evm.multiplier) + self.assertTrue(self.evm.sum4_called) + + def testOverridenMethodResultModification(self): + '''Injected code modifies the result of a call to a virtual + method overridden in Python.''' + orig_name = self.vm.callName() + self.assertEqual(orig_name, 'VirtualMethods') + name = self.evm.callName() + self.assertEqual(name, 'PimpedExtendedVirtualMethods') + self.assertEqual(name, Str('PimpedExtendedVirtualMethods')) + self.assertTrue(self.evm.name_called) + + def testInjectCodeCallsPythonVirtualMethodOverride(self): + '''When injected code calls the Python override by itself + no code for the method call should be generated.''' + self.evm.callCallMe() + self.assertEqual(self.evm.callMe_called, 1) + + def testAllArgumentsRemoved(self): + values = (10, 20, 30, 40) + self.vm.setMargins(*values) + self.assertEqual(self.vm.getMargins(), values) + + def testAllArgumentsRemovedCallVirtual(self): + values = (10, 20, 30, 40) + self.vm.setMargins(*values) + self.assertEqual(self.vm.callGetMargins(), values) + + def testExtendedAllArgumentsRemoved(self): + values = (10, 20, 30, 40) + self.evm.setMargins(*values) + double = tuple([m * 2 for m in values]) + self.assertEqual(self.evm.getMargins(), double) + + def testExtendedAllArgumentsRemovedCallVirtual(self): + values = (10, 20, 30, 40) + self.evm.setMargins(*values) + double = tuple([m * 2 for m in values]) + self.assertEqual(self.evm.callGetMargins(), double) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/multi_cpp_inheritance_test.py b/sources/shiboken6/tests/samplebinding/multi_cpp_inheritance_test.py new file mode 100644 index 000000000..fc6b26c3f --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/multi_cpp_inheritance_test.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for multiple inheritance''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType, Point, Str + + +class SimpleUseCase(ObjectType, Str): + def __init__(self, name): + ObjectType.__init__(self) + Str.__init__(self, name) + + +class SimpleUseCaseReverse(Str, ObjectType): + def __init__(self, name): + ObjectType.__init__(self) + Str.__init__(self, name) + + +class SimpleUseCase2(SimpleUseCase): + def __init__(self, name): + SimpleUseCase.__init__(self, name) + + +class ComplexUseCase(SimpleUseCase2, Point): + def __init__(self, name): + SimpleUseCase2.__init__(self, name) + Point.__init__(self) + + +class ComplexUseCaseReverse(Point, SimpleUseCase2): + def __init__(self, name): + SimpleUseCase2.__init__(self, name) + Point.__init__(self) + + +class MultipleCppDerivedTest(unittest.TestCase): + def testInstantiation(self): + s = SimpleUseCase("Hi") + self.assertEqual(s, "Hi") + s.setObjectName(s) + self.assertEqual(s.objectName(), "Hi") + + def testInstantiation2(self): + s = SimpleUseCase2("Hi") + self.assertEqual(s, "Hi") + s.setObjectName(s) + self.assertEqual(s.objectName(), "Hi") + + def testComplexInstantiation(self): + c = ComplexUseCase("Hi") + self.assertEqual(c, "Hi") + c.setObjectName(c) + self.assertEqual(c.objectName(), "Hi") + c.setX(2) + self.assertEqual(c.x(), 2) + + +class MultipleCppDerivedReverseTest(unittest.TestCase): + def testInstantiation(self): + s = SimpleUseCaseReverse("Hi") + self.assertEqual(s, "Hi") + s.setObjectName(s) + self.assertEqual(s.objectName(), "Hi") + + def testInstantiation2(self): + s = SimpleUseCase2("Hi") + self.assertEqual(s, "Hi") + s.setObjectName(s) + self.assertEqual(s.objectName(), "Hi") + + def testComplexInstantiation(self): + # PYSIDE-1564: This test can no longer work because of this MRO: + # ('ComplexUseCaseReverse', 'Point', 'SimpleUseCase2', 'SimpleUseCase', + # 'ObjectType', 'Str', 'Object', 'object') + # By multiple inheritance Point would be called first but has no argument. + with self.assertRaises(TypeError): + c = ComplexUseCaseReverse("Hi") # noqa: F841 + # c.setObjectName(c) + # self.assertEqual(c.objectName(), "Hi") + # c.setX(2); + # self.assertEqual(c, Point(2, 0)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/multiple_derived_test.py b/sources/shiboken6/tests/samplebinding/multiple_derived_test.py new file mode 100644 index 000000000..7497714a8 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/multiple_derived_test.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for multiple inheritance''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Base1, Base2 +from sample import MDerived1, MDerived2, MDerived3, MDerived4, MDerived5, SonOfMDerived1 + + +class ExtMDerived1(MDerived1): + def __init__(self): + MDerived1.__init__(self) + self.multiplier = 20 + self.base2Method_called = False + + def base2Method(self): + return Base2.base2Method(self) * self.multiplier + + +class MultipleDerivedTest(unittest.TestCase): + '''Test cases for multiple inheritance''' + + def testIsInstance(self): + '''MDerived1 is instance of its parents Base1 and Base2.''' + a = MDerived1() + self.assertTrue(isinstance(a, MDerived1)) + self.assertTrue(isinstance(a, Base1)) + self.assertTrue(isinstance(a, Base2)) + + def testIsSubclass(self): + '''MDerived1 is subclass of its parents Base1 and Base2.''' + self.assertTrue(issubclass(MDerived1, Base1)) + self.assertTrue(issubclass(MDerived1, Base2)) + + def testCallToFunctionWithBase1ArgumentThatCastsBackToMDerived1(self): + '''MDerived1 is passed as an Base1 argument to a method that returns + it casted back to MDerived1.''' + a = MDerived1() + b = MDerived1.transformFromBase1(a) + self.assertEqual(a, b) + + def testCallToFunctionWithBase2ArgumentThatCastsBackToMDerived1(self): + '''MDerived1 is passed as an Base2 argument to a method that returns + it casted back to MDerived1.''' + a = MDerived1() + b = MDerived1.transformFromBase2(a) + self.assertEqual(a, b) + + def testPythonClassIsInstance(self): + '''Python defined class ExtMDerived1 is instance of its parents + MDerived1, Base1 and Base2.''' + a = ExtMDerived1() + self.assertTrue(isinstance(a, ExtMDerived1)) + self.assertTrue(isinstance(a, MDerived1)) + self.assertTrue(isinstance(a, Base1)) + self.assertTrue(isinstance(a, Base2)) + + def testPythonClassIsSubclass(self): + '''Python defined class ExtMDerived1 is subclass of its parents + MDerived1, Base1 and Base2.''' + self.assertTrue(issubclass(ExtMDerived1, MDerived1)) + self.assertTrue(issubclass(ExtMDerived1, Base1)) + self.assertTrue(issubclass(ExtMDerived1, Base2)) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testCastFromMDerived1ToBases(self): + '''MDerived1 is casted by C++ to its parents and the binding must return the + MDerived1 wrapper.''' + a = MDerived1() + refcnt = sys.getrefcount(a) + b1 = a.castToBase1() + b2 = a.castToBase2() + self.assertTrue(isinstance(b1, MDerived1)) + self.assertTrue(isinstance(b2, MDerived1)) + self.assertEqual(a, b1) + self.assertEqual(a, b2) + self.assertEqual(sys.getrefcount(a), refcnt + 2) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testCastFromExtMDerived1ToMDerived1Bases(self): + '''Python defined class ExtMDerived1 is casted by C++ to MDerived1 parents + and the binding must return the correct ExtMDerived1 instance.''' + a = ExtMDerived1() + refcnt = sys.getrefcount(a) + b1 = a.castToBase1() + self.assertTrue(isinstance(b1, MDerived1)) + self.assertTrue(isinstance(b1, ExtMDerived1)) + b2 = a.castToBase2() + self.assertTrue(isinstance(b2, MDerived1)) + self.assertTrue(isinstance(b2, ExtMDerived1)) + self.assertEqual(a, b1) + self.assertEqual(a, b2) + self.assertEqual(sys.getrefcount(a), refcnt + 2) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testCastFromSonOfMDerived1ToBases(self): + '''SonOfMDerived1 is casted by C++ to its parents and the binding must return + the SonOfMDerived1 wrapper.''' + a = SonOfMDerived1() + refcnt = sys.getrefcount(a) + md1 = a.castToMDerived1() + b1 = a.castToBase1() + b2 = a.castToBase2() + self.assertTrue(isinstance(md1, SonOfMDerived1)) + self.assertTrue(isinstance(b2, SonOfMDerived1)) + self.assertTrue(isinstance(b2, SonOfMDerived1)) + self.assertEqual(a, md1) + self.assertEqual(a, b1) + self.assertEqual(a, b2) + self.assertEqual(sys.getrefcount(a), refcnt + 3) + + def testReimplementedBase2VirtualMethodOnClassInheritingFromMDerived1(self): + a = ExtMDerived1() + value = a.base2Method() + self.assertTrue(value, Base2.base2Method(a) * a.multiplier) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testCastFromMDerived2ToBases(self): + '''MDerived2 is casted by C++ to its parents and the binding must + return the MDerived2 wrapper.''' + a = MDerived2() + refcnt = sys.getrefcount(a) + b3 = a.castToBase3() + b4 = a.castToBase4() + b5 = a.castToBase5() + b6 = a.castToBase6() + self.assertTrue(isinstance(b3, MDerived2)) + self.assertTrue(isinstance(b4, MDerived2)) + self.assertTrue(isinstance(b5, MDerived2)) + self.assertTrue(isinstance(b6, MDerived2)) + self.assertEqual(a, b3) + self.assertEqual(a, b4) + self.assertEqual(a, b5) + self.assertEqual(a, b6) + self.assertEqual(sys.getrefcount(a), refcnt + 4) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testCastFromMDerived3ToBases(self): + '''MDerived3 is casted by C++ to its parents and the binding must + return the MDerived3 wrapper.''' + a = MDerived3() + refcnt = sys.getrefcount(a) + md1 = a.castToMDerived1() + md2 = a.castToMDerived2() + b1 = a.castToBase1() + b2 = a.castToBase2() + b3 = a.castToBase3() + b4 = a.castToBase4() + b5 = a.castToBase5() + b6 = a.castToBase6() + self.assertTrue(isinstance(md1, MDerived3)) + self.assertTrue(isinstance(md2, MDerived3)) + self.assertTrue(isinstance(b1, MDerived3)) + self.assertTrue(isinstance(b2, MDerived3)) + self.assertTrue(isinstance(b3, MDerived3)) + self.assertTrue(isinstance(b4, MDerived3)) + self.assertTrue(isinstance(b5, MDerived3)) + self.assertTrue(isinstance(b6, MDerived3)) + self.assertEqual(a, md1) + self.assertEqual(a, md2) + self.assertEqual(a, b1) + self.assertEqual(a, b2) + self.assertEqual(a, b3) + self.assertEqual(a, b4) + self.assertEqual(a, b5) + self.assertEqual(a, b6) + self.assertEqual(sys.getrefcount(a), refcnt + 8) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testCastFromMDerived4ToBases(self): + '''MDerived4 is casted by C++ to its parents and the binding must + return the MDerived4 wrapper.''' + a = MDerived4() + refcnt = sys.getrefcount(a) + b3 = a.castToBase3() + b4 = a.castToBase4() + self.assertTrue(isinstance(b3, MDerived4)) + self.assertTrue(isinstance(b4, MDerived4)) + self.assertEqual(a, b3) + self.assertEqual(a, b4) + self.assertEqual(sys.getrefcount(a), refcnt + 2) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testCastFromMDerived5ToBases(self): + '''MDerived5 is casted by C++ to its parents and the binding must + return the MDerived5 wrapper.''' + a = MDerived5() + refcnt = sys.getrefcount(a) + b3 = a.castToBase3() + b4 = a.castToBase4() + self.assertTrue(isinstance(b3, MDerived5)) + self.assertTrue(isinstance(b4, MDerived5)) + self.assertEqual(a, b3) + self.assertEqual(a, b4) + self.assertEqual(sys.getrefcount(a), refcnt + 2) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testCastFromMDerived3ToBase3(self): + '''MDerived3 is casted by C++ to Base3 grandparent using both the inherited + and reimplement castToBase3 methods.''' + a = MDerived3() + refcnt = sys.getrefcount(a) + b3_reimplemented = a.castToBase3() + b3_inherited = MDerived2.castToBase3(a) + self.assertTrue(isinstance(b3_reimplemented, MDerived3)) + self.assertTrue(isinstance(b3_inherited, MDerived3)) + self.assertEqual(a, b3_reimplemented) + self.assertEqual(a, b3_inherited) + self.assertEqual(sys.getrefcount(a), refcnt + 2) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/namespace_test.py b/sources/shiboken6/tests/samplebinding/namespace_test.py new file mode 100644 index 000000000..64a6792ac --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/namespace_test.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for std::map container conversions''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import SampleNamespace +from shiboken_test_helper import objectFullname + +from shibokensupport.signature import get_signature + +# For tests of invisible namespaces, see +# enumfromremovednamespace_test.py / removednamespaces.h + + +class TestVariablesUnderNamespace(unittest.TestCase): + def testIt(self): + self.assertEqual(SampleNamespace.variableInNamespace, 42) + + +class TestClassesUnderNamespace(unittest.TestCase): + def testIt(self): + c1 = SampleNamespace.SomeClass() # noqa F841 + e1 = SampleNamespace.SomeClass.ProtectedEnum() # noqa F841 + c2 = SampleNamespace.SomeClass.SomeInnerClass() # noqa F841 + e2 = SampleNamespace.SomeClass.SomeInnerClass.ProtectedEnum() # noqa F841 + c3 = SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough() # noqa F841 + e3 = SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough.NiceEnum(0) # noqa F841 + + def testFunctionAddedOnNamespace(self): + res = SampleNamespace.ImInsideANamespace(2, 2) + self.assertEqual(res, 4) + + def testTpNames(self): + self.assertEqual(str(SampleNamespace.SomeClass), + "<class 'sample.SampleNamespace.SomeClass'>") + self.assertEqual(str(SampleNamespace.SomeClass.ProtectedEnum), + "<enum 'ProtectedEnum'>") + self.assertEqual(str(SampleNamespace.SomeClass.SomeInnerClass.ProtectedEnum), + "<enum 'ProtectedEnum'>") + self.assertEqual(str(SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough), + "<class 'sample.SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough'>") # noqa: E501 + self.assertEqual(str(SampleNamespace.SomeClass.SomeInnerClass.OkThisIsRecursiveEnough.NiceEnum), # noqa: E501 + "<enum 'NiceEnum'>") + + # Test if enum inside of class is correct represented + an = objectFullname(get_signature(SampleNamespace.enumInEnumOut).parameters['in_'].annotation) # noqa: E501 + self.assertEqual(an, "sample.SampleNamespace.InValue") + an = objectFullname(get_signature(SampleNamespace.enumAsInt).parameters['value'].annotation) + self.assertEqual(an, "sample.SampleNamespace.SomeClass.PublicScopedEnum") + + def testInlineNamespaces(self): + cls = SampleNamespace.ClassWithinInlineNamespace() + cls.setValue(SampleNamespace.EWIN_Value1) + self.assertEqual(cls.value(), SampleNamespace.EWIN_Value1) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/newdivision_test.py b/sources/shiboken6/tests/samplebinding/newdivision_test.py new file mode 100644 index 000000000..0e7dfbee1 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/newdivision_test.py @@ -0,0 +1,25 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Point + + +class TestNewDivision(unittest.TestCase): + + def testIt(self): + p = Point(4, 4) + p2 = p / 2 + self.assertEqual(p2, Point(2, 2)) + + +if __name__ == "__main__": + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/nondefaultctor_test.py b/sources/shiboken6/tests/samplebinding/nondefaultctor_test.py new file mode 100644 index 000000000..bc8d29e50 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/nondefaultctor_test.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for ...''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import NonDefaultCtor + + +class DerivedNonDefaultCtor (NonDefaultCtor): + def returnMyselfVirtual(self): + return NonDefaultCtor(self.value() + 1) + + +class AnotherDerivedNonDefaultCtor (NonDefaultCtor): + def __init__(self, some_string): + pass + + +class NonDefaultCtorTest(unittest.TestCase): + + def testNonDefaultCtor(self): + c = NonDefaultCtor(2) + # these functions returns NonDefaultCtor by value, so a PyObjecy is created every time + self.assertNotEqual(c.returnMyself(), c) + self.assertEqual(c.returnMyself().value(), 2) + self.assertNotEqual(c.returnMyself(3), c) + self.assertEqual(c.returnMyself(3).value(), 2) + self.assertNotEqual(c.returnMyself(4, c), c) + self.assertEqual(c.returnMyself(4, c).value(), 2) + + def testVirtuals(self): + c = DerivedNonDefaultCtor(3) + # these functions returns NonDefaultCtor by value, so a PyObjecy is created every time + self.assertNotEqual(c.returnMyselfVirtual(), c) + self.assertEqual(c.returnMyselfVirtual().value(), 4) + self.assertEqual(c.callReturnMyselfVirtual().value(), 4) + + def testCtorOverload(self): + c = AnotherDerivedNonDefaultCtor("testing") # noqa: F841 + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/nontypetemplate_test.py b/sources/shiboken6/tests/samplebinding/nontypetemplate_test.py new file mode 100644 index 000000000..a10547728 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/nontypetemplate_test.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +hasNumPy = False + +try: + import numpy + hasNumPy = True +except ImportError: + pass + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import IntArray2, IntArray3 + + +class NonTypeTemplateTest(unittest.TestCase): + + def testNonTypeTemplate(self): + array2 = IntArray2(3) + self.assertEqual(array2.sum(), 6) + array3 = IntArray3(5) + self.assertEqual(array3.sum(), 15) + + def testArrayInitializer(self): + if not hasNumPy: + return + array3 = IntArray3(numpy.array([1, 2, 3], dtype='int32')) + self.assertEqual(array3.sum(), 6) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/nonzero_test.py b/sources/shiboken6/tests/samplebinding/nonzero_test.py new file mode 100644 index 000000000..7be239fc4 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/nonzero_test.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Color, Brush + + +class TestNonZeroOperator(unittest.TestCase): + def testColor(self): + """Color has a Qt-style isNull()""" + c = Color() + self.assertFalse(c) + c = Color(2) + self.assertTrue(c) + + def testBrush(self): + """Brush enables its operator bool in the typesystem XML""" + b = Brush() + self.assertFalse(b) + b = Brush(Color(2)) + self.assertTrue(b) + + +if __name__ == "__main__": + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/numericaltypedef_test.py b/sources/shiboken6/tests/samplebinding/numericaltypedef_test.py new file mode 100644 index 000000000..f714a4fc8 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/numericaltypedef_test.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import SizeF + + +class NumericalTypedefTest(unittest.TestCase): + + def testNumericalTypedefExact(self): + width, height = (1.1, 2.2) + size = SizeF(width, height) + self.assertEqual(size.width(), width) + self.assertEqual(size.height(), height) + + def testNumericalTypedefConvertible(self): + width, height = (1, 2) + size = SizeF(width, height) + self.assertEqual(size.width(), float(width)) + self.assertEqual(size.height(), float(height)) + + def testNumericalTypedefOfUnsignedShort(self): + self.assertEqual(SizeF.passTypedefOfUnsignedShort(123), 123) + self.assertEqual(SizeF.passTypedefOfUnsignedShort(321), 321) + self.assertNotEqual(SizeF.passTypedefOfUnsignedShort(123), 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/numpy_test.py b/sources/shiboken6/tests/samplebinding/numpy_test.py new file mode 100644 index 000000000..42094a463 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/numpy_test.py @@ -0,0 +1,41 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import sys + +try: + import sysconfig + if bool(sysconfig.get_config_var('Py_DEBUG')): + sys.exit(0) + import numpy +except: # noqa: E722 + sys.exit(0) + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import PointF + + +class TestNumpyTypes(unittest.TestCase): + + def testNumpyConverted(self): + x, y = (0.1, 0.2) + p = PointF(float(numpy.float32(x)), float(numpy.float32(y))) + self.assertAlmostEqual(p.x(), x) + self.assertAlmostEqual(p.y(), y) + + def testNumpyAsIs(self): + x, y = (0.1, 0.2) + p = PointF(numpy.float32(x), numpy.float32(y)) + self.assertAlmostEqual(p.x(), x) + self.assertAlmostEqual(p.y(), y) + + +if __name__ == "__main__": + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/objecttype_test.py b/sources/shiboken6/tests/samplebinding/objecttype_test.py new file mode 100644 index 000000000..ead68ba13 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/objecttype_test.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Tests ObjectType class of object-type with privates copy constructor and = operator.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +import sys + +from sample import ObjectType, Str +from shiboken6 import Shiboken + + +class ObjectTypeTest(unittest.TestCase): + '''Test cases ObjectType class of object-type with privates copy constructor and = operator.''' + + def testObjectTypeSetObjectNameWithStrVariable(self): + '''ObjectType.setObjectName with Str variable as argument.''' + s = Str('object name') + o = ObjectType() + o.setObjectName(s) + self.assertEqual(str(o.objectName()), str(s)) + + def testObjectTypeSetObjectNameWithStrInstantiation(self): + '''ObjectType.setObjectName with Str instantiation as argument.''' + s = 'object name' + o = ObjectType() + o.setObjectName(Str(s)) + self.assertEqual(str(o.objectName()), s) + + def testObjectTypeSetObjectNameWithPythonString(self): + '''ObjectType.setObjectName with Python string as argument.''' + o = ObjectType() + o.setObjectName('object name') + self.assertEqual(str(o.objectName()), 'object name') + + def testNullOverload(self): + o = ObjectType() + o.setObject(None) + self.assertEqual(o.callId(), 0) + o.setNullObject(None) + self.assertEqual(o.callId(), 1) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testParentFromCpp(self): + o = ObjectType() + self.assertEqual(sys.getrefcount(o), 2) + o.getCppParent().setObjectName('parent') + self.assertEqual(sys.getrefcount(o), 3) + o.getCppParent().setObjectName('parent') + self.assertEqual(sys.getrefcount(o), 3) + o.getCppParent().setObjectName('parent') + self.assertEqual(sys.getrefcount(o), 3) + o.getCppParent().setObjectName('parent') + self.assertEqual(sys.getrefcount(o), 3) + o.getCppParent().setObjectName('parent') + self.assertEqual(sys.getrefcount(o), 3) + o.destroyCppParent() + self.assertEqual(sys.getrefcount(o), 2) + + def testNextInFocusChainCycle(self): + parent = ObjectType() + child = ObjectType(parent) + next_focus = child.nextInFocusChain() # noqa: F841 + + Shiboken.invalidate(parent) + + def testNextInFocusChainCycleList(self): + '''As above but in for a list of objects''' + parents = [] + children = [] + focus_chains = [] + for i in range(10): + parent = ObjectType() + child = ObjectType(parent) + next_focus = child.nextInFocusChain() + parents.append(parent) + children.append(child) + focus_chains.append(next_focus) + + Shiboken.invalidate(parents) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testClassDecref(self): + # Bug was that class PyTypeObject wasn't decrefed when instance died + before = sys.getrefcount(ObjectType) + + for i in range(1000): + obj = ObjectType() + Shiboken.delete(obj) + + after = sys.getrefcount(ObjectType) + + self.assertLess(abs(before - after), 5) + + def testInvalidProperty(self): + o = ObjectType() + with self.assertRaises(AttributeError): + o.typo + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/objecttype_with_named_args_test.py b/sources/shiboken6/tests/samplebinding/objecttype_with_named_args_test.py new file mode 100644 index 000000000..285e2313b --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/objecttype_with_named_args_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType + + +class NamedArgsTest(unittest.TestCase): + + def testOneArgument(self): + p = ObjectType() + o = ObjectType(parent=p) + self.assertEqual(o.parent(), p) + + def testMoreArguments(self): + o = ObjectType() + + o.setObjectSplittedName("", prefix="pys", suffix="ide") + self.assertEqual(o.objectName(), "pyside") + + o.setObjectSplittedName("", suffix="ide", prefix="pys") + self.assertEqual(o.objectName(), "pyside") + + o.setObjectNameWithSize(name="pyside", size=6) + self.assertEqual(o.objectName(), "pyside") + + o.setObjectNameWithSize(size=6, name="pyside") + self.assertEqual(o.objectName(), "pyside") + + def testUseDefaultValues(self): + o = ObjectType() + + o.setObjectNameWithSize(size=3) + self.assertEqual(o.objectName(), "<un") # use name='unknown' default argument + + o.setObjectSplittedName("") + self.assertEqual(o.objectName(), "<unknown>") # user prefix='<unk' and suffix='nown>' + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/objecttypebyvalue_test.py b/sources/shiboken6/tests/samplebinding/objecttypebyvalue_test.py new file mode 100644 index 000000000..8f74af3ab --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/objecttypebyvalue_test.py @@ -0,0 +1,27 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectTypeByValue + + +class ObjectTypeByValueTest (unittest.TestCase): + def testIt(self): + factory = ObjectTypeByValue() + obj = factory.returnSomeKindOfMe() + # This should crash! + obj.prop.protectedValueTypeProperty.setX(1.0) + # just to make sure it will segfault + obj.prop.protectedValueTypeProperty.setY(2.0) + + +if __name__ == "__main__": + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/objecttypelayout_test.py b/sources/shiboken6/tests/samplebinding/objecttypelayout_test.py new file mode 100644 index 000000000..677b89281 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/objecttypelayout_test.py @@ -0,0 +1,301 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Tests cases for ObjectTypeLayout class.''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType, ObjectTypeLayout + + +class ObjectTypeLayoutTest(unittest.TestCase): + '''Test cases for ObjectTypeLayout class.''' + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testOwnershipOverride(self): + lt = ObjectTypeLayout() + + o1 = ObjectType(lt) + o1.setObjectName('o1') + + self.assertEqual(sys.getrefcount(o1), 3) + lt.takeChild('o1') + self.assertEqual(sys.getrefcount(o1), 2) + + def testSetNullLayout(self): + '''ObjectType.setLayout(0).''' + o2 = ObjectType() + o2.setLayout(None) + + def testSetNullLayoutToObjectTypeCreatedInCpp(self): + '''ObjectType.setLayout(0) to object created in C++.''' + o1 = ObjectType.create() + o1.setLayout(None) + + def testObjectTypeLayout(self): + '''ObjectType.setLayout.''' + p1 = ObjectType() + c1 = ObjectType() + c2 = ObjectType() + c3 = ObjectType() + layout = ObjectTypeLayout() + layout.addObject(c1) + layout.addObject(c2) + layout.addObject(c3) + self.assertEqual(c1.parent(), None) + self.assertEqual(c2.parent(), None) + self.assertEqual(c3.parent(), None) + + p1.setLayout(layout) + del p1 # This must kill c1, c2 and c3 + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + self.assertRaises(RuntimeError, c1.objectName) + self.assertRaises(RuntimeError, c2.objectName) + self.assertRaises(RuntimeError, c3.objectName) + self.assertRaises(RuntimeError, layout.objectName) + + def testObjectTypeLayoutWithObjectsCreatedInCpp(self): + '''ObjectType.setLayout with objects created in C++.''' + p1 = ObjectType.create() + c1 = ObjectType.create() + c2 = ObjectType.create() + c3 = ObjectType.create() + layout = ObjectTypeLayout() + layout.addObject(c1) + layout.addObject(c2) + layout.addObject(c3) + self.assertEqual(c1.parent(), None) + self.assertEqual(c2.parent(), None) + self.assertEqual(c3.parent(), None) + + p1.setLayout(layout) + del p1 # This must kill c1, c2 and c3 + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + self.assertRaises(RuntimeError, c1.objectName) + self.assertRaises(RuntimeError, c2.objectName) + self.assertRaises(RuntimeError, c3.objectName) + self.assertRaises(RuntimeError, layout.objectName) + + def testObjectTypeLayoutTransference(self): + '''Transfer a layout from one ObjectType to another, so that all the items in + the layout get reparented.''' + p1 = ObjectType() + p2 = ObjectType() + c1 = ObjectType() + c2 = ObjectType() + + layout = ObjectTypeLayout() + layout.addObject(c1) + layout.addObject(c2) + + p1.setLayout(layout) + + self.assertEqual(len(p2.children()), 0) + self.assertEqual(c1.parent(), p1) + self.assertEqual(c2.parent(), p1) + self.assertEqual(set(p1.children()), set([c1, c2, layout])) + + p2.setLayout(layout) + + self.assertEqual(len(p1.children()), 0) + self.assertEqual(c1.parent(), p2) + self.assertEqual(c2.parent(), p2) + self.assertEqual(set(p2.children()), set([c1, c2, layout])) + + def testObjectTypeLayoutInsideAnotherLayout(self): + '''Adds one ObjectTypeLayout to another and sets the parent to an ObjectType.''' + p1 = ObjectType() + + l1 = ObjectTypeLayout() + c1 = ObjectType() + l1.addObject(c1) + c2 = ObjectType() + l1.addObject(c2) + + l2 = ObjectTypeLayout() + c3 = ObjectType() + l2.addObject(c3) + c4 = ObjectType() + l2.addObject(c4) + + l1.addObject(l2) + + p1.setLayout(l1) + + self.assertEqual(c1.parent(), p1) + self.assertEqual(c2.parent(), p1) + self.assertEqual(c3.parent(), p1) + self.assertEqual(c4.parent(), p1) + self.assertEqual(l1.parent(), p1) + self.assertEqual(l2.parent(), l1) + + del p1 + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + self.assertRaises(RuntimeError, c1.objectName) + self.assertRaises(RuntimeError, c2.objectName) + self.assertRaises(RuntimeError, c3.objectName) + self.assertRaises(RuntimeError, c4.objectName) + self.assertRaises(RuntimeError, l1.objectName) + self.assertRaises(RuntimeError, l2.objectName) + + def testObjectTypeLayoutInsideAnotherLayoutAndEveryoneCreatedInCpp(self): + '''Adds one ObjectTypeLayout to another and sets the parent to an ObjectType. + All the objects are created in C++.''' + p1 = ObjectType.create() + + l1 = ObjectTypeLayout.create() + c1 = ObjectType.create() + l1.addObject(c1) + c2 = ObjectType.create() + l1.addObject(c2) + + l2 = ObjectTypeLayout.create() + c3 = ObjectType.create() + l2.addObject(c3) + c4 = ObjectType.create() + l2.addObject(c4) + + l1.addObject(l2) + + p1.setLayout(l1) + + self.assertEqual(c1.parent(), p1) + self.assertEqual(c2.parent(), p1) + self.assertEqual(c3.parent(), p1) + self.assertEqual(c4.parent(), p1) + self.assertEqual(l1.parent(), p1) + self.assertEqual(l2.parent(), l1) + + del p1 + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + self.assertRaises(RuntimeError, c1.objectName) + self.assertRaises(RuntimeError, c2.objectName) + self.assertRaises(RuntimeError, c3.objectName) + self.assertRaises(RuntimeError, c4.objectName) + self.assertRaises(RuntimeError, l1.objectName) + self.assertRaises(RuntimeError, l2.objectName) + + def testTransferNestedLayoutsBetweenObjects(self): + '''Adds one ObjectTypeLayout to another, sets the parent to an ObjectType + and then transfer it to another object.''' + p1 = ObjectType() + p2 = ObjectType() + + l1 = ObjectTypeLayout() + c1 = ObjectType() + l1.addObject(c1) + c2 = ObjectType() + l1.addObject(c2) + + l2 = ObjectTypeLayout() + c3 = ObjectType() + l2.addObject(c3) + c4 = ObjectType() + l2.addObject(c4) + + l1.addObject(l2) + + p1.setLayout(l1) + + self.assertEqual(c1.parent(), p1) + self.assertEqual(c2.parent(), p1) + self.assertEqual(c3.parent(), p1) + self.assertEqual(c4.parent(), p1) + self.assertEqual(l1.parent(), p1) + self.assertEqual(l2.parent(), l1) + + p2.setLayout(l1) + del p1 + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + self.assertEqual(c1.parent(), p2) + self.assertEqual(c2.parent(), p2) + self.assertEqual(c3.parent(), p2) + self.assertEqual(c4.parent(), p2) + self.assertEqual(l1.parent(), p2) + self.assertEqual(l2.parent(), l1) + + del p2 + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + self.assertRaises(RuntimeError, c1.objectName) + self.assertRaises(RuntimeError, c2.objectName) + self.assertRaises(RuntimeError, c3.objectName) + self.assertRaises(RuntimeError, c4.objectName) + self.assertRaises(RuntimeError, l1.objectName) + self.assertRaises(RuntimeError, l2.objectName) + + def testTransferNestedLayoutsBetweenObjectsAndEveryoneCreatedInCpp(self): + '''Adds one ObjectTypeLayout to another, sets the parent to an ObjectType and then + transfer it to another object. All the objects are created in C++.''' + p1 = ObjectType.create() + p2 = ObjectType.create() + + l1 = ObjectTypeLayout.create() + c1 = ObjectType.create() + l1.addObject(c1) + c2 = ObjectType.create() + l1.addObject(c2) + + l2 = ObjectTypeLayout.create() + c3 = ObjectType.create() + l2.addObject(c3) + c4 = ObjectType.create() + l2.addObject(c4) + + l1.addObject(l2) + + p1.setLayout(l1) + + self.assertEqual(c1.parent(), p1) + self.assertEqual(c2.parent(), p1) + self.assertEqual(c3.parent(), p1) + self.assertEqual(c4.parent(), p1) + self.assertEqual(l1.parent(), p1) + self.assertEqual(l2.parent(), l1) + + p2.setLayout(l1) + del p1 + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + self.assertEqual(c1.parent(), p2) + self.assertEqual(c2.parent(), p2) + self.assertEqual(c3.parent(), p2) + self.assertEqual(c4.parent(), p2) + self.assertEqual(l1.parent(), p2) + self.assertEqual(l2.parent(), l1) + + del p2 + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + self.assertRaises(RuntimeError, c1.objectName) + self.assertRaises(RuntimeError, c2.objectName) + self.assertRaises(RuntimeError, c3.objectName) + self.assertRaises(RuntimeError, c4.objectName) + self.assertRaises(RuntimeError, l1.objectName) + self.assertRaises(RuntimeError, l2.objectName) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/objecttypeoperators_test.py b/sources/shiboken6/tests/samplebinding/objecttypeoperators_test.py new file mode 100644 index 000000000..ceeee6c8d --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/objecttypeoperators_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import ObjectTypeOperators + + +class ObjectTypeOperatorsTest(unittest.TestCase): + + def testIt(self): + a = ObjectTypeOperators("a") + b = ObjectTypeOperators("b") + self.assertFalse(a == b) + self.assertEqual(a, a < b) + + # this should change a.key() and return nothing. + self.assertEqual(None, a > b) + self.assertEqual(a.key(), "aoperator>") + + def testPointerOpeators(self): + a = ObjectTypeOperators("a") + b = ObjectTypeOperators("b") # noqa: F841 + self.assertEqual(a + "bc", "abc") + self.assertEqual("bc" + a, "bca") + self.assertEqual("a", a) + self.assertEqual(a, "a") + + def testOperatorInjection(self): + a = ObjectTypeOperators("a") + self.assertNotEqual(a, "b") + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/objecttypereferenceasvirtualmethodargument_test.py b/sources/shiboken6/tests/samplebinding/objecttypereferenceasvirtualmethodargument_test.py new file mode 100644 index 000000000..5fa6f824e --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/objecttypereferenceasvirtualmethodargument_test.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import ObjectTypeHolder + + +class TestObjectTypeReferenceAsVirtualMethodArgument(unittest.TestCase): + + def testBasic(self): + holder = ObjectTypeHolder('TheObjectFromC++') + self.assertEqual(holder.callPassObjectTypeAsReference(), 'TheObjectFromC++') + + def testExtended(self): + class Holder(ObjectTypeHolder): + def passObjectTypeAsReference(self, objectType): + return objectType.objectName().prepend(('ThisIs')) + holder = Holder('TheObjectFromC++') + self.assertEqual(holder.callPassObjectTypeAsReference(), 'ThisIsTheObjectFromC++') + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/oddbool_test.py b/sources/shiboken6/tests/samplebinding/oddbool_test.py new file mode 100644 index 000000000..87a8cdb1f --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/oddbool_test.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for OddBool user's primitive type conversion.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import OddBoolUser, ComparisonTester, SpaceshipComparisonTester + + +class DerivedOddBoolUser (OddBoolUser): + def returnMyselfVirtual(self): + return OddBoolUser() + pass + + +class OddBoolTest(unittest.TestCase): + + def testOddBoolUser(self): + obuTrue = OddBoolUser() + obuFalse = OddBoolUser() + obuTrue.setOddBool(True) + self.assertEqual(obuFalse.oddBool(), False) + self.assertEqual(obuTrue.oddBool(), True) + self.assertEqual(obuTrue.callInvertedOddBool(), False) + + self.assertTrue(obuTrue.oddBool()) + self.assertFalse(obuFalse.oddBool()) + self.assertTrue(obuTrue.oddBool() != obuFalse.oddBool()) + + self.assertFalse(obuFalse.oddBool()) + self.assertFalse(obuFalse.oddBool()) + self.assertTrue(obuTrue.oddBool() != obuFalse.oddBool()) + + def testVirtuals(self): + dobu = DerivedOddBoolUser() + self.assertEqual(dobu.invertedOddBool(), True) + + def testImplicitConversionWithUsersPrimitiveType(self): + obu = OddBoolUser(True) + self.assertTrue(obu.oddBool()) + obu = OddBoolUser(False) + self.assertFalse(obu.oddBool()) + cpx = complex(1.0, 0.0) + obu = OddBoolUser(cpx) + self.assertTrue(obu.oddBool()) + cpx = complex(0.0, 0.0) + obu = OddBoolUser(cpx) + self.assertFalse(obu.oddBool()) + + def testOddOperators(self): + t1 = ComparisonTester(42) + t2 = ComparisonTester(42) + self.assertEqual(t1, t2) + + def testSpaceshipOperator(self): + if not SpaceshipComparisonTester.HasSpaceshipOperator: + print("Skipping Spaceship Operator test") + return + t1 = SpaceshipComparisonTester(42) + t2 = SpaceshipComparisonTester(42) + self.assertEqual(t1, t2) + self.assertTrue(t1 <= t2) + self.assertTrue(t1 >= t2) + t2 = SpaceshipComparisonTester(43) + self.assertTrue(t1 < t2) + self.assertFalse(t1 > t2) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/onlycopyclass_test.py b/sources/shiboken6/tests/samplebinding/onlycopyclass_test.py new file mode 100644 index 000000000..bcb154c52 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/onlycopyclass_test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import OnlyCopy, FriendOfOnlyCopy + + +class ClassWithOnlyCopyCtorTest(unittest.TestCase): + def testGetOne(self): + obj = FriendOfOnlyCopy.createOnlyCopy(123) + self.assertEqual(type(obj), OnlyCopy) + self.assertEqual(obj.value(), 123) + + def testGetMany(self): + objs = FriendOfOnlyCopy.createListOfOnlyCopy(3) + self.assertEqual(type(objs), list) + self.assertEqual(len(objs), 3) + for value, obj in enumerate(objs): + self.assertEqual(obj.value(), value) + + def testPassAsValue(self): + obj = FriendOfOnlyCopy.createOnlyCopy(123) + self.assertEqual(obj.value(), OnlyCopy.getValue(obj)) + + def testPassAsReference(self): + obj = FriendOfOnlyCopy.createOnlyCopy(123) + self.assertEqual(obj.value(), OnlyCopy.getValueFromReference(obj)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/overflow_test.py b/sources/shiboken6/tests/samplebinding/overflow_test.py new file mode 100644 index 000000000..84442306a --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/overflow_test.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test case for overflowing C++ numeric types.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import (Point, doubleLongLong, doubleShort, doubleUnsignedInt, + doubleUnsignedLongLong) + + +class OverflowTest(unittest.TestCase): + '''Test case for overflowing C++ numeric types.''' + + def assertRaises(self, *args, **kwds): + if not hasattr(sys, "pypy_version_info"): + # PYSIDE-535: PyPy complains "Fatal RPython error: NotImplementedError" + return super().assertRaises(*args, **kwds) + + def testUnsignedInt(self): + '''C++ function receives an unsigned int argument and raise OverflowError + if the value is negative.''' + val = 100 + self.assertEqual(doubleUnsignedInt(val), 2 * val) + val *= -1 + self.assertRaises(OverflowError, doubleUnsignedInt, val) + + def testLongLong(self): + '''C++ function receives an long long argument and raise OverflowError + if the value is negative.''' + val = 100 + self.assertEqual(doubleLongLong(val), 2 * val) + val = int(100) + self.assertEqual(doubleLongLong(val), 2 * val) + val = (2 << 64) + 1 + self.assertRaises(OverflowError, doubleLongLong, val) + + def testUnsignedLongLong(self): + '''C++ function receives an unsigned long long argument and raise OverflowError + if the value is negative.''' + val = 100 + self.assertEqual(doubleUnsignedLongLong(val), 2 * val) + val = int(100) + self.assertEqual(doubleUnsignedLongLong(val), 2 * val) + val = -100 + self.assertRaises(OverflowError, doubleUnsignedLongLong, val) + val = int(-200) + self.assertRaises(OverflowError, doubleUnsignedLongLong, val) + + def testOverflow(self): + '''Calls function with unsigned int parameter using an overflowing value.''' + self.assertRaises(OverflowError, doubleUnsignedInt, 42415335332353253) + doubleUnsignedInt(0xdeadbeef) + + def testShortOverflow(self): + '''Calls function with short parameter using an overflowing value.''' + doubleShort(-3) + self.assertRaises(OverflowError, doubleShort, 0xFFFF * -1) + self.assertRaises(OverflowError, doubleShort, 0xFFFF + 1) + + def testOverflowOnCtor(self): + '''Calls object ctor with int parameter using overflowing values.''' + self.assertRaises(OverflowError, Point, 42415335332353253, 42415335332353253) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/overload_sorting_test.py b/sources/shiboken6/tests/samplebinding/overload_sorting_test.py new file mode 100644 index 000000000..060d91510 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/overload_sorting_test.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for overload sorting''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import (CustomOverloadSequence, ImplicitBase, ImplicitConv, + ImplicitTarget, SortedOverload) + + +class Dummy(object): + pass + + +class SimpleOverloadSorting(unittest.TestCase): + + def setUp(self): + self.obj = SortedOverload() + + def testIntDouble(self): + '''Overloads with int and double''' + self.assertEqual(self.obj.overload(3), "int") + self.assertEqual(self.obj.overload(3.14), "double") + + def testImplicitConvert(self): + '''Overloads with implicit convertible types''' + self.assertEqual(self.obj.overload(ImplicitTarget()), "ImplicitTarget") + self.assertEqual(self.obj.overload(ImplicitBase()), "ImplicitBase") + + def testContainer(self): + '''Overloads with containers arguments''' + self.assertEqual(self.obj.overload([ImplicitBase()]), "list(ImplicitBase)") + + def testPyObject(self): + '''Overloads with PyObject args''' + self.assertEqual(self.obj.overload(Dummy()), "PyObject") + + def testImplicitOnly(self): + '''Passing an implicit convertible object to an overload''' + self.assertTrue(self.obj.implicit_overload(ImplicitTarget())) + + def testPyObjectSort(self): + self.assertEqual(self.obj.pyObjOverload(1, 2), "int,int") + self.assertEqual(self.obj.pyObjOverload(object(), 2), "PyObject,int") + + +class DeepOverloadSorting(unittest.TestCase): + + def setUp(self): + self.obj = SortedOverload() + + def testPyObject(self): + '''Deep Overload - (int, PyObject *)''' + self.assertEqual(self.obj.overloadDeep(1, Dummy()), "PyObject") + + def testImplicit(self): + '''Deep Overload - (int, ImplicitBase *)''' + self.assertEqual(self.obj.overloadDeep(1, ImplicitBase()), "ImplicitBase") + + +class EnumOverIntSorting(unittest.TestCase): + def testEnumOverInt(self): + ic = ImplicitConv(ImplicitConv.CtorTwo) + self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorTwo) + + +class TestCustomOverloadSequence(unittest.TestCase): + '''Ensure the int-overload (returning v + sizeof(v)) is first as specified via + overload-number in XML.''' + def testCustomOverloadSequence(self): + s = CustomOverloadSequence() + self.assertEqual(s.overload(42), 46) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/overload_test.py b/sources/shiboken6/tests/samplebinding/overload_test.py new file mode 100644 index 000000000..62fa8d8d2 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/overload_test.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for Overload class''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import Echo, Overload, Point, PointF, Polygon, Rect, RectF, Size, Str + + +def raisesWithErrorMessage(func, arguments, errorType, errorMsg): + '''NOTE: Using 'try' because assertRaisesRegexp is not available + to check the error message.''' + try: + func(*arguments) + return False + except TypeError as err: + return errorMsg in str(err) + except Exception: + return False + return True + + +class OverloadTest(unittest.TestCase): + '''Test case for Overload class''' + + def testOverloadMethod0(self): + '''Check overloaded method call for signature "overloaded()".''' + overload = Overload() + self.assertEqual(overload.overloaded(), Overload.Function0) + + def testOverloadMethod1(self): + '''Check overloaded method call for signature "overloaded(Size*)".''' + overload = Overload() + size = Size() + self.assertEqual(overload.overloaded(size), Overload.Function1) + + def testOverloadMethod2(self): + '''Check overloaded method call for signature "overloaded(Point*, ParamEnum)".''' + overload = Overload() + point = Point() + self.assertEqual(overload.overloaded(point, Overload.Param1), Overload.Function2) + + def testOverloadMethod3(self): + '''Check overloaded method call for signature "overloaded(const Point&)".''' + overload = Overload() + point = Point() + self.assertEqual(overload.overloaded(point), Overload.Function3) + + def testDifferentReturnTypes(self): + '''Check method calls for overloads with different return types.''' + overload = Overload() + self.assertEqual(overload.differentReturnTypes(), None) + self.assertEqual(overload.differentReturnTypes(Overload.Param1), None) + self.assertEqual(overload.differentReturnTypes(Overload.Param0, 13), 13) + + def testIntOverloads(self): + overload = Overload() + self.assertEqual(overload.intOverloads(2, 3), 2) + self.assertEqual(overload.intOverloads(2, 4.5), 3) + self.assertEqual(overload.intOverloads(Point(0, 0), 3), 1) + + def testIntDoubleOverloads(self): + overload = Overload() + self.assertEqual(overload.intDoubleOverloads(1, 2), Overload.Function0) + self.assertEqual(overload.intDoubleOverloads(1, 2.0), Overload.Function0) + self.assertEqual(overload.intDoubleOverloads(1.0, 2), Overload.Function1) + self.assertEqual(overload.intDoubleOverloads(1.0, 2.0), Overload.Function1) + + def testWrapperIntIntOverloads(self): + overload = Overload() + self.assertEqual(overload.wrapperIntIntOverloads(Point(), 1, 2), Overload.Function0) + self.assertEqual(overload.wrapperIntIntOverloads(Polygon(), 1, 2), Overload.Function1) + + def testDrawTextPointAndStr(self): + overload = Overload() + self.assertEqual(overload.drawText(Point(), Str()), Overload.Function0) + self.assertEqual(overload.drawText(Point(), ''), Overload.Function0) + self.assertEqual(overload.drawText(PointF(), Str()), Overload.Function1) + self.assertEqual(overload.drawText(PointF(), ''), Overload.Function1) + + def testDrawTextRectIntStr(self): + overload = Overload() + self.assertEqual(overload.drawText(Rect(), 1, Str()), Overload.Function2) + self.assertEqual(overload.drawText(Rect(), 1, ''), Overload.Function2) + self.assertEqual(overload.drawText(RectF(), 1, Str()), Overload.Function3) + self.assertEqual(overload.drawText(RectF(), 1, ''), Overload.Function3) + + def testDrawTextRectFStrEcho(self): + overload = Overload() + self.assertEqual(overload.drawText(RectF(), Str()), Overload.Function4) + self.assertEqual(overload.drawText(RectF(), ''), Overload.Function4) + self.assertEqual(overload.drawText(RectF(), Str(), Echo()), Overload.Function4) + self.assertEqual(overload.drawText(RectF(), '', Echo()), Overload.Function4) + self.assertEqual(overload.drawText(Rect(), Str()), Overload.Function4) + self.assertEqual(overload.drawText(Rect(), ''), Overload.Function4) + self.assertEqual(overload.drawText(Rect(), Str(), Echo()), Overload.Function4) + self.assertEqual(overload.drawText(Rect(), '', Echo()), Overload.Function4) + + def testDrawTextIntIntStr(self): + overload = Overload() + self.assertEqual(overload.drawText(1, 2, Str()), Overload.Function5) + self.assertEqual(overload.drawText(1, 2, ''), Overload.Function5) + + def testDrawTextIntIntIntIntStr(self): + overload = Overload() + self.assertEqual(overload.drawText(1, 2, 3, 4, 5, Str()), Overload.Function6) + self.assertEqual(overload.drawText(1, 2, 3, 4, 5, ''), Overload.Function6) + + def testDrawText2IntIntIntIntStr(self): + overload = Overload() + self.assertEqual(overload.drawText2(1, 2, 3, 4, 5, Str()), Overload.Function6) + self.assertEqual(overload.drawText2(1, 2, 3, 4, 5, ''), Overload.Function6) + self.assertEqual(overload.drawText2(1, 2, 3, 4, 5), Overload.Function6) + self.assertEqual(overload.drawText2(1, 2, 3, 4), Overload.Function6) + self.assertEqual(overload.drawText2(1, 2, 3), Overload.Function6) + + def testDrawText3(self): + overload = Overload() + self.assertEqual(overload.drawText3(Str(), Str(), Str()), Overload.Function0) + self.assertEqual(overload.drawText3('', '', ''), Overload.Function0) + self.assertEqual(overload.drawText3(1, 2, 3, 4, 5), Overload.Function1) + self.assertEqual(overload.drawText3(1, 2, 3, 4, 5), Overload.Function1) + + def testDrawText3Exception(self): + overload = Overload() + args = (Str(), Str(), Str(), 4, 5) + result = raisesWithErrorMessage(overload.drawText3, args, + TypeError, 'called with wrong argument types:') + self.assertTrue(result) + + def testDrawText4(self): + overload = Overload() + self.assertEqual(overload.drawText4(1, 2, 3), Overload.Function0) + self.assertEqual(overload.drawText4(1, 2, 3, 4, 5), Overload.Function1) + + def testAcceptSequence(self): + # Overload.acceptSequence() + overload = Overload() + self.assertEqual(overload.acceptSequence(), Overload.Function0) + + def testAcceptSequenceIntInt(self): + # Overload.acceptSequence(int,int) + overload = Overload() + self.assertEqual(overload.acceptSequence(1, 2), Overload.Function1) + + def testAcceptSequenceStrParamEnum(self): + # Overload.acceptSequence(Str,Overload::ParamEnum) + overload = Overload() + self.assertEqual(overload.acceptSequence(''), Overload.Function2) + self.assertEqual(overload.acceptSequence('', Overload.Param0), Overload.Function2) + self.assertEqual(overload.acceptSequence(Str('')), Overload.Function2) + self.assertEqual(overload.acceptSequence(Str(''), Overload.Param0), Overload.Function2) + + def testAcceptSequenceSize(self): + # Overload.acceptSequence(Size) + overload = Overload() + self.assertEqual(overload.acceptSequence(Size()), Overload.Function3) + + def testAcceptSequenceStringList(self): + # Overload.acceptSequence(const char**) + overload = Overload() + strings = ['line 1', 'line 2'] + self.assertEqual(overload.acceptSequence(strings), Overload.Function4) + args = (['line 1', 2], ) + result = raisesWithErrorMessage(overload.acceptSequence, args, + TypeError, 'The argument must be a sequence of strings.') + self.assertTrue(result) + + def testAcceptSequencePyObject(self): + # Overload.acceptSequence(void*) + overload = Overload() + + class Foo(object): + pass + + foo = Foo() + self.assertEqual(overload.acceptSequence(foo), Overload.Function5) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/overloadwithdefault_test.py b/sources/shiboken6/tests/samplebinding/overloadwithdefault_test.py new file mode 100644 index 000000000..269b97299 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/overloadwithdefault_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Overload, Str + + +class OverloadTest(unittest.TestCase): + + def testNoArgument(self): + overload = Overload() + self.assertEqual(overload.strBufferOverloads(), Overload.Function2) + + def testStrArgument(self): + overload = Overload() + self.assertEqual(overload.strBufferOverloads(Str('')), Overload.Function0) + self.assertEqual(overload.strBufferOverloads(Str(''), ''), Overload.Function0) + self.assertEqual(overload.strBufferOverloads(Str(''), '', False), Overload.Function0) + + def testStringArgumentAsStr(self): + overload = Overload() + self.assertEqual(overload.strBufferOverloads('', ''), Overload.Function0) + self.assertEqual(overload.strBufferOverloads('', '', False), Overload.Function0) + + def testStringArgumentAsBuffer(self): + overload = Overload() + self.assertEqual(overload.strBufferOverloads(bytes('', "UTF-8"), 0), Overload.Function1) + + def testBufferArgument(self): + overload = Overload() + self.assertEqual(overload.strBufferOverloads(bytes('', "UTF-8"), 0), Overload.Function1) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ownership_argument_invalidation_test.py b/sources/shiboken6/tests/samplebinding/ownership_argument_invalidation_test.py new file mode 100644 index 000000000..8a55d3ab8 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ownership_argument_invalidation_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Wrapper validity tests for arguments.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Polygon, Point + + +class WrapperValidityOfArgumentsTest(unittest.TestCase): + '''Wrapper validity tests for arguments.''' + + def testInvalidArgumentToMethod(self): + '''Call to method using invalidated Python wrapper as argument should raise RuntimeError.''' + poly = Polygon() + Polygon.stealOwnershipFromPython(poly) + self.assertRaises(RuntimeError, Polygon.doublePolygonScale, poly) + + def testInvalidArgumentToConstructor(self): + '''Call to constructor using invalidated Python wrapper as argument + should raise RuntimeError.''' + pt = Point(1, 2) + Polygon.stealOwnershipFromPython(pt) + self.assertRaises(RuntimeError, Polygon, pt) + + def testInvalidArgumentWithImplicitConversion(self): + '''Call to method using invalidated Python wrapper to be implicitly converted + should raise RuntimeError.''' + pt = Point(1, 2) + Polygon.stealOwnershipFromPython(pt) + self.assertRaises(RuntimeError, Polygon.doublePolygonScale, pt) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ownership_delete_child_in_cpp_test.py b/sources/shiboken6/tests/samplebinding/ownership_delete_child_in_cpp_test.py new file mode 100644 index 000000000..25c6fea26 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ownership_delete_child_in_cpp_test.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Tests for destroy a child object in C++''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType + + +class DeleteChildInCpp(unittest.TestCase): + '''Test case for destroying a child in c++''' + + def testDeleteChild(self): + '''Delete child in C++ should invalidate child - using C++ wrapper''' + parent = ObjectType() + parent.setObjectName('parent') + child = ObjectType(parent) + child.setObjectName('child') + + parent.killChild('child') + self.assertRaises(RuntimeError, child.objectName) + self.assertEqual(parent.objectName(), 'parent') + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ownership_delete_child_in_python_test.py b/sources/shiboken6/tests/samplebinding/ownership_delete_child_in_python_test.py new file mode 100644 index 000000000..3ae186815 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ownership_delete_child_in_python_test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Tests for deleting a child object in python''' + +import gc +import os +import random +import string +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType + + +class DeleteChildInPython(unittest.TestCase): + '''Test case for deleting (unref) a child in python''' + + def testDeleteChild(self): + '''Delete child in python should not invalidate child''' + parent = ObjectType() + child = ObjectType(parent) + name = ''.join(random.sample(string.ascii_letters, 5)) + child.setObjectName(name) + + del child + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + new_child = parent.children()[0] + self.assertEqual(new_child.objectName(), name) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ownership_delete_parent_test.py b/sources/shiboken6/tests/samplebinding/ownership_delete_parent_test.py new file mode 100644 index 000000000..8f654639c --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ownership_delete_parent_test.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Tests for destroying the parent''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType + + +class DeleteParentTest(unittest.TestCase): + '''Test case for deleting a parent object''' + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testParentDestructor(self): + '''Delete parent object should invalidate child''' + parent = ObjectType() + child = ObjectType() + child.setParent(parent) + + refcount_before = sys.getrefcount(child) + + del parent + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertRaises(RuntimeError, child.objectName) + self.assertEqual(sys.getrefcount(child), refcount_before - 1) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testParentDestructorMultipleChildren(self): + '''Delete parent object should invalidate all children''' + parent = ObjectType() + children = [ObjectType() for _ in range(10)] + + for child in children: + child.setParent(parent) + + del parent + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + for i, child in enumerate(children): + self.assertRaises(RuntimeError, child.objectName) + self.assertEqual(sys.getrefcount(child), 4) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testRecursiveParentDelete(self): + '''Delete parent should invalidate grandchildren''' + parent = ObjectType() + child = ObjectType(parent) + grandchild = ObjectType(child) + + del parent + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertRaises(RuntimeError, child.objectName) + self.assertEqual(sys.getrefcount(child), 2) + self.assertRaises(RuntimeError, grandchild.objectName) + self.assertEqual(sys.getrefcount(grandchild), 2) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ownership_invalidate_after_use_test.py b/sources/shiboken6/tests/samplebinding/ownership_invalidate_after_use_test.py new file mode 100644 index 000000000..37b7591e4 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ownership_invalidate_after_use_test.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Ownership tests for cases of invalidation of Python wrapper after use.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType, ObjectTypeDerived, Event + + +class ExtObjectType(ObjectType): + def __init__(self): + ObjectType.__init__(self) + self.type_of_last_event = None + self.last_event = None + + def event(self, event): + self.last_event = event + self.type_of_last_event = event.eventType() + return True + + +class MyObjectType (ObjectType): + def __init__(self): + super(MyObjectType, self).__init__() + self.fail = False + + def event(self, ev): + self.callInvalidateEvent(ev) + try: + ev.eventType() + except: # noqa: E722 + self.fail = True + raise + return True + + def invalidateEvent(self, ev): + pass + + +class ExtObjectTypeDerived(ObjectTypeDerived): + def __init__(self): + ObjectTypeDerived.__init__(self) + self.type_of_last_event = None + self.last_event = None + + def event(self, event): + self.last_event = event + self.type_of_last_event = event.eventType() + return True + + +class OwnershipInvalidateAfterUseTest(unittest.TestCase): + '''Ownership tests for cases of invalidation of Python wrapper after use.''' + + def testInvalidateAfterUse(self): + '''In ObjectType.event(Event*) the wrapper object created for Event + must me marked as invalid after the method is called.''' + eot = ExtObjectType() + eot.causeEvent(Event.SOME_EVENT) + self.assertEqual(eot.type_of_last_event, Event.SOME_EVENT) + self.assertRaises(RuntimeError, eot.last_event.eventType) + + def testObjectInvalidatedAfterUseAsParameter(self): + '''Tries to use wrapper invalidated after use as a parameter to another method.''' + eot = ExtObjectType() + ot = ObjectType() + eot.causeEvent(Event.ANY_EVENT) + self.assertEqual(eot.type_of_last_event, Event.ANY_EVENT) + self.assertRaises(RuntimeError, ot.event, eot.last_event) + + def testit(self): + obj = MyObjectType() + obj.causeEvent(Event.BASIC_EVENT) + self.assertFalse(obj.fail) + + def testInvalidateAfterUseInDerived(self): + '''Invalidate was failing in a derived C++ class that also inherited + other base classes''' + eot = ExtObjectTypeDerived() + eot.causeEvent(Event.SOME_EVENT) + self.assertEqual(eot.type_of_last_event, Event.SOME_EVENT) + self.assertRaises(RuntimeError, eot.last_event.eventType) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ownership_invalidate_child_test.py b/sources/shiboken6/tests/samplebinding/ownership_invalidate_child_test.py new file mode 100644 index 000000000..77b7c576c --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ownership_invalidate_child_test.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Tests for invalidating a C++ created child that was already on the care of a parent.''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType, BlackBox + + +class InvalidateChildTest(unittest.TestCase): + '''Tests for invalidating a C++ created child that was already on the care of a parent.''' + + def testInvalidateChild(self): + '''Invalidating method call should remove child from the care of a parent if it has one.''' + parent = ObjectType() + child1 = ObjectType(parent) + child1.setObjectName('child1') + child2 = ObjectType.create() + child2.setParent(parent) + child2.setObjectName('child2') + + self.assertEqual(parent.children(), [child1, child2]) + + bbox = BlackBox() + + # This method steals ownership from Python to C++. + bbox.keepObjectType(child1) + self.assertEqual(parent.children(), [child2]) + + bbox.keepObjectType(child2) + self.assertEqual(parent.children(), []) + + del parent + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + # PYSIDE-535: Why do I need to do it twice, here? + gc.collect() + + self.assertEqual(child1.objectName(), 'child1') + self.assertRaises(RuntimeError, child2.objectName) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ownership_invalidate_nonpolymorphic_test.py b/sources/shiboken6/tests/samplebinding/ownership_invalidate_nonpolymorphic_test.py new file mode 100644 index 000000000..8cbefc30c --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ownership_invalidate_nonpolymorphic_test.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''The BlackBox class has cases of ownership transference between Python and C++.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Point, BlackBox + + +class OwnershipInvalidateNonPolymorphicTest(unittest.TestCase): + '''The BlackBox class has cases of ownership transference between Python and C++.''' + + def testOwnershipTransference(self): + '''Ownership transference from Python to C++ and back again.''' + p1 = Point(10, 20) + bb = BlackBox() + p1_ticket = bb.keepPoint(p1) + self.assertRaises(RuntimeError, p1.x) + p1_ret = bb.retrievePoint(p1_ticket) + self.assertEqual(p1_ret, Point(10, 20)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ownership_invalidate_parent_test.py b/sources/shiboken6/tests/samplebinding/ownership_invalidate_parent_test.py new file mode 100644 index 000000000..c721a212c --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ownership_invalidate_parent_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Tests for invalidating a parent of other objects.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType, BlackBox + + +class InvalidateParentTest(unittest.TestCase): + '''Tests for invalidating a parent of other objects.''' + + def testInvalidateParent(self): + '''Invalidate parent should invalidate children''' + parent = ObjectType.create() + child1 = ObjectType(parent) + child1.setObjectName("child1") + child2 = ObjectType.create() + child2.setObjectName("child2") + child2.setParent(parent) + grandchild1 = ObjectType(child1) + grandchild1.setObjectName("grandchild1") + grandchild2 = ObjectType.create() + grandchild2.setObjectName("grandchild2") + grandchild2.setParent(child2) + bbox = BlackBox() + + bbox.keepObjectType(parent) # Should invalidate the parent + + self.assertRaises(RuntimeError, parent.objectName) + # some children still valid they are wrapper classes + self.assertEqual(child1.objectName(), "child1") + self.assertRaises(RuntimeError, child2.objectName) + self.assertEqual(grandchild1.objectName(), "grandchild1") + self.assertRaises(RuntimeError, grandchild2.objectName) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ownership_reparenting_test.py b/sources/shiboken6/tests/samplebinding/ownership_reparenting_test.py new file mode 100644 index 000000000..304223063 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ownership_reparenting_test.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Tests for object reparenting.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +import sys + +from sample import ObjectType + + +class ExtObjectType(ObjectType): + def __init__(self): + ObjectType.__init__(self) + + +class ReparentingTest(unittest.TestCase): + '''Tests for object reparenting.''' + + def testReparentedObjectTypeIdentity(self): + '''Reparent children from one parent to another.''' + object_list = [] + old_parent = ObjectType() + new_parent = ObjectType() + for i in range(3): + obj = ObjectType() + object_list.append(obj) + obj.setParent(old_parent) + for obj in object_list: + obj.setParent(new_parent) + for child in new_parent.children(): + self.assertTrue(child in object_list) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testReparentWithTheSameParent(self): + '''Set the same parent twice to check if the ref continue the same''' + obj = ObjectType() + parent = ObjectType() + self.assertEqual(sys.getrefcount(obj), 2) + obj.setParent(parent) + self.assertEqual(sys.getrefcount(obj), 3) + obj.setParent(parent) + self.assertEqual(sys.getrefcount(obj), 3) + + def testReparentedExtObjectType(self): + '''Reparent children from one extended parent to another.''' + object_list = [] + old_parent = ExtObjectType() + new_parent = ExtObjectType() + for i in range(3): + obj = ExtObjectType() + object_list.append(obj) + obj.setParent(old_parent) + for obj in object_list: + obj.setParent(new_parent) + for orig, child in zip(object_list, new_parent.children()): + self.assertEqual(type(orig), type(child)) + + def testReparentedObjectTypeIdentityWithParentsCreatedInCpp(self): + '''Reparent children from one parent to another, both created in C++.''' + object_list = [] + old_parent = ObjectType.create() + new_parent = ObjectType.create() + for i in range(3): + obj = ObjectType() + object_list.append(obj) + obj.setParent(old_parent) + for obj in object_list: + obj.setParent(new_parent) + for child in new_parent.children(): + self.assertTrue(child in object_list) + + def testReparentedObjectTypeIdentityWithChildrenCreatedInCpp(self): + '''Reparent children created in C++ from one parent to another.''' + object_list = [] + old_parent = ObjectType() + new_parent = ObjectType() + for i in range(3): + obj = ObjectType.create() + object_list.append(obj) + obj.setParent(old_parent) + for obj in object_list: + obj.setParent(new_parent) + for child in new_parent.children(): + self.assertTrue(child in object_list) + + def testReparentedObjectTypeIdentityWithParentsAndChildrenCreatedInCpp(self): + '''Reparent children from one parent to another. Parents and children are created in C++.''' + object_list = [] + old_parent = ObjectType.create() + new_parent = ObjectType.create() + for i in range(3): + obj = ObjectType.create() + object_list.append(obj) + obj.setParent(old_parent) + for obj in object_list: + obj.setParent(new_parent) + for child in new_parent.children(): + self.assertTrue(child in object_list) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/ownership_transference_test.py b/sources/shiboken6/tests/samplebinding/ownership_transference_test.py new file mode 100644 index 000000000..0e9f08b72 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/ownership_transference_test.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''The BlackBox class has cases of ownership transference between C++ and Python.''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType, BlackBox + + +class BlackBoxTest(unittest.TestCase): + '''The BlackBox class has cases of ownership transference between C++ and Python.''' + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testOwnershipTransference(self): + '''Ownership transference from Python to C++ and back again.''' + o1 = ObjectType() + o1.setObjectName('object1') + o1_refcnt = sys.getrefcount(o1) + o2 = ObjectType() + o2.setObjectName('object2') + o2_refcnt = sys.getrefcount(o2) + bb = BlackBox() + o1_ticket = bb.keepObjectType(o1) # noqa: F841 + o2_ticket = bb.keepObjectType(o2) + self.assertEqual(set(bb.objects()), set([o1, o2])) + self.assertEqual(str(o1.objectName()), 'object1') + self.assertEqual(str(o2.objectName()), 'object2') + # PySide give +1 ref to object with c++ ownership + self.assertEqual(sys.getrefcount(o1), o1_refcnt + 1) + self.assertEqual(sys.getrefcount(o2), o2_refcnt + 1) + o2 = bb.retrieveObjectType(o2_ticket) + self.assertEqual(sys.getrefcount(o2), o2_refcnt) + del bb + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertRaises(RuntimeError, o1.objectName) + self.assertEqual(str(o2.objectName()), 'object2') + self.assertEqual(sys.getrefcount(o2), o2_refcnt) + + def testBlackBoxReleasingUnknownObjectType(self): + '''Asks BlackBox to release an unknown ObjectType.''' + o1 = ObjectType() + o2 = ObjectType() # noqa: F841 + bb = BlackBox() + o1_ticket = bb.keepObjectType(o1) # noqa: F841 + o3 = bb.retrieveObjectType(-5) + self.assertEqual(o3, None) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testOwnershipTransferenceCppCreated(self): + '''Ownership transference using a C++ created object.''' + o1 = ObjectType.create() + o1.setObjectName('object1') + o1_refcnt = sys.getrefcount(o1) # noqa: F841 + bb = BlackBox() + o1_ticket = bb.keepObjectType(o1) # noqa: F841 + self.assertRaises(RuntimeError, o1.objectName) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/pair_test.py b/sources/shiboken6/tests/samplebinding/pair_test.py new file mode 100644 index 000000000..4bd5c697c --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/pair_test.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for std::pair container conversions''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import PairUser + + +class ExtendedPairUser(PairUser): + def __init__(self): + PairUser.__init__(self) + self.create_pair_called = False + + def createPair(self): + self.create_pair_called = True + return (7, 13) + + +class PairConversionTest(unittest.TestCase): + '''Test case for std::pair container conversions''' + + def testReimplementedVirtualMethodCall(self): + '''Test if a Python override of a virtual method is correctly called from C++.''' + pu = ExtendedPairUser() + pair = pu.callCreatePair() + self.assertTrue(pu.create_pair_called) + self.assertEqual(type(pair), tuple) + self.assertEqual(type(pair[0]), int) + self.assertEqual(type(pair[1]), int) + self.assertEqual(pair, (7, 13)) + + def testPrimitiveConversionInsideContainer(self): + '''Test primitive type conversion inside conversible std::pair container.''' + cpx0 = complex(1.2, 3.4) + cpx1 = complex(5.6, 7.8) + cp = PairUser.createComplexPair(cpx0, cpx1) + self.assertEqual(type(cp), tuple) + self.assertEqual(type(cp[0]), complex) + self.assertEqual(type(cp[1]), complex) + self.assertEqual(cp, (cpx0, cpx1)) + + def testSumPair(self): + '''Test method that sums the items of a pair using values of the types + expected by C++ (int and double)''' + pu = PairUser() + pair = (3, 7.13) + result = pu.sumPair(pair) + self.assertEqual(result, sum(pair)) + + def testSumPairDifferentTypes(self): + '''Test method that sums the items of a pair using values of types different + from the ones expected by C++ (int and double)''' + pu = PairUser() + pair = (3.3, 7) + result = pu.sumPair(pair) + self.assertNotEqual(result, sum(pair)) + self.assertEqual(result, int(pair[0]) + pair[1]) + + def testConversionInBothDirections(self): + '''Test converting a pair from Python to C++ and the other way around.''' + pu = PairUser() + pair = (3, 5) + pu.setPair(pair) + result = pu.getPair() + self.assertEqual(result, pair) + + def testConversionInBothDirectionsWithSimilarContainer(self): + '''Test converting a list, instead of the expected tuple, from Python to C++ + and the other way around.''' + pu = PairUser() + pair = [3, 5] + pu.setPair(pair) + result = pu.getPair() + self.assertNotEqual(result, pair) + self.assertEqual(result, tuple(pair)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/pen_test.py b/sources/shiboken6/tests/samplebinding/pen_test.py new file mode 100644 index 000000000..106f3bd61 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/pen_test.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for <add-function> with const char* as argument''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Color, Pen, SampleNamespace + + +class TestPen(unittest.TestCase): + '''Simple test case for Pen.''' + + def testPenWithEmptyConstructor(self): + pen = Pen() + self.assertEqual(pen.ctorType(), Pen.EmptyCtor) + + def testPenWithEnumConstructor(self): + pen = Pen(SampleNamespace.RandomNumber) + self.assertEqual(pen.ctorType(), Pen.EnumCtor) + + def testPenWithColorConstructor(self): + pen = Pen(Color()) + self.assertEqual(pen.ctorType(), Pen.ColorCtor) + + def testPenWithCopyConstructor(self): + pen = Pen(Pen()) + self.assertEqual(pen.ctorType(), Pen.CopyCtor) + + def testPenWithIntConvertedToColor(self): + pen = Pen(1) + self.assertEqual(pen.ctorType(), Pen.ColorCtor) + pen.drawLine(0, 0, 5, 5) + + def testPenRenderHintsProperty(self): + """Exercise the generated property setter and getters, checking + against the C++ getter/setter functions.""" + pen = Pen(1) + self.assertEqual(pen.getRenderHints(), Pen.RenderHints.None_) + self.assertEqual(pen.renderHints, Pen.RenderHints.None_) + pen.renderHints = Pen.RenderHints.TextAntialiasing + self.assertEqual(pen.getRenderHints(), Pen.RenderHints.TextAntialiasing) + self.assertEqual(pen.renderHints, Pen.RenderHints.TextAntialiasing) + pen.setRenderHints(Pen.RenderHints.Antialiasing) + self.assertEqual(pen.getRenderHints(), Pen.RenderHints.Antialiasing) + self.assertEqual(pen.renderHints, Pen.RenderHints.Antialiasing) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/point_test.py b/sources/shiboken6/tests/samplebinding/point_test.py new file mode 100644 index 000000000..f86c0f423 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/point_test.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for Point class''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Point + + +class PointTest(unittest.TestCase): + '''Test case for Point class, including operator overloads.''' + + def assertRaises(self, *args, **kwds): + if not hasattr(sys, "pypy_version_info"): + # PYSIDE-535: PyPy complains "Fatal RPython error: NotImplementedError" + return super().assertRaises(*args, **kwds) + + def testConstructor(self): + '''Test Point class constructor.''' + pt = Point(5.0, 2.3) + self.assertEqual(pt.x(), 5.0) + self.assertEqual(pt.y(), 2.3) + + def testPlusOperator(self): + '''Test Point class + operator.''' + pt1 = Point(5.0, 2.3) + pt2 = Point(0.5, 3.2) + self.assertEqual(pt1 + pt2, Point(5.0 + 0.5, 2.3 + 3.2)) + + def testEqualOperator(self): + '''Test Point class == operator.''' + pt1 = Point(5.0, 2.3) + pt2 = Point(5.0, 2.3) + pt3 = Point(0.5, 3.2) + self.assertTrue(pt1 == pt1) + self.assertTrue(pt1 == pt2) + self.assertFalse(pt1 == pt3) + self.assertFalse(pt1 == object()) + + def testNotEqualOperator(self): + '''Test Point class != operator.''' + pt1 = Point(5.0, 2.3) + pt2 = Point(5.0, 2.3) + # This test no longer makes sense because we always supply default `==`, `!=`. + #self.assertRaises(NotImplementedError, pt1.__ne__, pt2) + # Since we use the default identity comparison, this results in `!=` . + self.assertTrue(pt1 != pt2) + + def testReturnNewCopy(self): + '''Point returns a copy of itself.''' + pt1 = Point(1.1, 2.3) + pt2 = pt1.copy() + self.assertEqual(pt1, pt2) + pt2 += pt1 + self.assertFalse(pt1 == pt2) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testReturnConstPointer(self): + '''Point returns a const pointer for itself.''' + pt1 = Point(5.0, 2.3) + refcount1 = sys.getrefcount(pt1) + pt2 = pt1.getSelf() + self.assertEqual(pt1, pt2) + self.assertEqual(sys.getrefcount(pt1), refcount1 + 1) + self.assertEqual(sys.getrefcount(pt1), sys.getrefcount(pt2)) + + def testUintOverflow(self): + pt1 = Point(0.0, 0.0) + self.assertRaises(OverflowError, pt1.setXAsUint, 840835495615213080) + self.assertEqual(pt1.x(), 0.0) + + def testAddedOperator(self): + p = Point(0.0, 0.0) + r = p - 'Hi' + self.assertEqual(r, 'Hi') + + # now the reverse op. + r = 'Hi' - p + self.assertEqual(r, 'Hi') + + def testModifiedMethod(self): + pt1 = Point(0.0, 0.0) + pt2 = Point(10.0, 10.0) + expected = Point((pt1.x() + pt2.x()) / 2.0, (pt1.y() + pt2.y()) / 2.0) + self.assertEqual(pt1.midpoint(pt2), expected) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/pointerholder_test.py b/sources/shiboken6/tests/samplebinding/pointerholder_test.py new file mode 100644 index 000000000..633525a9c --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/pointerholder_test.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for a class that holds an arbitraty pointer and is modified to hold an PyObject.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import PointerHolder + + +class TestPointerHolder(unittest.TestCase): + '''Test cases for a class that holds an arbitraty pointer and + is modified to hold an PyObject.''' + + def testStoringAndRetrievingPointer(self): + ph = PointerHolder('Hello') + self.assertEqual(ph.pointer(), 'Hello') + a = (1, 2, 3) + ph = PointerHolder(a) + self.assertEqual(ph.pointer(), a) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testReferenceCounting(self): + '''Test reference counting when retrieving data with PointerHolder.pointer().''' + a = (1, 2, 3) + refcnt = sys.getrefcount(a) + ph = PointerHolder(a) + ptr = ph.pointer() # noqa: F841 + self.assertEqual(sys.getrefcount(a), refcnt + 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/pointerprimitivetype_test.py b/sources/shiboken6/tests/samplebinding/pointerprimitivetype_test.py new file mode 100644 index 000000000..4da1a89c6 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/pointerprimitivetype_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +""" +pointerprimitivetype_test.py + +check that the primitive types are correctly mapped by the signature module. + +Mapping +------- +IntArray2(const int*) -- <Signature (self, data: typing.Sequence)> +getMargins(int*,int*,int*,int*)const -- <Signature (self) -> typing.Tuple[int, int, int, int]> + +We explicitly check only against typing.Iterable in the first test, +because typing.Sequence is a subclass, but we will generalize this +to typing.Iterable in the future. +""" + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import IntArray2, VirtualMethods + +from shibokensupport.signature import get_signature + +import typing + + +class PointerPrimitiveTypeTest(unittest.TestCase): + + def testArraySignature(self): + # signature="IntArray2(const int*)" + found = False + for sig in get_signature(IntArray2): + if "data" in sig.parameters: + found = True + break + self.assertTrue(found) + ann = sig.parameters["data"].annotation + self.assertEqual(ann.__args__, (int,)) + self.assertTrue(issubclass(ann.__origin__, typing.Iterable)) + + def testReturnVarSignature(self): + # signature="getMargins(int*,int*,int*,int*)const"> + ann = get_signature(VirtualMethods.getMargins).return_annotation + self.assertEqual(ann, typing.Tuple[int, int, int, int]) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/pointf_test.py b/sources/shiboken6/tests/samplebinding/pointf_test.py new file mode 100644 index 000000000..91c58eb1d --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/pointf_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for PointF class''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import PointF + + +class PointFTest(unittest.TestCase): + '''Test case for PointF class, including operator overloads.''' + + def testConstructor(self): + '''Test PointF class constructor.''' + pt = PointF(5.0, 2.3) + self.assertEqual(pt.x(), 5.0) + self.assertEqual(pt.y(), 2.3) + + def testPlusOperator(self): + '''Test PointF class + operator.''' + pt1 = PointF(5.0, 2.3) + pt2 = PointF(0.5, 3.2) + self.assertEqual(pt1 + pt2, PointF(5.0 + 0.5, 2.3 + 3.2)) + + def testEqualOperator(self): + '''Test PointF class == operator.''' + pt1 = PointF(5.0, 2.3) + pt2 = PointF(5.0, 2.3) + pt3 = PointF(0.5, 3.2) + self.assertTrue(pt1 == pt1) + self.assertTrue(pt1 == pt2) + self.assertFalse(pt1 == pt3) + + def testModifiedMethod(self): + pt1 = PointF(0.0, 0.0) + pt2 = PointF(10.0, 10.0) + expected = PointF((pt1.x() + pt2.x()) / 2.0, (pt1.y() + pt2.y()) / 2.0) + self.assertEqual(pt1.midpoint(pt2), expected) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/primitivereferenceargument_test.py b/sources/shiboken6/tests/samplebinding/primitivereferenceargument_test.py new file mode 100644 index 000000000..0b9fe2249 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/primitivereferenceargument_test.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +import sample + + +class PrimitiveReferenceArgumentTest(unittest.TestCase): + + def testIntReferenceArgument(self): + '''C++ signature: int acceptIntReference(int&)''' + self.assertEqual(sample.acceptIntReference(123), 123) + + def testIntReturnPtr(self): + '''C++ signature: const int *acceptIntReturnPtr(int x)''' + self.assertEqual(sample.acceptIntReturnPtr(123), 123) + + def testOddBoolReferenceArgument(self): + '''C++ signature: OddBool acceptOddBoolReference(OddBool&)''' + self.assertEqual(sample.acceptOddBoolReference(True), True) + self.assertEqual(sample.acceptOddBoolReference(False), False) + self.assertNotEqual(sample.acceptOddBoolReference(True), False) + self.assertNotEqual(sample.acceptOddBoolReference(False), True) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/privatector_test.py b/sources/shiboken6/tests/samplebinding/privatector_test.py new file mode 100644 index 000000000..63040388d --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/privatector_test.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for a class with only a private constructor.''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import PrivateCtor + + +class PrivateCtorTest(unittest.TestCase): + '''Test case for PrivateCtor class''' + + def assertRaises(self, *args, **kwds): + if not hasattr(sys, "pypy_version_info"): + # PYSIDE-535: PyPy complains "Fatal RPython error: NotImplementedError" + return super().assertRaises(*args, **kwds) + + def testPrivateCtorInstanciation(self): + '''Test if instanciation of class with a private constructor raises an exception.''' + self.assertRaises(TypeError, PrivateCtor) + + def testPrivateCtorInheritance(self): + '''Test if inheriting from PrivateCtor raises an exception.''' + def inherit(): + class Foo(PrivateCtor): + pass + self.assertRaises(TypeError, inherit) + + def testPrivateCtorInstanceMethod(self): + '''Test if PrivateCtor.instance() method return the proper singleton.''' + pd1 = PrivateCtor.instance() + calls = pd1.instanceCalls() + self.assertEqual(type(pd1), PrivateCtor) + pd2 = PrivateCtor.instance() + self.assertEqual(pd2, pd1) + self.assertEqual(pd2.instanceCalls(), calls + 1) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testPrivateCtorRefCounting(self): + '''Test refcounting of the singleton returned by PrivateCtor.instance().''' + pd1 = PrivateCtor.instance() + calls = pd1.instanceCalls() + refcnt = sys.getrefcount(pd1) + pd2 = PrivateCtor.instance() + self.assertEqual(pd2.instanceCalls(), calls + 1) + self.assertEqual(sys.getrefcount(pd2), sys.getrefcount(pd1)) + self.assertEqual(sys.getrefcount(pd2), refcnt + 1) + del pd1 + self.assertEqual(sys.getrefcount(pd2), refcnt) + del pd2 + gc.collect() + pd3 = PrivateCtor.instance() + self.assertEqual(type(pd3), PrivateCtor) + self.assertEqual(pd3.instanceCalls(), calls + 2) + self.assertEqual(sys.getrefcount(pd3), refcnt) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/privatedtor_test.py b/sources/shiboken6/tests/samplebinding/privatedtor_test.py new file mode 100644 index 000000000..651f63b15 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/privatedtor_test.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for a class with a private destructor.''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from shiboken6 import Shiboken +from sample import PrivateDtor + + +class PrivateDtorTest(unittest.TestCase): + '''Test case for PrivateDtor class''' + + def assertRaises(self, *args, **kwds): + if not hasattr(sys, "pypy_version_info"): + # PYSIDE-535: PyPy complains "Fatal RPython error: NotImplementedError" + return super().assertRaises(*args, **kwds) + + def testPrivateDtorInstanciation(self): + '''Test if instanciation of class with a private destructor raises an exception.''' + self.assertRaises(TypeError, PrivateDtor) + + def testPrivateDtorInheritance(self): + '''Test if inheriting from PrivateDtor raises an exception.''' + def inherit(): + class Foo(PrivateDtor): + pass + self.assertRaises(TypeError, inherit) + + def testPrivateDtorInstanceMethod(self): + '''Test if PrivateDtor.instance() method return the proper singleton.''' + pd1 = PrivateDtor.instance() + calls = pd1.instanceCalls() + self.assertEqual(type(pd1), PrivateDtor) + pd2 = PrivateDtor.instance() + self.assertEqual(pd2, pd1) + self.assertEqual(pd2.instanceCalls(), calls + 1) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testPrivateDtorRefCounting(self): + '''Test refcounting of the singleton returned by PrivateDtor.instance().''' + pd1 = PrivateDtor.instance() + calls = pd1.instanceCalls() + refcnt = sys.getrefcount(pd1) + pd2 = PrivateDtor.instance() + self.assertEqual(pd2.instanceCalls(), calls + 1) + self.assertEqual(sys.getrefcount(pd2), sys.getrefcount(pd1)) + self.assertEqual(sys.getrefcount(pd2), refcnt + 1) + del pd1 + self.assertEqual(sys.getrefcount(pd2), refcnt) + del pd2 + gc.collect() + pd3 = PrivateDtor.instance() + self.assertEqual(type(pd3), PrivateDtor) + self.assertEqual(pd3.instanceCalls(), calls + 2) + self.assertEqual(sys.getrefcount(pd3), refcnt) + + @unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount") + def testClassDecref(self): + # Bug was that class PyTypeObject wasn't decrefed when instance + # was invalidated + + before = sys.getrefcount(PrivateDtor) + + for i in range(1000): + obj = PrivateDtor.instance() + Shiboken.invalidate(obj) + + after = sys.getrefcount(PrivateDtor) + + self.assertLess(abs(before - after), 5) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/protected_test.py b/sources/shiboken6/tests/samplebinding/protected_test.py new file mode 100644 index 000000000..e4ccf721d --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/protected_test.py @@ -0,0 +1,401 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for protected methods.''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import cacheSize +from sample import ProtectedNonPolymorphic, ProtectedVirtualDestructor +from sample import (ProtectedPolymorphic, ProtectedPolymorphicDaughter, + ProtectedPolymorphicGrandDaughter) +from sample import createProtectedProperty, ProtectedProperty, ProtectedEnumClass +from sample import PrivateDtor +from sample import Event, ObjectType, Point + + +class ExtendedProtectedPolymorphic(ProtectedPolymorphic): + def __init__(self, name): + ProtectedPolymorphic.__init__(self, name) + self.protectedName_called = False + + def protectedName(self): + self.protectedName_called = True + self._name = 'Extended' + ProtectedPolymorphic.protectedName(self) + return self._name + + +class ExtendedProtectedPolymorphicDaughter(ProtectedPolymorphicDaughter): + def __init__(self, name): + self.protectedName_called = False + ProtectedPolymorphicDaughter.__init__(self, name) + + def protectedName(self): + self.protectedName_called = True + self._name = 'ExtendedDaughter' + ProtectedPolymorphicDaughter.protectedName(self) + return self._name + + +class ExtendedProtectedPolymorphicGrandDaughter(ProtectedPolymorphicGrandDaughter): + def __init__(self, name): + self.protectedName_called = False + ProtectedPolymorphicGrandDaughter.__init__(self, name) + + def protectedName(self): + self.protectedName_called = True + self._name = 'ExtendedGrandDaughter' + ProtectedPolymorphicGrandDaughter.protectedName(self) + return self._name + + +class ExtendedProtectedVirtualDestructor(ProtectedVirtualDestructor): + def __init__(self): + ProtectedVirtualDestructor.__init__(self) + + +class ProtectedNonPolymorphicTest(unittest.TestCase): + '''Test cases for protected method in a class without virtual methods.''' + + def tearDown(self): + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(cacheSize(), 0) + + def testProtectedCall(self): + '''Calls a non-virtual protected method.''' + p = ProtectedNonPolymorphic('NonPoly') + self.assertEqual(p.publicName(), p.protectedName()) + a0, a1 = 1, 2 + self.assertEqual(p.protectedSum(a0, a1), a0 + a1) + + def testProtectedCallWithInstanceCreatedOnCpp(self): + '''Calls a non-virtual protected method on an instance created in C++.''' + p = ProtectedNonPolymorphic.create() + self.assertEqual(p.publicName(), p.protectedName()) + a0, a1 = 1, 2 + self.assertEqual(p.protectedSum(a0, a1), a0 + a1) + + def testModifiedProtectedCall(self): + '''Calls a non-virtual protected method modified with code injection.''' + p = ProtectedNonPolymorphic('NonPoly') + self.assertEqual(p.dataTypeName(), 'integer') + self.assertEqual(p.dataTypeName(1), 'integer') + self.assertEqual(p.dataTypeName(Point(1, 2)), 'pointer') + + +class ProtectedPolymorphicTest(unittest.TestCase): + '''Test cases for protected method in a class with virtual methods.''' + + def tearDown(self): + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(cacheSize(), 0) + + def testProtectedCall(self): + '''Calls a virtual protected method.''' + p = ProtectedNonPolymorphic('Poly') + self.assertEqual(p.publicName(), p.protectedName()) + a0, a1 = 1, 2 + self.assertEqual(p.protectedSum(a0, a1), a0 + a1) + + def testProtectedCallWithInstanceCreatedOnCpp(self): + '''Calls a virtual protected method on an instance created in C++.''' + p = ProtectedPolymorphic.create() + self.assertEqual(p.publicName(), p.protectedName()) + self.assertEqual(p.callProtectedName(), p.protectedName()) + + def testReimplementedProtectedCall(self): + '''Calls a reimplemented virtual protected method.''' + original_name = 'Poly' + p = ExtendedProtectedPolymorphic(original_name) + name = p.callProtectedName() + self.assertTrue(p.protectedName_called) + self.assertEqual(p.protectedName(), name) + self.assertEqual(ProtectedPolymorphic.protectedName(p), original_name) + + +class ProtectedPolymorphicDaugherTest(unittest.TestCase): + '''Test cases for protected method in a class inheriting for a class with virtual methods.''' + + def testProtectedCallWithInstanceCreatedOnCpp(self): + '''Calls a virtual protected method from parent class on an instance created in C++.''' + p = ProtectedPolymorphicDaughter.create() + self.assertEqual(p.publicName(), p.protectedName()) + self.assertEqual(p.callProtectedName(), p.protectedName()) + + def testReimplementedProtectedCall(self): + '''Calls a reimplemented virtual protected method from parent class.''' + original_name = 'Poly' + p = ExtendedProtectedPolymorphicDaughter(original_name) + name = p.callProtectedName() + self.assertTrue(p.protectedName_called) + self.assertEqual(p.protectedName(), name) + self.assertEqual(ProtectedPolymorphicDaughter.protectedName(p), original_name) + + +class ProtectedPolymorphicGrandDaugherTest(unittest.TestCase): + '''Test cases for protected method in a class inheriting for a class that inherits from + another with protected virtual methods.''' + + def tearDown(self): + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(cacheSize(), 0) + + def testProtectedCallWithInstanceCreatedOnCpp(self): + '''Calls a virtual protected method from parent class on an instance created in C++.''' + p = ProtectedPolymorphicGrandDaughter.create() + self.assertEqual(p.publicName(), p.protectedName()) + self.assertEqual(p.callProtectedName(), p.protectedName()) + + def testReimplementedProtectedCall(self): + '''Calls a reimplemented virtual protected method from parent class.''' + original_name = 'Poly' + p = ExtendedProtectedPolymorphicGrandDaughter(original_name) + name = p.callProtectedName() + self.assertTrue(p.protectedName_called) + self.assertEqual(p.protectedName(), name) + self.assertEqual(ProtectedPolymorphicGrandDaughter.protectedName(p), original_name) + + +class ProtectedVirtualDtorTest(unittest.TestCase): + '''Test cases for protected virtual destructor.''' + + def setUp(self): + ProtectedVirtualDestructor.resetDtorCounter() + + def tearDown(self): + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(cacheSize(), 0) + + def testVirtualProtectedDtor(self): + '''Original protected virtual destructor is being called.''' + dtor_called = ProtectedVirtualDestructor.dtorCalled() + for i in range(1, 10): + pvd = ProtectedVirtualDestructor() + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + del pvd + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(ProtectedVirtualDestructor.dtorCalled(), dtor_called + i) + + def testVirtualProtectedDtorOnCppCreatedObject(self): + '''Original protected virtual destructor is being called for a C++ created object.''' + dtor_called = ProtectedVirtualDestructor.dtorCalled() + for i in range(1, 10): + pvd = ProtectedVirtualDestructor.create() + del pvd + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(ProtectedVirtualDestructor.dtorCalled(), dtor_called + i) + + def testProtectedDtorOnDerivedClass(self): + '''Original protected virtual destructor is being called for a derived class.''' + dtor_called = ExtendedProtectedVirtualDestructor.dtorCalled() + for i in range(1, 10): + pvd = ExtendedProtectedVirtualDestructor() + del pvd + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(ExtendedProtectedVirtualDestructor.dtorCalled(), dtor_called + i) + + +class ExtendedProtectedEnumClass(ProtectedEnumClass): + def __init__(self): + ProtectedEnumClass.__init__(self) + + def protectedEnumMethod(self, value): + if value == ProtectedEnumClass.ProtectedItem0: + return ProtectedEnumClass.ProtectedItem1 + return ProtectedEnumClass.ProtectedItem0 + + def publicEnumMethod(self, value): + if value == ProtectedEnumClass.PublicItem0: + return ProtectedEnumClass.PublicItem1 + return ProtectedEnumClass.PublicItem0 + + +class ProtectedEnumTest(unittest.TestCase): + '''Test cases for protected enum.''' + + def tearDown(self): + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(cacheSize(), 0) + + def testProtectedMethodWithProtectedEnumArgument(self): + '''Calls protected method with protected enum argument.''' + obj = ProtectedEnumClass() + + self.assertEqual(type(ProtectedEnumClass.ProtectedItem0), ProtectedEnumClass.ProtectedEnum) + + self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem0), + ProtectedEnumClass.ProtectedItem0) + self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem1), + ProtectedEnumClass.ProtectedItem1) + self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem0), + ProtectedEnumClass.ProtectedItem0) + self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem1), + ProtectedEnumClass.ProtectedItem1) + + def testProtectedMethodWithPublicEnumArgument(self): + '''Calls protected method with public enum argument.''' + obj = ProtectedEnumClass() + + self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem0), + ProtectedEnumClass.PublicItem0) + self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem1), + ProtectedEnumClass.PublicItem1) + + self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem0), + ProtectedEnumClass.PublicItem0) + self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem1), + ProtectedEnumClass.PublicItem1) + + def testOverriddenProtectedMethodWithProtectedEnumArgument(self): + '''Calls overridden protected method with protected enum argument.''' + obj = ExtendedProtectedEnumClass() + + self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem0), + ProtectedEnumClass.ProtectedItem1) + self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem1), + ProtectedEnumClass.ProtectedItem0) + + self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj, ProtectedEnumClass.ProtectedItem0), # noqa: E501 + ProtectedEnumClass.ProtectedItem0) + self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj, + ProtectedEnumClass.ProtectedItem1), ProtectedEnumClass.ProtectedItem1) + + self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem0), + ProtectedEnumClass.ProtectedItem1) + self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem1), + ProtectedEnumClass.ProtectedItem0) + + def testOverriddenProtectedMethodWithPublicEnumArgument(self): + '''Calls overridden protected method with public enum argument.''' + obj = ExtendedProtectedEnumClass() + + self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem0), + ProtectedEnumClass.PublicItem1) + self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem1), + ProtectedEnumClass.PublicItem0) + + self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicItem0), + ProtectedEnumClass.PublicItem0) + self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicItem1), + ProtectedEnumClass.PublicItem1) + + self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem0), + ProtectedEnumClass.PublicItem1) + self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem1), + ProtectedEnumClass.PublicItem0) + + +class ProtectedPropertyTest(unittest.TestCase): + '''Test cases for a class with a protected property (or field in C++).''' + + def setUp(self): + self.obj = ProtectedProperty() + + def tearDown(self): + del self.obj + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(cacheSize(), 0) + + def testProtectedProperty(self): + '''Writes and reads a protected integer property.''' + self.obj.protectedProperty = 3 + self.assertEqual(self.obj.protectedProperty, 3) + + def testProtectedContainerProperty(self): + '''Writes and reads a protected list of integers property.''' + lst = [1, 2, 3, 4] + self.obj.protectedContainerProperty = lst + self.assertEqual(self.obj.protectedContainerProperty, lst) + + def testProtectedEnumProperty(self): + '''Writes and reads a protected enum property.''' + self.obj.protectedEnumProperty = Event.SOME_EVENT + self.assertEqual(self.obj.protectedEnumProperty, Event.SOME_EVENT) + + def testProtectedValueTypeProperty(self): + '''Writes and reads a protected value type property.''' + point = Point(12, 34) + self.obj.protectedValueTypeProperty = point + self.assertEqual(self.obj.protectedValueTypeProperty, point) + self.assertFalse(self.obj.protectedValueTypeProperty is point) + pointProperty = self.obj.protectedValueTypeProperty + self.assertTrue(self.obj.protectedValueTypeProperty is pointProperty) + + def testProtectedValueTypePropertyWrapperRegistration(self): + '''Access colocated protected value type property.''' + cache_size = cacheSize() + point = Point(12, 34) + obj = createProtectedProperty() + obj.protectedValueTypeProperty + self.assertEqual(obj.protectedValueTypeProperty.copy(), + obj.protectedValueTypeProperty) + obj.protectedValueTypeProperty = point + self.assertEqual(obj.protectedValueTypeProperty, point) + self.assertFalse(obj.protectedValueTypeProperty is point) + pointProperty = obj.protectedValueTypeProperty + self.assertTrue(obj.protectedValueTypeProperty is pointProperty) + del obj, point, pointProperty + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(cacheSize(), cache_size) + + def testProtectedValueTypePointerProperty(self): + '''Writes and reads a protected value type pointer property.''' + pt1 = Point(12, 34) + pt2 = Point(12, 34) + self.obj.protectedValueTypePointerProperty = pt1 + self.assertEqual(self.obj.protectedValueTypePointerProperty, pt1) + self.assertEqual(self.obj.protectedValueTypePointerProperty, pt2) + self.assertTrue(self.obj.protectedValueTypePointerProperty is pt1) + self.assertFalse(self.obj.protectedValueTypePointerProperty is pt2) + # PYSIDE-535: Need to assign None to break the cycle + self.obj.protectedValueTypePointerProperty = None + + def testProtectedObjectTypeProperty(self): + '''Writes and reads a protected object type property.''' + obj = ObjectType() + self.obj.protectedObjectTypeProperty = obj + self.assertEqual(self.obj.protectedObjectTypeProperty, obj) + # PYSIDE-535: Need to assign None to break the cycle + self.obj.protectedObjectTypeProperty = None + + +class PrivateDtorProtectedMethodTest(unittest.TestCase): + '''Test cases for classes with private destructors and protected methods.''' + + def tearDown(self): + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(cacheSize(), 0) + + def testProtectedMethod(self): + '''Calls protected method of a class with a private destructor.''' + obj = PrivateDtor.instance() + + self.assertEqual(type(obj), PrivateDtor) + self.assertEqual(obj.instanceCalls(), 1) + self.assertEqual(obj.instanceCalls(), obj.protectedInstanceCalls()) + obj = PrivateDtor.instance() + self.assertEqual(obj.instanceCalls(), 2) + self.assertEqual(obj.instanceCalls(), obj.protectedInstanceCalls()) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/pstrlist_test.py b/sources/shiboken6/tests/samplebinding/pstrlist_test.py new file mode 100644 index 000000000..d60f9cf35 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/pstrlist_test.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +import sample + + +class PStrListTest(unittest.TestCase): + + def testPStrList(self): + a = 'str0' + b = 'str1' + lst = sample.createPStrList(a, b) + self.assertEqual(lst, [a, b]) + + def testListOfPStr(self): + a = 'str0' + b = 'str1' + lst = sample.createListOfPStr(a, b) + self.assertEqual(lst, [a, b]) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/pystr_test.py b/sources/shiboken6/tests/samplebinding/pystr_test.py new file mode 100644 index 000000000..ec64c1e31 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/pystr_test.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for definition of __str__ method.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Point + + +class PyStrTest(unittest.TestCase): + '''Test case for definition of __str__ method.''' + + def testPyStr(self): + '''Test case for defined __str__ method.''' + pt = Point(5, 2) + self.assertEqual(str(pt), 'Point(5.0, 2.0)') + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/python_thread_test.py b/sources/shiboken6/tests/samplebinding/python_thread_test.py new file mode 100644 index 000000000..65398b5c6 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/python_thread_test.py @@ -0,0 +1,96 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#!/usr/bin/env python + +'''Tests for using Shiboken-based bindings with python threads''' + +import logging +import os +from random import random +import sys +import threading +import time +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +import sample + +#logging.basicConfig(level=logging.DEBUG) + + +class Producer(threading.Thread): + '''Producer thread''' + + def __init__(self, bucket, max_runs, *args): + #Constructor. Receives the bucket + super(Producer, self).__init__(*args) + self.runs = 0 + self.bucket = bucket + self.max_runs = max_runs + self.production_list = [] + + def run(self): + while self.runs < self.max_runs: + value = int(random() * 10) % 10 + self.bucket.push(value) + self.production_list.append(value) + logging.debug(f'PRODUCER - pushed {value}') + self.runs += 1 + #self.msleep(5) + time.sleep(0.01) + + +class Consumer(threading.Thread): + '''Consumer thread''' + def __init__(self, bucket, max_runs, *args): + #Constructor. Receives the bucket + super(Consumer, self).__init__(*args) + self.runs = 0 + self.bucket = bucket + self.max_runs = max_runs + self.consumption_list = [] + + def run(self): + while self.runs < self.max_runs: + if not self.bucket.empty(): + value = self.bucket.pop() + self.consumption_list.append(value) + logging.debug(f'CONSUMER - got {value}') + self.runs += 1 + else: + logging.debug('CONSUMER - empty bucket') + time.sleep(0.01) + + +class ProducerConsumer(unittest.TestCase): + '''Basic test case for producer-consumer QThread''' + + def finishCb(self): + #Quits the application + self.app.exit(0) + + def testProdCon(self): + #QThread producer-consumer example + bucket = sample.Bucket() + prod = Producer(bucket, 10) + cons = Consumer(bucket, 10) + + prod.start() + cons.start() + + #QObject.connect(prod, SIGNAL('finished()'), self.finishCb) + #QObject.connect(cons, SIGNAL('finished()'), self.finishCb) + + prod.join() + cons.join() + + self.assertEqual(prod.production_list, cons.consumption_list) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/receive_null_cstring_test.py b/sources/shiboken6/tests/samplebinding/receive_null_cstring_test.py new file mode 100644 index 000000000..1d19de941 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/receive_null_cstring_test.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test case for a function that could receive a NULL pointer in a '[const] char*' parameter.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import countCharacters + + +class ReceiveNullCStringTest(unittest.TestCase): + '''Test case for a function that could receive a NULL pointer in a '[const] char*' + parameter.''' + + def testBasic(self): + '''The test function should be working for the basic cases.''' + a = '' + b = 'abc' + self.assertEqual(countCharacters(a), len(a)) + self.assertEqual(countCharacters(b), len(b)) + + def testReceiveNull(self): + '''The test function returns '-1' when receives a None value instead of a string.''' + self.assertEqual(countCharacters(None), -1) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/reference_test.py b/sources/shiboken6/tests/samplebinding/reference_test.py new file mode 100644 index 000000000..1b6dd3a7a --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/reference_test.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for methods that receive references to objects.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Reference, Str + + +class ExtendedReference(Reference): + def __init__(self): + Reference.__init__(self) + self.uses_reference_virtual_called = False + self.uses_const_reference_virtual_called = False + self.reference_inc = 1 + self.const_reference_inc = 2 + self.multiplier = 333 + + def usesReferenceVirtual(self, ref, inc): + self.uses_reference_virtual_called = True + return ref.objId() + inc + self.reference_inc + + def usesConstReferenceVirtual(self, ref, inc): + self.uses_const_reference_virtual_called = True + return ref.objId() + inc + self.const_reference_inc + + def alterReferenceIdVirtual(self, ref): + ref.setObjId(ref.objId() * self.multiplier) + + +class ReferenceTest(unittest.TestCase): + '''Test case for methods that receive references to objects.''' + + def testMethodThatReceivesReference(self): + '''Test a method that receives a reference to an object as argument.''' + objId = 123 + r = Reference(objId) + self.assertEqual(Reference.usesReference(r), objId) + + def testCantSegFaultWhenReceiveNone(self): + '''do not segfault when receiving None as argument.''' + s = Str() + self.assertFalse(bool(s)) + + def testMethodThatReceivesConstReference(self): + '''Test a method that receives a const reference to an object as argument.''' + objId = 123 + r = Reference(objId) + self.assertEqual(Reference.usesConstReference(r), objId) + + def testModificationOfReference(self): + '''Tests if the identity of a reference argument is preserved when passing + it to be altered in C++.''' + objId = 123 + r1 = Reference(objId) + r1.alterReferenceIdVirtual(r1) + self.assertEqual(r1.objId(), objId * Reference.multiplier()) + + def testModificationOfReferenceCallingAVirtualIndirectly(self): + '''Tests if the identity of a reference argument is preserved when passing it + to be altered in C++ through a method that calls a virtual method.''' + objId = 123 + r1 = Reference(objId) + r1.callAlterReferenceIdVirtual(r1) + self.assertEqual(r1.objId(), objId * Reference.multiplier()) + + def testModificationOfReferenceCallingAReimplementedVirtualIndirectly(self): + '''Test if a Python override of a virtual method with a reference parameter + called from C++ alters the argument properly.''' + objId = 123 + r = Reference(objId) + er = ExtendedReference() + result = er.callAlterReferenceIdVirtual(r) # noqa: F841 + self.assertEqual(r.objId(), objId * er.multiplier) + + def testReimplementedVirtualMethodCallWithReferenceParameter(self): + '''Test if a Python override of a virtual method with a reference parameter + is correctly called from C++.''' + inc = 9 + objId = 123 + r = Reference(objId) + er = ExtendedReference() + result = er.callUsesReferenceVirtual(r, inc) + self.assertEqual(result, objId + inc + er.reference_inc) + + def testReimplementedVirtualMethodCallWithConstReferenceParameter(self): + '''Test if a Python override of a virtual method with a const reference + parameter is correctly called from C++.''' + inc = 9 + objId = 123 + r = Reference(objId) + er = ExtendedReference() + result = er.callUsesConstReferenceVirtual(r, inc) + self.assertEqual(result, objId + inc + er.const_reference_inc) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/referencetopointer_test.py b/sources/shiboken6/tests/samplebinding/referencetopointer_test.py new file mode 100644 index 000000000..942c7ea29 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/referencetopointer_test.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for a reference to pointer argument type.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import VirtualMethods, Str + + +class ExtendedVirtualMethods(VirtualMethods): + def __init__(self): + VirtualMethods.__init__(self) + self.prefix = 'Ext' + + def createStr(self, text): + ext_text = text + if text is not None: + ext_text = self.prefix + text + return VirtualMethods.createStr(self, ext_text) + + +class ReferenceToPointerTest(unittest.TestCase): + '''Test cases for a reference to pointer argument type.''' + + def testSimpleCallWithNone(self): + '''Simple call to createStr method with a None argument.''' + obj = VirtualMethods() + ok, string = obj.createStr(None) + self.assertFalse(ok) + self.assertEqual(string, None) + + def testSimpleCallWithString(self): + '''Simple call to createStr method with a Python string argument.''' + obj = VirtualMethods() + ok, string = obj.createStr('foo') + self.assertTrue(ok) + self.assertEqual(string, Str('foo')) + + def testCallNonReimplementedMethodWithNone(self): + '''Calls createStr method from C++ with a None argument.''' + obj = VirtualMethods() + ok, string = obj.callCreateStr(None) + self.assertFalse(ok) + self.assertEqual(string, None) + + def testCallNonReimplementedMethodWithString(self): + '''Calls createStr method from C++ with a Python string argument.''' + obj = VirtualMethods() + ok, string = obj.callCreateStr('foo') + self.assertTrue(ok) + self.assertEqual(string, Str('foo')) + + def testCallReimplementedMethodWithNone(self): + '''Calls reimplemented createStr method from C++ with a None argument.''' + obj = ExtendedVirtualMethods() + ok, string = obj.callCreateStr(None) + self.assertFalse(ok) + self.assertEqual(string, None) + + def testCallReimplementedMethodWithString(self): + '''Calls reimplemented createStr method from C++ with a Python string argument.''' + obj = ExtendedVirtualMethods() + ok, string = obj.callCreateStr('foo') + self.assertTrue(ok) + self.assertEqual(string, Str(obj.prefix + 'foo')) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/renaming_test.py b/sources/shiboken6/tests/samplebinding/renaming_test.py new file mode 100644 index 000000000..b08438ef3 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/renaming_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for renaming using target-lang-name attribute.''' + +import os +import re +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import RenamedValue, RenamedUser + +from shibokensupport.signature import get_signature + + +class RenamingTest(unittest.TestCase): + def test(self): + '''Tests whether the C++ class ToBeRenamedValue renamed via attribute + target-lang-name to RenamedValue shows up in consuming function + signature strings correctly. + ''' + renamed_value = RenamedValue() + self.assertEqual(str(type(renamed_value)), + "<class 'sample.RenamedValue'>") + rename_user = RenamedUser() + rename_user.useRenamedValue(renamed_value) + actual_signature = str(get_signature(rename_user.useRenamedValue)) + self.assertTrue(re.match(r"^\(self,\s*?v:\s*?sample.RenamedValue\)\s*?->\s*?None$", + actual_signature)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/return_null_test.py b/sources/shiboken6/tests/samplebinding/return_null_test.py new file mode 100644 index 000000000..2c4f07c65 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/return_null_test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test case for functions that could return a NULL pointer.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import (returnNullPrimitivePointer, returnNullValueTypePointer, + returnNullObjectTypePointer) + + +class ReturnNullTest(unittest.TestCase): + '''Test case for functions that could return a NULL pointer.''' + + def testReturnNull(self): + '''Function returns a NULL pointer to a primitive type.''' + o = returnNullPrimitivePointer() + self.assertEqual(o, None) + + def testReturnNullObjectType(self): + '''Function returns a NULL pointer to an object-type.''' + o = returnNullObjectTypePointer() + self.assertEqual(o, None) + + def testReturnNullValueType(self): + '''Function returns a NULL pointer to a value-type.''' + o = returnNullValueTypePointer() + self.assertEqual(o, None) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/richcompare_test.py b/sources/shiboken6/tests/samplebinding/richcompare_test.py new file mode 100644 index 000000000..3146d0faf --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/richcompare_test.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Expression + + +class TestRichCompare(unittest.TestCase): + + def testIt(self): + a = Expression(2) + b = Expression(3) + c = a + b + d = a + c < b + a + self.assertEqual(d.toString(), "((2+(2+3))<(3+2))") + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/sample-binding.txt.in b/sources/shiboken6/tests/samplebinding/sample-binding.txt.in new file mode 100644 index 000000000..bcf9de90f --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/sample-binding.txt.in @@ -0,0 +1,16 @@ +[generator-project] + +generator-set = shiboken + +header-file = @CMAKE_CURRENT_SOURCE_DIR@/global.h +typesystem-file = @sample_TYPESYSTEM@ + +output-directory = @CMAKE_CURRENT_BINARY_DIR@ + +include-path = @libsample_SOURCE_DIR@ + +typesystem-path = @CMAKE_CURRENT_SOURCE_DIR@ + +enable-parent-ctor-heuristic +use-isnull-as-nb_nonzero +lean-headers diff --git a/sources/shiboken6/tests/samplebinding/sample_test.py b/sources/shiboken6/tests/samplebinding/sample_test.py new file mode 100644 index 000000000..19b2f708d --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/sample_test.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for libsample bindings module''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +import sample + + +class ModuleTest(unittest.TestCase): + '''Test case for module and global functions''' + + def testAddedFunctionAtModuleLevel(self): + '''Calls function added to module from type system description.''' + str1 = 'Foo' + self.assertEqual(sample.multiplyString(str1, 3), str1 * 3) + self.assertEqual(sample.multiplyString(str1, 0), str1 * 0) + + def testAddedFunctionWithVarargs(self): + '''Calls function that receives varargs added to module from type system description.''' + self.assertEqual(sample.countVarargs(1), 0) + self.assertEqual(sample.countVarargs(1, 2), 1) + self.assertEqual(sample.countVarargs(1, 2, 3, 'a', 'b', 4, (5, 6)), 6) + + def testSampleComparisonOpInNamespace(self): + s1 = sample.sample.sample(10) + s2 = sample.sample.sample(10) + self.assertEqual(s1, s2) + + def testConstant(self): + self.assertEqual(sample.sample.INT_CONSTANT, 42) + + def testStringFunctions(self): + # Test plain ASCII, UCS1 and UCS4 encoding which have different + # representations in the PyUnicode objects. + for t1 in ["ascii", "Ümläut", "😀"]: + expected = t1 + t1 + self.assertEqual(sample.addStdStrings(t1, t1), expected) + self.assertEqual(sample.addStdWStrings(t1, t1), expected) + + def testNullPtrT(self): + sample.testNullPtrT(None) + self.assertRaises(TypeError, sample.testNullPtrT, 42) + + def testRValueRefsWithValueTypes(self): + """Passing value types by rvalue refs: For value types, nothing should + happen since the argument is copied in the call and the copy is + moved from.""" + polygon = sample.Polygon() + polygon.addPoint(sample.Point(1, 2)) + polygon.addPoint(sample.Point(3, 4)) + point_count = len(polygon.points()) + self.assertEqual(point_count, sample.takePolygon(polygon)) + + def testRValueRefsWithObjectTypes(self): + """Passing object types by rvalue refs: The underlying object should + be moved from.""" + o = sample.ObjectType() + object_name = "Name" + o.setObjectName(object_name) + self.assertEqual(len(object_name), sample.takeObjectType(o)) + # o should be moved from, name is now empty + self.assertEqual(len(o.objectName()), 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/samplebinding.pyproject b/sources/shiboken6/tests/samplebinding/samplebinding.pyproject new file mode 100644 index 000000000..ba6ba6f8f --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/samplebinding.pyproject @@ -0,0 +1,131 @@ +{ + "files": ["__del___test.py", + "abstract_test.py", + "addedfunction_test.py", + "addedfunction_with_container_args_test.py", + "argumentmodifications_test.py", + "array_numpy_test.py", + "array_sequence_test.py", + "bug_554_test.py", + "bug_704_test.py", + "bytearray_test.py", + "child_return_test.py", + "class_fields_test.py", + "collector_test.py", + "complex_test.py", + "conversion_operator_test.py", + "copy_test.py", + "ctorconvrule_test.py", + "cyclic_test.py", + "date_test.py", + "decisor_test.py", + "delete_test.py", + "deprecated_test.py", + "derived_test.py", + "duck_punching_test.py", + "echo_test.py", + "enum_test.py", + "enumfromremovednamespace_test.py", + "event_loop_call_virtual_test.py", + "event_loop_thread_test.py", + "exception_test.py", + "filter_test.py", + "handleholder_test.py", + "hashabletype_test.py", + "ignorederefop_test.py", + "implicitconv_numerical_test.py", + "implicitconv_test.py", + "inheritanceandscope_test.py", + "injectcode_test.py", + "innerclass_test.py", + "intlist_test.py", + "intwrapper_test.py", + "invalid_virtual_return_test.py", + "keep_reference_test.py", + "list_test.py", + "lock_test.py", + "map_test.py", + "metaclass_test.py", + "mi_virtual_methods_test.py", + "mixed_mi_test.py", + "modelindex_test.py", + "modelview_test.py", + "modifications_test.py", + "modified_constructor_test.py", + "modifiedvirtualmethods_test.py", + "multi_cpp_inheritance_test.py", + "multiple_derived_test.py", + "namespace_test.py", + "newdivision_test.py", + "nondefaultctor_test.py", + "nontypetemplate_test.py", + "nonzero_test.py", + "numericaltypedef_test.py", + "numpy_test.py", + "objecttype_test.py", + "objecttype_with_named_args_test.py", + "objecttypebyvalue_test.py", + "objecttypelayout_test.py", + "objecttypeoperators_test.py", + "objecttypereferenceasvirtualmethodargument_test.py", + "oddbool_test.py", + "onlycopyclass_test.py", + "overflow_test.py", + "overload_sorting_test.py", + "overload_test.py", + "overloadwithdefault_test.py", + "ownership_argument_invalidation_test.py", + "ownership_delete_child_in_cpp_test.py", + "ownership_delete_child_in_python_test.py", + "ownership_delete_parent_test.py", + "ownership_invalidate_after_use_test.py", + "ownership_invalidate_child_test.py", + "ownership_invalidate_nonpolymorphic_test.py", + "ownership_invalidate_parent_test.py", + "ownership_reparenting_test.py", + "ownership_transference_test.py", + "pair_test.py", + "pen_test.py", + "point_test.py", + "pointerholder_test.py", + "pointerprimitivetype_test.py", + "pointf_test.py", + "primitivereferenceargument_test.py", + "privatector_test.py", + "privatedtor_test.py", + "protected_test.py", + "pstrlist_test.py", + "pystr_test.py", + "python_thread_test.py", + "receive_null_cstring_test.py", + "reference_test.py", + "referencetopointer_test.py", + "renaming_test.py", + "return_null_test.py", + "richcompare_test.py", + "sample_test.py", + "samplesnippets.cpp", + "simplefile_glue.cpp", + "simplefile_test.py", + "size_test.py", + "snakecase_test.py", + "static_nonstatic_methods_test.py", + "str_test.py", + "strlist_test.py", + "templateinheritingclass_test.py", + "time_test.py", + "transform_test.py", + "typeconverters_test.py", + "typedealloc_test.py", + "typedtordoublefree_test.py", + "typesystypedef_test.py", + "unsafe_parent_test.py", + "useraddedctor_test.py", + "virtualdtor_test.py", + "virtualmethods_test.py", + "visibilitychange_test.py", + "voidholder_test.py", + "weakref_test.py", + "writableclassdict_test.py", + "typesystem_sample.xml"] +} diff --git a/sources/shiboken6/tests/samplebinding/samplesnippets.cpp b/sources/shiboken6/tests/samplebinding/samplesnippets.cpp new file mode 100644 index 000000000..43e6b08de --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/samplesnippets.cpp @@ -0,0 +1,54 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +// @snippet intwrapper_add_ints +extern "C" { +static PyObject *Sbk_IntWrapper_add_ints(PyObject * /* self */, PyObject *args) +{ + PyObject *result = nullptr; + if (PyTuple_Check(args) != 0 && PyTuple_Size(args) == 2) { + PyObject *arg1 = PyTuple_GetItem(args, 0); + PyObject *arg2 = PyTuple_GetItem(args, 1); + if (PyLong_Check(arg1) != 0 && PyLong_Check(arg2) != 0) + result = PyLong_FromLong(PyLong_AsLong(arg1) + PyLong_AsLong(arg2)); + } + if (result == nullptr) + PyErr_SetString(PyExc_TypeError, "expecting 2 ints"); + return result; +} +} +// @snippet intwrapper_add_ints + +// @snippet stdcomplex_floor +%PYARG_0 = PyFloat_FromDouble(std::floor(%CPPSELF.abs_value())); +// @snippet stdcomplex_floor + +// @snippet stdcomplex_ceil +%PYARG_0 = PyFloat_FromDouble(std::ceil(%CPPSELF.abs_value())); +// @snippet stdcomplex_ceil + +// @snippet stdcomplex_abs +%PYARG_0 = PyFloat_FromDouble(%CPPSELF.abs_value()); +// @snippet stdcomplex_abs + +// @snippet stdcomplex_pow +%RETURN_TYPE %0 = %CPPSELF.pow(%1); +%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); +// @snippet stdcomplex_pow + +// @snippet size_char_ct +// Convert a string "{width}x{height}" specification +{ + double width = -1; + double height = -1; + const std::string s = %1; + const auto pos = s.find('x'); + if (pos != std::string::npos) { + std::istringstream wstr(s.substr(0, pos)); + wstr >> width; + std::istringstream hstr(s.substr(pos + 1, s.size() - pos - 1)); + hstr >> height; + } + %0 = new %TYPE(width, height); +} +// @snippet size_char_ct diff --git a/sources/shiboken6/tests/samplebinding/simplefile_glue.cpp b/sources/shiboken6/tests/samplebinding/simplefile_glue.cpp new file mode 100644 index 000000000..76c0cfaf2 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/simplefile_glue.cpp @@ -0,0 +1,9 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +if (!%CPPSELF.%FUNCTION_NAME()) { + PyObject* error_msg = PyBytes_FromFormat( + "Could not open file: \"%s\"", %CPPSELF->filename()); + PyErr_SetObject(PyExc_IOError, error_msg); + return 0; +} diff --git a/sources/shiboken6/tests/samplebinding/simplefile_test.py b/sources/shiboken6/tests/samplebinding/simplefile_test.py new file mode 100644 index 000000000..55c894a35 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/simplefile_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for SimpleFile class''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import SimpleFile + + +class SimpleFileTest(unittest.TestCase): + '''Test cases for SimpleFile class.''' + + def setUp(self): + filename = f'simplefile{os.getpid()}.txt' + self.existing_filename = Path(os.curdir) / filename + self.delete_file = False + if not self.existing_filename.exists(): + with self.existing_filename.open('w') as f: + for line in range(10): + f.write('sbrubbles\n') + self.delete_file = True + + self.non_existing_filename = Path(os.curdir) / 'inexistingfile.txt' + i = 0 + while self.non_existing_filename.exists(): + i += 1 + filename = f'inexistingfile-{i}.txt' + self.non_existing_filename = Path(os.curdir) / filename + + def tearDown(self): + if self.delete_file: + os.remove(self.existing_filename) + + def testExistingFile(self): + '''Test SimpleFile class with existing file.''' + f = SimpleFile(os.fspath(self.existing_filename)) + self.assertEqual(f.filename(), os.fspath(self.existing_filename)) + f.open() + self.assertNotEqual(f.size(), 0) + f.close() + + def testNonExistingFile(self): + '''Test SimpleFile class with non-existing file.''' + f = SimpleFile(os.fspath(self.non_existing_filename)) + self.assertEqual(f.filename(), os.fspath(self.non_existing_filename)) + self.assertRaises(IOError, f.open) + self.assertEqual(f.size(), 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/size_test.py b/sources/shiboken6/tests/samplebinding/size_test.py new file mode 100644 index 000000000..069ce59b3 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/size_test.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for operator overloads on Size class''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Size + + +class PointTest(unittest.TestCase): + '''Test case for Size class, including operator overloads.''' + + def testConstructor(self): + '''Test Size class constructor.''' + width, height = (5.0, 2.3) + size = Size(width, height) + self.assertEqual(size.width(), width) + self.assertEqual(size.height(), height) + self.assertEqual(size.calculateArea(), width * height) + + def testCopyConstructor(self): + '''Test Size class copy constructor.''' + width, height = (5.0, 2.3) + s1 = Size(width, height) + s2 = Size(s1) + self.assertFalse(s1 is s2) + self.assertEqual(s1, s2) + + def testPlusOperator(self): + '''Test Size class + operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(0.5, 3.2) + self.assertEqual(s1 + s2, Size(5.0 + 0.5, 2.3 + 3.2)) + + def testEqualOperator(self): + '''Test Size class == operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(5.0, 2.3) + s3 = Size(0.5, 3.2) + self.assertTrue(s1 == s1) + self.assertTrue(s1 == s2) + self.assertFalse(s1 == s3) + + def testNotEqualOperator(self): + '''Test Size class != operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(5.0, 2.3) + s3 = Size(0.5, 3.2) + self.assertFalse(s1 != s1) + self.assertFalse(s1 != s2) + self.assertTrue(s1 != s3) + + def testMinorEqualOperator(self): + '''Test Size class <= operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(5.0, 2.3) + s3 = Size(0.5, 3.2) + self.assertTrue(s1 <= s1) + self.assertTrue(s1 <= s2) + self.assertTrue(s3 <= s1) + self.assertFalse(s1 <= s3) + + def testMinorOperator(self): + '''Test Size class < operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(0.5, 3.2) + self.assertFalse(s1 < s1) + self.assertFalse(s1 < s2) + self.assertTrue(s2 < s1) + + def testMajorEqualOperator(self): + '''Test Size class >= operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(5.0, 2.3) + s3 = Size(0.5, 3.2) + self.assertTrue(s1 >= s1) + self.assertTrue(s1 >= s2) + self.assertTrue(s1 >= s3) + self.assertFalse(s3 >= s1) + + def testMajorOperator(self): + '''Test Size class > operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(0.5, 3.2) + self.assertFalse(s1 > s1) + self.assertTrue(s1 > s2) + self.assertFalse(s2 > s1) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/snakecase_test.py b/sources/shiboken6/tests/samplebinding/snakecase_test.py new file mode 100644 index 000000000..a1538796a --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/snakecase_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for snake case generation''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import (SnakeCaseTest, SnakeCaseDerivedTest, + snake_case_global_function) + + +class OverrideTest(SnakeCaseDerivedTest): + def virtual_func(self): + return 4711 + + +class SnakeCaseTestCase(unittest.TestCase): + '''Test for SnakeCaseTest''' + def testMemberFunctions(self): + s = SnakeCaseTest() + self.assertEqual(s.test_function1(), 42) + + self.assertEqual(s.testFunctionDisabled(), 42) + + self.assertEqual(s.testFunctionBoth(), 42) + self.assertEqual(s.test_function_both(), 42) + + def virtualFunctions(self): + s = OverrideTest() + self.assertEqual(s.call_virtual_func(), 4711) + + def testMemberFields(self): + s = SnakeCaseTest() + old_value = s.test_field + s.test_field = old_value + 1 + self.assertEqual(s.test_field, old_value + 1) + + old_value = s.testFieldDisabled + s.testFieldDisabled = old_value + 1 + self.assertEqual(s.testFieldDisabled, old_value + 1) + + old_value = s.test_field_both + s.test_field_both = old_value + 1 + self.assertEqual(s.test_field_both, old_value + 1) + self.assertEqual(s.testFieldBoth, old_value + 1) + + def testGlobalFunction(self): + self.assertEqual(snake_case_global_function(), 42) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/static_nonstatic_methods_test.py b/sources/shiboken6/tests/samplebinding/static_nonstatic_methods_test.py new file mode 100644 index 000000000..cf0889299 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/static_nonstatic_methods_test.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for overloads involving static and non-static versions of a method.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import SimpleFile + + +class SimpleFile2 (SimpleFile): + def exists(self): + return "Mooo" + + +class SimpleFile3 (SimpleFile): + pass + + +class SimpleFile4 (SimpleFile): + exists = 5 + + +class StaticNonStaticMethodsTest(unittest.TestCase): + '''Test cases for overloads involving static and non-static versions of a method.''' + + def setUp(self): + filename = f'simplefile{os.getpid()}.txt' + self.existing_filename = Path(filename) + self.delete_file = False + if not self.existing_filename.exists(): + with self.existing_filename.open('w') as f: + for line in range(10): + f.write('sbrubbles\n') + self.delete_file = True + + self.non_existing_filename = Path('inexistingfile.txt') + i = 0 + while self.non_existing_filename.exists(): + i += 1 + filename = 'inexistingfile-{i}.txt' + self.non_existing_filename = Path(filename) + + def tearDown(self): + if self.delete_file: + os.remove(self.existing_filename) + + def testCallingStaticMethodWithClass(self): + '''Call static method using class.''' + self.assertTrue(SimpleFile.exists(os.fspath(self.existing_filename))) + self.assertFalse(SimpleFile.exists(os.fspath(self.non_existing_filename))) + + def testCallingStaticMethodWithInstance(self): + '''Call static method using instance of class.''' + f = SimpleFile(os.fspath(self.non_existing_filename)) + self.assertTrue(f.exists(os.fspath(self.existing_filename))) + self.assertFalse(f.exists(os.fspath(self.non_existing_filename))) + + def testCallingInstanceMethod(self): + '''Call instance method.''' + f1 = SimpleFile(os.fspath(self.non_existing_filename)) + self.assertFalse(f1.exists()) + f2 = SimpleFile(os.fspath(self.existing_filename)) + self.assertTrue(f2.exists()) + + def testOverridingStaticNonStaticMethod(self): + f = SimpleFile2(os.fspath(self.existing_filename)) + self.assertEqual(f.exists(), "Mooo") + + f = SimpleFile3(os.fspath(self.existing_filename)) + self.assertTrue(f.exists()) + + f = SimpleFile4(os.fspath(self.existing_filename)) + self.assertEqual(f.exists, 5) + + def testDuckPunchingStaticNonStaticMethod(self): + f = SimpleFile(os.fspath(self.existing_filename)) + f.exists = lambda: "Meee" + self.assertEqual(f.exists(), "Meee") + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/stdcomplex_test.py b/sources/shiboken6/tests/samplebinding/stdcomplex_test.py new file mode 100644 index 000000000..0caa9764d --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/stdcomplex_test.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for StdComplex class''' + +import os +import math +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import StdComplex + + +REAL = 5.0 +IMAG = 2.3 + + +class StdComplexTest(unittest.TestCase): + '''Test case for StdComplex class, exercising esoteric number + protocols (Py_nb_). For standard number protocols, see Point.''' + + def testConversion(self): + pt = StdComplex(REAL, IMAG) + self.assertEqual(int(pt), int(round(pt.abs_value()))) + self.assertEqual(float(pt), pt.abs_value()) + + def testAbs(self): + pt = StdComplex(REAL, IMAG) + self.assertEqual(abs(pt), pt.abs_value()) + + def testPow(self): + '''Compare pow() function to builtin Python type.''' + pt = StdComplex(REAL, IMAG) + result = pow(pt, StdComplex(2.0, 0)) + py_pt = complex(REAL, IMAG) + py_result = pow(py_pt, complex(2.0, 0)) + self.assertAlmostEqual(result.real(), py_result.real) + self.assertAlmostEqual(result.imag(), py_result.imag) + + def testFloor(self): + pt = StdComplex(REAL, IMAG) + self.assertEqual(math.floor(pt), math.floor(pt.abs_value())) + + def testCeil(self): + pt = StdComplex(REAL, IMAG) + self.assertEqual(math.ceil(pt), math.ceil(pt.abs_value())) + + def testPlusOperator(self): + '''Test StdComplex class + operator.''' + pt1 = StdComplex(REAL, IMAG) + pt2 = StdComplex(0.5, 3.2) + self.assertEqual(pt1 + pt2, StdComplex(REAL + 0.5, IMAG + 3.2)) + + def testEqualOperator(self): + '''Test StdComplex class == operator.''' + pt1 = StdComplex(REAL, IMAG) + pt2 = StdComplex(REAL, IMAG) + pt3 = StdComplex(0.5, 3.2) + self.assertTrue(pt1 == pt1) + self.assertTrue(pt1 == pt2) + self.assertFalse(pt1 == pt3) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/str_test.py b/sources/shiboken6/tests/samplebinding/str_test.py new file mode 100644 index 000000000..c06fd6428 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/str_test.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for a method that receives a reference to class that is implicitly + convertible from a Python native type.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Str + + +class StrTest(unittest.TestCase): + '''Test cases for thr Str class.''' + + def test__str__Method(self): + '''Test if the binding correcly implements the Python __str__ method.''' + s1 = 'original string' + s2 = Str(s1) + self.assertEqual(s1, s2) + self.assertEqual(s1, str(s2)) + + def testPassExactClassAsReferenceToArgument(self): + '''Test passing the expected class as an argument to a method that expects a reference.''' + s1 = Str('This is %VAR!').arg(Str('Sparta')) + self.assertEqual(str(s1), 'This is Sparta!') + + def testPassPythonTypeImplictlyConvertibleToAClassUsedAsReference(self): + '''Test passing a Python class implicitly convertible to a wrapped class + that is expected to be passed as reference.''' + s1 = Str('This is %VAR!').arg('Athens') + self.assertEqual(str(s1), 'This is Athens!') + + def testSequenceOperators(self): + s1 = Str("abcdef") + self.assertEqual(len(s1), 6) + self.assertEqual(len(Str()), 0) + + # getitem + self.assertEqual(s1[0], "a") + self.assertEqual(s1[1], "b") + self.assertEqual(s1[2], "c") + self.assertEqual(s1[3], "d") + self.assertEqual(s1[4], "e") + self.assertEqual(s1[5], "f") + self.assertEqual(s1[-1], "f") + self.assertEqual(s1[-2], "e") + + self.assertRaises(TypeError, s1.__getitem__, 6) + + # setitem + s1[0] = 'A' + s1[1] = 'B' + self.assertEqual(s1[0], 'A') + self.assertEqual(s1[1], 'B') + self.assertRaises(TypeError, s1.__setitem__(6, 67)) + + def testReverseOperator(self): + s1 = Str("hello") + self.assertEqual(s1 + 2, "hello2") + self.assertEqual(2 + s1, "2hello") + + def testToIntError(self): + self.assertEqual(Str('Z').toInt(), (0, False)) + + def testToIntWithDecimal(self): + decimal = Str('37') + val, ok = decimal.toInt() + self.assertEqual(type(val), int) + self.assertEqual(type(ok), bool) + self.assertEqual(val, int(str(decimal))) + + def testToIntWithOctal(self): + octal = Str('52') + val, ok = octal.toInt(8) + self.assertEqual(type(val), int) + self.assertEqual(type(ok), bool) + self.assertEqual(val, int(str(octal), 8)) + + def testToIntWithHexadecimal(self): + hexa = Str('2A') + val, ok = hexa.toInt(16) + self.assertEqual(type(val), int) + self.assertEqual(type(ok), bool) + self.assertEqual(val, int(str(hexa), 16)) + self.assertEqual(hexa.toInt(), (0, False)) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/strlist_test.py b/sources/shiboken6/tests/samplebinding/strlist_test.py new file mode 100644 index 000000000..2bfb80b67 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/strlist_test.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for StrList class that inherits from std::list<Str>.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Str, StrList + + +class StrListTest(unittest.TestCase): + '''Test cases for StrList class that inherits from std::list<Str>.''' + + def testStrListCtor_NoParams(self): + '''StrList constructor receives no parameter.''' + sl = StrList() + self.assertEqual(len(sl), 0) + self.assertEqual(sl.constructorUsed(), StrList.NoParamsCtor) + + def testStrListCtor_Str(self): + '''StrList constructor receives a Str object.''' + s = Str('Foo') + sl = StrList(s) + self.assertEqual(len(sl), 1) + self.assertEqual(sl[0], s) + self.assertEqual(sl.constructorUsed(), StrList.StrCtor) + + def testStrListCtor_PythonString(self): + '''StrList constructor receives a Python string.''' + s = 'Foo' + sl = StrList(s) + self.assertEqual(len(sl), 1) + self.assertEqual(sl[0], s) + self.assertEqual(sl.constructorUsed(), StrList.StrCtor) + + def testStrListCtor_StrList(self): + '''StrList constructor receives a StrList object.''' + sl1 = StrList(Str('Foo')) + sl2 = StrList(sl1) + #self.assertEqual(len(sl1), len(sl2)) + #self.assertEqual(sl1, sl2) + self.assertEqual(sl2.constructorUsed(), StrList.CopyCtor) + + def testStrListCtor_ListOfStrs(self): + '''StrList constructor receives a Python list of Str objects.''' + strs = [Str('Foo'), Str('Bar')] + sl = StrList(strs) + self.assertEqual(len(sl), len(strs)) + self.assertEqual(sl, strs) + self.assertEqual(sl.constructorUsed(), StrList.ListOfStrCtor) + + def testStrListCtor_MixedListOfStrsAndPythonStrings(self): + '''StrList constructor receives a Python list of mixed Str objects and Python strings.''' + strs = [Str('Foo'), 'Bar'] + sl = StrList(strs) + self.assertEqual(len(sl), len(strs)) + self.assertEqual(sl, strs) + self.assertEqual(sl.constructorUsed(), StrList.ListOfStrCtor) + + def testCompareStrListWithTupleOfStrs(self): + '''Compares StrList with a Python tuple of Str objects.''' + sl = StrList() + sl.append(Str('Foo')) + sl.append(Str('Bar')) + self.assertEqual(len(sl), 2) + self.assertEqual(sl, (Str('Foo'), Str('Bar'))) + + def testCompareStrListWithTupleOfPythonStrings(self): + '''Compares StrList with a Python tuple of Python strings.''' + sl = StrList() + sl.append(Str('Foo')) + sl.append(Str('Bar')) + self.assertEqual(len(sl), 2) + self.assertEqual(sl, ('Foo', 'Bar')) + + def testCompareStrListWithTupleOfStrAndPythonString(self): + '''Compares StrList with a Python tuple of mixed Str objects and Python strings.''' + sl = StrList() + sl.append(Str('Foo')) + sl.append(Str('Bar')) + self.assertEqual(len(sl), 2) + self.assertEqual(sl, (Str('Foo'), 'Bar')) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/templateinheritingclass_test.py b/sources/shiboken6/tests/samplebinding/templateinheritingclass_test.py new file mode 100644 index 000000000..11279c7ec --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/templateinheritingclass_test.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Photon + +'''This tests classes that inherit from template classes, + simulating a situation found in Qt's phonon module.''' + + +class TemplateInheritingClassTest(unittest.TestCase): + def testClassBasics(self): + self.assertEqual(Photon.ValueIdentity.classType(), Photon.IdentityType) + self.assertEqual(Photon.ValueDuplicator.classType(), Photon.DuplicatorType) + + def testInstanceBasics(self): + value = 123 + samer = Photon.ValueIdentity(value) + self.assertEqual(samer.multiplicator(), 1) + doubler = Photon.ValueDuplicator(value) + self.assertEqual(doubler.multiplicator(), 2) + self.assertEqual(samer.value(), doubler.value()) + self.assertEqual(samer.calculate() * 2, doubler.calculate()) + + def testPassToFunctionAsPointer(self): + obj = Photon.ValueDuplicator(123) + self.assertEqual(Photon.callCalculateForValueDuplicatorPointer(obj), obj.calculate()) + + def testPassToFunctionAsReference(self): + obj = Photon.ValueDuplicator(321) + self.assertEqual(Photon.callCalculateForValueDuplicatorReference(obj), obj.calculate()) + + def testPassToMethodAsValue(self): + value1, value2 = 123, 321 + one = Photon.ValueIdentity(value1) + other = Photon.ValueIdentity(value2) + self.assertEqual(one.sumValueUsingPointer(other), value1 + value2) + + def testPassToMethodAsReference(self): + value1, value2 = 123, 321 + one = Photon.ValueDuplicator(value1) + other = Photon.ValueDuplicator(value2) + self.assertEqual(one.sumValueUsingReference(other), value1 + value2) + + def testPassPointerThrough(self): + obj1 = Photon.ValueIdentity(123) + self.assertEqual(obj1, obj1.passPointerThrough(obj1)) + obj2 = Photon.ValueDuplicator(321) + self.assertEqual(obj2, obj2.passPointerThrough(obj2)) + self.assertRaises(TypeError, obj1.passPointerThrough, obj2) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/time_test.py b/sources/shiboken6/tests/samplebinding/time_test.py new file mode 100644 index 000000000..6283a6744 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/time_test.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for constructor and method signature decisor on Time class.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +import datetime + +from sample import Time, ImplicitConv, ObjectType + + +class TimeTest(unittest.TestCase): + '''Test cases for constructor and method signature decisor on Time class. + The constructor and one method have these signatures: CTORMETHOD() and + CTORMETHOD(int h, int m, int s = 0, int ms = 0); there another method + with a more complex signature METH(int, int, ImplicitConv=DEFVALUE, ObjectType=0), + to produce an even worse scenario. + ''' + + def testConstructorWithoutParamers(self): + '''Constructor without parameters: Time()''' + time = Time() + self.assertTrue(time.isNull()) + + def testConstructorWithAllParamers(self): + '''Constructor with all parameters: Time(int h, int m, int s = 0, int ms = 0)''' + time = Time(1, 2, 3, 4) + self.assertTrue(time.toString(), '01:02:03.004') + + def testConstructorWithThreeParamers(self): + '''Constructor with 3 parameters: Time(int h, int m, int s = 0, int ms = 0)''' + time = Time(1, 2, 3) + self.assertTrue(time.toString(), '01:02:03.000') + + def testConstructorWithTwoParamers(self): + '''Constructor with 2 parameters: Time(int h, int m, int s = 0, int ms = 0)''' + time = Time(1, 2) + self.assertTrue(time.toString(), '01:02:00.000') + + def testSimpleMethodWithoutParamers(self): + '''Constructor without parameters: Time.setTime()''' + time = Time(1, 2, 3, 4) + time.setTime() + self.assertTrue(time.isNull()) + + def testSimpleMethodWithAllParamers(self): + '''Simple method with all parameters: Time.setTime(int h, int m, int s = 0, int ms = 0)''' + time = Time() + time.setTime(1, 2, 3, 4) + self.assertTrue(time.toString(), '01:02:03.004') + + def testSimpleMethodWithThreeParamers(self): + '''Simple method with 3 parameters: Time.setTime(int h, int m, int s = 0, int ms = 0)''' + time = Time() + time.setTime(1, 2, 3) + self.assertTrue(time.toString(), '01:02:03.000') + + def testSimpleMethodWithTwoParamers(self): + '''Simple method with 2 parameters: Time.setTime(int h, int m, int s = 0, int ms = 0)''' + time = Time() + time.setTime(1, 2) + self.assertTrue(time.toString(), '01:02:00.000') + + def testMethodWithoutParamers(self): + '''Method without parameters: Time.somethingCompletelyDifferent()''' + time = Time() + result = time.somethingCompletelyDifferent() + self.assertEqual(result, Time.ZeroArgs) + + def testMethodWithAllParamers(self): + '''Method with all parameters: + Time.somethingCompletelyDifferent( + int h, int m, ImplicitConv ic = ImplicitConv::CtorThree, ObjectType* type = 0 + ); + ''' + time = Time() + obj = ObjectType() + result = time.somethingCompletelyDifferent(1, 2, ImplicitConv(2), obj) + self.assertEqual(result, Time.FourArgs) + + def testMethodWithThreeParamers(self): + '''Method with 3 parameters: Time.somethingCompletelyDifferent(...)''' + time = Time() + result = time.somethingCompletelyDifferent(1, 2, ImplicitConv(ImplicitConv.CtorOne)) + self.assertEqual(result, Time.ThreeArgs) + + def testMethodWithTwoParamers(self): + '''Method with 2 parameters: Time.somethingCompletelyDifferent(...)''' + time = Time() + result = time.somethingCompletelyDifferent(1, 2) + self.assertEqual(result, Time.TwoArgs) + + def testMethodWithThreeParamersAndImplicitConversion(self): + '''Method with 3 parameters, the last one triggers an implicit conversion.''' + time = Time() + result = time.somethingCompletelyDifferent(1, 2, ImplicitConv.CtorOne) + self.assertEqual(result, Time.ThreeArgs) + + # PYSIDE-1436: These tests crash at shutdown due to `assert(Not)?Equal`. + def testCompareWithPythonTime(self): + time = Time(12, 32, 5) + py = datetime.time(12, 32, 5) + self.assertEqual(time, py) + + def testNotEqual(self): + time = Time(12, 32, 6) + py = datetime.time(12, 32, 5) + self.assertNotEqual(time, py) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/transform_test.py b/sources/shiboken6/tests/samplebinding/transform_test.py new file mode 100644 index 000000000..7dfd18a4a --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/transform_test.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for argument modification with more than nine arguments.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Point, applyHomogeneousTransform + + +class TransformTest(unittest.TestCase): + '''Test cases for modifying a function with > 9 arguments.''' + + def testTransform_ValidMatrix(self): + '''Transform applies successfully.''' + p = Point(3, 4) + r = applyHomogeneousTransform(p, 0, 1, 0, -1, 0, 0, 0, 0, 1) + self.assertTrue(type(r) is Point) + self.assertEqual(r.x(), 4) + self.assertEqual(r.y(), -3) + + def testTransform_InvalidMatrix(self): + '''Transform does not apply successfully.''' + p = Point(3, 4) + r = applyHomogeneousTransform(p, 1, 0, 0, 0, 1, 0, 0, 0, 0) + self.assertTrue(r is None) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/typeconverters_test.py b/sources/shiboken6/tests/samplebinding/typeconverters_test.py new file mode 100644 index 000000000..987ba6dfd --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/typeconverters_test.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Tests various usages of the type converters.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +import sample + + +class GetPythonTypeByNameTest(unittest.TestCase): + + '''Uses an added function with inject code that uses the libshiboken + function "Shiboken::Conversions::getPythonTypeObject(typeName)".''' + + def testGetObjectType(self): + pyType1 = sample.getPythonType('ObjectType') + self.assertEqual(pyType1, sample.ObjectType) + pyType2 = sample.getPythonType('ObjectType*') + self.assertEqual(pyType2, sample.ObjectType) + self.assertEqual(pyType1, pyType2) + + def testGetValueType(self): + pyType1 = sample.getPythonType('Point') + self.assertEqual(pyType1, sample.Point) + pyType2 = sample.getPythonType('Point*') + self.assertEqual(pyType2, sample.Point) + self.assertEqual(pyType1, pyType2) + + def testGetUsersPrimitiveType(self): + pyType = sample.getPythonType('OddBool') + self.assertEqual(pyType, bool) + + def testGetUsersPrimitiveTypeWithoutTargetLangApiName(self): + '''If the primitive type attribute "target-lang-api-name" is not set + there'll be no Python type associated with the C++ type.''' + pyType = sample.getPythonType('PStr') + self.assertEqual(pyType, None) + + def testPrimitiveTypeAndTypedef(self): + pyType = sample.getPythonType('double') + self.assertEqual(pyType, float) + pyTypedef = sample.getPythonType('real') + self.assertEqual(pyType, pyTypedef) + + def testPairContainerType(self): + pyType = sample.getPythonType('std::pair<Complex,int>') + self.assertEqual(pyType, list) + + def testListContainerType(self): + pyType = sample.getPythonType('std::list<int>') + self.assertEqual(pyType, list) + + def testMapContainerType(self): + pyType = sample.getPythonType('std::map<std::string,int>') + self.assertEqual(pyType, dict) + + def testGlobalEnumType(self): + pyType = sample.getPythonType('GlobalEnum') + self.assertEqual(pyType, sample.GlobalEnum) + + def testScopedEnumType(self): + pyType = sample.getPythonType('Abstract::Type') + self.assertEqual(pyType, sample.Abstract.Type) + + +class CheckValueAndObjectTypeByNameTest(unittest.TestCase): + + '''Uses an added function with inject code that uses the libshiboken + functions that check if a type is Object or Value, based on its converter.''' + + def testErrors(self): + '''not existent type''' + self.assertRaises(ValueError, sample.cppTypeIsValueType, 'NotExistentType') + self.assertRaises(ValueError, sample.cppTypeIsObjectType, 'NotExistentType') + + def testObjectType1(self): + self.assertTrue(sample.cppTypeIsObjectType('ObjectType')) + self.assertFalse(sample.cppTypeIsValueType('ObjectType')) + + def testObjectType2(self): + self.assertTrue(sample.cppTypeIsObjectType('ObjectType*')) + self.assertFalse(sample.cppTypeIsValueType('ObjectType*')) + + def testValueType1(self): + self.assertTrue(sample.cppTypeIsValueType('Point')) + self.assertFalse(sample.cppTypeIsObjectType('Point')) + + def testValueType2(self): + self.assertTrue(sample.cppTypeIsValueType('Point*')) + self.assertFalse(sample.cppTypeIsObjectType('Point*')) + + def testUsersPrimitiveType(self): + self.assertFalse(sample.cppTypeIsValueType('Complex')) + self.assertFalse(sample.cppTypeIsObjectType('Complex')) + + def testContainerType(self): + self.assertFalse(sample.cppTypeIsValueType('std::list<int>')) + self.assertFalse(sample.cppTypeIsObjectType('std::list<int>')) + + +class SpecificConverterTest(unittest.TestCase): + + '''Uses an added function with inject code that uses the libshiboken + adapter class "Shiboken::Conversions::SpecificConverter".''' + + def testNotExistentType(self): + conversion = sample.getConversionTypeString('NotExistentType') + self.assertEqual(conversion, 'Invalid conversion') + + def testObjectType(self): + conversion = sample.getConversionTypeString('ObjectType') + self.assertEqual(conversion, 'Pointer conversion') + conversion = sample.getConversionTypeString('ObjectType*') + self.assertEqual(conversion, 'Pointer conversion') + conversion = sample.getConversionTypeString('ObjectType&') + self.assertEqual(conversion, 'Reference conversion') + + def testValueType(self): + conversion = sample.getConversionTypeString('Point') + self.assertEqual(conversion, 'Copy conversion') + conversion = sample.getConversionTypeString('Point*') + self.assertEqual(conversion, 'Pointer conversion') + conversion = sample.getConversionTypeString('Point&') + self.assertEqual(conversion, 'Reference conversion') + + +class StringBasedConversionTest(unittest.TestCase): + + def testValueType(self): + pts = (sample.Point(1, 1), sample.Point(2, 2), sample.Point(3, 3)) + result = sample.convertValueTypeToCppAndThenToPython(pts[0], pts[1], pts[2]) + for orig, new in zip(pts, result): + self.assertEqual(orig, new) + self.assertFalse(pts[0] is result[0]) + self.assertTrue(pts[1] is result[1]) + self.assertTrue(pts[2] is result[2]) + + def testObjectType(self): + objs = (sample.ObjectType(), sample.ObjectType()) + objs[0].setObjectName('obj0') + objs[1].setObjectName('obj1') + result = sample.convertObjectTypeToCppAndThenToPython(objs[0], objs[1]) + for orig, new in zip(objs, result): + self.assertEqual(orig, new) + self.assertEqual(orig.objectName(), new.objectName()) + self.assertTrue(orig is new) + + def testContainerType(self): + lst = range(4) + result = sample.convertListOfIntegersToCppAndThenToPython(lst) + self.assertTrue(len(result), 1) + self.assertTrue(lst, result[0]) + + +class PrimitiveConversionTest(unittest.TestCase): + + def testCppPrimitiveType(self): + integers = (12, 34) + result = sample.convertIntegersToCppAndThenToPython(integers[0], integers[1]) + for orig, new in zip(integers, result): + self.assertEqual(orig, new) + + def testLargeIntAsFloat(self): + """PYSIDE-2417: When passing an int to a function taking float, + a 64bit conversion should be done.""" + point = sample.PointF(1, 2) + large_int = 2**31 + 2 + point.setX(large_int) + self.assertEqual(round(point.x()), large_int) + + def testUnsignedLongLongAsFloat(self): + """PYSIDE-2652: When passing an unsigned long long to a function taking float, + an unsigned 64bit conversion should be done.""" + point = sample.PointF(1, 2) + large_int = 2**63 + point.setX(large_int) + self.assertEqual(round(point.x()), large_int) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/typedealloc_test.py b/sources/shiboken6/tests/samplebinding/typedealloc_test.py new file mode 100644 index 000000000..ce881e802 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/typedealloc_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test deallocation of type extended in Python.''' + +import gc +import os +import sys +import unittest +import weakref + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Point + + +class TypeDeallocTest(unittest.TestCase): + + def setUp(self): + self.called = False + + def tearDown(self): + del self.called + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + def callback(self, *args): + self.called = True + + def testScopeEnd(self): + ref = None + + def scope(): + + class Ext(Point): + pass + + o = Ext() # noqa: F841 + global ref + ref = weakref.ref(Ext, self.callback) + + scope() + gc.collect() + self.assertTrue(self.called) + + def testDeleteType(self): + class Ext(Point): + pass + ref = weakref.ref(Ext, self.callback) # noqa: F841 + del Ext + gc.collect() + self.assertTrue(self.called) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/typedtordoublefree_test.py b/sources/shiboken6/tests/samplebinding/typedtordoublefree_test.py new file mode 100644 index 000000000..ab8e535b5 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/typedtordoublefree_test.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import ObjectType + + +class TestTypeDestructorDoubleFree(unittest.TestCase): + def testTypeDestructorDoubleFree(self): + '''Causes the type destructors of two derived classes to be called.''' + def scope(): + class ExtObj1(ObjectType): + def __init__(self): + ObjectType.__init__(self) + obj = ExtObj1() + child = ObjectType(parent=obj) + self.assertEqual(obj.takeChild(child), child) + + class ExtObj2(ObjectType): + def __init__(self): + ObjectType.__init__(self) + + obj = ExtObj2() + child = ObjectType(parent=obj) + self.assertEqual(obj.takeChild(child), child) + scope() + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml new file mode 100644 index 000000000..e315e599e --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml @@ -0,0 +1,2457 @@ +<?xml version="1.0" encoding="UTF-8"?> +<typesystem package="sample"> + <primitive-type name="ObjectType::Identifier"/> + <primitive-type name="std::nullptr_t"/> + + <primitive-type name="Foo::SAMPLE_HANDLE" target-lang-api-name="PyLong"/> + + <primitive-type name="std::size_t" target-lang-api-name="PyLong"> + <conversion-rule> + <native-to-target> + return PyLong_FromSize_t(%in); + </native-to-target> + <target-to-native> + <add-conversion type="PyLong"> + %out = %OUTTYPE(PyLong_AsSsize_t(%in)); + </add-conversion> + </target-to-native> + </conversion-rule> + </primitive-type> + + <inject-code class="native" position="beginning"> + static bool Check2TupleOfNumbers(PyObject* pyIn) { + if (!PySequence_Check(pyIn) || !(PySequence_Size(pyIn) == 2)) + return false; + Shiboken::AutoDecRef pyReal(PySequence_GetItem(pyIn, 0)); + if (!PyNumber_Check(pyReal)) + return false; + Shiboken::AutoDecRef pyImag(PySequence_GetItem(pyIn, 1)); + if (!PyNumber_Check(pyImag)) + return false; + return true; + } + </inject-code> + <primitive-type name="Complex" target-lang-api-name="PyComplex"> + <include file-name="complex.h" location="global"/> + <conversion-rule> + <native-to-target> + return PyComplex_FromDoubles(%in.real(), %in.imag()); + </native-to-target> + <target-to-native> + <!-- The 'check' attribute can be derived from the 'type' attribute, + it is defined here to test the CHECKTYPE type system variable. --> + <add-conversion type="PyComplex" check="%CHECKTYPE[Complex](%in)"> + double real = PyComplex_RealAsDouble(%in); + double imag = PyComplex_ImagAsDouble(%in); + %out = %OUTTYPE(real, imag); + </add-conversion> + <add-conversion type="PySequence" check="Check2TupleOfNumbers(%in)"> + Shiboken::AutoDecRef pyReal(PySequence_GetItem(%in, 0)); + Shiboken::AutoDecRef pyImag(PySequence_GetItem(%in, 1)); + double real = %CONVERTTOCPP[double](pyReal); + double imag = %CONVERTTOCPP[double](pyImag); + %out = %OUTTYPE(real, imag); + </add-conversion> + </target-to-native> + </conversion-rule> + </primitive-type> + + <primitive-type name="Null"> + <include file-name="null.h" location="global"/> + <conversion-rule> + <native-to-target> + SBK_UNUSED(%in); + Py_RETURN_NONE; + </native-to-target> + <target-to-native> + <add-conversion type="PyObject" check="%in == 0 || %in == Py_None"> + %out = %OUTTYPE(%in == 0); + </add-conversion> + </target-to-native> + </conversion-rule> + </primitive-type> + + <primitive-type name="SAMPLE_HANDLE" target-lang-api-name="PyComplex"> + <include file-name="handle.h" location="local"/> + <conversion-rule> + <native-to-target> + if (!%in) + Py_RETURN_NONE; + return PyCapsule_New(%in, nullptr, nullptr); + </native-to-target> + <target-to-native> + <add-conversion type="PyNone"> + SBK_UNUSED(%in) + %out = 0; + </add-conversion> + <add-conversion check="checkPyCapsuleOrPyCObject(%in)" type="PyObject"> + void *ptr = PyCapsule_GetPointer(%in, nullptr); + %out = (%OUTTYPE)ptr; + </add-conversion> + </target-to-native> + </conversion-rule> + </primitive-type> + + <inject-code class="native" position="beginning"> + static bool checkPyCapsuleOrPyCObject(PyObject* pyObj) + { + return PyCapsule_CheckExact(pyObj); + } + </inject-code> + + <primitive-type name="PrimitiveStructPtr"> + <include file-name="handle.h" location="local"/> + <conversion-rule> + <native-to-target> + return PyCapsule_New(&%in, nullptr, nullptr); + </native-to-target> + <target-to-native> + <add-conversion check="checkPyCapsuleOrPyCObject(%in)" type="PyObject"> + void *ptr = PyCapsule_GetPointer(%in, nullptr); + %out = *reinterpret_cast<%OUTTYPE*>(ptr); + </add-conversion> + </target-to-native> + </conversion-rule> + </primitive-type> + + <primitive-type name="OddBool" target-lang-api-name="PyBool" default-constructor="OddBool(false)"> + <include file-name="oddbool.h" location="global"/> + <include file-name="complex.h" location="global"/> + <conversion-rule> + <native-to-target> + return PyBool_FromLong(%in.value()); + </native-to-target> + <target-to-native> + <add-conversion type="PyBool"> + // Tests CONVERTTOCPP macro with C++ primitive type. + bool b = %CONVERTTOCPP[bool](%in); + %out = %OUTTYPE(b); + </add-conversion> + <add-conversion type="PyComplex"> + // Tests CONVERTTOCPP macro with user's primitive type. + Complex cpx = %CONVERTTOCPP[Complex](%in); + %out = %OUTTYPE(cpx.real() != 0.0 || cpx.imag() != 0.0); + </add-conversion> + </target-to-native> + </conversion-rule> + </primitive-type> + + <!-- As of Qt 6, there is a trend of hiding bool returns of comparison + operators of container classes behind some template expression using + SFINAE depending on their value's traits, like: + template <typename U = T> + friend QTypeTraits::compare_eq_result<U> operator==(const QList &l, const QList &r) + which the clang parser cannot identify. Rich comparison of classes + inheriting QList (QPolygon, QItemSelection) will then not be generated. + To work around, the operators should be added manually without + injecting code. The code should just use the standard implementation. --> + <value-type name="ComparisonTester"> + <include file-name="oddbool.h" location="global"/> + <add-function signature="operator==(const ComparisonTester&)" return-type="bool"/> + <add-function signature="operator!=(const ComparisonTester&)" return-type="bool"/> + </value-type> + <value-type name="SpaceshipComparisonTester"> + <enum-type name="Enabled"/> + </value-type> + + <primitive-type name="PStr"> + <include file-name="str.h" location="global"/> + <conversion-rule> + <native-to-target> + return Shiboken::String::fromCString(%in.cstring(), %in.size()); + </native-to-target> + <target-to-native> + <add-conversion type="PyUnicode" check="Shiboken::String::check(%in)"> + const char* str = %CONVERTTOCPP[const char*](%in); + %out = %OUTTYPE(str); + </add-conversion> + <add-conversion type="Py_None"> + SBK_UNUSED(%in) + %out = %OUTTYPE(); + </add-conversion> + </target-to-native> + </conversion-rule> + </primitive-type> + + <function signature="changePStr(PStr*, const char*)"> + <!-- + Comment out these modifications and the Shiboken generator + will issue a fatal error, because it can't handle a pointer + to a primitive type (PStr*) without help from the binding + developer. + --> + <modify-function> + <modify-argument index="1"> + <replace-type modified-type="PStr"/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject"/> + </modify-argument> + <inject-code class="target" position="beginning"> + %FUNCTION_NAME(&%1, %2); + %PYARG_0 = %CONVERTTOPYTHON[PStr](%1); + </inject-code> + </modify-function> + </function> + + <function signature="duplicatePStr(PStr*)"> + <modify-function> + <modify-argument index="return"> + <replace-type modified-type="PyObject"/> + </modify-argument> + <modify-argument index="1"> + <replace-type modified-type="PStr"/> + <replace-default-expression with="PStr()"/> + </modify-argument> + <inject-code class="target" position="end"> + %FUNCTION_NAME(&%1); + %PYARG_0 = %CONVERTTOPYTHON[PStr](%1); + </inject-code> + </modify-function> + </function> + + <primitive-type name="PStrList"> + <include file-name="strlist.h" location="global"/> + <conversion-rule> + <native-to-target> + PyObject *%out = PyList_New(Py_ssize_t(%in.size())); + Py_ssize_t idx = 0; + for (const auto &s : %in) { + PStr cppItem(s); + PyList_SET_ITEM(%out, idx++, %CONVERTTOPYTHON[PStr](cppItem)); + } + return %out; + </native-to-target> + <target-to-native> + <add-conversion type="PySequence"> + %OUTTYPE& list = %out; + Shiboken::AutoDecRef seq(PySequence_Fast(%in, 0)); + for (int i = 0; i < PySequence_Fast_GET_SIZE(seq.object()); i++) { + PyObject* pyItem = PySequence_Fast_GET_ITEM(seq.object(), i); + PStr cppItem = %CONVERTTOCPP[PStr](pyItem); + list.push_back(cppItem); + } + </add-conversion> + </target-to-native> + </conversion-rule> + </primitive-type> + + <add-function signature="createPStrList(PStr, PStr)" return-type="PyObject"> + <inject-code class="target"> + PStrList %0; + %0.push_back(%1); + %0.push_back(%2); + %PYARG_0 = %CONVERTTOPYTHON[PStrList](%0); + </inject-code> + </add-function> + <add-function signature="createListOfPStr(PStr, PStr)" return-type="PyObject"> + <inject-code class="target"> + std::list<PStr> %0; + %0.push_back(%1); + %0.push_back(%2); + %PYARG_0 = %CONVERTTOPYTHON[std::list<PStr>](%0); + </inject-code> + </add-function> + + <add-function signature="getPythonType(const char*)" return-type="PyObject"> + <inject-code class="target" position="beginning"> + SBK_UNUSED(self) + %PYARG_0 = (PyObject*) Shiboken::Conversions::getPythonTypeObject(%1); + if (!%PYARG_0) + %PYARG_0 = Py_None; + Py_INCREF(%PYARG_0); + </inject-code> + </add-function> + + <template name="cpp_type_is_object_or_value_type"> + SbkConverter* converter = Shiboken::Conversions::getConverter(%1); + if (converter) { + if (Shiboken::Conversions::pythonTypeIs$TYPEType(converter)) + %PYARG_0 = Py_True; + else + %PYARG_0 = Py_False; + Py_INCREF(%PYARG_0); + } else { + PyErr_Format(PyExc_ValueError, "Type '%s' has no converter associated to it", %1); + } + </template> + <add-function signature="cppTypeIsObjectType(const char*)" return-type="bool"> + <inject-code class="target" position="beginning"> + <insert-template name="cpp_type_is_object_or_value_type"> + <replace from="$TYPE" to="Object" /> + </insert-template> + </inject-code> + </add-function> + <add-function signature="cppTypeIsValueType(const char*)" return-type="bool"> + <inject-code class="target" position="beginning"> + <insert-template name="cpp_type_is_object_or_value_type"> + <replace from="$TYPE" to="Value" /> + </insert-template> + </inject-code> + </add-function> + + <add-function signature="getConversionTypeString(const char*)" return-type="PyObject"> + <inject-code class="target" position="beginning"> + Shiboken::Conversions::SpecificConverter converter(%1); + const char* %0 = 0; + switch (converter.conversionType()) { + case Shiboken::Conversions::SpecificConverter::CopyConversion: + %0 = "Copy conversion"; + break; + case Shiboken::Conversions::SpecificConverter::PointerConversion: + %0 = "Pointer conversion"; + break; + case Shiboken::Conversions::SpecificConverter::ReferenceConversion: + %0 = "Reference conversion"; + break; + default: + %0 = "Invalid conversion"; + } + %PYARG_0 = %CONVERTTOPYTHON[const char*](%0); + </inject-code> + </add-function> + + <inject-code class="native" position="beginning"> + static PyObject* __convertCppValuesToPython(const char** typeName, void** values, int size) + { + PyObject* result = PyTuple_New(size); + for (int i = 0; i < size; ++i) { + Shiboken::Conversions::SpecificConverter converter(typeName[i]); + PyTuple_SET_ITEM(result, i, converter.toPython(values[i])); + } + return result; + } + </inject-code> + <add-function signature="convertValueTypeToCppAndThenToPython(Point,Point*,Point&)" return-type="PyObject"> + <inject-code class="target" position="beginning"> + const char* typeNames[] = { "Point", "Point*", "Point&" }; + void* values[] = { &%1, &%2, &(%3) }; + %PYARG_0 = __convertCppValuesToPython(typeNames, values, 3); + </inject-code> + </add-function> + <add-function signature="convertObjectTypeToCppAndThenToPython(ObjectType*,ObjectType&)" return-type="PyObject"> + <inject-code class="target" position="beginning"> + const char* typeNames[] = { "ObjectType*", "ObjectType&" }; + void* values[] = { &%1, &(%2) }; + %PYARG_0 = __convertCppValuesToPython(typeNames, values, 2); + </inject-code> + </add-function> + <add-function signature="convertListOfIntegersToCppAndThenToPython(std::list<int>)" return-type="PyObject"> + <inject-code class="target" position="beginning"> + const char* typeNames[] = { "std::list<int>" }; + void* values[] = { &%1 }; + %PYARG_0 = __convertCppValuesToPython(typeNames, values, 1); + </inject-code> + </add-function> + <add-function signature="convertIntegersToCppAndThenToPython(int,int)" return-type="PyObject"> + <inject-code class="target" position="beginning"> + const char* typeNames[] = { "int", "int" }; + void* values[] = { &%1, &%2 }; + %PYARG_0 = __convertCppValuesToPython(typeNames, values, 2); + </inject-code> + </add-function> + + <template name="cpp_indexed_list_to_pylist_conversion"> + PyObject *%out = PyList_New(Py_ssize_t(%in.size())); + Py_ssize_t idx = 0; + for (auto it = %in.cbegin(), end = %in.cend(); it != end; ++it, ++idx) { + %INTYPE_0 cppItem(*it); + PyList_SET_ITEM(%out, idx, %CONVERTTOPYTHON[%INTYPE_0](cppItem)); + } + return %out; + </template> + <container-type name="List" type="list"> + <include file-name="list" location="global"/> + <conversion-rule> + <native-to-target> + <insert-template name="cpp_indexed_list_to_pylist_conversion"/> + </native-to-target> + <target-to-native> + <add-conversion type="PySequence"> + <insert-template name="shiboken_conversion_pyiterable_to_cppsequentialcontainer"/> + </add-conversion> + </target-to-native> + </conversion-rule> + </container-type> + <add-function signature="cacheSize()" return-type="int"> + <inject-code class="target"> + %RETURN_TYPE %0 = Shiboken::BindingManager::instance().getAllPyObjects().size(); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </add-function> + + <namespace-type name="sample"> + <value-type name="sample" /> + </namespace-type> + + <function signature="sumComplexPair(std::pair<Complex, Complex>)" /> + <function signature="gimmeComplexList()" /> + <function signature="transmuteComplexIntoPoint(const Complex&)" /> + <function signature="transmutePointIntoComplex(const Point&)" /> + <function signature="sumComplexPair(std::pair<Complex, Complex>)" /> + <function signature="doubleUnsignedInt(unsigned int)" /> + <function signature="doubleLongLong(long long)" /> + <function signature="doubleUnsignedLongLong(unsigned long long)" /> + <function signature="doubleShort(short)" /> + <function signature="returnNullPrimitivePointer()" /> + <function signature="returnNullValueTypePointer()" /> + <function signature="returnNullObjectTypePointer()" /> + <function signature="acceptInt(int)" /> + <function signature="acceptIntReturnPtr(int)"/> + <function signature="acceptUInt(unsigned int)" /> + <function signature="acceptLong(long)" /> + <function signature="acceptULong(unsigned long)" /> + <function signature="acceptDouble(double)" /> + <function signature="acceptIntReference(int&)" /> + <function signature="acceptOddBoolReference(OddBool&)" /> + <function signature="countCharacters(const char*)" /> + <function signature="gimmeInt()" /> + <function signature="gimmeDouble()" /> + <function signature="makeCString()" /> + <function signature="sumIntArray(int[4])"/> + <function signature="sumDoubleArray(double[4])"/> + <function signature="sumIntMatrix(int[2][3])"/> + <function signature="sumDoubleMatrix(double[2][3])"/> + <function signature="multiplyPair(std::pair<double, double>)" /> + <function signature="returnCString()" /> + <function signature="overloadedFunc(double)" /> + <function signature="overloadedFunc(int)" /> + <function signature="addStdStrings(const std::string&, const std::string&)"/> + <function signature="addStdWStrings(const std::wstring&, const std::wstring&)"/> + <function signature="testNullPtrT(std::nullptr_t)"/> + <function signature="takePolygon(Polygon&&)"/> + <function signature="takeObjectType(ObjectType&&)"/> + + <value-type name="ArrayModifyTest"> + <modify-function signature="sumIntArray(int, int*)"> + <modify-argument index="2"><array/></modify-argument> + </modify-function> + </value-type> + + <value-type name="ClassWithFunctionPointer"> + <suppress-warning text="^skipping public function 'void ClassWithFunctionPointer::callFunctionPointer.*$" /> + </value-type> + + <value-type name="IntArray" generate="no"/> + <value-type name="IntArray2"> + <modify-function signature="IntArray2(const int*)"> + <modify-argument index="1"><array/></modify-argument> + </modify-function> + </value-type> + + <value-type name="IntArray3"> + <modify-function signature="IntArray3(const int*)"> + <modify-argument index="1"><array/></modify-argument> + </modify-function> + </value-type> + + <enum-type name="OverloadedFuncEnum"/> + <!-- BUG: + renaming the ICOverloadedFuncEnum to the same name + of a global enum causes the generator to confuse the + two types. + --> + <enum-type name="GlobalEnum"/> + <enum-type name="GlobalOverloadFuncEnum"/> + + <enum-type identified-by-value="AnonymousGlobalEnum_Value0"/> + + <namespace-type name="SampleNamespace"> + <namespace-type name="InlineNamespace"> + <value-type name="ClassWithinInlineNamespace"/> + <enum-type name="EnumWithinInlineNamespace"/> + </namespace-type> + <enum-type name="Option"/> + <enum-type name="InValue"/> + <enum-type name="OutValue"/> + <enum-type identified-by-value="AnonymousClassEnum_Value1"/> + + <object-type name="DerivedFromNamespace"> + <enum-type name="SampleNamespace"/> + </object-type> + <value-type name="SomeClass"> + <enum-type name="PublicScopedEnum"/> + <value-type name="SomeInnerClass"> + <object-type name="OkThisIsRecursiveEnough"> + <enum-type name="NiceEnum" /> + <enum-type name="NiceEnumClass" /> + </object-type> + <enum-type name="ProtectedEnum"/> + </value-type> + <value-type name="SomeOtherInnerClass"/> + <enum-type name="ProtectedEnum"/> + </value-type> + + <modify-function signature="doSomethingWithArray(const unsigned char*, unsigned int, const char*)"> + <modify-argument index="1"> + <replace-type modified-type="const char*"/> + <conversion-rule> + <target-to-native> + <add-conversion> + const unsigned char* %out = reinterpret_cast<const unsigned char*>(Shiboken::String::toCString(%PYARG_1)); + </add-conversion> + </target-to-native> + </conversion-rule> + </modify-argument> + <modify-argument index="2"> + <remove-argument/> + <conversion-rule class="native"> + unsigned int %out = static_cast<unsigned int>(Shiboken::String::len(%PYARG_1)); + </conversion-rule> + </modify-argument> + </modify-function> + <add-function signature="ImInsideANamespace(int, int)" return-type="int"> + <inject-code class="target"> + %RETURN_TYPE %0 = %1 + %2; + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </add-function> + <add-function signature="passReferenceToValueType(Point&)" return-type="double"> + <inject-code> + %RETURN_TYPE %0 = %1.x() + %1.y(); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </add-function> + + <!-- Do change the argument from pointer to reference to comply with the C++ overload + of this function. The generator must be able to deal with this for Object Types. --> + <add-function signature="passReferenceToObjectType(ObjectType*)" return-type="int"> + <inject-code> + // The dot in "%1." must be replaced with a "->" by the generator. + %RETURN_TYPE %0 = %1.objectName().size(); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </add-function> + + <object-type name="CtParam"/> + </namespace-type> + + <namespace-type name="RemovedNamespace1" visible='false'> + <enum-type name="RemovedNamespace1_Enum" /> + <function signature="mathSum(int,int)"/> + <value-type name="ObjectOnInvisibleNamespace" /> + <namespace-type name="RemovedNamespace2" visible='false'> + <enum-type name="RemovedNamespace2_Enum" /> + </namespace-type> + <enum-type identified-by-value="RemovedNamespace1_AnonymousEnum_Value0" /> + </namespace-type> + + <namespace-type name="UnremovedNamespace"> + <namespace-type name="RemovedNamespace3" visible='false'> + <enum-type name="RemovedNamespace3_Enum" /> + <enum-type identified-by-value="RemovedNamespace3_AnonymousEnum_Value0" /> + </namespace-type> + </namespace-type> + + <namespace-type name="Photon"> + <enum-type name="ClassType"/> + <value-type name="Base"/> + <value-type name="TemplateBase" generate="no"/> + <value-type name="ValueIdentity"/> + <value-type name="ValueDuplicator"/> + </namespace-type> + + <value-type name="CVValueType"/> + <value-type name="CVListUser"/> + + <value-type name="IntList"> + <enum-type name="CtorEnum"/> + </value-type> + <value-type name="PointValueList"> + <enum-type name="CtorEnum"/> + </value-type> + <value-type name="ObjectTypePtrList"> + <enum-type name="CtorEnum"/> + </value-type> + + <object-type name="Abstract"> + <enum-type name="Type"/> + <enum-type name="PrintFormat"/> + <modify-function signature="id()" rename="id_"/> + <modify-function signature="hideFunction(HideType*)" remove="all"/> + <modify-field name="toBeRenamedField" rename="renamedField"/> + <modify-field name="readOnlyField" write="false"/> + <modify-function signature="virtualWithOutParameter(int&)const"> + <inject-code class="shell" position="override"> + x = virtualWithOutParameterPyOverride(gil, pyOverride.object()); + return; + </inject-code> + </modify-function> + <add-function signature="virtualWithOutParameterPyOverride()" + return-type="int" python-override="true"/> + </object-type> + + <object-type name="Derived" polymorphic-id-expression="%1->type() == Derived::TpDerived"> + <enum-type name="OtherOverloadedFuncEnum"/> + <value-type name="SomeInnerClass" /> + </object-type> + + <object-type name="DerivedUsingCt"/> + + <object-type name="ModifiedConstructor"> + <modify-function signature="ModifiedConstructor(int)"> + <modify-argument index="1"> + <replace-type modified-type="str"/> + </modify-argument> + <inject-code class='target' position='beginning'> + const char* tmpArg = %CONVERTTOCPP[const char*](%PYARG_1); + %0 = new %FUNCTION_NAME(atoi(tmpArg)); + </inject-code> + </modify-function> + </object-type> + + <object-type name="ObjectType" hash-function="objectTypeHash" parent-management="yes"> + <modify-function signature="deprecatedFunction()" deprecated="yes" /> + <!-- rename function to avoid Python signature conflit --> + <modify-function signature="setObject(const Null&)" rename="setNullObject" /> + + <modify-function signature="getCppParent()"> + <modify-argument index="this"> + <parent index="return" action="add" /> + </modify-argument> + <modify-argument index="return"> + <define-ownership class="target" owner="default"/> + </modify-argument> + </modify-function> + + <modify-function signature="event(Event*)"> + <modify-argument index="1" invalidate-after-use="yes"/> + </modify-function> + <modify-function signature="invalidateEvent(Event*)"> + <modify-argument index="1" invalidate-after-use="yes"/> + </modify-function> + <modify-function signature="create()"> + <modify-argument index="return"> + <define-ownership owner="target"/> + </modify-argument> + </modify-function> + <modify-function signature="createWithChild()"> + <modify-argument index="return"> + <define-ownership owner="target"/> + </modify-argument> + </modify-function> + <modify-function signature="setParent(ObjectType*)"> + <modify-argument index="this"> + <parent index="1" action="add"/> + </modify-argument> + </modify-function> + <inject-code class="native" position="beginning"> + static void reparent_layout_items(PyObject* parent, PyObject* layout) + { + // CHECKTYPE and ISCONVERTIBLE are used here for test purposes, don't change them. + if (!%CHECKTYPE[ObjectTypeLayout*](layout) && !%ISCONVERTIBLE[ObjectTypeLayout*](layout)) + return; + /* %CHECKTYPE[ObjectTypeLayout*](layout) */ + /* %ISCONVERTIBLE[ObjectTypeLayout*](layout) */ + ObjectTypeLayout* var; + var = %CONVERTTOCPP[ObjectTypeLayout*](layout); + // TODO-CONVERTER: erase this + /* + ObjectTypeLayout* var2 = %CONVERTTOCPP[ObjectTypeLayout*](layout); + */ + const ObjectTypeList& objChildren = var->objects(); + ObjectTypeList::const_iterator it = objChildren.begin(); + for (; it != objChildren.end(); ++it) { + if ((*it)->isLayoutType()) { + ObjectTypeLayout* l = reinterpret_cast<ObjectTypeLayout*>(*it); + reparent_layout_items(parent, %CONVERTTOPYTHON[ObjectTypeLayout*](l)); + Shiboken::Object::setParent(layout, %CONVERTTOPYTHON[ObjectTypeLayout*](l)); + } else { + Shiboken::Object::setParent(parent, %CONVERTTOPYTHON[ObjectType*](*it)); + } + } + } + </inject-code> + <modify-function signature="setLayout(ObjectTypeLayout*)"> + <modify-argument index="1"> + <parent index="this" action="add"/> + </modify-argument> + <inject-code class="target" position="end"> + if (%PYARG_1 != Py_None) + reparent_layout_items(%PYSELF, %PYARG_1); + </inject-code> + </modify-function> + <modify-function signature="takeChild(ObjectType*)"> + <modify-argument index="return"> + <define-ownership owner="target"/> + <parent index="this" action="remove"/> + </modify-argument> + </modify-function> + <modify-function signature="takeChild(const Str&)"> + <modify-argument index="return"> + <define-ownership owner="target"/> + <parent index="this" action="remove"/> + </modify-argument> + </modify-function> + <modify-function signature="findChild(const Str&)"> + <modify-argument index="return"> + <parent index="this" action="add"/> + </modify-argument> + </modify-function> + <modify-function signature="children()const"> + <modify-argument index="return"> + <parent index="this" action="add"/> + </modify-argument> + </modify-function> + <modify-function signature="createChild(ObjectType*)"> + <modify-argument index="return"> + <define-ownership owner="c++" /> + </modify-argument> + </modify-function> + <modify-function signature="nextInFocusChain()"> + <modify-argument index="return"> + <parent index="this" action="add"/> + </modify-argument> + </modify-function> + </object-type> + + <object-type name="OtherBase" /> + <object-type name="ObjectTypeDerived" /> + + <object-type name="ObjectTypeLayout"> + <modify-function signature="create()"> + <modify-argument index="return"> + <define-ownership owner="target"/> + </modify-argument> + </modify-function> + </object-type> + + <object-type name="ObjectView"> + <modify-function signature="ObjectView(ObjectModel*, ObjectType*)"> + <modify-argument index="1"> + <reference-count action="set" variable-name="setModel(ObjectModel*)1"/> + </modify-argument> + </modify-function> + <modify-function signature="setModel(ObjectModel*)"> + <modify-argument index="1"> + <reference-count action="set"/> + </modify-argument> + </modify-function> + </object-type> + + <object-type name="ObjectTypeHolder"/> + <value-type name="OnlyCopy"/> + <value-type name="FriendOfOnlyCopy"/> + + <object-type name="ObjectModel"> + <enum-type name="MethodCalled" /> + <modify-function signature="data() const"> + <modify-argument index="return"> + <define-ownership class="native" owner="c++"/> + </modify-argument> + </modify-function> + </object-type> + + <value-type name="Event"> + <enum-type name="EventType"/> + <enum-type name="EventTypeClass"/> + </value-type> + + <value-type name="BlackBox"> + <modify-function signature="keepObjectType(ObjectType*)"> + <modify-argument index="1"> + <define-ownership owner="c++"/> + </modify-argument> + </modify-function> + <modify-function signature="retrieveObjectType(int)"> + <modify-argument index="return"> + <define-ownership owner="target"/> + </modify-argument> + </modify-function> + <modify-function signature="keepPoint(Point*)"> + <modify-argument index="1"> + <define-ownership owner="c++"/> + </modify-argument> + </modify-function> + <modify-function signature="retrievePoint(int)"> + <modify-argument index="return"> + <define-ownership owner="target"/> + </modify-argument> + </modify-function> + </value-type> + + <value-type name="ProtectedNonPolymorphic"> + <modify-function signature="create()"> + <modify-argument index="return"> + <define-ownership owner="target"/> + </modify-argument> + </modify-function> + <modify-function signature="modifiedProtectedSum(int, int)"> + <inject-code class="target" position="beginning"> + %RETURN_TYPE %0 = %CPPSELF.%TYPE::%FUNCTION_NAME(%1, %2) * 10; + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </modify-function> + <modify-function signature="dataTypeName(void*) const" remove="all"/> + <modify-function signature="dataTypeName(int) const"> + <modify-argument index="1"> + <replace-default-expression with="0"/> + </modify-argument> + </modify-function> + <add-function signature="dataTypeName(PyObject*)const" return-type="const char*"> + <inject-code class="target" position="beginning"> + %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(%PYARG_1); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </add-function> + </value-type> + + <value-type name="ProtectedPolymorphic"> + <modify-function signature="create()"> + <modify-argument index="return"> + <define-ownership owner="target"/> + </modify-argument> + </modify-function> + </value-type> + + <value-type name="ProtectedPolymorphicDaughter"> + <modify-function signature="create()"> + <modify-argument index="return"> + <define-ownership owner="target"/> + </modify-argument> + </modify-function> + </value-type> + + <value-type name="ProtectedPolymorphicGrandDaughter"> + <modify-function signature="create()"> + <modify-argument index="return"> + <define-ownership owner="target"/> + </modify-argument> + </modify-function> + </value-type> + + <object-type name="ProtectedVirtualDestructor"> + <modify-function signature="create()"> + <modify-argument index="return"> + <define-ownership owner="target"/> + </modify-argument> + </modify-function> + </object-type> + + + <object-type name="ProtectedEnumClass"> + <enum-type name="ProtectedEnum" /> + <enum-type name="PublicEnum" /> + </object-type> + + <value-type name="ProtectedProperty" /> + + <function signature="createProtectedProperty()" /> + + <template name="boolptr_at_end_fix_beginning"> + bool __ok__; + %RETURN_TYPE %0 = %CPPSELF.%TYPE::%FUNCTION_NAME(%ARGUMENT_NAMES, &__ok__); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </template> + + <template name="boolptr_at_start_fix_beginning"> + bool __ok__; + %RETURN_TYPE %0 = %CPPSELF.%TYPE::%FUNCTION_NAME(&__ok__, %ARGUMENT_NAMES); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </template> + + <template name="boolptr_at_start_and_one_arg_fix_beginning"> + bool __ok__; + %RETURN_TYPE %0 = %CPPSELF.%TYPE::%FUNCTION_NAME(&__ok__, %2); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </template> + + <template name="boolptr_fix_end"> + PyObject* _tuple_ = PyTuple_New(2); + PyTuple_SET_ITEM(_tuple_, 0, %PYARG_0); + PyObject* _item_ = %CONVERTTOPYTHON[bool](__ok__); + PyTuple_SET_ITEM(_tuple_, 1, _item_); + %PYARG_0 = _tuple_; + </template> + + <template name="return_4_arguments_as_tuple"> + %PYARG_0 = PyTuple_New(4); + PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](%1)); + PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG2_TYPE](%2)); + PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG3_TYPE](%3)); + PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[%ARG4_TYPE](%4)); + </template> + + <template name="return_5_arguments_as_tuple"> + %PYARG_0 = PyTuple_New(5); + PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](%1)); + PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG2_TYPE](%2)); + PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG3_TYPE](%3)); + PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[%ARG4_TYPE](%4)); + PyTuple_SET_ITEM(%PYARG_0, 4, %CONVERTTOPYTHON[%ARG5_TYPE](%5)); + </template> + + <template name="return_none"> + %PYARG_0 = Py_None; + Py_INCREF(Py_None); + </template> + + <object-type name="Modifications"> + <enum-type name="OverloadedModFunc"/> + <enum-type name="TestEnum"/> + + <modify-function signature="overloaded(int, bool, int, double)"> + <modify-argument index="2"> + <remove-argument/> + </modify-argument> + <inject-code class="target" position="beginning"> + %RETURN_TYPE %0 = %CPPSELF.%TYPE::%FUNCTION_NAME(%1, true, %3, %4); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </modify-function> + + <modify-function signature="overloaded(int, bool, int, int)"> + <modify-argument index="3"> + <remove-argument/> + <replace-default-expression with="321"/> + </modify-argument> + <!-- + <modify-argument index="4"> + <remove-default-expression/> + </modify-argument> + --> + </modify-function> + + <modify-function signature="argRemoval0(int, bool, int, int)"> + <modify-argument index="3"> + <remove-argument/> + <replace-default-expression with="321"/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject*"/> + </modify-argument> + <inject-code class="target" position="end"> + <insert-template name="return_4_arguments_as_tuple"/> + </inject-code> + </modify-function> + + <modify-function signature="argRemoval1(int, bool, Point, Point, int)"> + <modify-argument index="3"> + <remove-argument/> + </modify-argument> + <modify-argument index="4"> + <remove-argument/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject*"/> + </modify-argument> + <inject-code class="target" position="end"> + <insert-template name="return_5_arguments_as_tuple"/> + </inject-code> + </modify-function> + + <modify-function signature="argRemoval1(int, bool, int, bool)"> + <inject-code class="target" position="end"> + <insert-template name="return_none"/> + </inject-code> + </modify-function> + + <modify-function signature="argRemoval2(int, bool, Point, Point, int)"> + <modify-argument index="3"> + <remove-argument/> + </modify-argument> + <modify-argument index="4"> + <remove-argument/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject*"/> + </modify-argument> + <inject-code class="target" position="end"> + <insert-template name="return_5_arguments_as_tuple"/> + </inject-code> + </modify-function> + + <modify-function signature="argRemoval3(int, Point, bool, Point, int)"> + <modify-argument index="2"> + <remove-argument/> + </modify-argument> + <modify-argument index="4"> + <remove-argument/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject*"/> + </modify-argument> + <inject-code class="target" position="end"> + <insert-template name="return_5_arguments_as_tuple"/> + </inject-code> + </modify-function> + + <modify-function signature="argRemoval4(int, Point, bool, Point, int)"> + <modify-argument index="2"> + <remove-argument/> + <replace-default-expression with="Point(6, 9)"/> + </modify-argument> + <modify-argument index="4"> + <remove-argument/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject*"/> + </modify-argument> + <inject-code class="target" position="end"> + <insert-template name="return_5_arguments_as_tuple"/> + </inject-code> + </modify-function> + + <modify-function signature="argRemoval5(int, bool, Point, Point, int)"> + <modify-argument index="1"> + <remove-argument/> + <replace-default-expression with="100"/> + </modify-argument> + <modify-argument index="3"> + <remove-argument/> + </modify-argument> + <modify-argument index="4"> + <remove-argument/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject*"/> + </modify-argument> + <inject-code class="target" position="end"> + <insert-template name="return_5_arguments_as_tuple"/> + </inject-code> + </modify-function> + + <modify-function signature="argRemoval5(int, bool, int, bool)"> + <modify-argument index="1"> + <remove-argument/> + <replace-default-expression with="200"/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject*"/> + </modify-argument> + <inject-code class="target" position="end"> + <insert-template name="return_4_arguments_as_tuple"/> + </inject-code> + </modify-function> + + <!-- + this alteration will trigger an interesting + compile time error on the binding + --> + <!-- + <modify-function signature="overloaded(int, bool, Point, Point)"> + <modify-argument index="3"> + <remove-argument/> + </modify-argument> + </modify-function> + --> + + <!-- + renaming this signature should remove it from the other + overloaded methods decision tree + --> + <modify-function signature="overloaded(int, bool, Point, Point)" rename="over"/> + + <!-- + 'ok' must be removed and the return value will be changed + to a tuple (PyObject*) containing the expected result plus + the 'ok' value as a Python boolean + --> + <modify-function signature="pointToPair(Point, bool*)"> + <modify-argument index="2"> + <remove-argument/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject*"/> + </modify-argument> + <inject-code class="target" position="beginning"> + <insert-template name="boolptr_at_end_fix_beginning"/> + </inject-code> + <inject-code class="target" position="end"> + <insert-template name="boolptr_fix_end"/> + </inject-code> + </modify-function> + + <!-- same as 'pointToPair' except that this time 'ok' is the first argument --> + <modify-function signature="multiplyPointCoordsPlusValue(bool*, Point, double)"> + <modify-argument index="1"> + <remove-argument/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject*"/> + </modify-argument> + <inject-code class="target" position="beginning"> + <insert-template name="boolptr_at_start_fix_beginning"/> + </inject-code> + <inject-code class="target" position="end"> + <insert-template name="boolptr_fix_end"/> + </inject-code> + </modify-function> + + <!-- completely remove 'plus' from the Python side --> + <modify-function signature="doublePlus(int, int)"> + <modify-argument index="2"> + <remove-argument/> + </modify-argument> + </modify-function> + + <!-- the default value for both arguments must be changed in Python --> + <modify-function signature="power(int, int)"> + <modify-argument index="1"> + <replace-default-expression with="2"/> + </modify-argument> + <modify-argument index="2"> + <replace-default-expression with="1"/> + </modify-argument> + </modify-function> + + <!-- in Python set argument default value to 10 --> + <modify-function signature="timesTen(int)"> + <modify-argument index="1"> + <replace-default-expression with="10"/> + </modify-argument> + </modify-function> + + <!-- in Python remove the argument default value --> + <modify-function signature="increment(int)"> + <modify-argument index="1"> + <remove-default-expression/> + </modify-argument> + </modify-function> + + <!-- don't export this method to Python --> + <modify-function signature="exclusiveCppStuff()" remove="all"/> + + <!-- change the name of this regular method --> + <modify-function signature="cppMultiply(int, int)" rename="calculateArea"/> + + <!-- change the name of this virtual method --> + <modify-function signature="className()" rename="name"/> + + <modify-function signature="sumPointArray(int, const Point[])"> + <modify-argument index="1"> + <remove-argument/> + <conversion-rule class="native"> + const auto %out = PySequence_Size(%PYARG_1); + </conversion-rule> + </modify-argument> + <modify-argument index="2"> + <replace-type modified-type="PySequence" /> + <conversion-rule class="native"> + Shiboken::AutoArrayPointer<Point> %out(%1); + for (Py_ssize_t i = 0; i < %1; ++i) + %out[i] = %CONVERTTOCPP[Point](PySequence_Fast_GET_ITEM(%PYARG_1, i)); + </conversion-rule> + </modify-argument> + </modify-function> + <modify-function signature="getSize(const void*,int)"> + <modify-argument index="1"> + <replace-type modified-type="ByteArray&"/> + </modify-argument> + <modify-argument index="2"> + <replace-default-expression with="-1"/> + </modify-argument> + <inject-code class="target" position="beginning"> + int size = (%2 < 0) ? %1.size() : %2; + %BEGIN_ALLOW_THREADS + %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME((const void*) %1.data(), size); + %END_ALLOW_THREADS + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </modify-function> + <modify-function signature="sumPointCoordinates(const Point*)"> + <modify-argument index="1"> + <no-null-pointer/> + </modify-argument> + </modify-function> + <template name="differenceOfPointCoordinates_arg2"> + bool _status; + bool* %2 = &_status; + </template> + <template name="differenceOfPointCoordinates_returnTarget"> + %PYARG_0 = PyTuple_New(2); + PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[bool](*%2)); + PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%RETURN_TYPE](%0)); + </template> + <modify-function signature="differenceOfPointCoordinates(const Point*, bool*)"> + <modify-argument index="2"> + <remove-argument/> + <conversion-rule class="native"> + <insert-template name="differenceOfPointCoordinates_arg2"/> + </conversion-rule> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PySequence"/> + <conversion-rule> + <target-to-native> + <add-conversion> + Shiboken::AutoDecRef _py_ok_(PySequence_GetItem(%PYARG_0, 0)); + Shiboken::AutoDecRef _py_ret_(PySequence_GetItem(%PYARG_0, 1)); + *%2 = %CONVERTTOCPP[bool](_py_ok_); + %RETURN_TYPE %out = %CONVERTTOCPP[%RETURN_TYPE](_py_ret_); + </add-conversion> + </target-to-native> + <native-to-target> + <insert-template name="differenceOfPointCoordinates_returnTarget"/> + </native-to-target> + </conversion-rule> + </modify-argument> + </modify-function> + <modify-function signature="callDifferenceOfPointCoordinates(const Point*, bool*)"> + <modify-argument index="2"> + <remove-argument/> + <conversion-rule class="native"> + <insert-template name="differenceOfPointCoordinates_arg2"/> + </conversion-rule> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PySequence"/> + <conversion-rule class="target"> + <insert-template name="differenceOfPointCoordinates_returnTarget"/> + </conversion-rule> + </modify-argument> + </modify-function> + <modify-function signature="nonConversionRuleForArgumentWithDefaultValue(ObjectType**)"> + <modify-argument index="1"> + <remove-argument/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="(status, object)"/> + </modify-argument> + <inject-code class="target" position="beginning"> + ObjectType* tmpObject = 0; + %BEGIN_ALLOW_THREADS + %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(&tmpObject); + %END_ALLOW_THREADS + %PYARG_0 = PyTuple_New(2); + PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0)); + PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[ObjectType*](tmpObject)); + </inject-code> + </modify-function> + <modify-function signature="passOddBool(OddBool)" rename="invertBoolean"> + <inject-code class="target" position="beginning"> + %RETURN_TYPE %0 = !%CPPSELF.%FUNCTION_NAME(%1); + %PYARG_0 = %CONVERTTOPYTHON[OddBool](%0); + </inject-code> + </modify-function> + <modify-function signature="setEnumValue(Modifications::TestEnum)"> + <modify-argument index="1"> + <replace-default-expression with="cppSelf->defaultEnumValue()"/> + </modify-argument> + </modify-function> + <add-function signature="__getattro__" return-type="PyObject *"> + <inject-code class="target" position="beginning"> + cppSelf->notifyGetAttroCalled(); + </inject-code> + </add-function> + <add-function signature="__setattro__" return-type="int"> + <inject-code class="target" position="beginning"> + cppSelf->notifySetAttroCalled(); + </inject-code> + </add-function> + </object-type> + + <object-type name="AbstractModifications"> + <!-- + completely removing the pure virtual method from this + class will generate an #error directive. + --> + <!-- + <modify-function signature="pointlessPureVirtualMethod()" remove="all"/> + --> + </object-type> + + <value-type name="Reference"> + <modify-function signature="returnMyFirstArg(Reference&)"> + <modify-argument index="return"> + <replace-default-expression with="%1"/> + </modify-argument> + </modify-function> + <modify-function signature="returnMySecondArg(int, Reference&)"> + <modify-argument index="return"> + <replace-default-expression with="%2"/> + </modify-argument> + </modify-function> + </value-type> + <object-type name="ObjTypeReference"> + <modify-function signature="returnMyFirstArg(ObjTypeReference&)"> + <modify-argument index="return"> + <replace-default-expression with="%1"/> + </modify-argument> + </modify-function> + <modify-function signature="returnMySecondArg(int, ObjTypeReference&)"> + <modify-argument index="return"> + <replace-default-expression with="%2"/> + </modify-argument> + </modify-function> + <modify-function signature="justAPureVirtualFunc(ObjTypeReference&)"> + <modify-argument index="return"> + <replace-default-expression with="%1"/> + </modify-argument> + </modify-function> + </object-type> + <value-type name="ImplicitConv"> + <enum-type name="CtorEnum"/> + <enum-type name="ICOverloadedFuncEnum"/> + </value-type> + + <value-type name="VirtualMethods"> + <modify-function signature="sum0(int, int, int)" rename="sumThree"/> + <modify-function signature="sum1(int, int, int)"> + <modify-argument index="3"> + <replace-default-expression with="1000"/> + </modify-argument> + </modify-function> + <modify-function signature="sum2(int, int, int)"> + <modify-argument index="3"> + <remove-argument/> + <replace-default-expression with="2000"/> + </modify-argument> + </modify-function> + <modify-function signature="sum3(int, int, int)"> + <modify-argument index="2"> + <remove-argument/> + </modify-argument> + <inject-code class="target" position="beginning"> + %RETURN_TYPE %0 = %CPPSELF.%TYPE::%FUNCTION_NAME(%1, %1+%3, %3); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </modify-function> + <modify-function signature="sum4(int, int, int)"> + <modify-argument index="2"> + <remove-argument/> + <replace-default-expression with="3000"/> + </modify-argument> + <inject-code class="native" position="beginning"> + PyObject* new_arg0 = PyLong_FromLong(PyLong_AS_LONG(%PYARG_1) - %2); + Py_DECREF(%PYARG_1); + %PYARG_1 = new_arg0; + </inject-code> + </modify-function> + <modify-function signature="name()"> + <inject-code class="native" position="end"> + %0.prepend(Str("Pimped")); + </inject-code> + </modify-function> + <modify-function signature="callMe()"> + <inject-code class="native" position="end"> + PyObject_Call(%PYTHON_METHOD_OVERRIDE, %PYTHON_ARGUMENTS, NULL); + </inject-code> + </modify-function> + <modify-function signature="createStr(const char*, Str*&)"> + <modify-argument index="2"> + <remove-argument/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PySequence"/> + <conversion-rule class="native"> + Shiboken::AutoDecRef _py_ok_(PySequence_GetItem(%PYARG_0, 0)); + Shiboken::AutoDecRef _py_ret_(PySequence_GetItem(%PYARG_0, 1)); + %RETURN_TYPE %out = %CONVERTTOCPP[%RETURN_TYPE](_py_ok_); + %2 = %CONVERTTOCPP[Str*](_py_ret_); + </conversion-rule> + </modify-argument> + <inject-code class="target" position="beginning"> + Str* _str_arg_ = 0; + %RETURN_TYPE %0 = %CPPSELF.%TYPE::%FUNCTION_NAME(%1, _str_arg_); + </inject-code> + <inject-code class="target" position="end"> + %PYARG_0 = PyTuple_New(2); + PyObject* _item_ = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + PyTuple_SET_ITEM(%PYARG_0, 0, _item_); + _item_ = %CONVERTTOPYTHON[Str*](_str_arg_); + PyTuple_SET_ITEM(%PYARG_0, 1, _item_); + </inject-code> + </modify-function> + <modify-function signature="callCreateStr(const char*, Str*&)"> + <modify-argument index="2"> + <remove-argument/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PySequence"/> + </modify-argument> + <inject-code class="target" position="beginning"> + Str* _str_arg_ = 0; + %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(%1, _str_arg_); + </inject-code> + <inject-code class="target" position="end"> + %PYARG_0 = PyTuple_New(2); + PyObject* _item_ = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + PyTuple_SET_ITEM(%PYARG_0, 0, _item_); + _item_ = %CONVERTTOPYTHON[Str*](_str_arg_); + PyTuple_SET_ITEM(%PYARG_0, 1, _item_); + </inject-code> + </modify-function> + <template name="fix_int*,int*,int*,int*"> + int a0, a1, a2, a3; + %BEGIN_ALLOW_THREADS + %CPPSELF->::%TYPE::%FUNCTION_NAME(&a0, &a1, &a2, &a3); + %END_ALLOW_THREADS + %PYARG_0 = PyTuple_New(4); + PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[int](a0)); + PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[int](a1)); + PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[int](a2)); + PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[int](a3)); + </template> + <template name="fix_native_return_int*,int*,int*,int*"> + PyObject* _obj = %PYARG_0; + if (!PySequence_Check(_obj) + || PySequence_Fast_GET_SIZE(_obj) != 4 + || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 0)) + || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 1)) + || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 2)) + || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 3))) { + PyErr_SetString(PyExc_TypeError, "Sequence of 4 numbers expected"); + } else { + *%1 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 0)); + *%2 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 1)); + *%3 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 2)); + *%4 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 3)); + } + </template> + <modify-function signature="getMargins(int*,int*,int*,int*)const"> + <modify-argument index="return" pyi-type="Tuple[int, int, int, int]"> + <replace-type modified-type="PyObject" /> + </modify-argument> + <modify-argument index="1"> + <remove-argument/> + </modify-argument> + <modify-argument index="2"> + <remove-argument/> + </modify-argument> + <modify-argument index="3"> + <remove-argument/> + </modify-argument> + <modify-argument index="4"> + <remove-argument/> + <remove-default-expression/> + </modify-argument> + <inject-code class="target" position="beginning"> + <insert-template name="fix_int*,int*,int*,int*"/> + </inject-code> + <inject-code class="native" position="end"> + <insert-template name="fix_native_return_int*,int*,int*,int*"/> + </inject-code> + </modify-function> + <modify-function signature="callGetMargins(int*,int*,int*,int*)const"> + <modify-argument index="0"> + <replace-type modified-type="PyObject" /> + </modify-argument> + <modify-argument index="1"> + <remove-argument/> + </modify-argument> + <modify-argument index="2"> + <remove-argument/> + </modify-argument> + <modify-argument index="3"> + <remove-argument/> + </modify-argument> + <modify-argument index="4"> + <remove-argument/> + <remove-default-expression/> + </modify-argument> + <inject-code class="target" position="beginning"> + <insert-template name="fix_int*,int*,int*,int*"/> + </inject-code> + </modify-function> + <modify-function signature="recursionOnModifiedVirtual(Str)const"> + <inject-code class="target" position="beginning"> + %BEGIN_ALLOW_THREADS + // It's important for test purposes to use a constructor with parenthesis as argument. + %RETURN_TYPE %0 = %RETURN_TYPE(%CPPSELF.%FUNCTION_NAME(Str(%1))); + %END_ALLOW_THREADS + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </modify-function> + </value-type> + <value-type name="VirtualDaughter" /> + <object-type name="VirtualDaughter2" /> + <object-type name="VirtualFinalDaughter" /> + + <value-type name="VirtualDtor"> + <modify-function signature="create()"> + <modify-argument index="return"> + <define-ownership owner="target"/> + </modify-argument> + </modify-function> + </value-type> + + <value-type name="PointerHolder"> + <modify-function signature="PointerHolder(void*)" remove="all"/> + <add-function signature="PointerHolder(PyObject*)"> + <inject-code class="target" position="beginning"> + %0 = new %TYPE(%PYARG_1); + </inject-code> + </add-function> + <modify-function signature="pointer() const"> + <inject-code class="target" position="beginning"> + %PYARG_0 = reinterpret_cast<PyObject*>(%CPPSELF.%FUNCTION_NAME()); + if (!%PYARG_0) + %PYARG_0 = Py_None; + Py_INCREF(%PYARG_0); + </inject-code> + </modify-function> + </value-type> + + <function signature="applyHomogeneousTransform(Point,double,double,double,double,double,double,double,double,double,bool*)"> + <!-- + Tests handling of the '%#' substitution for # > 9. + --> + <modify-function signature="applyHomogeneousTransform(Point,double,double,double,double,double,double,double,double,double,bool*)"> + <modify-argument index="11"> + <remove-argument/> + </modify-argument> + <inject-code class="target" position="beginning"> + bool ok_; + %RETURN_TYPE retval_ = + %FUNCTION_NAME(%1, %2, %3, %4, %5, %6, %7, %8, %9, %10, &ok_); + if (!ok_) + %PYARG_0 = Py_None; + else + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](retval_); + </inject-code> + </modify-function> + </function> + + <!-- Tests add-function for nested template types --> + <add-function signature="sum2d(std::list<std::list<int> >)" return-type="int"> + <inject-code class="target" position="beginning"> + typedef std::list<int> Inner; + typedef std::list<Inner> Outer; + + int result = 0; + + Outer::const_iterator oiter, oend = %1.end(); + for (oiter = %1.begin(); oiter != oend; ++oiter) { + const Inner& inner = *oiter; + Inner::const_iterator iiter, iend = inner.end(); + for (iiter = inner.begin(); iiter != iend; ++iiter) + result += *iiter; + } + + %PYARG_0 = %CONVERTTOPYTHON[int](result); + </inject-code> + </add-function> + + <!-- Tests add-function for nested template types --> + <add-function signature="sumproduct(std::list<std::pair<int, int> >)" return-type="int"> + <inject-code class="target" position="beginning"> + typedef std::pair<int, int> Pair; + typedef std::list<Pair> List; + + int result = 0; + + List::const_iterator iter, end = %1.end(); + for (iter = %1.begin(); iter != end; ++iter) + result += iter->first * iter->second; + + %PYARG_0 = %CONVERTTOPYTHON[int](result); + </inject-code> + </add-function> + + + <value-type name="InjectCode"> + <!-- + Various tests for inject codes. + Note: Some uses of inject code here are used just for testing purposes, consider using the add-function tag. + --> + + <modify-function signature="sumArrayAndLength(int *) const"> + <modify-argument index="1"> + <replace-type modified-type="PyObject"/> + </modify-argument> + <inject-code class="target" position="beginning"> + int* array = NULL; + bool errorOccurred = false; + + if (PySequence_Check(%PYARG_1)) { + if((array = Shiboken::sequenceToIntArray(%PYARG_1, true)) == NULL && PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "Should be a sequence of ints"); + errorOccurred = true; + } + } else { + PyErr_SetString(PyExc_TypeError, "Should be a sequence of ints"); + errorOccurred = true; + } + + if (!errorOccurred) { + %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(array); + if (array) + delete[] array; + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + } + </inject-code> + </modify-function> + + <modify-function signature="arrayMethod(int, int*) const"> + <modify-argument index="1"> + <remove-argument/> + <conversion-rule class="native"> + const auto %out = PySequence_Size(%PYARG_1); + </conversion-rule> + </modify-argument> + <modify-argument index="2"> + <replace-type modified-type="PySequence"/> + <conversion-rule class="native"> + const auto numItems = PySequence_Size(%PYARG_1); + Shiboken::AutoArrayPointer<int> %out(numItems); + for (Py_ssize_t i = 0; i < numItems; ++i) { + if (%CHECKTYPE[int](PySequence_Fast_GET_ITEM(%PYARG_1, i))) + %out[i] = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(%PYARG_1, i)); + else if (%ISCONVERTIBLE[int](PySequence_Fast_GET_ITEM(%PYARG_1, i))) + %out[i] = -1; + else + %out[i] = -2; + } + </conversion-rule> + <conversion-rule class="target"> + PyObject* %out = PyList_New(count); + for (int i = 0; i < count; ++i) + PyList_SET_ITEM(%out, i, %CONVERTTOPYTHON[int](%in[i])); + </conversion-rule> + </modify-argument> + </modify-function> + + <modify-function signature="callArrayMethod(int, int*) const"> + <modify-argument index="1"> + <remove-argument/> + </modify-argument> + <modify-argument index="2"> + <replace-type modified-type="PySequence"/> + </modify-argument> + <inject-code class="target" position="beginning"> + int numItems = PySequence_Size(%PYARG_1); + int *cppItems = new int[numItems]; + for (int i = 0; i < numItems; i++) + cppItems[i] = %CONVERTTOCPP[int](PySequence_GetItem(%PYARG_1, i)); + %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(numItems, cppItems); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + delete[] cppItems; + </inject-code> + </modify-function> + + <!-- + Inject the tp_str method using this alternative way + Tested in InjectCodeTest.testTypeNativeBeginning_TypeTargetBeginning: + --> + <inject-code class="native" position="beginning"> + PyObject* InjectCode_tpstr(PyObject*) { return Shiboken::String::fromCString("Hi! I'm the inject code dummy class."); } + </inject-code> + <!-- + Register our tp_str class using another inject code + Tested in InjectCodeTest.testTypeNativeBeginning_TypeTargetBeginning: + --> + <inject-code class="target" position="beginning"> + %PYTHONTYPEOBJECT.tp_str = InjectCode_tpstr; + </inject-code> + + <!-- Tested in InjectCodeTest.testFunctionTargetBeginning_FunctionTargetEnd --> + <modify-function signature="simpleMethod1(int, int)"> + <inject-code class="target" position="beginning"> + %1 += 1; + </inject-code> + <inject-code class="target" position="end"> + PyObject* tmp = Shiboken::String::fromCString("end"); + Shiboken::String::concat(&%PYARG_0, tmp); + Py_DECREF(tmp); + </inject-code> + </modify-function> + + <!-- Tested in InjectCodeTest.testFunctionTargetBeginning --> + <modify-function signature="simpleMethod2()"> + <inject-code class="target" position="end"> + PyObject* tmp = Shiboken::String::fromCString("end"); + Shiboken::String::concat(&%PYARG_0, tmp); + Py_DECREF(tmp); + </inject-code> + </modify-function> + + <!-- Tested in InjectCodeTest.testArgsModification --> + <modify-function signature="overloadedMethod(int, char**)"> + <modify-argument index="1"> + <replace-type modified-type="PySequence" /> + </modify-argument> + <modify-argument index="2"> + <remove-argument /> + </modify-argument> + <inject-code class="target" position="beginning"> + int argc; + char** argv; + if (!Shiboken::listToArgcArgv(%PYARG_1, &argc, &argv)) { + PyErr_SetString(PyExc_TypeError, "error"); + return 0; + } + %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(argc, argv); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + for (int i = 0; i < argc; ++i) + free(argv[i]); + delete[] argv; + </inject-code> + </modify-function> + + <!-- Tested in InjectCodeTest.testArgsModification2 --> + <modify-function signature="simpleMethod3(int, char**)"> + <modify-argument index="1"> + <replace-type modified-type="PySequence" /> + </modify-argument> + <modify-argument index="2"> + <remove-argument /> + </modify-argument> + <inject-code class="target" position="beginning"> + int argc; + char** argv; + if (!Shiboken::listToArgcArgv(%PYARG_1, &argc, &argv)) { + PyErr_SetString(PyExc_TypeError, "error"); + return 0; + } + %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(argc, argv); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + for (int i = 0; i < argc; ++i) + free(argv[i]); + delete[] argv; + </inject-code> + </modify-function> + </value-type> + + <value-type name="ImplicitBase"/> + <value-type name="SortedOverload"> + <add-function signature="overload(PyObject *)" return-type="const char *"> + <inject-code class="target" position="beginning"> + return Shiboken::String::fromCString("PyObject"); + </inject-code> + </add-function> + + <add-function signature="overloadDeep(int, PyObject *)" return-type="const char *"> + <inject-code class="target" position="beginning"> + return Shiboken::String::fromCString("PyObject"); + </inject-code> + </add-function> + <modify-function signature="pyObjOverload(unsigned char*, int)"> + <modify-argument index="1"> + <replace-type modified-type="PyObject" /> + <conversion-rule class="native"> + unsigned char* %out = 0; + </conversion-rule> + </modify-argument> + </modify-function> + </value-type> + <value-type name="ImplicitTarget"/> + + <object-type name="CustomOverloadSequence"> + <modify-function signature="overload(int) const" overload-number="0"/> + <modify-function signature="overload(short) const" overload-number="1"/> + </object-type> + + <value-type name="Point"> + <add-function signature="__str__" return-type="PyObject*"> + <inject-code class="target" position="beginning"> + int x1 = (int) %CPPSELF.x(); + int x2 = ((int) (%CPPSELF.x() * 100)) - (x1 * 100); + int y1 = (int) %CPPSELF.y(); + int y2 = ((int) (%CPPSELF.y() * 100)) - (y1 * 100); + %PYARG_0 = Shiboken::String::fromFormat("Point(%d.%d, %d.%d)", x1, x2, y1, y2); + </inject-code> + </add-function> + <add-function signature="__repr__" return-type="PyObject*"> + <inject-code class="target" position="beginning"> + int x1 = (int) %CPPSELF.x(); + int x2 = ((int) (%CPPSELF.x() * 10)) - (x1 * 10); + int y1 = (int) %CPPSELF.y(); + int y2 = ((int) (%CPPSELF.y() * 10)) - (y1 * 10); + %PYARG_0 = Shiboken::String::fromFormat("<Point object at %p: (%d.%d, %d.%d)>", %CPPSELF, x1, x2, y1, y2); + </inject-code> + </add-function> + + <add-function signature="__reduce__" return-type="PyObject*"> + <inject-code class="target" position="beginning"> + PyObject* type = PyObject_Type(%PYSELF); + PyObject* args = NULL; + + args = Py_BuildValue("(dd)", %CPPSELF.x(), %CPPSELF.y()); + + %PYARG_0 = Py_BuildValue("(OO)", type, args); + </inject-code> + </add-function> + + <modify-function signature="midpoint(const Point&, Point*)const"> + <modify-argument index="2"> + <remove-argument /> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="Point" /> + </modify-argument> + <inject-code class="target" position="beginning"> + Point _midpoint; + // The test consists in *NOT* using the ARGUMENT_NAMES type system variable. + %CPPSELF.%FUNCTION_NAME(%1, &_midpoint); + %PYARG_0 = %CONVERTTOPYTHON[Point](_midpoint); + </inject-code> + </modify-function> + + <template name="return_self"> + %PYARG_0 = %PYARG_1; + Py_INCREF(%PYARG_1); + </template> + <add-function signature="operator-(PyUnicode)"> + <inject-code> + <insert-template name="return_self" /> + </inject-code> + </add-function> + <!-- A reverse operator --> + <add-function signature="operator-(PyUnicode, Point)"> + <inject-code> + <insert-template name="return_self" /> + </inject-code> + </add-function> + </value-type> + + <value-type name="PointF"> + <add-function signature="__str__" return-type="PyObject*"> + <inject-code class="target" position="beginning"> + int x1 = (int) %CPPSELF.x(); + int x2 = ((int) (%CPPSELF.x() * 100)) - (x1 * 100); + int y1 = (int) %CPPSELF.y(); + int y2 = ((int) (%CPPSELF.y() * 100)) - (y1 * 100); + %PYARG_0 = Shiboken::String::fromFormat("PointF(%d.%d, %d.%d)", x1, x2, y1, y2); + </inject-code> + </add-function> + <add-function signature="__repr__" return-type="PyObject*"> + <inject-code class="target" position="beginning"> + int x1 = (int) %CPPSELF.x(); + int x2 = ((int) (%CPPSELF.x() * 10)) - (x1 * 10); + int y1 = (int) %CPPSELF.y(); + int y2 = ((int) (%CPPSELF.y() * 10)) - (y1 * 10); + %PYARG_0 = Shiboken::String::fromFormat("<PointF object at %p: (%d.%d, %d.%d)>", %CPPSELF, x1, x2, y1, y2); + </inject-code> + </add-function> + + <add-function signature="__reduce__" return-type="PyObject*"> + <inject-code class="target" position="beginning"> + PyObject *type = PyObject_Type(%PYSELF); + PyObject *args = NULL; + + args = Py_BuildValue("(dd)", %CPPSELF.x(), %CPPSELF.y()); + + %PYARG_0 = Py_BuildValue("(OO)", type, args); + </inject-code> + </add-function> + + <modify-function signature="midpoint(const PointF&, PointF*)const"> + <modify-argument index="2"> + <remove-argument /> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PointF" /> + </modify-argument> + <inject-code class="target" position="beginning"> + PointF _midpoint; + // The test consists in using the ARGUMENT_NAMES type system variable. + %CPPSELF.%FUNCTION_NAME(%ARGUMENT_NAMES, &_midpoint); + %PYARG_0 = %CONVERTTOPYTHON[PointF](_midpoint); + </inject-code> + </modify-function> + </value-type> + + <value-type name="Rect" /> + <value-type name="RectF" /> + + <value-type name="Polygon"> + <modify-function signature="stealOwnershipFromPython(Point*)"> + <modify-argument index="1"> + <define-ownership owner="c++"/> + </modify-argument> + </modify-function> + <modify-function signature="stealOwnershipFromPython(Polygon*)"> + <modify-argument index="1"> + <define-ownership owner="c++"/> + </modify-argument> + </modify-function> + </value-type> + + <value-type name="Time"> + <extra-includes> + <include file-name="datetime.h" location="global"/> + </extra-includes> + <enum-type name="NumArgs"/> + <add-function signature="operator!=(const PyObject*)" return-type="PyObject"> + <inject-code> + if (!PyDateTimeAPI) + PyDateTime_IMPORT; + if (PyTime_Check(%1)) { + int pyH = PyDateTime_TIME_GET_HOUR(%1); + int pyM = PyDateTime_TIME_GET_MINUTE(%1); + int pyS = PyDateTime_TIME_GET_SECOND(%1); + if ((pyH == %CPPSELF.hour()) && + (pyM == %CPPSELF.minute()) && + (pyS == %CPPSELF.second())) + %PYARG_0 = Py_False; + else + %PYARG_0 = Py_True; + Py_INCREF(%PYARG_0); + } + </inject-code> + </add-function> + <add-function signature="operator==(const PyObject*)" return-type="PyObject"> + <inject-code> + if (!PyDateTimeAPI) + PyDateTime_IMPORT; + if (PyTime_Check(%1)) { + int pyH = PyDateTime_TIME_GET_HOUR(%1); + int pyM = PyDateTime_TIME_GET_MINUTE(%1); + int pyS = PyDateTime_TIME_GET_SECOND(%1); + if ((pyH == %CPPSELF.hour()) && + (pyM == %CPPSELF.minute()) && + (pyS == %CPPSELF.second())) + %PYARG_0 = Py_True; + else + %PYARG_0 = Py_False; + Py_INCREF(%PYARG_0); + } + </inject-code> + </add-function> + + </value-type> + <value-type name="Size"> + <extra-includes> + <include file-name="string" location="global"/> + <include file-name="sstream" location="global"/> + </extra-includes> + <add-function signature="Size(const char*)"> + <inject-code class="target" position="beginning" + file="samplesnippets.cpp" snippet="size_char_ct"/> + </add-function> + </value-type> + <value-type name="SizeF"/> + <function signature="SnakeCaseGlobalFunction()" snake-case="yes"/> + <object-type name="SnakeCaseTest" snake-case="yes"> + <modify-function signature="testFunctionDisabled()const" snake-case="no"/> + <modify-function signature="testFunctionBoth()const" snake-case="both"/> + <modify-field name="testFieldDisabled" snake-case="no"/> + <modify-field name="testFieldBoth" snake-case="both"/> + </object-type> + <object-type name="SnakeCaseDerivedTest" snake-case="yes"/> + <value-type name="MapUser"/> + <value-type name="PairUser"/> + <value-type name="ListUser"> + <enum-type name="ListOfSomething"/> + </value-type> + <value-type name="NoImplicitConversion" /> + <value-type name="NonDefaultCtor" /> + <value-type name="OddBoolUser" /> + <object-type name="Overload"> + <enum-type name="FunctionEnum"/> + <enum-type name="ParamEnum"/> + <modify-function signature="intOverloads(int, int, double)"> + <modify-argument index="2"> + <remove-argument /> + </modify-argument> + <inject-code class="target"> + %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(%1, 2, %3); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </modify-function> + <modify-function signature="singleOverload(Point*)"> + <modify-argument index="1"> + <define-ownership owner="c++"/> + </modify-argument> + </modify-function> + <modify-function signature="acceptSequence(const char*const[])"> + <modify-argument index="1"> + <replace-type modified-type="PySequence" /> + <conversion-rule class="native"> + { + Shiboken::AutoDecRef strList(PySequence_Fast(%PYARG_1, "The argument must be a sequence.")); + int lineCount = PySequence_Fast_GET_SIZE(strList.object()); + for (int line = 0; line < lineCount; ++line) { + if (!Shiboken::String::check(PySequence_Fast_GET_ITEM(strList.object(), line))) { + PyErr_SetString(PyExc_TypeError, "The argument must be a sequence of strings."); + break; + } + } + } + // PySIDE-1735: Enums are now implemented in Python, so we need to avoid asserts. + if (PyErr_Occurred()) + break; + const char** %out = 0; + </conversion-rule> + </modify-argument> + </modify-function> + <modify-function signature="acceptSequence(void*)"> + <modify-argument index="1"> + <replace-type modified-type="PyObject" /> + <conversion-rule class="native"> + void* %out = 0; + </conversion-rule> + </modify-argument> + </modify-function> + + <template name="buffer_argument"> + unsigned char* %out = (unsigned char*) Shiboken::Buffer::getPointer(%PYARG_1); + </template> + + <modify-function signature="strBufferOverloads(unsigned char*,int)"> + <modify-argument index="1"> + <replace-type modified-type="PyBuffer"/> + <conversion-rule class="native"> + <insert-template name="buffer_argument" /> + </conversion-rule> + </modify-argument> + </modify-function> + <!-- + This added function simulates the solution given to PySide's QImage + constructor problem, as seen in PySide/bbba1cc4, and described in + bug #489 [http://bugs.pyside.org/show_bug.cgi?id=489]. + This is not the best solution, just one that works. The proper way + to handle it would be to fix the overload decisor. + --> + <add-function signature="strBufferOverloads(Str&,int)" return-type="Overload::FunctionEnum"> + <inject-code class="target" position="beginning"> + <insert-template name="buffer_argument"> + <replace from="%out" to="argOut" /> + </insert-template> + %BEGIN_ALLOW_THREADS + %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(argOut, %2); + %END_ALLOW_THREADS + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </add-function> + </object-type> + <object-type name="Overload2" /> + + <object-type name="Collector" stream="yes"/> + + <value-type name="IntWrapper"> + <inject-code class="native" position="beginning" + file="samplesnippets.cpp" snippet="intwrapper_add_ints"/> + <add-pymethoddef name="add_ints" function="Sbk_IntWrapper_add_ints" + flags="METH_VARARGS"/> + </value-type> + + <value-type name="Str" hash-function="strHash"> + <add-function signature="__str__" return-type="PyObject*"> + <inject-code class="target" position="beginning"> + %PYARG_0 = Shiboken::String::fromCString(%CPPSELF.cstring()); + </inject-code> + </add-function> + <add-function signature="__len__" > + <inject-code class="target" position="end"> + return %CPPSELF.size(); + </inject-code> + </add-function> + <add-function signature="__getitem__" > + <inject-code class="target" position="beginning"> + if (_i < 0 || _i >= %CPPSELF.size()) { + PyErr_BadArgument(); + return 0; + } else { + char res[2]; + res[0] = %CPPSELF.get_char(_i); + res[1] = 0; + return Shiboken::String::fromCString(res); + } + </inject-code> + </add-function> + <add-function signature="__setitem__" > + <inject-code class="target" position="beginning"> + PyObject* args = Py_BuildValue("(iO)", _i, _value); + PyObject* result = Sbk_StrFunc_set_char(self, args); + Py_DECREF(args); + int ok = result == Py_True; + if (result) { + Py_DECREF(result); + } + return !ok ? -1 : 0; + </inject-code> + </add-function> + <modify-function signature="toInt(bool*, int)const"> + <modify-argument index="1"> + <remove-argument/> + </modify-argument> + <modify-argument index="return"> + <replace-type modified-type="PyObject*"/> + </modify-argument> + <inject-code class="target" position="beginning"> + <insert-template name="boolptr_at_start_and_one_arg_fix_beginning"/> + </inject-code> + <inject-code class="target" position="end"> + <insert-template name="boolptr_fix_end"/> + </inject-code> + </modify-function> + </value-type> + + <value-type name="ByteArray" hash-function="ByteArray::hash"> + <conversion-rule> + <target-to-native> + <add-conversion type="Py_None"> + SBK_UNUSED(%in) + %out = %OUTTYPE(); + </add-conversion> + <add-conversion type="PyObject" check="Shiboken::String::check(%in) || PyBytes_Check(%in)"> + Py_ssize_t len; + const char* str = Shiboken::String::toCString(%in, &len); + %out = %OUTTYPE(str, len); + </add-conversion> + </target-to-native> + </conversion-rule> + + <modify-function signature="ByteArray(const char*,int)" remove="all" /> + <modify-function signature="ByteArray(const char*)" remove="all" > + <!-- Keep \x00 bytes passed in Python strings. --> + <modify-argument index="1"> + <replace-type modified-type="PyBytes"/> + </modify-argument> + <inject-code class="target" position="beginning"> + PyObject* data = 0; + if (PyUnicode_CheckExact(%PYARG_1)) { + data = PyUnicode_AsASCIIString(%PYARG_1); + } else { + data = %PYARG_1; + Py_INCREF(data); + } + + %0 = new %TYPE(PyBytes_AsString(data), PyBytes_GET_SIZE(data)); + Py_DECREF(data); + </inject-code> + </modify-function> + + <!-- buffer protocol --> + <inject-code class="target" position="end"> + </inject-code> + + <modify-function signature="data() const"> + <inject-code class="target" position="beginning"> + %PYARG_0 = PyBytes_FromStringAndSize(%CPPSELF.%FUNCTION_NAME(), %CPPSELF.size()); + </inject-code> + </modify-function> + + <modify-function signature="hash(const ByteArray&)" remove="all" /> + <!-- Functions removed to proper deal with strings containing zeroes --> + <modify-function signature="append(const char*)" remove="all" /> + <modify-function signature="append(const char*,int)" remove="all" /> + <modify-function signature="operator==(const char*,ByteArray)" remove="all" /> + <modify-function signature="operator==(ByteArray,const char*)" remove="all" /> + <modify-function signature="operator!=(const char*,ByteArray)" remove="all" /> + <modify-function signature="operator!=(ByteArray,const char*)" remove="all" /> + <modify-function signature="operator+(ByteArray,const char*)" remove="all" /> + <modify-function signature="operator+(const char*,ByteArray)" remove="all" /> + <modify-function signature="operator+=(const char*)" remove="all" /> + <modify-function signature="operator[](int)const" remove="all"/> + + <add-function signature="operator+(PyUnicode)" return-type="ByteArray"> + <inject-code> + Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1)); + if (!data.isNull()) { + ByteArray ba(*%CPPSELF); + ba.append(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object())); + %PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba); + } + </inject-code> + </add-function> + <add-function signature="operator+(PyUnicode,ByteArray)" return-type="ByteArray"> + <inject-code> + Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1)); + if (!data.isNull()) { + ByteArray ba(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object())); + ba.append(*%CPPSELF); + %PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba); + } + </inject-code> + </add-function> + <add-function signature="operator+(PyBytes,ByteArray)"> + <inject-code> + ByteArray ba(PyBytes_AsString(%PYARG_1), PyBytes_GET_SIZE(%PYARG_1)); + ba = ba + *%CPPSELF; + %PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba); + </inject-code> + </add-function> + <add-function signature="operator+(PyBytes)"> + <inject-code> + ByteArray ba(PyBytes_AsString(%PYARG_1), PyBytes_GET_SIZE(%PYARG_1)); + ba.append(*%CPPSELF); + %PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba); + </inject-code> + </add-function> + <add-function signature="__repr__" return-type="PyObject*"> + <inject-code class="target" position="beginning"> + ByteArray b(Py_TYPE(%PYSELF)->tp_name); + PyObject* aux = Shiboken::String::fromStringAndSize(%CPPSELF.data(), %CPPSELF.size()); + if (PyUnicode_CheckExact(aux)) { + PyObject* tmp = PyUnicode_AsASCIIString(aux); + Py_DECREF(aux); + aux = tmp; + } + b += "('"; + b += ByteArray(PyBytes_AS_STRING(aux), PyBytes_GET_SIZE(aux)); + b += "')"; + %PYARG_0 = Shiboken::String::fromStringAndSize(b.data(), b.size()); + </inject-code> + </add-function> + + <add-function signature="__str__" return-type="str"> + <inject-code class="target" position="beginning"> + %PYARG_0 = Shiboken::String::fromStringAndSize(%CPPSELF.data(), %CPPSELF.size()); + </inject-code> + </add-function> + + <add-function signature="__len__"> + <inject-code class="target" position="beginning"> + return %CPPSELF.size(); + </inject-code> + </add-function> + <add-function signature="__getitem__"> + <inject-code class="target" position="beginning"> + if (_i < 0 || _i >= %CPPSELF.size()) { + PyErr_SetString(PyExc_IndexError, "index out of bounds"); + return 0; + } else { + char res[2]; + res[0] = %CPPSELF.at(_i); + res[1] = 0; + return PyBytes_FromStringAndSize(res, 1); + } + </inject-code> + </add-function> + </value-type> + + <value-type name="StrList"> + <enum-type name="CtorEnum"/> + <add-function signature="__len__" > + <inject-code class="target" position="end"> + return %CPPSELF.size(); + </inject-code> + </add-function> + <add-function signature="__getitem__" > + <inject-code class="target" position="beginning"> + if (_i < 0 || _i >= static_cast<Py_ssize_t>(%CPPSELF.size())) { + PyErr_BadArgument(); + return 0; + } else { + %TYPE::const_iterator it = %CPPSELF.begin(); + for (Py_ssize_t i = 1; i <= _i; i++) + ++it; + return %CONVERTTOPYTHON[Str](*it); + } + </inject-code> + </add-function> + </value-type> + + <object-type name="SimpleFile"> + <modify-function signature="open()"> + <modify-argument index="return"> + <remove-argument/> + </modify-argument> + <inject-code class="target" position="end" file="simplefile_glue.cpp"/> + </modify-function> + </object-type> + + <value-type name="VoidHolder" /> + + <object-type name="PrivateCtor" /> + <object-type name="PrivateDtor" /> + <value-type name="DeletedDefaultCtor"/> + + <object-type name="Base1"/> + <object-type name="Base2"/> + <object-type name="Base3"/> + <object-type name="Base4"/> + <object-type name="Base5"/> + <object-type name="Base6"/> + <object-type name="MDerived1"/> + <object-type name="MDerived2"/> + <object-type name="MDerived3"/> + <object-type name="MDerived4"/> + <object-type name="MDerived5"/> + <object-type name="SonOfMDerived1"/> + + <object-type name="Bucket" private="true"> + <modify-function signature="lock()" allow-thread="yes" /> + <modify-function signature="virtualBlockerMethod()" allow-thread="yes"/> + <modify-function signature="callVirtualBlockerMethodButYouDontKnowThis()" allow-thread="yes"/> + </object-type> + + <value-type name="Echo"> + <add-function signature="echo(const char *)" return-type="PyObject*"> + <inject-code class="target" position="beginning"> + %PYARG_0 = Shiboken::String::fromCString(%1); + </inject-code> + </add-function> + <add-function signature="operator>(int)"> + <inject-code> + // This should test if code injections works inside rich comparison operators + Py_INCREF(Py_True); + %PYARG_0 = Py_True; + </inject-code> + </add-function> + </value-type> + + <value-type name="Color" /> + <value-type name="Brush" operator-bool="true"> + <enum-type name="Style"/> + </value-type> + <value-type name="Pen"> + <enum-type identified-by-value="EnumCtor"/> + <enum-type name="RenderHints"/> + <property type="RenderHints" name="renderHints" get="getRenderHints" set="setRenderHints"/> + </value-type> + + <value-type name="CtorConvRule" private="true"> + <modify-function signature="CtorConvRule(long)"> + <modify-argument index="1"> + <!--<replace-type modified-type="long"/>--> + <conversion-rule class="native"> + // Does nothing really, just test the code generation + // of constructors whose arguments where + long %out = PyLong_AS_LONG(%PYARG_1) + 1; + </conversion-rule> + </modify-argument> + </modify-function> + </value-type> + + <add-function signature="multiplyString(str, unsigned int)" return-type="const char*"> + <inject-code class="target" position="beginning"> + %PYARG_0 = Shiboken::String::fromCString(""); + for (unsigned int i = 0; i < %2; ++i) + Shiboken::String::concat(&%PYARG_0, %PYARG_1); + </inject-code> + </add-function> + + <add-function signature="countVarargs(int, ...)" return-type="int"> + <inject-code class="target" position="beginning"> + %RETURN_TYPE %0 = PyTuple_GET_SIZE(%PYARG_2); + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </add-function> + + <value-type name="SbkDate"> + <extra-includes> + <include file-name="datetime.h" location="global"/> + </extra-includes> + <inject-code class="native" position="beginning"> + static bool PyDate_ImportAndCheck(PyObject* pyIn) { + if (!PyDateTimeAPI) + PyDateTime_IMPORT; + return PyDate_Check(pyIn); + } + </inject-code> + <conversion-rule> + <target-to-native> + <add-conversion type="PyDate" check="PyDate_ImportAndCheck(%in)"> + int day = PyDateTime_GET_DAY(%in); + int month = PyDateTime_GET_MONTH(%in); + int year = PyDateTime_GET_YEAR(%in); + %out = %OUTTYPE(day, month, year); + </add-conversion> + </target-to-native> + </conversion-rule> + <add-function signature="toPython()" return-type="PyDate"> + <inject-code class="target"> + if (!PyDateTimeAPI) + PyDateTime_IMPORT; + %PYARG_0 = PyDate_FromDate(%CPPSELF.day(), %CPPSELF.month(), %CPPSELF.year()); + </inject-code> + </add-function> + </value-type> + + <object-type name="HandleHolder" /> + <value-type name="PrimitiveStructPointerHolder" /> + + <object-type name="ObjectTypeOperators"> + <add-function signature="operator!=(std::string)" return-type="bool"> + <inject-code class="target"> + %RETURN_TYPE %0 = %CPPSELF.key() != %1; + %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </add-function> + </object-type> + + <value-type name="Filter" /> + <value-type name="Data"> + <enum-type name="Field" /> + <add-function signature="operator&(const Union&)" return-type="Intersection"> + <inject-code class="target"> + %RETURN_TYPE %0 = *%CPPSELF & %1; + return %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </add-function> + </value-type> + <value-type name="Union"> + <add-function signature="operator&(const Data&)" return-type="Intersection"> + <inject-code class="target"> + %RETURN_TYPE %0 = *%CPPSELF & %1; + return %CONVERTTOPYTHON[%RETURN_TYPE](%0); + </inject-code> + </add-function> + </value-type> + <value-type name="Intersection" /> + + <!-- type used in abstract method --> + <object-type name="HideType" generate="no" /> + + <value-type name="Expression" /> + + <object-type name="ExceptionTest" exception-handling="auto-on"> + <modify-function signature="create(bool)"> + <modify-argument index="return"> + <define-ownership owner="c++"/> + </modify-argument> + <inject-code class="target" position="end"> + // Test comment + </inject-code> + </modify-function> + </object-type> + + <value-type name="ModelIndex" /> + <value-type name="ReferentModelIndex"> + <modify-function signature="operator const ModelIndex&()const"> + <modify-argument index="return"> + <define-ownership owner="c++"/> + </modify-argument> + </modify-function> + </value-type> + <value-type name="PersistentModelIndex" /> + + <!-- test rejections using full signatures; this method is a template and + cannot be wrapped, but is otherwise recognized by shiboken and will + result in a compile error if the rejection is not matched --> + <rejection class="Photon::Base" function-name="isType()"/> + + <value-type name="ValueAndVirtual" /> + + <object-type name="ObjectTypeByValue" /> + + <value-type name="StdComplex"> + <extra-includes> + <include file-name="cmath" location="global"/> + </extra-includes> + <!-- PYSIDE-2446: number protocols without a Py_nb_ constant. --> + <add-function signature="__floor__()" return-type="double"> + <inject-code class="target" position="end" + file="samplesnippets.cpp" snippet="stdcomplex_floor"/> + </add-function> + <add-function signature="__ceil__()" return-type="double"> + <inject-code class="target" position="end" + file="samplesnippets.cpp" snippet="stdcomplex_ceil"/> + </add-function> + <!-- PYSIDE-2446: number protocols with Py_nb_ constants. --> + <add-function signature="__abs__()" return-type="double"> + <inject-code class="target" position="end" + file="samplesnippets.cpp" snippet="stdcomplex_abs"/> + </add-function> + <add-function signature="__pow__(StdComplex@exp@)" return-type="StdComplex"> + <inject-code class="target" position="end" + file="samplesnippets.cpp" snippet="stdcomplex_pow"/> + </add-function> + + </value-type> + + <object-type name="TemplatePtr"> + <modify-function signature="dummy(std::list<std::pair<BlackBox *, BlackBox *> > &)" rename="dummy_method" /> + </object-type> + + <value-type name="ToBeRenamedValue" target-lang-name="RenamedValue"/> + <value-type name="RenamedUser"/> + + <enum-type name="LengthUnit"/> + <value-type name="ValueWithUnit" generate="no"/> + <typedef-type name="ValueWithUnitDoubleInch" source="ValueWithUnit<double,LengthUnit::Inch>"/> + <typedef-type name="ValueWithUnitDoubleMillimeter" source="ValueWithUnit<double,LengthUnit::Millimeter>"/> + <value-type name="ValueWithUnitUser"/> + + <suppress-warning text="horribly broken type '__off64_t'" /> + <suppress-warning text="enum '__codecvt_result' does not have a type entry or is not an enum" /> + <suppress-warning text="Pure virtual method 'Abstract::hideFunction(HideType*)' must be implemented but was completely removed on type system." /> + <suppress-warning text="Shadowing: MDerived2::castToBase3() and MDerived3::castToBase3()" /> + <suppress-warning text="Visibility of function 'publicMethod' modified in class 'MDerived1'" /> + + <suppress-warning text="^skipping public function 'std::enable_if.*ComparisonTester::operator[!=]=.*ComparisonTester.*$"/> +</typesystem> diff --git a/sources/shiboken6/tests/samplebinding/typesystypedef_test.py b/sources/shiboken6/tests/samplebinding/typesystypedef_test.py new file mode 100644 index 000000000..f7f5342ee --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/typesystypedef_test.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test case for a class that holds a void pointer.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ValueWithUnitUser, ValueWithUnitDoubleInch + + +class TypeSysTypeDefTest(unittest.TestCase): + '''Test case type system typedefs.''' + + def test(self): + inch_value = ValueWithUnitDoubleInch(10) + mm_value = ValueWithUnitUser.doubleInchToMillimeter(inch_value) + self.assertEqual(int(mm_value.value()), 2540) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/unsafe_parent_test.py b/sources/shiboken6/tests/samplebinding/unsafe_parent_test.py new file mode 100644 index 000000000..2a7e5cac7 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/unsafe_parent_test.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for ...''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType + + +class DerivedObjectType(ObjectType): + def isPython(self): + return True + + def createChild(self, parent): + return DerivedObjectType(parent) + + +class ParentTest(unittest.TestCase): + + def testUunsafeParent(self): + o = DerivedObjectType() + o.callVirtualCreateChild() + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/useraddedctor_test.py b/sources/shiboken6/tests/samplebinding/useraddedctor_test.py new file mode 100644 index 000000000..45d4095b6 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/useraddedctor_test.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for user added constructors''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() +from sample import Size + + +class PointTest(unittest.TestCase): + def testUsingSelfOnCtor(self): + # This is a user added ctor and no errors should happen! + s = Size("3x2") + self.assertEqual(s.height(), 2) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/virtualdtor_test.py b/sources/shiboken6/tests/samplebinding/virtualdtor_test.py new file mode 100644 index 000000000..6be870269 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/virtualdtor_test.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for virtual destructor.''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import VirtualDtor + + +class ExtendedVirtualDtor(VirtualDtor): + def __init__(self): + VirtualDtor.__init__(self) + + +class VirtualDtorTest(unittest.TestCase): + '''Test case for virtual destructor.''' + + def setUp(self): + VirtualDtor.resetDtorCounter() + + def testVirtualDtor(self): + '''Original virtual destructor is being called.''' + dtor_called = VirtualDtor.dtorCalled() + for i in range(1, 10): + vd = VirtualDtor() + del vd + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(VirtualDtor.dtorCalled(), dtor_called + i) + + def testVirtualDtorOnCppCreatedObject(self): + '''Original virtual destructor is being called for a C++ created object.''' + dtor_called = VirtualDtor.dtorCalled() + for i in range(1, 10): + vd = VirtualDtor.create() + del vd + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(VirtualDtor.dtorCalled(), dtor_called + i) + + def testDtorOnDerivedClass(self): + '''Original virtual destructor is being called for a derived class.''' + dtor_called = ExtendedVirtualDtor.dtorCalled() + for i in range(1, 10): + evd = ExtendedVirtualDtor() + del evd + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertEqual(ExtendedVirtualDtor.dtorCalled(), dtor_called + i) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/virtualmethods_test.py b/sources/shiboken6/tests/samplebinding/virtualmethods_test.py new file mode 100644 index 000000000..52dc66c90 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/virtualmethods_test.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test cases for virtual methods.''' + +import gc +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Point, Str, StrList, VirtualDaughter, VirtualMethods + +import warnings + + +class ExtendedVirtualMethods(VirtualMethods): + def __init__(self): + VirtualMethods.__init__(self) + self.virtual_method0_called = False + + def virtualMethod0(self, pt, val, cpx, b): + self.virtual_method0_called = True + return VirtualMethods.virtualMethod0(self, pt, val, cpx, b) * -1.0 + + def strListToStdList(self, arg): + warnings.simplefilter('error') + # returning wrong type for test purposes. + return True + + def recursionOnModifiedVirtual(self, arg): + # check if recursion is caused by injected code that calls C++. + return VirtualMethods.recursionOnModifiedVirtual(self, arg) + 10 + + +class ExtendedVirtualDaughter(VirtualDaughter): + def __init__(self, name): + VirtualDaughter.__init__(self, name) + self.grand_daughter_name_called = False + + def name(self): + self.grand_daughter_name_called = True + return VirtualDaughter.name(self).prepend('Extended') + + +class ExtendedExtendedVirtualDaughter(ExtendedVirtualDaughter): + def __init__(self, name): + ExtendedVirtualDaughter.__init__(self, name) + self.grand_grand_daughter_name_called = False + + def name(self): + self.grand_grand_daughter_name_called = True + return ExtendedVirtualDaughter.name(self).prepend('Extended') + + +class VirtualMethodsTest(unittest.TestCase): + '''Test case for virtual methods''' + + def setUp(self): + self.prefix_from_codeinjection = Str('Pimped') + + def tearDown(self): + del self.prefix_from_codeinjection + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + def testReimplementedVirtualMethod0(self): + '''Test Python override of a virtual method with various different parameters + is correctly called from C++.''' + vm = VirtualMethods() + evm = ExtendedVirtualMethods() + pt = Point(1.1, 2.2) + val = 4 + cpx = complex(3.3, 4.4) + b = True + result0 = vm.callVirtualMethod0(pt, val, cpx, b) + result1 = evm.callVirtualMethod0(pt, val, cpx, b) + self.assertEqual(result0 * -1.0, result1) + + def testRecursionOnModifiedVirtual(self): + evm = ExtendedVirtualMethods() + self.assertEqual(evm.recursionOnModifiedVirtual(''), 10) + self.assertEqual(evm.callRecursionOnModifiedVirtual(''), 10) + + def testReimplementedVirtualMethodInheritedFromGrandParent(self): + '''Test Python override of a virtual method inherited from a grand parent.''' + original_name = 'Foo' + evd = ExtendedVirtualDaughter(original_name) + + self.assertEqual(VirtualDaughter.name(evd), original_name) + self.assertEqual(VirtualMethods.name(evd), original_name) + self.assertFalse(evd.grand_daughter_name_called) + + name = evd.callName() + self.assertTrue(evd.grand_daughter_name_called) + self.assertEqual(evd.name().prepend(self.prefix_from_codeinjection), name) + + def testReimplementedVirtualMethodInheritedFromGrandGrandParent(self): + '''Test Python override of a virtual method inherited from a grand grand parent.''' + original_name = 'Foo' + eevd = ExtendedExtendedVirtualDaughter(original_name) + + self.assertEqual(VirtualDaughter.name(eevd), original_name) + self.assertEqual(VirtualMethods.name(eevd), original_name) + self.assertFalse(eevd.grand_daughter_name_called) + self.assertFalse(eevd.grand_grand_daughter_name_called) + + name = eevd.callName() + self.assertTrue(eevd.grand_daughter_name_called) + self.assertTrue(eevd.grand_grand_daughter_name_called) + self.assertEqual(eevd.name().prepend(self.prefix_from_codeinjection), name) + + def testStringView(self): + virtual_methods = VirtualMethods() + self.assertEqual(virtual_methods.stringViewLength('bla'), 3) + + +class PrettyErrorMessageTest(unittest.TestCase): + def testIt(self): + obj = ExtendedVirtualMethods() + self.assertRaises(RuntimeWarning, obj.callStrListToStdList, StrList()) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/visibilitychange_test.py b/sources/shiboken6/tests/samplebinding/visibilitychange_test.py new file mode 100644 index 000000000..becdf7423 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/visibilitychange_test.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Base1, MDerived1 + + +class VisibilityChangeTest(unittest.TestCase): + + def testVisibilityChange(self): + b1 = Base1() + b1.publicMethod() # ok... + d1 = MDerived1() + self.assertRaises(TypeError, d1.publicMethod) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/voidholder_test.py b/sources/shiboken6/tests/samplebinding/voidholder_test.py new file mode 100644 index 000000000..186cb473e --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/voidholder_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test case for a class that holds a void pointer.''' + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import VoidHolder, Point +from shiboken6 import Shiboken + + +class VoidHolderTest(unittest.TestCase): + '''Test case for void pointer manipulation.''' + + def testGetVoidPointerFromCppAndPutsOnVoidHolder(self): + '''Passes a void pointer created in C++ to be kept by VoidHolder.''' + voidptr = VoidHolder.gimmeMeSomeVoidPointer() + voidholder = VoidHolder(voidptr) + self.assertEqual(voidptr, voidholder.voidPointer()) + + def testPassVoidPointerAsArgument(self): + '''Passes a void pointer created in C++ as an argument to a function.''' + voidptr = VoidHolder.gimmeMeSomeVoidPointer() + voidHolder = VoidHolder() + returnValue = voidHolder.takeVoidPointer(voidptr) + self.assertEqual(returnValue, voidptr) + + def testPutRandomObjectInsideVoidHolder(self): + '''Passes a C++ pointer for an object created in Python to be kept by VoidHolder.''' + obj = Point(1, 2) + voidholder = VoidHolder(obj) + self.assertEqual(Shiboken.getCppPointer(obj)[0], int(voidholder.voidPointer())) + + def testGetNoneObjectFromVoidHolder(self): + '''A VoidHolder created without parameters returns a NULL pointer + that should be converted to a Python None.''' + voidholder = VoidHolder() + self.assertEqual(voidholder.voidPointer(), None) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/weakref_test.py b/sources/shiboken6/tests/samplebinding/weakref_test.py new file mode 100644 index 000000000..01c6d58d5 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/weakref_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Test weakref support''' + +import gc +import os +import sys +import unittest +import weakref + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import ObjectType, PrivateDtor + + +class WeakrefBasicTest(unittest.TestCase): + '''Simple test case of using a weakref''' + + def setUp(self): + self.called = False + + def cb(self, *args): + self.called = True + + def testBasic(self): + '''ObjectType weakref''' + obj = ObjectType() + ref = weakref.ref(obj, self.cb) # noqa: F841 + del obj + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertTrue(self.called) + + def testPrivateDtor(self): + '''PrivateDtor weakref''' + obj = PrivateDtor.instance() + ref = weakref.ref(obj, self.cb) # noqa: F841 + del obj + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + self.assertTrue(self.called) + + +if __name__ == '__main__': + unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/writableclassdict_test.py b/sources/shiboken6/tests/samplebinding/writableclassdict_test.py new file mode 100644 index 000000000..dfc962db9 --- /dev/null +++ b/sources/shiboken6/tests/samplebinding/writableclassdict_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import os +import sys +import unittest + +from pathlib import Path +sys.path.append(os.fspath(Path(__file__).resolve().parents[1])) +from shiboken_paths import init_paths +init_paths() + +from sample import Point + + +class ExtPoint(Point): + pass + + +class TestWritableClassDict(unittest.TestCase): + def testSetattrOnClass(self): + setattr(Point, 'foo', 123) + self.assertEqual(Point.foo, 123) + pt = Point() + self.assertEqual(pt.foo, 123) + + def testSetattrOnInheritingClass(self): + setattr(Point, 'bar', 321) + self.assertEqual(Point.bar, 321) + self.assertEqual(ExtPoint.bar, 321) + pt = ExtPoint() + self.assertEqual(pt.bar, 321) + + +if __name__ == '__main__': + unittest.main() |