blender/tests/python/curves_extrude.py
laurynas a6fd1f5034 Fix: Curves extrude with all points selected
CurvesGeometry has no ".selection" attribute when all control points
are selected. The earlier code assumed that the attribute always exists.
Also Python tests are added for the "extrude" operator.

Pull Request: https://projects.blender.org/blender/blender/pulls/117095
2024-01-16 21:59:15 +01:00

193 lines
6.8 KiB
Python

# SPDX-FileCopyrightText: 2020-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
from abc import ABC, abstractmethod
import bpy
import os
import sys
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from modules.mesh_test import RunTest
class CurvesTest(ABC):
def __init__(self, test_object_name, exp_object_name, test_name=None):
self.test_object_name = test_object_name
self.exp_object_name = exp_object_name
self.test_object = bpy.data.objects[self.test_object_name]
self.expected_object = bpy.data.objects[self.exp_object_name]
self.verbose = os.getenv("BLENDER_VERBOSE") is not None
if test_name:
self.test_name = test_name
else:
filepath = bpy.data.filepath
self.test_name = bpy.path.display_name_from_filepath(filepath)
self._failed_tests_list = []
def create_evaluated_object(self):
"""
Creates an evaluated object.
"""
bpy.context.view_layer.objects.active = self.test_object
# Duplicate test object.
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.select_all(action="DESELECT")
bpy.context.view_layer.objects.active = self.test_object
self.test_object.select_set(True)
bpy.ops.object.duplicate()
self.evaluated_object = bpy.context.active_object
self.evaluated_object.name = "evaluated_object"
@staticmethod
def _print_result(result):
"""
Prints the comparison, selection and validation result.
"""
print("Results:")
for key in result:
print("{} : {}".format(key, result[key][1]))
print()
def run_test(self):
"""
Runs a single test, runs it again if test file is updated.
"""
print("\nSTART {} test.".format(self.test_name))
self.create_evaluated_object()
self.apply_operations()
result = self.compare_objects(self.evaluated_object, self.expected_object)
# Initializing with True to get correct resultant of result_code booleans.
success = True
inside_loop_flag = False
for key in result:
inside_loop_flag = True
success = success and result[key][0]
# Check "success" is actually evaluated and is not the default True value.
if not inside_loop_flag:
success = False
if success:
self.print_passed_test_result(result)
# Clean up.
if self.verbose:
print("Cleaning up...")
# Delete evaluated_test_object.
bpy.ops.object.delete()
return True
else:
self.print_failed_test_result(result)
return False
@abstractmethod
def apply_operations(self, evaluated_test_object_name):
pass
@staticmethod
def compare_curves(evaluated_curves, expected_curves):
if len(evaluated_curves.attributes.items()) != len(expected_curves.attributes.items()):
print("Attribute count doesn't match")
for a_idx, attribute in evaluated_curves.attributes.items():
expected_attribute = expected_curves.attributes[a_idx]
if len(attribute.data.items()) != len(expected_attribute.data.items()):
print("Attribute data length doesn't match")
value_attr_name = ('vector' if attribute.data_type == 'FLOAT_VECTOR'
or attribute.data_type == 'FLOAT2' else
'color' if attribute.data_type == 'FLOAT_COLOR' else 'value')
for v_idx, attribute_value in attribute.data.items():
if getattr(
attribute_value,
value_attr_name) != getattr(
expected_attribute.data[v_idx],
value_attr_name):
print("Attribute '{}' values do not match".format(attribute.name))
return False
return True
def compare_objects(self, evaluated_object, expected_object):
result_codes = {}
equal = self.compare_curves(evaluated_object.data, expected_object.data)
result_codes['Curves Comparison'] = (equal, evaluated_object.data)
return result_codes
def print_failed_test_result(self, result):
"""
Print results for failed test.
"""
print("FAILED {} test with the following: ".format(self.test_name))
def print_passed_test_result(self, result):
"""
Print results for passing test.
"""
print("PASSED {} test successfully.".format(self.test_name))
class CurvesOpTest(CurvesTest):
def __init__(self, test_name, test_object_name, exp_object_name, operators_stack):
super().__init__(test_object_name, exp_object_name, test_name)
self.operators_stack = operators_stack
def apply_operations(self):
for operator_name in self.operators_stack:
bpy.ops.object.mode_set(mode='EDIT')
curves_operator = getattr(bpy.ops.curves, operator_name)
try:
retval = curves_operator()
except AttributeError:
raise AttributeError("bpy.ops.curves has no attribute {}".format(operator_name))
except TypeError as ex:
raise TypeError("Incorrect operator parameters {!r} raised {!r}".format([], ex))
if retval != {'FINISHED'}:
raise RuntimeError("Unexpected operator return value: {}".format(operator_name))
bpy.ops.object.mode_set(mode='OBJECT')
def main():
tests = [
CurvesOpTest("Extrude 1 Point Curve", "a_test1PointCurve", "a_test1PointCurveExpected", ['extrude']),
CurvesOpTest("Extrude Middle Points", "b_testMiddlePoints", "b_testMiddlePointsExpected", ['extrude']),
CurvesOpTest("Extrude End Points", "c_testEndPoints", "c_testEndPointsExpected", ['extrude']),
CurvesOpTest("Extrude Neighbors In Separate Curves", "d_testNeighborsInCurves", "d_testNeighborsInCurvesExpected", ['extrude']),
CurvesOpTest("Extrude Edge Curves", "e_testEdgeCurves", "e_testEdgeCurvesExpected", ['extrude']),
CurvesOpTest("Extrude Middle Curve", "f_testMiddleCurve", "f_testMiddleCurveExpected", ['extrude']),
CurvesOpTest("Extrude All Points", "g_testAllPoints", "g_testAllPointsExpected", ['extrude'])
]
curves_extrude_test = RunTest(tests)
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
curves_extrude_test.do_compare = True
curves_extrude_test.run_all_tests()
break
elif cmd == "--run-test":
curves_extrude_test.do_compare = False
name = command[i + 1]
curves_extrude_test.run_test(name)
break
if __name__ == "__main__":
main()