Adding object conversion tests to verify the refactored object type conversion code works as intended. The object conversion operator `exec()` function is restructured by PR #130668 to clean up code path for better readability, since it's a relatively big change, this test is put up to verify that: - For supported object type pairs, converted objects would have the expected target object type. - For unsupported/unimplemented type pairs, the test expect the converted object to have the same type as the source object. - Verify that the code behaviour stays exactly the same as prior to the change in #130668, including code logic that are later subject to change and redesign, in which case this test file/script would need to be updated accrodingly. This test added such files: - `tests/data/modelling/object_conversion.blend`: Sets up differnt types of input objects and their expected converted object combinations. - `tests/python/object_conversion.py`: Does mesh equal checks for objects that are converted to mesh against expected objects in the blend file, and does object type checks for other target types. Note: This test currently does not do topology-level equal check except for when target object type is `MESH`. Equal check hasn't been implemented for other object types yet, but ideally in the future we should use topology check on top of type check for other object types as well. See https://projects.blender.org/blender/blender/pulls/130668 Pull Request: https://projects.blender.org/blender/blender/pulls/130738
171 lines
7.3 KiB
Python
171 lines
7.3 KiB
Python
# 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", "<test_name>"))
|
|
|
|
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()
|