diff --git a/tests/python/bl_animation_keyframing.py b/tests/python/bl_animation_keyframing.py index 6f1d606ba9d..47cb8459e52 100644 --- a/tests/python/bl_animation_keyframing.py +++ b/tests/python/bl_animation_keyframing.py @@ -15,7 +15,11 @@ blender -b -noaudio --factory-startup --python tests/python/bl_animation_keyfram def _fcurve_paths_match(fcurves: list, expected_paths: list) -> bool: data_paths = list(set([fcurve.data_path for fcurve in fcurves])) - return sorted(data_paths) == sorted(expected_paths) + data_paths.sort() + expected_paths.sort() + if data_paths != expected_paths: + raise AssertionError( + f"Expected paths do not match F-Curve paths. Expected: {expected_paths}. F-Curve: {data_paths}") def _get_view3d_context(): @@ -69,9 +73,8 @@ def _insert_by_name_test(insert_key: str, expected_paths: list): keyed_object = _create_animation_object() with bpy.context.temp_override(**_get_view3d_context()): bpy.ops.anim.keyframe_insert_by_name(type=insert_key) - match = _fcurve_paths_match(keyed_object.animation_data.action.fcurves, expected_paths) + _fcurve_paths_match(keyed_object.animation_data.action.fcurves, expected_paths) bpy.data.objects.remove(keyed_object, do_unlink=True) - return match def _insert_from_user_preference_test(enabled_user_pref_fields: set, expected_paths: list): @@ -79,9 +82,8 @@ def _insert_from_user_preference_test(enabled_user_pref_fields: set, expected_pa bpy.context.preferences.edit.key_insert_channels = enabled_user_pref_fields with bpy.context.temp_override(**_get_view3d_context()): bpy.ops.anim.keyframe_insert() - match = _fcurve_paths_match(keyed_object.animation_data.action.fcurves, expected_paths) + _fcurve_paths_match(keyed_object.animation_data.action.fcurves, expected_paths) bpy.data.objects.remove(keyed_object, do_unlink=True) - return match def _get_keying_set(scene, name: str): @@ -95,9 +97,8 @@ def _insert_with_keying_set_test(keying_set_name: str, expected_paths: list): keyed_object = _create_animation_object() with bpy.context.temp_override(**_get_view3d_context()): bpy.ops.anim.keyframe_insert() - match = _fcurve_paths_match(keyed_object.animation_data.action.fcurves, expected_paths) + _fcurve_paths_match(keyed_object.animation_data.action.fcurves, expected_paths) bpy.data.objects.remove(keyed_object, do_unlink=True) - return match class AbstractKeyframingTest: @@ -110,30 +111,31 @@ class InsertKeyTest(AbstractKeyframingTest, unittest.TestCase): """ Ensure keying things by name or with a keying set adds the right keys. """ def test_insert_by_name(self): - self.assertTrue(_insert_by_name_test("Location", ["location"])) - self.assertTrue(_insert_by_name_test("Rotation", ["rotation_euler"])) - self.assertTrue(_insert_by_name_test("Scaling", ["scale"])) - self.assertTrue(_insert_by_name_test("LocRotScale", ["location", "rotation_euler", "scale"])) + _insert_by_name_test("Location", ["location"]) + _insert_by_name_test("Rotation", ["rotation_euler"]) + _insert_by_name_test("Scaling", ["scale"]) + _insert_by_name_test("LocRotScale", ["location", "rotation_euler", "scale"]) def test_insert_with_keying_set(self): - self.assertTrue(_insert_with_keying_set_test("Location", ["location"])) - self.assertTrue(_insert_with_keying_set_test("Rotation", ["rotation_euler"])) - self.assertTrue(_insert_with_keying_set_test("Scale", ["scale"])) - self.assertTrue( - _insert_with_keying_set_test("Location, Rotation & Scale", ["location", "rotation_euler", "scale"]) - ) + _insert_with_keying_set_test("Location", ["location"]) + _insert_with_keying_set_test("Rotation", ["rotation_euler"]) + _insert_with_keying_set_test("Scale", ["scale"]) + _insert_with_keying_set_test("Location, Rotation & Scale", ["location", "rotation_euler", "scale"]) def test_insert_from_user_preferences(self): - self.assertTrue(_insert_from_user_preference_test({"LOCATION"}, ["location"])) - self.assertTrue(_insert_from_user_preference_test({"ROTATION"}, ["rotation_euler"])) - self.assertTrue(_insert_from_user_preference_test({"SCALE"}, ["scale"])) - self.assertTrue(_insert_from_user_preference_test( - {"LOCATION", "ROTATION", "SCALE"}, ["location", "rotation_euler", "scale"])) + _insert_from_user_preference_test({"LOCATION"}, ["location"]) + _insert_from_user_preference_test({"ROTATION"}, ["rotation_euler"]) + _insert_from_user_preference_test({"SCALE"}, ["scale"]) + _insert_from_user_preference_test({"LOCATION", "ROTATION", "SCALE"}, ["location", "rotation_euler", "scale"]) class VisualKeyingTest(AbstractKeyframingTest, unittest.TestCase): """ Check if visual keying produces the correct keyframe values. """ + def tearDown(self): + super().tearDown() + bpy.context.preferences.edit.use_visual_keying = False + def test_visual_location_keying_set(self): t_value = 1 target = _create_animation_object() @@ -148,9 +150,6 @@ class VisualKeyingTest(AbstractKeyframingTest, unittest.TestCase): for fcurve in constrained.animation_data.action.fcurves: self.assertEqual(fcurve.keyframe_points[0].co.y, t_value) - bpy.data.objects.remove(target, do_unlink=True) - bpy.data.objects.remove(constrained, do_unlink=True) - def test_visual_rotation_keying_set(self): rot_value_deg = 45 rot_value_rads = radians(rot_value_deg) @@ -167,9 +166,6 @@ class VisualKeyingTest(AbstractKeyframingTest, unittest.TestCase): for fcurve in constrained.animation_data.action.fcurves: self.assertAlmostEqual(fcurve.keyframe_points[0].co.y, rot_value_rads, places=4) - bpy.data.objects.remove(target, do_unlink=True) - bpy.data.objects.remove(constrained, do_unlink=True) - def test_visual_location_user_pref_override(self): # When enabling the user preference setting, # the normal keying sets behave like their visual keying set counterpart. @@ -187,10 +183,6 @@ class VisualKeyingTest(AbstractKeyframingTest, unittest.TestCase): for fcurve in constrained.animation_data.action.fcurves: self.assertEqual(fcurve.keyframe_points[0].co.y, t_value) - bpy.data.objects.remove(target, do_unlink=True) - bpy.data.objects.remove(constrained, do_unlink=True) - bpy.context.preferences.edit.use_visual_keying = False - def test_visual_location_user_pref(self): target = _create_animation_object() t_value = 1 @@ -208,10 +200,6 @@ class VisualKeyingTest(AbstractKeyframingTest, unittest.TestCase): for fcurve in constrained.animation_data.action.fcurves: self.assertEqual(fcurve.keyframe_points[0].co.y, t_value) - bpy.data.objects.remove(target, do_unlink=True) - bpy.data.objects.remove(constrained, do_unlink=True) - bpy.context.preferences.edit.use_visual_keying = False - class CycleAwareKeyingTest(AbstractKeyframingTest, unittest.TestCase): """ Check if cycle aware keying remaps the keyframes correctly and adds fcurve modifiers. """ @@ -246,7 +234,7 @@ class CycleAwareKeyingTest(AbstractKeyframingTest, unittest.TestCase): bpy.ops.anim.keyframe_insert_by_name(type="Location") # Check that only location keys have been created. - self.assertTrue(_fcurve_paths_match(action.fcurves, ["location"])) + _fcurve_paths_match(action.fcurves, ["location"]) expected_keys = [1, 3, 5, 9, 20] @@ -260,26 +248,29 @@ class CycleAwareKeyingTest(AbstractKeyframingTest, unittest.TestCase): # All fcurves should have a cycles modifier. self.assertTrue(fcurve.modifiers[0].type == "CYCLES") - bpy.data.objects.remove(keyed_object, do_unlink=True) - class AutoKeyframingTest(AbstractKeyframingTest, unittest.TestCase): - def test_autokey_basic(self): - keyed_object = _create_animation_object() + def setUp(self): + super().setUp() bpy.context.scene.tool_settings.use_keyframe_insert_auto = True bpy.context.preferences.edit.use_keyframe_insert_available = False + bpy.context.preferences.edit.use_keyframe_insert_needed = False + + def tearDown(self): + super().tearDown() + bpy.context.scene.tool_settings.use_keyframe_insert_auto = False + + def test_autokey_basic(self): + keyed_object = _create_animation_object() with bpy.context.temp_override(**_get_view3d_context()): bpy.ops.transform.translate(value=(1, 0, 0)) action = keyed_object.animation_data.action - self.assertTrue(_fcurve_paths_match(action.fcurves, ["location", "rotation_euler", "scale"])) - bpy.data.objects.remove(keyed_object, do_unlink=True) + _fcurve_paths_match(action.fcurves, ["location", "rotation_euler", "scale"]) def test_autokey_bone(self): armature_obj = _create_armature() - bpy.context.scene.tool_settings.use_keyframe_insert_auto = True - bpy.context.preferences.edit.use_keyframe_insert_available = False bpy.ops.object.mode_set(mode='POSE') # Not overriding the context because it would mean context.selected_pose_bones is empty @@ -290,18 +281,24 @@ class AutoKeyframingTest(AbstractKeyframingTest, unittest.TestCase): action = armature_obj.animation_data.action bone_path = f"pose.bones[\"{_BONE_NAME}\"]" expected_paths = [f"{bone_path}.location", f"{bone_path}.rotation_euler", f"{bone_path}.scale"] - self.assertTrue(_fcurve_paths_match(action.fcurves, expected_paths)) - - bpy.data.objects.remove(armature_obj, do_unlink=True) + _fcurve_paths_match(action.fcurves, expected_paths) class InsertAvailableTest(AbstractKeyframingTest, unittest.TestCase): - def test_autokey_available_object(self): - keyed_object = _create_animation_object() - + def setUp(self): + super().setUp() bpy.context.scene.tool_settings.use_keyframe_insert_auto = True bpy.context.preferences.edit.use_keyframe_insert_available = True + bpy.context.preferences.edit.use_keyframe_insert_needed = False + + def tearDown(self): + super().tearDown() + bpy.context.scene.tool_settings.use_keyframe_insert_auto = False + bpy.context.preferences.edit.use_keyframe_insert_available = False + + def test_autokey_available_object(self): + keyed_object = _create_animation_object() with bpy.context.temp_override(**_get_view3d_context()): bpy.context.scene.frame_set(1) @@ -311,7 +308,7 @@ class InsertAvailableTest(AbstractKeyframingTest, unittest.TestCase): # Test that no new keyframes have been added. action = keyed_object.animation_data.action - self.assertTrue(_fcurve_paths_match(action.fcurves, ["rotation_euler"])) + _fcurve_paths_match(action.fcurves, ["rotation_euler"]) with bpy.context.temp_override(**_get_view3d_context()): bpy.context.scene.frame_set(1) @@ -320,7 +317,7 @@ class InsertAvailableTest(AbstractKeyframingTest, unittest.TestCase): bpy.ops.transform.translate(value=(1, 0, 0)) action = keyed_object.animation_data.action - self.assertTrue(_fcurve_paths_match(action.fcurves, ["location", "rotation_euler"])) + _fcurve_paths_match(action.fcurves, ["location", "rotation_euler"]) for fcurve in action.fcurves: # Translating the bone would also add rotation keys as long as "Only Insert Needed" is off. @@ -329,13 +326,8 @@ class InsertAvailableTest(AbstractKeyframingTest, unittest.TestCase): else: raise AssertionError(f"Did not expect keys other than location and rotation, got {fcurve.data_path}.") - bpy.context.preferences.edit.use_keyframe_insert_available = False - bpy.data.objects.remove(keyed_object, do_unlink=True) - def test_autokey_available_bone(self): armature_obj = _create_armature() - bpy.context.scene.tool_settings.use_keyframe_insert_auto = True - bpy.context.preferences.edit.use_keyframe_insert_available = True bpy.ops.object.mode_set(mode='POSE') with bpy.context.temp_override(**_get_view3d_context()): @@ -348,7 +340,7 @@ class InsertAvailableTest(AbstractKeyframingTest, unittest.TestCase): action = armature_obj.animation_data.action bone_path = f"pose.bones[\"{_BONE_NAME}\"]" expected_paths = [f"{bone_path}.rotation_euler"] - self.assertTrue(_fcurve_paths_match(action.fcurves, expected_paths)) + _fcurve_paths_match(action.fcurves, expected_paths) with bpy.context.temp_override(**_get_view3d_context()): bpy.context.scene.frame_set(1) @@ -357,7 +349,7 @@ class InsertAvailableTest(AbstractKeyframingTest, unittest.TestCase): bpy.ops.transform.translate(value=(1, 0, 0)) expected_paths = [f"{bone_path}.location", f"{bone_path}.rotation_euler"] - self.assertTrue(_fcurve_paths_match(action.fcurves, expected_paths)) + _fcurve_paths_match(action.fcurves, expected_paths) for fcurve in action.fcurves: # Translating the bone would also add rotation keys as long as "Only Insert Needed" is off. @@ -366,14 +358,9 @@ class InsertAvailableTest(AbstractKeyframingTest, unittest.TestCase): else: raise AssertionError(f"Did not expect keys other than location and rotation, got {fcurve.data_path}.") - bpy.data.objects.remove(armature_obj, do_unlink=True) - def test_insert_available_keying_set(self): keyed_object = _create_animation_object() - bpy.context.scene.tool_settings.use_keyframe_insert_auto = True - bpy.context.preferences.edit.use_keyframe_insert_available = False - with bpy.context.temp_override(**_get_view3d_context()): self.assertRaises(RuntimeError, bpy.ops.anim.keyframe_insert_by_name, type="Available") @@ -386,14 +373,11 @@ class InsertAvailableTest(AbstractKeyframingTest, unittest.TestCase): bpy.ops.anim.keyframe_insert_by_name(type="Available") action = keyed_object.animation_data.action - self.assertTrue(_fcurve_paths_match(action.fcurves, ["location"])) + _fcurve_paths_match(action.fcurves, ["location"]) for fcurve in action.fcurves: self.assertEqual(len(fcurve.keyframe_points), 2) - bpy.context.preferences.edit.use_keyframe_insert_available = False - bpy.data.objects.remove(keyed_object, do_unlink=True) - class InsertNeededTest(AbstractKeyframingTest, unittest.TestCase): @@ -404,6 +388,7 @@ class InsertNeededTest(AbstractKeyframingTest, unittest.TestCase): bpy.context.preferences.edit.use_keyframe_insert_available = False def tearDown(self): + super().tearDown() bpy.context.scene.tool_settings.use_keyframe_insert_auto = False bpy.context.preferences.edit.use_keyframe_insert_needed = False @@ -417,7 +402,7 @@ class InsertNeededTest(AbstractKeyframingTest, unittest.TestCase): bpy.ops.transform.translate(value=(1, 0, 0)) action = keyed_object.animation_data.action - self.assertTrue(_fcurve_paths_match(action.fcurves, ["location"])) + _fcurve_paths_match(action.fcurves, ["location"]) # With "Insert Needed" enabled it has to key all location channels first, # before it can add keys only to the channels where values have actually @@ -447,7 +432,7 @@ class InsertNeededTest(AbstractKeyframingTest, unittest.TestCase): action = armature_obj.animation_data.action bone_path = f"pose.bones[\"{_BONE_NAME}\"]" - self.assertTrue(_fcurve_paths_match(action.fcurves, [f"{bone_path}.location"])) + _fcurve_paths_match(action.fcurves, [f"{bone_path}.location"]) # With "Insert Needed" enabled it has to key all location channels first, # before it can add keys only to the channels where values have actually