Blender V5.0
action_legacy_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4#include "ANIM_action.hh"
6
7#include "BKE_fcurve.hh"
8#include "BKE_idtype.hh"
9#include "BKE_lib_id.hh"
10#include "BKE_main.hh"
11#include "BKE_object.hh"
12
13#include "DNA_anim_types.h"
14
15#include "BLI_listbase.h"
16
17#include "CLG_log.h"
18#include "testing/testing.h"
19
21class ActionLegacyTest : public testing::Test {
22 public:
24
25 static void SetUpTestSuite()
26 {
27 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
28 CLG_init();
29
30 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
32 }
33
34 static void TearDownTestSuite()
35 {
36 CLG_exit();
37 }
38
39 void SetUp() override
40 {
42 }
43
44 void TearDown() override
45 {
47 }
48
50 {
51 return BKE_id_new<bAction>(bmain, "ACAction");
52 }
53
54 FCurve *fcurve_add_legacy(bAction *action, const StringRefNull rna_path, const int array_index)
55 {
56 FCurve *fcurve = MEM_callocN<FCurve>(__func__);
57 BKE_fcurve_rnapath_set(*fcurve, rna_path);
58 fcurve->array_index = array_index;
59 BLI_addtail(&action->curves, fcurve);
60 return fcurve;
61 }
62};
63
65{
66 { /* nil pointer. */
67 bAction *action = nullptr;
68 Vector<FCurve *> fcurves = legacy::fcurves_all(action);
69 EXPECT_TRUE(fcurves.is_empty());
70 }
71
72 { /* Empty Action. */
73 Vector<FCurve *> fcurves = legacy::fcurves_all(create_empty_action());
74 EXPECT_TRUE(fcurves.is_empty());
75 }
76
77 { /* Legacy Action. */
78 bAction *action = create_empty_action();
79
80 FCurve *fcurve = MEM_callocN<FCurve>(__func__);
81 BLI_addtail(&action->curves, fcurve);
82
83 Vector<FCurve *> fcurves_expect = {fcurve};
84 EXPECT_EQ(fcurves_expect, legacy::fcurves_all(action));
85 }
86}
87
88TEST_F(ActionLegacyTest, fcurves_all_layered)
89{
90 Action &action = create_empty_action()->wrap();
91 Slot &slot1 = action.slot_add();
92 Slot &slot2 = action.slot_add();
93
94 action.layer_keystrip_ensure();
95 StripKeyframeData &key_data = action.layer(0)->strip(0)->data<StripKeyframeData>(action);
96
97 FCurve &fcurve1 = key_data.channelbag_for_slot_ensure(slot1).fcurve_ensure(bmain,
98 {"location", 1});
99 FCurve &fcurve2 = key_data.channelbag_for_slot_ensure(slot2).fcurve_ensure(bmain, {"scale", 2});
100
101 Vector<FCurve *> fcurves_expect = {&fcurve1, &fcurve2};
102 EXPECT_EQ(fcurves_expect, legacy::fcurves_all(&action));
103}
104
106{
107 { /* nil pointer. */
108 bAction *action = nullptr;
110 EXPECT_TRUE(fcurves.is_empty());
111 }
112
113 { /* Empty Action. */
114 Vector<FCurve *> fcurves = legacy::fcurves_for_action_slot(create_empty_action(),
116 EXPECT_TRUE(fcurves.is_empty());
117 }
118
119 { /* Legacy Action. */
120 bAction *action = create_empty_action();
121
122 FCurve *fcurve = MEM_callocN<FCurve>(__func__);
123 BLI_addtail(&action->curves, fcurve);
124
125 Vector<FCurve *> fcurves_expect = {fcurve};
127 }
128}
129
130TEST_F(ActionLegacyTest, fcurves_for_action_slot_layered)
131{
132 Action &action = create_empty_action()->wrap();
133 Slot &slot1 = action.slot_add();
134 Slot &slot2 = action.slot_add();
135
136 action.layer_keystrip_ensure();
137 StripKeyframeData &key_data = action.layer(0)->strip(0)->data<StripKeyframeData>(action);
138
139 FCurve &fcurve1 = key_data.channelbag_for_slot_ensure(slot1).fcurve_ensure(bmain,
140 {"location", 1});
141 FCurve &fcurve2 = key_data.channelbag_for_slot_ensure(slot2).fcurve_ensure(bmain, {"scale", 2});
142
143 Vector<FCurve *> fcurve1_expect = {&fcurve1};
144 Vector<FCurve *> fcurve2_expect = {&fcurve2};
145 EXPECT_EQ(fcurve1_expect, legacy::fcurves_for_action_slot(&action, slot1.handle));
146 EXPECT_EQ(fcurve2_expect, legacy::fcurves_for_action_slot(&action, slot2.handle));
147}
148
149TEST_F(ActionLegacyTest, action_fcurves_remove_legacy)
150{
151 { /* Empty Action. */
152 bAction *action = create_empty_action();
153 EXPECT_FALSE(legacy::action_fcurves_remove(*action, Slot::unassigned, "rotation"));
154 }
155
156 { /* Legacy Action. */
157 bAction *action = create_empty_action();
158 FCurve *fcurve_loc_x = fcurve_add_legacy(action, "location", 0);
159 fcurve_add_legacy(action, "rotation_euler", 2);
160 fcurve_add_legacy(action, "rotation_mode", 0);
161 FCurve *fcurve_loc_y = fcurve_add_legacy(action, "location", 1);
162
163 EXPECT_TRUE(legacy::action_fcurves_remove(*action, Slot::unassigned, "rotation"));
164 Vector<FCurve *> fcurves_expect = {fcurve_loc_x, fcurve_loc_y};
165 EXPECT_EQ(fcurves_expect, legacy::fcurves_all(action));
166 }
167}
168
169TEST_F(ActionLegacyTest, action_fcurves_remove_layered)
170{
171 /* Create an Action with two slots, to check that the 2nd slot is not affected
172 * by removal from the 1st. */
173 Action &action = create_empty_action()->wrap();
174 Slot &slot_1 = action.slot_add();
175 Slot &slot_2 = action.slot_add();
176
177 action.layer_keystrip_ensure();
178 StripKeyframeData *strip_data = action.strip_keyframe_data()[0];
179 Channelbag &bag_1 = strip_data->channelbag_for_slot_ensure(slot_1);
180 Channelbag &bag_2 = strip_data->channelbag_for_slot_ensure(slot_2);
181
182 /* Add some F-Curves to each channelbag. */
183 FCurve &fcurve_loc_x = bag_1.fcurve_ensure(nullptr, {"location", 0});
184 bag_1.fcurve_ensure(nullptr, {"rotation_euler", 2});
185 bag_1.fcurve_ensure(nullptr, {"rotation_mode", 0});
186 FCurve &fcurve_loc_y = bag_1.fcurve_ensure(nullptr, {"location", 1});
187
188 bag_2.fcurve_ensure(nullptr, {"location", 0});
189 bag_2.fcurve_ensure(nullptr, {"rotation_euler", 2});
190 bag_2.fcurve_ensure(nullptr, {"rotation_mode", 0});
191 bag_2.fcurve_ensure(nullptr, {"location", 1});
192
193 /* Check that removing from slot_1 works as expected. */
194 EXPECT_TRUE(legacy::action_fcurves_remove(action, slot_1.handle, "rotation"));
195
196 Vector<FCurve *> fcurves_bag_1_expect = {&fcurve_loc_x, &fcurve_loc_y};
197 EXPECT_EQ(fcurves_bag_1_expect, legacy::fcurves_for_action_slot(&action, slot_1.handle));
198
199 EXPECT_EQ(4, bag_2.fcurves().size())
200 << "Expected all F-Curves for slot 2 to be there after manipulating slot 1";
201}
202
203} // namespace blender::animrig::tests
Functions and classes to work with Actions.
Functions for backward compatibility with the legacy Action API.
void BKE_fcurve_rnapath_set(FCurve &fcu, blender::StringRef rna_path)
void BKE_idtype_init()
Definition idtype.cc:121
void * BKE_id_new(Main *bmain, short type, const char *name)
Definition lib_id.cc:1514
Main * BKE_main_new()
Definition main.cc:89
void BKE_main_free(Main *bmain)
Definition main.cc:192
General operations, lookup, etc. for blender objects.
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void CLG_exit()
Definition clog.cc:880
void CLG_init()
Definition clog.cc:873
bool is_empty() const
const Layer * layer(int64_t index) const
Span< const StripKeyframeData * > strip_keyframe_data() const
FCurve & fcurve_ensure(Main *bmain, const FCurveDescriptor &fcurve_descriptor)
blender::Span< const FCurve * > fcurves() const
const Strip * strip(int64_t index) const
static constexpr slot_handle_t unassigned
Channelbag & channelbag_for_slot_ensure(const Slot &slot)
const T & data(const Action &owning_action) const
FCurve * fcurve_add_legacy(bAction *action, const StringRefNull rna_path, const int array_index)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
Vector< FCurve * > fcurves_for_action_slot(bAction *action, slot_handle_t slot_handle)
Vector< const FCurve * > fcurves_all(const bAction *action)
bool action_fcurves_remove(bAction &action, slot_handle_t slot_handle, StringRefNull rna_path_prefix)
TEST_F(ActionIteratorsTest, iterate_all_fcurves_of_slot)
Span< FCurve * > fcurves_for_action_slot(Action &action, slot_handle_t slot_handle)
int array_index
ListBase curves