Commit: 7e45a760b4fc786af4674a0e44398d65cf709af2 Author: Himanshi Kalra Date: Wed Jul 7 18:46:09 2021 +0530 Branches: soc-2021-geometry-nodes-regression-test https://developer.blender.org/rB7e45a760b4fc786af4674a0e44398d65cf709af2
Added backward support for existing tests. - Added create_expected_object if it doesn't exist. - Added a counter condition for recusrion. - Added optional test_name in basemesh test. - Added code of previous MeshTest to SpecMeshTest. - Changed default value of apply_modifier(s) flag and do_compare flag. =================================================================== M tests/python/modules/base_mesh_test.py =================================================================== diff --git a/tests/python/modules/base_mesh_test.py b/tests/python/modules/base_mesh_test.py index 1e8f48b74fc..d855af74c6d 100644 --- a/tests/python/modules/base_mesh_test.py +++ b/tests/python/modules/base_mesh_test.py @@ -3,24 +3,42 @@ import bpy import os import sys -from modules.spec_classes import ModifierSpec, DeformModifierSpec, OperatorSpecEditMode, OperatorSpecObjectMode +from modules.spec_classes import ModifierSpec, DeformModifierSpec, OperatorSpecEditMode, OperatorSpecObjectMode, ParticleSystemSpec class MeshTest(ABC): - def __init__(self, test_object_name, exp_object_name, threshold=None): + def __init__(self, test_object_name, exp_object_name, test_name=None, threshold=None): self.test_object_name = test_object_name self.exp_object_name = exp_object_name + self.test_name = test_name self.threshold = threshold self.update = os.getenv("BLENDER_TEST_UPDATE") is not None + self.verbose = os.getenv("BLENDER_VERBOSE") is not None # self.eval_object_name = "evaluated_object" - # Private flag to indicate whether the blend file was updated after the test. - self._test_updated = False + + self.test_updated_counter = 0 objects = bpy.data.objects self.test_object = objects[self.test_object_name] + # self.expected_object = objects[self.exp_object_name] - # TODO - create exp object, in case doesn't exist. - self.expected_object = objects[self.exp_object_name] + if self.update: + if objects.find(exp_object_name) > -1: + self.expected_object = objects[self.exp_object_name] + else: + self.create_expected_object() + else: + self.expected_object = objects[self.exp_object_name] + + + def create_expected_object(self): + print("Creating expected object...") + evaluated_object = self.create_evaluated_object() + self.expected_object = evaluated_object + self.expected_object.name = self.exp_object_name + x, y, z = self.test_object.location + self.expected_object.location = (x, y+10, z) + bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath) def create_evaluated_object(self): bpy.context.view_layer.objects.active = self.test_object @@ -40,11 +58,13 @@ class MeshTest(ABC): @staticmethod def _print_result(result): comparison_result, selection_result, validation_result = result - print("Mesh Compariosn: {}".format(comparison_result)) + print("Mesh Comparison: {}".format(comparison_result)) print("Selection Result: {}".format(selection_result)) print("Mesh Validation: {}".format(validation_result)) + print() def run_test(self): + # self.test_updated_counter = 0 evaluated_test_object = self.create_evaluated_object() self.apply_operations(evaluated_test_object) result = self.compare_meshes(evaluated_test_object) @@ -53,25 +73,35 @@ class MeshTest(ABC): if comparison_result == "Same" and selection_result == "Same" and validation_result == "Valid": self.passed_test(result) + # Clean up. + if self.verbose: + print("Cleaning up...") + # Delete evaluated_test_object. + bpy.ops.object.delete() + # bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath) + + return True elif self.update: self.failed_test(result) self.update_failed_test(evaluated_test_object) + if self.test_updated_counter == 1: + self.run_test() + else: + print("The test fails consistently. Exiting...") else: self.failed_test(result) - # Real implementation. - pass def failed_test(self, result): - print("The test failed with the following: ") + print("\nFAILED {} test with the following: ".format(self.test_name)) self._print_result(result) # Real implementation. pass def passed_test(self, result): - print("The tests passed successfully.") + print("\nPASSED {} test successfully.".format(self.test_name)) self._print_result(result) # Real implementation pass @@ -117,7 +147,7 @@ class MeshTest(ABC): # Save file. bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath) - self._test_updated = True + self.test_updated_counter += 1 self.expected_object = evaluated_test_object # Real implementation. @@ -125,7 +155,7 @@ class MeshTest(ABC): def compare_meshes(self, evaluated_object): objects = bpy.data.objects evaluated_test_mesh = objects[evaluated_object.name].data - expected_mesh = objects[self.exp_object_name].data + expected_mesh = self.expected_object.data result_codes = [] # Mesh Comparison. @@ -139,12 +169,12 @@ class MeshTest(ABC): # Selection comparison. - selected_evaluatated_verts = [ + selected_evaluated_verts = [ v.index for v in evaluated_test_mesh.vertices if v.select] selected_expected_verts = [ v.index for v in expected_mesh.vertices if v.select] - if selected_evaluatated_verts == selected_expected_verts: + if selected_evaluated_verts == selected_expected_verts: result_selection = "Same" else: result_selection = "Selection doesn't match." @@ -169,13 +199,312 @@ class MeshTest(ABC): class SpecMeshTest(MeshTest): - def __init__(self, test_name, test_object_name, exp_object_name, operation_stack=None, threshold=None): + def __init__(self, test_name, test_object_name, exp_object_name, operations_stack=None, apply_modifier=True, + do_compare=True, threshold=None): + + super().__init__(test_object_name, exp_object_name, test_name, threshold) self.test_name = test_name - super().__init__(test_object_name, exp_object_name, threshold) - pass + if operations_stack is None: + self.operations_stack = [] + else: + self.operations_stack = operations_stack + self.apply_modifier = apply_modifier + self.do_compare = do_compare - def apply_operations(self): - pass + def apply_operations(self, evaluated_test_object): + # Add modifiers and operators. + print("Applying operations...") + for operation in self.operations_stack: + if isinstance(operation, ModifierSpec): + self._add_modifier(evaluated_test_object, operation) + if self.apply_modifier: + self._apply_modifier( + evaluated_test_object, operation.modifier_name) + + elif isinstance(operation, OperatorSpecEditMode): + self._apply_operator_edit_mode( + evaluated_test_object, operation) + + elif isinstance(operation, OperatorSpecObjectMode): + self._apply_operator_object_mode(operation) + + elif isinstance(operation, DeformModifierSpec): + self._apply_deform_modifier(evaluated_test_object, operation) + + elif isinstance(operation, ParticleSystemSpec): + self._apply_particle_system(evaluated_test_object, operation) + + else: + raise ValueError("Expected operation of type {} or {} or {} or {}. Got {}". + format(type(ModifierSpec), type(OperatorSpecEditMode), + type(OperatorSpecObjectMode), type(ParticleSystemSpec), type(operation))) + + def _set_parameters_impl(self, modifier, modifier_parameters, nested_settings_path, modifier_name): + """ + Doing a depth first traversal of the modifier parameters and setting their values. + :param: modifier: Of type modifier, its altered to become a setting in recursion. + :param: modifier_parameters : dict or sequence, a simple/nested dictionary of modifier parameters. + :param: nested_settings_path : list(stack): helps in tracing path to each node. + """ + if not isinstance(modifier_parameters, dict): + param_setting = None + for i, setting in enumerate(nested_settings_path): + + # We want to set the attribute only when we have reached the last setting. + # Applying of intermediate settings is meaningless. + if i == len(nested_settings_path) - 1: + setattr(modifier, setting, modifier_parameters) + + elif hasattr(modifier, setting): + param_setting = getattr(modifier, setting) + # getattr doesn't accept canvas_surfaces["Surface"], but we need to pass it to setattr. + if setting == "canvas_surfaces": + modifier = param_setting.active + else: + modifier = param_setting + else: + # Clean up first + bpy.ops.object.delete() + raise Exception("Modifier '{}' has no parameter named '{}'". + format(modifier_name, setting)) + + # It pops the current node before moving on to its sibling. + nested_settings_path.pop() + return + + for key in modifier_parameters: + nested_settings_path.append(key) + self._set_parameters_impl( + modifier, modifier_parameters[key], nested_settings_path, modifier_name) + + if nested_settings_path: + nested_settings_path.pop() + + def set_parameters(self, modifier, modifier_parameters): + """ + Wrapper for _set_parameters_util + """ + settings = [] + modifier_name = modifier.name + self._set_parameters_impl( + modifier, modifier_parameters, settings, modifier_name) + + def _add_modifier(self, test_object, modifier @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org List details, subscription details or unsubscribe: https://lists.blender.org/mailman/listinfo/bf-blender-cvs