diff --git a/tests/data b/tests/data index 15cc5f5da28..fefe28828e9 160000 --- a/tests/data +++ b/tests/data @@ -1 +1 @@ -Subproject commit 15cc5f5da281af95d0333eeb9955ae5ee50525b5 +Subproject commit fefe28828e9428f6dd9293428a66e85fcfcb0a2e diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index a051cc0e9f1..48f90f67998 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -287,6 +287,14 @@ add_blender_test( --run-all-tests ) +add_blender_test( + object_conversion + ${TEST_SRC_DIR}/modeling/object_conversion.blend + --python ${TEST_PYTHON_DIR}/object_conversion.py + -- + --run-all-tests +) + # ------------------------------------------------------------------------------ # MODIFIERS TESTS add_blender_test( diff --git a/tests/python/object_conversion.py b/tests/python/object_conversion.py new file mode 100644 index 00000000000..32de9320e53 --- /dev/null +++ b/tests/python/object_conversion.py @@ -0,0 +1,170 @@ +# SPDX-FileCopyrightText: 2020-2024 Blender Authors +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# To run all tests, use +# BLENDER_VERBOSE=1 ./bin/blender ../blender/tests/data/modeling/object_conversion.blend --python ../blender/tests/python/object_conversion.py -- --run-all-tests +# (that assumes the test is run from a build directory in the same directory as the source code) +import bpy +import os +import sys +import inspect + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import SpecMeshTest, OperatorSpecObjectMode, RunTest + + +class ConversionTypeTestHelper: + def __init__(self, tests): + self.tests = tests + self._failed_tests_list = [] + + def run_all_tests(self): + """ + Run all tests in self.tests list. Displays all failed tests at bottom. + """ + for _, each_test in enumerate(self.tests): + test_name = each_test.test_name + success = self.run_test(test_name) + + if not success: + self._failed_tests_list.append(test_name) + + if len(self._failed_tests_list) != 0: + print("\nFollowing tests failed: {}".format(self._failed_tests_list)) + + blender_path = bpy.app.binary_path + blend_path = bpy.data.filepath + frame = inspect.stack()[1] + module = inspect.getmodule(frame[0]) + python_path = module.__file__ + + print("Run following command to open Blender and run the failing test:") + print("{} {} --python {} -- {} {}" + .format(blender_path, blend_path, python_path, "--run-test", "")) + + raise Exception("Tests {} failed".format(self._failed_tests_list)) + + def run_test(self, test_name: str): + """ + Run a single test from self.tests list. + + :arg test_name: int - name of test + :return: bool - True if test passed, False otherwise. + """ + case = None + for index, each_test in enumerate(self.tests): + if test_name == each_test.test_name: + case = self.tests[index] + break + + if case is None: + raise Exception('No test called {} found!'.format(test_name)) + + test = case + print("Running test '{}'".format(test.test_name)) + + test_object = bpy.data.objects[test.from_object] + with bpy.context.temp_override(object=test_object, selected_objects=[test_object]): + bpy.context.view_layer.objects.active = test_object + + selection = test_object.select_get() + test_object.select_set(True) + retval = bpy.ops.object.convert(target=test.to_type, keep_original=True) + test_object.select_set(False) + + if retval != {'FINISHED'}: + raise RuntimeError("Unexpected operator return value: {}".format(retval)) + + resulting_type = bpy.context.view_layer.objects.active.type + bpy.ops.object.delete() + if resulting_type != test.resulting_type: + raise RuntimeError( + "Converted object does not match expected type.\nTest '{}': Converting '{}' to '{}' expecting '{}' got '{}'\n" .format( + test.test_name, + test.from_object, + test.to_type, + test.resulting_type, + resulting_type)) + + print("Success\n") + return True + + +class ConversionPair: + def __init__(self, test_name, from_object, to_type, resulting_type): + self.test_name = test_name + self.from_object = from_object + self.to_type = to_type + self.resulting_type = resulting_type + + +def main(): + tests = [ + SpecMeshTest('Mesh 1', 'Cube', 'Cube_Mesh', + [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), + SpecMeshTest('Mesh 2', 'CubeWithEdges', 'CubeWithEdges_Mesh', + [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), + SpecMeshTest('Mesh 3', 'Plane', 'Plane_Mesh', + [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), + SpecMeshTest('Mesh 4', 'HollowPlane', 'HollowPlane_Mesh', + [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), + SpecMeshTest('Mesh 5', 'Suzanne', 'Suzanne_Mesh', + [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), + SpecMeshTest('Mesh 6', 'BezierCircle', 'BezierCircle_Mesh', + [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), + SpecMeshTest('Mesh 7', 'BezierCurve', 'BezierCurve_Mesh', + [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), + SpecMeshTest('Mesh 8', 'Text', 'Text_Mesh', + [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), + ] + + type_tests = [ + ConversionPair('Legacy Curves 1', 'Cube', 'CURVE', 'MESH'), + ConversionPair('Legacy Curves 2', 'CubeWithEdges', 'CURVE', 'CURVE'), + ConversionPair('Legacy Curves 3', 'Plane', 'CURVE', 'CURVE'), + ConversionPair('Legacy Curves 4', 'HollowPlane', 'CURVE', 'CURVE'), + ConversionPair('Legacy Curves 5', 'Suzanne', 'CURVE', 'GREASEPENCIL'), + ConversionPair('Legacy Curves 6', 'BezierCircle', 'CURVE', 'CURVE'), + ConversionPair('Legacy Curves 7', 'BezierCurve', 'CURVE', 'CURVE'), + ConversionPair('Legacy Curves 8', 'Text', 'CURVE', 'CURVE'), + ConversionPair('Curves 1', 'Cube', 'CURVES', 'MESH'), + ConversionPair('Curves 2', 'CubeWithEdges', 'CURVES', 'MESH'), + ConversionPair('Curves 3', 'Plane', 'CURVES', 'MESH'), + ConversionPair('Curves 4', 'HollowPlane', 'CURVES', 'MESH'), + ConversionPair('Curves 5', 'Suzanne', 'CURVES', 'CURVES'), + ConversionPair('Curves 6', 'BezierCircle', 'CURVES', 'CURVES'), + ConversionPair('Curves 7', 'BezierCurve', 'CURVES', 'CURVES'), + ConversionPair('Curves 8', 'Text', 'CURVES', 'FONT'), + ConversionPair('GreasePencil 1', 'Cube', 'GREASEPENCIL', 'MESH'), + ConversionPair('GreasePencil 2', 'CubeWithEdges', 'GREASEPENCIL', 'MESH'), + ConversionPair('GreasePencil 3', 'Plane', 'GREASEPENCIL', 'MESH'), + ConversionPair('GreasePencil 4', 'HollowPlane', 'GREASEPENCIL', 'MESH'), + ConversionPair('GreasePencil 5', 'Suzanne', 'GREASEPENCIL', 'GREASEPENCIL'), + ConversionPair('GreasePencil 6', 'BezierCircle', 'GREASEPENCIL', 'CURVE'), + ConversionPair('GreasePencil 7', 'BezierCurve', 'GREASEPENCIL', 'CURVE'), + ConversionPair('GreasePencil 8', 'Text', 'GREASEPENCIL', 'CURVE'), + ConversionPair('GreasePencil 9', 'Suzanne_Curves', 'GREASEPENCIL', 'GREASEPENCIL'), + ConversionPair('GreasePencil 10', 'BezierCircle_Curves', 'GREASEPENCIL', 'GREASEPENCIL'), + ConversionPair('GreasePencil 11', 'BezierCurve_Curves', 'GREASEPENCIL', 'GREASEPENCIL'), + ] + + operator_test = RunTest(tests) + all_type_tests = ConversionTypeTestHelper(type_tests) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + operator_test.do_compare = True + operator_test.run_all_tests() + all_type_tests.run_all_tests() + break + elif cmd == "--run-test": + name = command[i + 1] + operator_test.do_compare = False + operator_test.run_test(name) + break + + +if __name__ == "__main__": + main()