Blender V5.0
animrig/intern/action_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "ANIM_action.hh"
6
7#include "BKE_action.hh"
8#include "BKE_anim_data.hh"
9#include "BKE_fcurve.hh"
10#include "BKE_idtype.hh"
11#include "BKE_lib_id.hh"
12#include "BKE_main.hh"
13#include "BKE_object.hh"
14
15#include "DNA_action_defaults.h"
16#include "DNA_anim_types.h"
17#include "DNA_object_types.h"
18
19#include "RNA_access.hh"
20
21#include "BLI_listbase.h"
22#include "BLI_string.h"
23#include "BLI_string_utf8.h"
24
25#include <limits>
26
27#include "CLG_log.h"
28#include "testing/testing.h"
29
31
32TEST(action, low_level_initialisation)
33{
34 bAction *action = BKE_id_new_nomain<bAction>("NewAction");
35
36 EXPECT_NE(action->last_slot_handle, 0)
37 << "bAction::last_slot_handle should not be initialised to 0";
38
39 BKE_id_free(nullptr, action);
40}
41
42class ActionLayersTest : public testing::Test {
43 public:
49
50 static void SetUpTestSuite()
51 {
52 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
53 CLG_init();
54
55 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
57 }
58
59 static void TearDownTestSuite()
60 {
61 CLG_exit();
62 }
63
64 void SetUp() override
65 {
67 action = BKE_id_new<Action>(bmain, "ACÄnimåtië");
71 }
72
73 void TearDown() override
74 {
76 }
77};
78
80{
81 Layer &layer = action->layer_add("layer name");
82
83 EXPECT_EQ(action->layer(0), &layer);
84 EXPECT_EQ("layer name", std::string(layer.name));
85 EXPECT_EQ(1.0f, layer.influence) << "Expected DNA defaults to be used.";
86 EXPECT_EQ(0, action->layer_active_index)
87 << "Expected newly added layer to become the active layer.";
88 ASSERT_EQ(0, layer.strips().size()) << "Expected newly added layer to have no strip.";
89}
90
91TEST_F(ActionLayersTest, add_layer__reset_idroot)
92{
93 /* An empty Action is a valid legacy Action, and thus can have its idroot set to a non-zero
94 * value. If such an Action gets a layer, it no longer is a valid legacy Action, and thus its
95 * idtype should be reset to zero. */
96 action->idroot = ID_CA; /* Fake that this was assigned to a camera data-block. */
97 ASSERT_NE(0, action->idroot) << "action->idroot should not be zero at the start of this test.";
98
99 action->layer_add("layer name");
100
101 EXPECT_EQ(0, action->idroot)
102 << "action->idroot should get reset when the Action becomes layered.";
103}
104
106{
107 Layer &layer0 = action->layer_add("Test Læür nul");
108 Layer &layer1 = action->layer_add("Test Læür één");
109 Layer &layer2 = action->layer_add("Test Læür twee");
110
111 /* Add some strips to check that they are freed correctly too (implicitly by the
112 * memory leak checker). */
113 layer0.strip_add(*action, Strip::Type::Keyframe);
114 layer1.strip_add(*action, Strip::Type::Keyframe);
115 layer2.strip_add(*action, Strip::Type::Keyframe);
116
117 { /* Test removing a layer that is not owned. */
118 Action *other_anim = BKE_id_new<Action>(bmain, "ACOtherAnim");
119 Layer &other_layer = other_anim->layer_add("Another Layer");
120 EXPECT_FALSE(action->layer_remove(other_layer))
121 << "Removing a layer not owned by the Action should be gracefully rejected";
122 BKE_id_free(bmain, &other_anim->id);
123 }
124
125 EXPECT_TRUE(action->layer_remove(layer1));
126 EXPECT_EQ(2, action->layers().size());
127 EXPECT_STREQ(layer0.name, action->layer(0)->name);
128 EXPECT_STREQ(layer2.name, action->layer(1)->name);
129
130 EXPECT_TRUE(action->layer_remove(layer2));
131 EXPECT_EQ(1, action->layers().size());
132 EXPECT_STREQ(layer0.name, action->layer(0)->name);
133
134 EXPECT_TRUE(action->layer_remove(layer0));
135 EXPECT_EQ(0, action->layers().size());
136}
137
139{
140 Layer &layer = action->layer_add("Test Læür");
141
142 Strip &strip = layer.strip_add(*action, Strip::Type::Keyframe);
143 ASSERT_EQ(1, layer.strips().size());
144 EXPECT_EQ(&strip, layer.strip(0));
145
146 constexpr float inf = std::numeric_limits<float>::infinity();
147 EXPECT_EQ(-inf, strip.frame_start) << "Expected strip to be infinite.";
148 EXPECT_EQ(inf, strip.frame_end) << "Expected strip to be infinite.";
149 EXPECT_EQ(0, strip.frame_offset) << "Expected infinite strip to have no offset.";
150
151 Strip &another_strip = layer.strip_add(*action, Strip::Type::Keyframe);
152 ASSERT_EQ(2, layer.strips().size());
153 EXPECT_EQ(&another_strip, layer.strip(1));
154
155 EXPECT_EQ(-inf, another_strip.frame_start) << "Expected strip to be infinite.";
156 EXPECT_EQ(inf, another_strip.frame_end) << "Expected strip to be infinite.";
157 EXPECT_EQ(0, another_strip.frame_offset) << "Expected infinite strip to have no offset.";
158
159 /* Add some keys to check that also the strip data is freed correctly. */
160 const KeyframeSettings settings = get_keyframe_settings(false);
161 Slot &slot = action->slot_add();
162 strip.data<StripKeyframeData>(*action).keyframe_insert(
163 bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
164 another_strip.data<StripKeyframeData>(*action).keyframe_insert(
165 bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
166}
167
169{
170 Layer &layer = action->layer_add("Test Læür");
171 Strip &strip0 = layer.strip_add(*action, Strip::Type::Keyframe);
172 Strip &strip1 = layer.strip_add(*action, Strip::Type::Keyframe);
173 Strip &strip2 = layer.strip_add(*action, Strip::Type::Keyframe);
174 Strip &strip3 = layer.strip_add(*action, Strip::Type::Keyframe);
175 StripKeyframeData &strip_data0 = strip0.data<StripKeyframeData>(*action);
176 StripKeyframeData &strip_data1 = strip1.data<StripKeyframeData>(*action);
177 StripKeyframeData &strip_data2 = strip2.data<StripKeyframeData>(*action);
178 StripKeyframeData &strip_data3 = strip3.data<StripKeyframeData>(*action);
179
180 /* Add some keys to check that also the strip data is freed correctly. */
181 const KeyframeSettings settings = get_keyframe_settings(false);
182 Slot &slot = action->slot_add();
183 strip_data0.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
184 strip_data1.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 48.0f}, settings);
185 strip_data2.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 49.0f}, settings);
186 strip_data3.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 50.0f}, settings);
187
188 EXPECT_EQ(4, action->strip_keyframe_data().size());
189 EXPECT_EQ(0, strip0.data_index);
190 EXPECT_EQ(1, strip1.data_index);
191 EXPECT_EQ(2, strip2.data_index);
192 EXPECT_EQ(3, strip3.data_index);
193
194 EXPECT_TRUE(layer.strip_remove(*action, strip1));
195 EXPECT_EQ(3, action->strip_keyframe_data().size());
196 EXPECT_EQ(3, layer.strips().size());
197 EXPECT_EQ(&strip0, layer.strip(0));
198 EXPECT_EQ(&strip2, layer.strip(1));
199 EXPECT_EQ(&strip3, layer.strip(2));
200 EXPECT_EQ(0, strip0.data_index);
201 EXPECT_EQ(2, strip2.data_index);
202 EXPECT_EQ(1, strip3.data_index); /* Swapped in when removing strip 1's data. */
203 EXPECT_EQ(&strip_data0, &strip0.data<StripKeyframeData>(*action));
204 EXPECT_EQ(&strip_data2, &strip2.data<StripKeyframeData>(*action));
205 EXPECT_EQ(&strip_data3, &strip3.data<StripKeyframeData>(*action));
206
207 EXPECT_TRUE(layer.strip_remove(*action, strip2));
208 EXPECT_EQ(2, action->strip_keyframe_data().size());
209 EXPECT_EQ(2, layer.strips().size());
210 EXPECT_EQ(&strip0, layer.strip(0));
211 EXPECT_EQ(&strip3, layer.strip(1));
212 EXPECT_EQ(0, strip0.data_index);
213 EXPECT_EQ(1, strip3.data_index);
214 EXPECT_EQ(&strip_data0, &strip0.data<StripKeyframeData>(*action));
215 EXPECT_EQ(&strip_data3, &strip3.data<StripKeyframeData>(*action));
216
217 EXPECT_TRUE(layer.strip_remove(*action, strip3));
218 EXPECT_EQ(1, action->strip_keyframe_data().size());
219 EXPECT_EQ(1, layer.strips().size());
220 EXPECT_EQ(&strip0, layer.strip(0));
221 EXPECT_EQ(0, strip0.data_index);
222 EXPECT_EQ(&strip_data0, &strip0.data<StripKeyframeData>(*action));
223
224 EXPECT_TRUE(layer.strip_remove(*action, strip0));
225 EXPECT_EQ(0, action->strip_keyframe_data().size());
226 EXPECT_EQ(0, layer.strips().size());
227
228 { /* Test removing a strip that is not owned. */
229 Layer &other_layer = action->layer_add("Another Layer");
230 Strip &other_strip = other_layer.strip_add(*action, Strip::Type::Keyframe);
231
232 EXPECT_FALSE(layer.strip_remove(*action, other_strip))
233 << "Removing a strip not owned by the layer should be gracefully rejected";
234 }
235}
236
237/* NOTE: this test creates strip instances in a bespoke way for the purpose of
238 * exercising the strip removal code, because at the time of writing we don't
239 * have a proper API for creating strip instances. When such an API is added,
240 * this test should be updated to use it. */
241TEST_F(ActionLayersTest, remove_strip_instances)
242{
243 Layer &layer = action->layer_add("Test Læür");
244 Strip &strip0 = layer.strip_add(*action, Strip::Type::Keyframe);
245 Strip &strip1 = layer.strip_add(*action, Strip::Type::Keyframe);
246 Strip &strip2 = layer.strip_add(*action, Strip::Type::Keyframe);
247
248 /* Make on of the strips an instance of another. */
249 strip0.data_index = strip1.data_index;
250
251 StripKeyframeData &strip_data_0_1 = strip0.data<StripKeyframeData>(*action);
252 StripKeyframeData &strip_data_2 = strip2.data<StripKeyframeData>(*action);
253
254 /* Add some keys to check that also the strip data is freed correctly. */
255 const KeyframeSettings settings = get_keyframe_settings(false);
256 Slot &slot = action->slot_add();
257 strip_data_0_1.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
258 strip_data_2.keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 48.0f}, settings);
259
260 EXPECT_EQ(3, action->strip_keyframe_data().size());
261 EXPECT_EQ(1, strip0.data_index);
262 EXPECT_EQ(1, strip1.data_index);
263 EXPECT_EQ(2, strip2.data_index);
264
265 /* Removing an instance should not delete the underlying data as long as there
266 * is still another strip using it. */
267 EXPECT_TRUE(layer.strip_remove(*action, strip1));
268 EXPECT_EQ(3, action->strip_keyframe_data().size());
269 EXPECT_EQ(2, layer.strips().size());
270 EXPECT_EQ(&strip0, layer.strip(0));
271 EXPECT_EQ(&strip2, layer.strip(1));
272 EXPECT_EQ(1, strip0.data_index);
273 EXPECT_EQ(2, strip2.data_index);
274 EXPECT_EQ(&strip_data_0_1, &strip0.data<StripKeyframeData>(*action));
275 EXPECT_EQ(&strip_data_2, &strip2.data<StripKeyframeData>(*action));
276
277 /* Removing the last user of strip data should also delete the data. */
278 EXPECT_TRUE(layer.strip_remove(*action, strip0));
279 EXPECT_EQ(2, action->strip_keyframe_data().size());
280 EXPECT_EQ(1, layer.strips().size());
281 EXPECT_EQ(&strip2, layer.strip(0));
282 EXPECT_EQ(1, strip2.data_index);
283 EXPECT_EQ(&strip_data_2, &strip2.data<StripKeyframeData>(*action));
284}
285
287{
288 { /* Creating an 'unused' Slot should just be called 'Slot'. */
289 Slot &slot = action->slot_add();
290 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 1, action->last_slot_handle);
292
293 EXPECT_STREQ("XXSlot", slot.identifier);
294 EXPECT_EQ(0, slot.idtype);
295 }
296
297 { /* Creating a Slot for a specific ID should name it after the ID. */
298 Slot &slot = action->slot_add_for_id(cube->id);
299 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 2, action->last_slot_handle);
301
302 EXPECT_STREQ(cube->id.name, slot.identifier);
303 EXPECT_EQ(ID_OB, slot.idtype);
304 }
305
306 { /* Creating a Slot for a specific ID that already had a slot assigned before should name it
307 * after that previous slot. This should also ensure that the first two characters are actually
308 * correct for the ID type. */
309 AnimData *adt = BKE_animdata_ensure_id(&cube->id);
310 STRNCPY_UTF8(adt->last_slot_identifier, "$$Kübuš 😹");
311 Slot &slot = action->slot_add_for_id(cube->id);
312 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 3, action->last_slot_handle);
314
315 EXPECT_STREQ("Kübuš 😹", slot.identifier + 2)
316 << "The last-assigned slot name should be reused";
317 EXPECT_STREQ("OBKübuš 😹", slot.identifier)
318 << "The ID type encoded in the slot identifier should be correct";
319 EXPECT_EQ(ID_OB, slot.idtype);
320 }
321}
322
323TEST_F(ActionLayersTest, add_slot__reset_idroot)
324{
325 /* An empty Action is a valid legacy Action, and thus can have its idroot set
326 * to a non-zero value. If such an Action gets a slot, it no longer is a
327 * valid legacy Action, and thus its idtype should be reset to zero. */
328 action->idroot = ID_CA; /* Fake that this was assigned to a camera data-block. */
329 ASSERT_NE(0, action->idroot) << "action->idroot should not be zero at the start of this test.";
330
331 action->slot_add();
332
333 EXPECT_EQ(0, action->idroot)
334 << "action->idroot should get reset when the Action becomes layered.";
335}
336
337TEST_F(ActionLayersTest, add_slot_multiple)
338{
339 Slot &slot_cube = action->slot_add();
340 Slot &slot_suzanne = action->slot_add();
341 EXPECT_TRUE(assign_action(action, cube->id));
343 EXPECT_TRUE(assign_action(action, suzanne->id));
344 EXPECT_EQ(assign_action_slot(&slot_suzanne, suzanne->id), ActionSlotAssignmentResult::OK);
345
346 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 2, action->last_slot_handle);
349}
350
352{
353 { /* Canary test: removing a just-created slot on an otherwise empty Action should work. */
354 Slot &slot = action->slot_add();
356 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 1, action->last_slot_handle);
357
358 EXPECT_TRUE(action->slot_remove(slot));
359 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 1, action->last_slot_handle)
360 << "Removing a slot should not change the last-used slot handle.";
361 EXPECT_EQ(0, action->slot_array_num);
362 }
363
364 { /* Removing a non-existing slot should return false. */
365 Slot slot;
366 EXPECT_FALSE(action->slot_remove(slot));
367 }
368
369 { /* Removing a slot should remove its Channelbag. */
370 Slot &slot = action->slot_add();
371 const slot_handle_t slot_handle = slot.handle;
373 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 2, action->last_slot_handle);
374
375 /* Create an F-Curve in a Channelbag for the slot. */
376 action->layer_keystrip_ensure();
377 StripKeyframeData &strip_data = action->layer(0)->strip(0)->data<StripKeyframeData>(*action);
378 Channelbag &channelbag = strip_data.channelbag_for_slot_ensure(slot);
379 channelbag.fcurve_create_unique(bmain, {"location", 1});
380
381 /* Remove the slot. */
382 EXPECT_TRUE(action->slot_remove(slot));
383 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 2, action->last_slot_handle)
384 << "Removing a slot should not change the last-used slot handle.";
385 EXPECT_EQ(0, action->slot_array_num);
386
387 /* Check that its channelbag is gone. */
388 Channelbag *found_cbag = strip_data.channelbag_for_slot(slot_handle);
389 EXPECT_EQ(found_cbag, nullptr);
390 }
391
392 { /* Removing one slot should leave the other two in place. */
393 Slot &slot1 = action->slot_add();
394 Slot &slot2 = action->slot_add();
395 Slot &slot3 = action->slot_add();
399 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 5, action->last_slot_handle);
400
401 /* For referencing the slot handle after the slot is removed. */
402 const slot_handle_t slot2_handle = slot2.handle;
403
404 /* Create a Channel-bag for each slot. */
405 action->layer_keystrip_ensure();
406 StripKeyframeData &strip_data = action->layer(0)->strip(0)->data<StripKeyframeData>(*action);
407 strip_data.channelbag_for_slot_ensure(slot1);
408 strip_data.channelbag_for_slot_ensure(slot2);
409 strip_data.channelbag_for_slot_ensure(slot3);
410
411 /* Remove the slot. */
412 EXPECT_TRUE(action->slot_remove(slot2));
413 EXPECT_EQ(DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE + 5, action->last_slot_handle);
414
415 /* Check the correct slot + channel-bag are removed. */
416 EXPECT_EQ(action->slot_for_handle(slot1.handle), &slot1);
417 EXPECT_EQ(action->slot_for_handle(slot2_handle), nullptr);
418 EXPECT_EQ(action->slot_for_handle(slot3.handle), &slot3);
419
420 EXPECT_NE(strip_data.channelbag_for_slot(slot1.handle), nullptr);
421 EXPECT_EQ(strip_data.channelbag_for_slot(slot2_handle), nullptr);
422 EXPECT_NE(strip_data.channelbag_for_slot(slot3.handle), nullptr);
423 }
424
425 { /* Removing an in-use slot doesn't un-assign it from its users.
426 * This is not that important, but it covers the current behavior. */
427 Slot &slot = action->slot_add_for_id(cube->id);
428 ASSERT_EQ(assign_action_and_slot(action, &slot, cube->id), ActionSlotAssignmentResult::OK);
429
430 ASSERT_TRUE(slot.runtime_users().contains(&cube->id));
431 ASSERT_EQ(cube->adt->slot_handle, slot.handle);
432
433 const slot_handle_t removed_slot_handle = slot.handle;
434 ASSERT_TRUE(action->slot_remove(slot));
435 EXPECT_EQ(cube->adt->slot_handle, removed_slot_handle);
436 }
437
438 { /* Creating a slot after removing one should not reuse its handle. */
439 action->last_slot_handle = 3; /* To create independence between sub-tests. */
440 Slot &slot1 = action->slot_add();
441 ASSERT_EQ(4, slot1.handle);
442 ASSERT_EQ(4, action->last_slot_handle);
443 ASSERT_TRUE(action->slot_remove(slot1));
444
445 Slot &slot2 = action->slot_add();
446 EXPECT_EQ(5, slot2.handle);
447 EXPECT_EQ(5, action->last_slot_handle);
448 }
449}
450
451TEST_F(ActionLayersTest, slot_move_to_index)
452{
453 Slot &slot_a = action->slot_add_for_id_type(ID_ME);
454 Slot &slot_b = action->slot_add_for_id_type(ID_CA);
455 Slot &slot_cube = action->slot_add_for_id(cube->id);
456 Slot &slot_suzanne = action->slot_add_for_id(suzanne->id);
457
458 assign_action_and_slot(action, &slot_cube, cube->id);
459 assign_action_and_slot(action, &slot_suzanne, suzanne->id);
460
461 const slot_handle_t handle_a = slot_a.handle;
462 const slot_handle_t handle_b = slot_b.handle;
463 const slot_handle_t handle_cube = slot_cube.handle;
464 const slot_handle_t handle_suzanne = slot_suzanne.handle;
465
466 ASSERT_EQ(action->slot(0)->handle, handle_a);
467 ASSERT_EQ(action->slot(0)->idtype_string(), "ME");
468 ASSERT_EQ(action->slot(1)->handle, handle_b);
469 ASSERT_EQ(action->slot(1)->idtype_string(), "CA");
470 ASSERT_EQ(action->slot(2)->handle, handle_cube);
471 ASSERT_EQ(action->slot(2)->idtype_string(), "OB");
472 ASSERT_EQ(action->slot(2)->users(*bmain)[0], &cube->id);
473 ASSERT_EQ(action->slot(3)->handle, handle_suzanne);
474 ASSERT_EQ(action->slot(3)->idtype_string(), "OB");
475 ASSERT_EQ(action->slot(3)->users(*bmain)[0], &suzanne->id);
476
477 /* First "move" a slot to its own location, which should do nothing. */
478 action->slot_move_to_index(slot_b, 1);
479 EXPECT_EQ(action->slot(0)->handle, handle_a);
480 EXPECT_EQ(action->slot(0)->idtype_string(), "ME");
481 EXPECT_EQ(action->slot(1)->handle, handle_b);
482 EXPECT_EQ(action->slot(1)->idtype_string(), "CA");
483 EXPECT_EQ(action->slot(2)->handle, handle_cube);
484 EXPECT_EQ(action->slot(2)->idtype_string(), "OB");
485 EXPECT_EQ(action->slot(2)->users(*bmain)[0], &cube->id);
486 EXPECT_EQ(action->slot(3)->handle, handle_suzanne);
487 EXPECT_EQ(action->slot(3)->idtype_string(), "OB");
488 EXPECT_EQ(action->slot(3)->users(*bmain)[0], &suzanne->id);
489
490 /* Then move slots around in various ways. */
491
492 action->slot_move_to_index(slot_a, 2);
493 EXPECT_EQ(action->slot(0)->handle, handle_b);
494 EXPECT_EQ(action->slot(0)->idtype_string(), "CA");
495 EXPECT_EQ(action->slot(1)->handle, handle_cube);
496 EXPECT_EQ(action->slot(1)->idtype_string(), "OB");
497 EXPECT_EQ(action->slot(1)->users(*bmain)[0], &cube->id);
498 EXPECT_EQ(action->slot(2)->handle, handle_a);
499 EXPECT_EQ(action->slot(2)->idtype_string(), "ME");
500 EXPECT_EQ(action->slot(3)->handle, handle_suzanne);
501 EXPECT_EQ(action->slot(3)->idtype_string(), "OB");
502 EXPECT_EQ(action->slot(3)->users(*bmain)[0], &suzanne->id);
503
504 action->slot_move_to_index(slot_suzanne, 1);
505 EXPECT_EQ(action->slot(0)->handle, handle_b);
506 EXPECT_EQ(action->slot(0)->idtype_string(), "CA");
507 EXPECT_EQ(action->slot(1)->handle, handle_suzanne);
508 EXPECT_EQ(action->slot(1)->idtype_string(), "OB");
509 EXPECT_EQ(action->slot(1)->users(*bmain)[0], &suzanne->id);
510 EXPECT_EQ(action->slot(2)->handle, handle_cube);
511 EXPECT_EQ(action->slot(2)->idtype_string(), "OB");
512 EXPECT_EQ(action->slot(2)->users(*bmain)[0], &cube->id);
513 EXPECT_EQ(action->slot(3)->handle, handle_a);
514 EXPECT_EQ(action->slot(3)->idtype_string(), "ME");
515
516 action->slot_move_to_index(slot_cube, 3);
517 EXPECT_EQ(action->slot(0)->handle, handle_b);
518 EXPECT_EQ(action->slot(0)->idtype_string(), "CA");
519 EXPECT_EQ(action->slot(1)->handle, handle_suzanne);
520 EXPECT_EQ(action->slot(1)->idtype_string(), "OB");
521 EXPECT_EQ(action->slot(1)->users(*bmain)[0], &suzanne->id);
522 EXPECT_EQ(action->slot(2)->handle, handle_a);
523 EXPECT_EQ(action->slot(2)->idtype_string(), "ME");
524 EXPECT_EQ(action->slot(3)->handle, handle_cube);
525 EXPECT_EQ(action->slot(3)->idtype_string(), "OB");
526 EXPECT_EQ(action->slot(3)->users(*bmain)[0], &cube->id);
527
528 action->slot_move_to_index(slot_suzanne, 0);
529 EXPECT_EQ(action->slot(0)->handle, handle_suzanne);
530 EXPECT_EQ(action->slot(0)->idtype_string(), "OB");
531 EXPECT_EQ(action->slot(0)->users(*bmain)[0], &suzanne->id);
532 EXPECT_EQ(action->slot(1)->handle, handle_b);
533 EXPECT_EQ(action->slot(1)->idtype_string(), "CA");
534 EXPECT_EQ(action->slot(2)->handle, handle_a);
535 EXPECT_EQ(action->slot(2)->idtype_string(), "ME");
536 EXPECT_EQ(action->slot(3)->handle, handle_cube);
537 EXPECT_EQ(action->slot(3)->idtype_string(), "OB");
538 EXPECT_EQ(action->slot(3)->users(*bmain)[0], &cube->id);
539}
540
541TEST_F(ActionLayersTest, action_assign_id)
542{
543 /* Assign to the only, 'virgin' Slot, should always work. */
544 Slot &slot_cube = action->slot_add();
545 ASSERT_NE(nullptr, slot_cube.runtime);
546 ASSERT_STREQ(slot_cube.identifier, "XXSlot");
547 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
548
549 EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
550 EXPECT_STREQ(slot_cube.identifier, "OBSlot");
551 EXPECT_STREQ(slot_cube.identifier, cube->adt->last_slot_identifier)
552 << "The slot identifier should be copied to the adt";
553
554 EXPECT_TRUE(slot_cube.users(*bmain).contains(&cube->id))
555 << "Expecting Cube to be registered as animated by its slot.";
556
557 /* Assign another ID to the same Slot. */
558 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, suzanne->id),
560 EXPECT_STREQ(slot_cube.identifier, "OBSlot");
561 EXPECT_STREQ(slot_cube.identifier, cube->adt->last_slot_identifier)
562 << "The slot identifier should be copied to the adt";
563
564 EXPECT_TRUE(slot_cube.users(*bmain).contains(&cube->id))
565 << "Expecting Suzanne to be registered as animated by the Cube slot.";
566
567 { /* Assign Cube to another action+slot without unassigning first. */
568 Action *another_anim = BKE_id_new<Action>(bmain, "ACOtherAnim");
569 Slot &another_slot = another_anim->slot_add();
570 ASSERT_EQ(assign_action_and_slot(another_anim, &another_slot, cube->id),
572 EXPECT_FALSE(slot_cube.users(*bmain).contains(&cube->id))
573 << "Expecting Cube to no longer be registered as user of its old slot.";
574 EXPECT_TRUE(another_slot.users(*bmain).contains(&cube->id))
575 << "Expecting Cube to be registered as user of its new slot.";
576 }
577
578 { /* Assign Cube to another slot of the same Action, this should work. */
579 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id),
581 const int user_count_pre = action->id.us;
582 Slot &slot_cube_2 = action->slot_add();
583 ASSERT_EQ(assign_action_and_slot(action, &slot_cube_2, cube->id),
585 ASSERT_EQ(action->id.us, user_count_pre)
586 << "Assigning to a different slot of the same Action should _not_ change the user "
587 "count of that Action";
588 EXPECT_FALSE(slot_cube.users(*bmain).contains(&cube->id))
589 << "Expecting Cube to no longer be registered as animated by the Cube slot.";
590 EXPECT_TRUE(slot_cube_2.users(*bmain).contains(&cube->id))
591 << "Expecting Cube to be registered as animated by the 'cube_2' slot.";
592 }
593
594 { /* Unassign the Action. */
595 const int user_count_pre = action->id.us;
596 EXPECT_TRUE(unassign_action(cube->id));
597 ASSERT_EQ(action->id.us, user_count_pre - 1)
598 << "Unassigning an Action should lower its user count";
599
600 ASSERT_EQ(2, action->slots().size()) << "Expecting the Action to have two Slots";
601 EXPECT_FALSE(action->slot(0)->users(*bmain).contains(&cube->id))
602 << "Expecting Cube to no longer be registered as animated by any slot.";
603 EXPECT_FALSE(action->slot(1)->users(*bmain).contains(&cube->id))
604 << "Expecting Cube to no longer be registered as animated by any slot.";
605 }
606
607 /* Assign Cube to another 'virgin' slot. This should not cause a name
608 * collision between the Slots. */
609 Slot &another_slot_cube = action->slot_add();
610 ASSERT_EQ(assign_action_and_slot(action, &another_slot_cube, cube->id),
612 EXPECT_EQ(another_slot_cube.handle, cube->adt->slot_handle);
613 EXPECT_STREQ("OBSlot.002", another_slot_cube.identifier) << "The slot should be uniquely named";
614 EXPECT_STREQ("OBSlot.002", cube->adt->last_slot_identifier)
615 << "The slot identifier should be copied to the adt";
616 EXPECT_TRUE(another_slot_cube.users(*bmain).contains(&cube->id))
617 << "Expecting Cube to be registered as animated by the 'another_slot_cube' slot.";
618
619 /* Create an ID of another type. This should not be assignable to this slot. */
620 ID *mesh = static_cast<ID *>(BKE_id_new_nomain(ID_ME, "Mesh"));
621 ASSERT_TRUE(assign_action(action, *mesh));
623 << "Mesh should not be animatable by an Object slot";
624 EXPECT_FALSE(another_slot_cube.users(*bmain).contains(mesh))
625 << "Expecting Mesh to not be registered as animated by the 'slot_cube' slot.";
626 BKE_id_free(nullptr, mesh);
627}
628
630{
631 Slot &slot_cube = action->slot_add();
632 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
633 EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
634 EXPECT_STREQ("OBSlot", slot_cube.identifier);
635 EXPECT_STREQ(slot_cube.identifier, cube->adt->last_slot_identifier)
636 << "The slot identifier should be copied to the adt";
637
638 action->slot_identifier_define(slot_cube, "OBNew Slot Name");
639 EXPECT_STREQ("OBNew Slot Name", slot_cube.identifier);
640 /* At this point the slot identifier will not have been copied to the cube
641 * AnimData. However, I don't want to test for that here, as it's not exactly
642 * desirable behavior, but more of a side-effect of the current
643 * implementation. */
644
645 action->slot_identifier_propagate(*bmain, slot_cube);
646 EXPECT_STREQ("OBNew Slot Name", cube->adt->last_slot_identifier);
647
648 /* Rename via the display name, which should propagate to the ADT. */
649 action->slot_display_name_set(*bmain, slot_cube, "Slot's New Display Name");
650 EXPECT_STREQ("OBSlot's New Display Name", slot_cube.identifier);
651 EXPECT_STREQ("OBSlot's New Display Name", cube->adt->last_slot_identifier);
652
653 /* Finally, do another rename, do NOT call the propagate function, then
654 * unassign. This should still result in the correct slot name being stored
655 * on the ADT. */
656 action->slot_identifier_define(slot_cube, "OBEven Newer Name");
657 EXPECT_TRUE(unassign_action(cube->id));
658 EXPECT_STREQ("OBEven Newer Name", cube->adt->last_slot_identifier);
659}
660
661TEST_F(ActionLayersTest, slot_identifier_ensure_prefix)
662{
663 class AccessibleSlot : public Slot {
664 public:
665 void identifier_ensure_prefix()
666 {
668 }
669 };
670
671 Slot &raw_slot = action->slot_add();
672 AccessibleSlot &slot = static_cast<AccessibleSlot &>(raw_slot);
673 ASSERT_STREQ("XXSlot", slot.identifier);
674 ASSERT_EQ(0, slot.idtype);
675
676 /* Check defaults, idtype zeroed. */
677 slot.identifier_ensure_prefix();
678 EXPECT_STREQ("XXSlot", slot.identifier);
679
680 /* idtype CA, default name. */
681 slot.idtype = ID_CA;
682 slot.identifier_ensure_prefix();
683 EXPECT_STREQ("CASlot", slot.identifier);
684
685 /* idtype ME, explicit name of other idtype. */
686 action->slot_identifier_define(slot, "CANewName");
687 slot.idtype = ID_ME;
688 slot.identifier_ensure_prefix();
689 EXPECT_STREQ("MENewName", slot.identifier);
690
691 /* Zeroing out idtype. */
692 slot.idtype = 0;
693 slot.identifier_ensure_prefix();
694 EXPECT_STREQ("XXNewName", slot.identifier);
695}
696
697TEST_F(ActionLayersTest, slot_identifier_prefix)
698{
699 Slot &slot = action->slot_add();
700 EXPECT_EQ("XX", slot.idtype_string());
701 EXPECT_EQ("XX", slot.identifier_prefix());
702
703 slot.idtype = ID_CA;
704 EXPECT_EQ("CA", slot.idtype_string());
705 EXPECT_EQ("XX", slot.identifier_prefix());
706
708 EXPECT_EQ("CA", slot.idtype_string());
709 EXPECT_EQ("CA", slot.identifier_prefix());
710}
711
712TEST_F(ActionLayersTest, rename_slot_identifier_collision)
713{
714 Slot &slot1 = action->slot_add();
715 Slot &slot2 = action->slot_add();
716
717 action->slot_identifier_define(slot1, "New Slot Name");
718 action->slot_identifier_define(slot2, "New Slot Name");
719 EXPECT_STREQ("New Slot Name", slot1.identifier);
720 EXPECT_STREQ("New Slot Name.001", slot2.identifier);
721}
722
724{
725 /* ===
726 * Empty case, no slots exist yet and the ID doesn't even have an AnimData. */
727 EXPECT_EQ(nullptr, generic_slot_for_autoassign(cube->id, *this->action, ""));
728
729 /* ===
730 * Slot exists with the same name & type as the ID, but the ID doesn't have any AnimData yet.
731 * These should nevertheless be matched up. */
732 Slot &slot = action->slot_add();
733 slot.handle = 327;
734 STRNCPY_UTF8(slot.identifier, "OBKüüübus");
735 slot.idtype = GS(cube->id.name);
736 EXPECT_EQ(&slot, generic_slot_for_autoassign(cube->id, *this->action, ""));
737
738 /* ===
739 * Slot exists with the same name & type as the ID, and the ID has an AnimData with the same
740 * slot identifier, but a different slot_handle. Since the Action has not yet been
741 * assigned to this ID, the slot_handle should be ignored, and the slot identifier used for
742 * matching. */
743
744 /* Create a slot with a handle that should be ignored. */
745 Slot &other_slot = action->slot_add();
746 other_slot.handle = 47;
747
748 AnimData *adt = BKE_animdata_ensure_id(&cube->id);
749 adt->action = nullptr;
750 /* Configure adt to use the handle of one slot, and the identifier of the other. */
751 adt->slot_handle = other_slot.handle;
753 EXPECT_EQ(&slot,
754 generic_slot_for_autoassign(cube->id, *this->action, cube->adt->last_slot_identifier));
755
756 /* ===
757 * Assigned slot info exists, but doesn't match anything in the action data of the cube. This
758 * should fall back to using the ID name. */
759 adt->slot_handle = 161;
760 STRNCPY_UTF8(adt->last_slot_identifier, "¿¿What's this??");
761 EXPECT_EQ(&slot,
762 generic_slot_for_autoassign(cube->id, *this->action, cube->adt->last_slot_identifier));
763}
764
765TEST_F(ActionLayersTest, generic_slot_for_autoassign_untyped_wildcarding)
766{
767 /* Test the untyped slot "wildcard" behavior, where OBSlot should be chosen when the last slot
768 * identifier was "XXSlot", and vice versa. */
769
770 /* ===
771 * Action has OBSlot, last-used slot is XXSlot. Should pick OBSlot. */
772 AnimData *adt = BKE_animdata_ensure_id(&cube->id);
773 STRNCPY_UTF8(adt->last_slot_identifier, "XXSlot");
774 Slot &ob_slot = action->slot_add_for_id_type(ID_OB);
775 action->slot_identifier_define(ob_slot, "OBSlot");
776
777 EXPECT_EQ(&ob_slot,
778 generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
779
780 /* ===
781 * Action has OBSlot and XXSlot, last-used slot is XXSlot. Should pick OBSlot. */
782 Slot &xx_slot = action->slot_add();
783 action->slot_identifier_define(xx_slot, "XXSlot");
784 ASSERT_FALSE(xx_slot.has_idtype());
785 ASSERT_STREQ("XXSlot", xx_slot.identifier);
786 ASSERT_STREQ("XXSlot", adt->last_slot_identifier);
787 EXPECT_EQ(&ob_slot,
788 generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
789
790 /* ===
791 * Action has OBSlot and XXSlot, last-used slot is OBSlot. Should pick OBSlot. */
792 STRNCPY_UTF8(adt->last_slot_identifier, "OBSlot");
793 EXPECT_EQ(&ob_slot,
794 generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
795
796 /* ===
797 * Action has XXSlot, last-used slot is OBSlot. Should pick XXSlot. */
798 action->slot_remove(ob_slot);
799 ASSERT_STREQ("OBSlot", adt->last_slot_identifier);
800 EXPECT_EQ(&xx_slot,
801 generic_slot_for_autoassign(cube->id, *this->action, adt->last_slot_identifier));
802}
803
805{
806 { /* Empty case, no slots exist yet. */
807 EXPECT_EQ(nullptr, action->slot_active_get());
808
809 action->slot_active_set(Slot::unassigned);
810 EXPECT_EQ(nullptr, action->slot_active_get());
811 }
812
813 { /* Single slot case. */
814 Slot &slot_cube = *assign_action_ensure_slot_for_keying(*action, cube->id);
815 EXPECT_EQ(nullptr, action->slot_active_get())
816 << "Adding the first slot should not change what is the active slot.";
817
818 action->slot_active_set(slot_cube.handle);
819 EXPECT_EQ(&slot_cube, action->slot_active_get())
820 << "It should be possible to activate the only available slot";
821 EXPECT_TRUE(slot_cube.is_active());
822
823 action->slot_active_set(Slot::unassigned);
824 EXPECT_EQ(nullptr, action->slot_active_get())
825 << "It should be possible to de-activate the only available slot";
826 EXPECT_FALSE(slot_cube.is_active());
827 }
828
829 {
830 /* Multiple slots case. */
831 Slot &slot_cube = *action->slot(0);
832 action->slot_active_set(slot_cube.handle);
833
834 Slot &slot_suz = *assign_action_ensure_slot_for_keying(*action, suzanne->id);
835 Slot &slot_bob = *assign_action_ensure_slot_for_keying(*action, bob->id);
836 EXPECT_EQ(&slot_cube, action->slot_active_get())
837 << "Adding a subsequent slot should not change what is the active slot.";
838 EXPECT_TRUE(slot_cube.is_active());
839
840 action->slot_active_set(slot_suz.handle);
841 EXPECT_EQ(&slot_suz, action->slot_active_get());
842 EXPECT_FALSE(slot_cube.is_active());
843 EXPECT_TRUE(slot_suz.is_active());
844 EXPECT_FALSE(slot_bob.is_active());
845
846 action->slot_active_set(slot_bob.handle);
847 EXPECT_EQ(&slot_bob, action->slot_active_get());
848 EXPECT_FALSE(slot_cube.is_active());
849 EXPECT_FALSE(slot_suz.is_active());
850 EXPECT_TRUE(slot_bob.is_active());
851
852 action->slot_active_set(Slot::unassigned);
853 EXPECT_EQ(nullptr, action->slot_active_get());
854 EXPECT_FALSE(slot_cube.is_active());
855 EXPECT_FALSE(slot_suz.is_active());
856 EXPECT_FALSE(slot_bob.is_active());
857 }
858}
859
861{
862 { /* Slotless Action, should create a typed slot. */
863 Action &action = action_add(*this->bmain, "ACEmpty");
864 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
865 ASSERT_NE(nullptr, chosen_slot);
866 EXPECT_EQ(ID_OB, chosen_slot->idtype);
867 EXPECT_STREQ("OBKüüübus", chosen_slot->identifier);
868 }
869
870 { /* Single slot with same name as ID, Action not yet assigned.
871 * Should assign the Action and the slot. */
872 Action &action = action_add(*this->bmain, "ACAction");
873 const Slot &slot_for_id = action.slot_add_for_id(cube->id);
874 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
875 ASSERT_NE(nullptr, chosen_slot);
876 EXPECT_EQ(&slot_for_id, chosen_slot) << "The expected slot should be chosen";
877 EXPECT_EQ(cube->adt->action, &action) << "The Action should be assigned";
878 EXPECT_EQ(cube->adt->slot_handle, chosen_slot->handle) << "The chosen slot should be assigned";
879 }
880
881 { /* Single slot with same name as ID, Action already assigned but not the slot.
882 * Should create new slot. */
883 Action &action = action_add(*this->bmain, "ACAction");
884 const Slot &slot_for_id = action.slot_add_for_id(cube->id);
885 ASSERT_EQ(ActionSlotAssignmentResult::OK, assign_action_and_slot(&action, nullptr, cube->id));
886
887 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
888 ASSERT_NE(nullptr, chosen_slot);
889 EXPECT_NE(&slot_for_id, chosen_slot) << "A new slot should be chosen";
890 EXPECT_STREQ("OBKüüübus.001", chosen_slot->identifier);
891 EXPECT_EQ(cube->adt->action, &action) << "The Action should be assigned";
892 EXPECT_EQ(cube->adt->slot_handle, chosen_slot->handle) << "The chosen slot should be assigned";
893 }
894
895 { /* Single untyped slot, Action already assigned but not the slot. Should assign the untyped
896 * slot. */
897 Action &action = action_add(*this->bmain, "ACAction");
898
899 /* Assign the Action before adding the untyped slot, otherwise the slot gets assigned & thus
900 * typed. */
901 ASSERT_EQ(ActionSlotAssignmentResult::OK, assign_action_and_slot(&action, nullptr, cube->id));
902
903 Slot &untyped_slot = action.slot_add();
904 action.slot_identifier_define(untyped_slot, "XXJust A Slot");
905
906 Slot *chosen_slot = assign_action_ensure_slot_for_keying(action, cube->id);
907
908 ASSERT_NE(nullptr, chosen_slot);
909 EXPECT_EQ(&untyped_slot, chosen_slot) << "The untyped slot should be chosen";
910 EXPECT_TRUE(untyped_slot.has_idtype()) << "Slot should have gotten an ID type";
911 EXPECT_STREQ("OBJust A Slot", untyped_slot.identifier);
912 EXPECT_EQ(cube->adt->action, &action) << "The Action should be assigned";
913 EXPECT_EQ(cube->adt->slot_handle, chosen_slot->handle) << "The chosen slot should be assigned";
914 }
915}
916
918{
919 constexpr float inf = std::numeric_limits<float>::infinity();
920 Layer &layer0 = action->layer_add("Test Læür nul");
921 Strip &strip = layer0.strip_add(*action, Strip::Type::Keyframe);
922
923 strip.resize(-inf, inf);
924 EXPECT_TRUE(strip.contains_frame(0.0f));
925 EXPECT_TRUE(strip.contains_frame(-100000.0f));
926 EXPECT_TRUE(strip.contains_frame(100000.0f));
927 EXPECT_TRUE(strip.is_last_frame(inf));
928
929 strip.resize(1.0f, 2.0f);
930 EXPECT_FALSE(strip.contains_frame(0.0f))
931 << "Strip should not contain frames before its first frame";
932 EXPECT_TRUE(strip.contains_frame(1.0f)) << "Strip should contain its first frame.";
933 EXPECT_TRUE(strip.contains_frame(2.0f)) << "Strip should contain its last frame.";
934 EXPECT_FALSE(strip.contains_frame(2.0001f))
935 << "Strip should not contain frames after its last frame";
936
937 EXPECT_FALSE(strip.is_last_frame(1.0f));
938 EXPECT_FALSE(strip.is_last_frame(1.5f));
939 EXPECT_FALSE(strip.is_last_frame(1.9999f));
940 EXPECT_TRUE(strip.is_last_frame(2.0f));
941 EXPECT_FALSE(strip.is_last_frame(2.0001f));
942
943 /* Same test as above, but with much larger end frame number. This is 2 hours at 24 FPS. */
944 strip.resize(1.0f, 172800.0f);
945 EXPECT_TRUE(strip.contains_frame(172800.0f)) << "Strip should contain its last frame.";
946 EXPECT_FALSE(strip.contains_frame(172800.1f))
947 << "Strip should not contain frames after its last frame";
948
949 /* You can't get much closer to the end frame before it's considered equal. */
950 EXPECT_FALSE(strip.is_last_frame(172799.925f));
951 EXPECT_TRUE(strip.is_last_frame(172800.0f));
952 EXPECT_FALSE(strip.is_last_frame(172800.075f));
953}
954
955TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert)
956{
957 Slot &slot = action->slot_add();
958 ASSERT_EQ(assign_action_and_slot(action, &slot, cube->id), ActionSlotAssignmentResult::OK);
959 Layer &layer = action->layer_add("Kübus layer");
960
961 Strip &strip = layer.strip_add(*action, Strip::Type::Keyframe);
962 StripKeyframeData &strip_data = strip.data<StripKeyframeData>(*action);
963
964 const KeyframeSettings settings = get_keyframe_settings(false);
965 SingleKeyingResult result_loc_a = strip_data.keyframe_insert(
966 bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
967 ASSERT_EQ(SingleKeyingResult::SUCCESS, result_loc_a)
968 << "Expected keyframe insertion to be successful";
969
970 /* Check the strip was created correctly, with the channels for the slot. */
971 ASSERT_EQ(1, strip_data.channelbags().size());
972 Channelbag *channels = strip_data.channelbag(0);
973 EXPECT_EQ(slot.handle, channels->slot_handle);
974
975 /* Insert a second key, should insert into the same FCurve as before. */
976 SingleKeyingResult result_loc_b = strip_data.keyframe_insert(
977 bmain, slot, {"location", 0}, {5.0f, 47.1f}, settings);
979 ASSERT_EQ(1, channels->fcurves().size()) << "Expect insertion with the same (slot/rna "
980 "path/array index) tuple to go into the same FCurve";
981 EXPECT_EQ(2, channels->fcurves()[0]->totvert)
982 << "Expect insertion with the same (slot/rna path/array index) tuple to go into the same "
983 "FCurve";
984
985 EXPECT_EQ(47.0f, evaluate_fcurve(channels->fcurves()[0], 1.0f));
986 EXPECT_EQ(47.1f, evaluate_fcurve(channels->fcurves()[0], 5.0f));
987
988 /* Insert another key for another property, should create another FCurve. */
989 SingleKeyingResult result_rot = strip_data.keyframe_insert(
990 bmain, slot, {"rotation_quaternion", 0}, {1.0f, 0.25f}, settings);
992 ASSERT_EQ(2, channels->fcurves().size()) << "Expected a second FCurve to be created.";
993 EXPECT_EQ(2, channels->fcurves()[0]->totvert);
994 EXPECT_EQ(1, channels->fcurves()[1]->totvert);
995}
996
998{
999 EXPECT_TRUE(is_action_assignable_to(nullptr, ID_OB))
1000 << "nullptr Actions should be assignable to any type.";
1001 EXPECT_TRUE(is_action_assignable_to(nullptr, ID_CA))
1002 << "nullptr Actions should be assignable to any type.";
1003
1004 EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
1005 << "Empty Actions should be assignable to any type.";
1006 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1007 << "Empty Actions should be assignable to any type.";
1008
1009 /* Make the Action a legacy one. */
1010 FCurve fake_fcurve;
1011 BLI_addtail(&action->curves, &fake_fcurve);
1012 ASSERT_FALSE(action->is_empty());
1013 ASSERT_TRUE(action->is_action_legacy());
1014 ASSERT_EQ(0, action->idroot);
1015
1016 EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
1017 << "Legacy Actions with idroot=0 should be assignable to any type.";
1018 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1019 << "Legacy Actions with idroot=0 should be assignable to any type.";
1020
1021 /* Set the legacy idroot. */
1022 action->idroot = ID_CA;
1023 EXPECT_FALSE(is_action_assignable_to(action, ID_OB))
1024 << "Legacy Actions with idroot=ID_CA should NOT be assignable to ID_OB.";
1025 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1026 << "Legacy Actions with idroot=CA should be assignable to ID_CA.";
1027
1028 /* Make the Action a layered one. */
1029 BLI_poptail(&action->curves);
1030 action->layer_add("layer");
1031 ASSERT_EQ(0, action->idroot) << "Adding a layer should clear the idroot.";
1032
1033 EXPECT_TRUE(is_action_assignable_to(action, ID_OB))
1034 << "Layered Actions should be assignable to any type.";
1035 EXPECT_TRUE(is_action_assignable_to(action, ID_CA))
1036 << "Layered Actions should be assignable to any type.";
1037}
1038
1039TEST_F(ActionLayersTest, action_slot_get_id_for_keying__empty_action)
1040{
1041 EXPECT_TRUE(assign_action(action, cube->id));
1042
1043 /* Double-check that the action is considered empty for the test. */
1044 EXPECT_TRUE(action->is_empty());
1045
1046 /* None should return an ID, since there are no slots yet which could have this ID assigned.
1047 * Assignment of the Action itself (cube) shouldn't matter. */
1048 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, &cube->id));
1049 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, nullptr));
1050 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, &suzanne->id));
1051}
1052
1053TEST_F(ActionLayersTest, action_slot_get_id_for_keying__legacy_action)
1054{
1055 FCurve *fcurve = action_fcurve_ensure_legacy(bmain, action, nullptr, nullptr, {"location", 0});
1056 EXPECT_FALSE(fcurve == nullptr);
1057
1058 EXPECT_TRUE(assign_action(action, cube->id));
1059
1060 /* Double-check that the action is considered legacy for the test. */
1061 EXPECT_TRUE(action->is_action_legacy());
1062
1063 /* A `primary_id` that uses the action should get returned. Every other case
1064 * should return nullptr. */
1065 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, 0, &cube->id));
1066 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, nullptr));
1067 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, 0, &suzanne->id));
1068}
1069
1070TEST_F(ActionLayersTest, action_slot_get_id_for_keying__layered_action)
1071{
1072 Slot &slot = action->slot_add();
1073
1074 /* Double-check that the action is considered layered for the test. */
1075 EXPECT_TRUE(action->is_action_layered());
1076
1077 /* A slot with no users should never return a user. */
1078 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, nullptr));
1079 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &cube->id));
1080
1081 /* A slot with precisely one user should always return that user. */
1082 ASSERT_EQ(assign_action_and_slot(action, &slot, cube->id), ActionSlotAssignmentResult::OK);
1083 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, nullptr));
1084 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &cube->id));
1085 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &suzanne->id));
1086
1087 /* A slot with more than one user should return the passed `primary_id` if it
1088 * is among its users, and nullptr otherwise. */
1089 ASSERT_EQ(assign_action_and_slot(action, &slot, suzanne->id), ActionSlotAssignmentResult::OK);
1090 EXPECT_EQ(&cube->id, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &cube->id));
1091 EXPECT_EQ(&suzanne->id,
1092 action_slot_get_id_for_keying(*bmain, *action, slot.handle, &suzanne->id));
1093 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, nullptr));
1094 EXPECT_EQ(nullptr, action_slot_get_id_for_keying(*bmain, *action, slot.handle, &bob->id));
1095}
1096
1097TEST_F(ActionLayersTest, conversion_to_layered)
1098{
1099 EXPECT_TRUE(action->is_empty());
1100 FCurve *legacy_fcu_0 = action_fcurve_ensure_legacy(
1101 bmain, action, "Test", nullptr, {"location", 0});
1102 FCurve *legacy_fcu_1 = action_fcurve_ensure_legacy(
1103 bmain, action, "Test", nullptr, {"location", 1});
1104
1105 KeyframeSettings settings;
1106 settings.handle = HD_AUTO;
1107 settings.interpolation = BEZT_IPO_BEZ;
1108 settings.keyframe_type = BEZT_KEYTYPE_KEYFRAME;
1109 insert_vert_fcurve(legacy_fcu_0, {0, 0}, settings, INSERTKEY_NOFLAGS);
1110 insert_vert_fcurve(legacy_fcu_0, {1, 1}, settings, INSERTKEY_NOFLAGS);
1111 add_fmodifier(&legacy_fcu_1->modifiers, FMODIFIER_TYPE_NOISE, legacy_fcu_1);
1112
1113 Action *converted = convert_to_layered_action(*bmain, *action);
1114 ASSERT_TRUE(converted != action);
1115 EXPECT_STREQ(converted->id.name, "ACACÄnimåtië_layered");
1116 Strip *strip = converted->layer(0)->strip(0);
1117 StripKeyframeData &strip_data = strip->data<StripKeyframeData>(*converted);
1118 Channelbag *bag = strip_data.channelbag(0);
1119 ASSERT_EQ(bag->fcurve_array_num, 2);
1120 ASSERT_EQ(bag->fcurve_array[0]->totvert, 2);
1121
1122 ASSERT_EQ(BLI_listbase_count(&action->groups), 1);
1123 ASSERT_EQ(BLI_listbase_count(&converted->groups), 0);
1124
1125 ASSERT_EQ(bag->channel_groups().size(), 1);
1126 bActionGroup *group = bag->channel_group(0);
1127 ASSERT_EQ(group->fcurve_range_length, 2);
1128 ASSERT_STREQ(group->name, "Test");
1129
1130 ASSERT_TRUE(bag->fcurve_array[0]->modifiers.first == nullptr);
1131 ASSERT_TRUE(bag->fcurve_array[1]->modifiers.first != nullptr);
1132
1133 constexpr char id_name_max[] =
1134 "name_for_an_action_that_is_exactly_255_bytes_MAX_ID_NAME-3______"
1135 "name_for_an_action_that_is_exactly_255_bytes_MAX_ID_NAME-3______"
1136 "name_for_an_action_that_is_exactly_255_bytes_MAX_ID_NAME-3______"
1137 "name_for_an_action_that_is_exactly_255_bytes_MAX_ID_NAME-3_____";
1138 BLI_STATIC_ASSERT(std::string::traits_type::length(id_name_max) == MAX_ID_NAME - 2 - 1,
1139 "Wrong 'max length' name");
1140 Action *long_name_action = BKE_id_new<Action>(bmain, id_name_max);
1141 action_fcurve_ensure_legacy(bmain, long_name_action, "Long", nullptr, {"location", 0});
1142 /* The long name is shortened to make space for "_layered". */
1143 constexpr char id_name_max_converted[] =
1144 "name_for_an_action_that_is_exactly_255_bytes_MAX_ID_NAME-3______"
1145 "name_for_an_action_that_is_exactly_255_bytes_MAX_ID_NAME-3______"
1146 "name_for_an_action_that_is_exactly_255_bytes_MAX_ID_NAME-3______"
1147 "name_for_an_action_that_is_exactly_255_bytes_MAX_ID_NAM_layered";
1148 BLI_STATIC_ASSERT(std::string::traits_type::length(id_name_max_converted) == MAX_ID_NAME - 2 - 1,
1149 "Wrong 'max length' name");
1150 converted = convert_to_layered_action(*bmain, *long_name_action);
1151 EXPECT_STREQ(BKE_id_name(converted->id), id_name_max_converted);
1152}
1153
1154TEST_F(ActionLayersTest, conversion_to_layered_action_groups)
1155{
1156 EXPECT_TRUE(action->is_empty());
1157 action_fcurve_ensure_legacy(bmain, action, "Test", nullptr, {"location", 0});
1158 action_fcurve_ensure_legacy(bmain, action, "Test", nullptr, {"rotation_euler", 1});
1159 action_fcurve_ensure_legacy(bmain, action, "Test_Two", nullptr, {"scale", 1});
1160 action_fcurve_ensure_legacy(bmain, action, "Test_Three", nullptr, {"show_name", 1});
1161 action_fcurve_ensure_legacy(bmain, action, "Test_Rename", nullptr, {"show_axis", 1});
1162
1163 bActionGroup *rename_group = static_cast<bActionGroup *>(BLI_findlink(&action->groups, 3));
1164 ASSERT_NE(rename_group, nullptr);
1165 ASSERT_STREQ(rename_group->name, "Test_Rename");
1166 /* Forcing a duplicate name which was allowed by legacy actions. */
1167 STRNCPY_UTF8(rename_group->name, "Test");
1168
1169 Action *converted = convert_to_layered_action(*bmain, *action);
1170 Strip *strip = converted->layer(0)->strip(0);
1171 StripKeyframeData &strip_data = strip->data<StripKeyframeData>(*converted);
1172 Channelbag *bag = strip_data.channelbag(0);
1173
1174 ASSERT_EQ(BLI_listbase_count(&converted->groups), 0);
1175 ASSERT_EQ(bag->channel_groups().size(), 4);
1176
1177 bActionGroup *test_group = bag->channel_group(0);
1178 EXPECT_STREQ(test_group->name, "Test");
1179 EXPECT_EQ(test_group->fcurve_range_length, 2);
1180
1181 bActionGroup *test_two_group = bag->channel_group(1);
1182 EXPECT_STREQ(test_two_group->name, "Test_Two");
1183 EXPECT_EQ(test_two_group->fcurve_range_length, 1);
1184 EXPECT_STREQ(bag->fcurve_array[test_two_group->fcurve_range_start]->rna_path, "scale");
1185
1186 bActionGroup *test_three_group = bag->channel_group(2);
1187 EXPECT_STREQ(test_three_group->name, "Test_Three");
1188 EXPECT_EQ(test_three_group->fcurve_range_length, 1);
1189 EXPECT_STREQ(bag->fcurve_array[test_three_group->fcurve_range_start]->rna_path, "show_name");
1190
1191 bActionGroup *test_rename_group = bag->channel_group(3);
1192 EXPECT_STREQ(test_rename_group->name, "Test.001");
1193 EXPECT_EQ(test_rename_group->fcurve_range_length, 1);
1194 EXPECT_STREQ(bag->fcurve_array[test_rename_group->fcurve_range_start]->rna_path, "show_axis");
1195
1196 ASSERT_NE(converted, action);
1197}
1198
1199TEST_F(ActionLayersTest, empty_to_layered)
1200{
1201 ASSERT_TRUE(action->is_empty());
1202 Action *converted = convert_to_layered_action(*bmain, *action);
1203 ASSERT_TRUE(converted != action);
1204 ASSERT_TRUE(converted->is_action_layered());
1205 ASSERT_FALSE(converted->is_action_legacy());
1206}
1207
1208TEST_F(ActionLayersTest, action_move_slot)
1209{
1210 Action *action_2 = BKE_id_new<Action>(bmain, "Action 2");
1211 EXPECT_TRUE(action->is_empty());
1212
1213 Slot &slot_cube = action->slot_add();
1214 Slot &slot_suzanne = action_2->slot_add();
1215 EXPECT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1216 EXPECT_EQ(assign_action_and_slot(action_2, &slot_suzanne, suzanne->id),
1218
1219 PointerRNA cube_rna_pointer = RNA_id_pointer_create(&cube->id);
1220 PointerRNA suzanne_rna_pointer = RNA_id_pointer_create(&suzanne->id);
1221
1222 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"location", 0});
1223 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"rotation_euler", 1});
1224
1225 action_fcurve_ensure_ex(bmain, action_2, "Test_2", &suzanne_rna_pointer, {"location", 0});
1226 action_fcurve_ensure_ex(bmain, action_2, "Test_2", &suzanne_rna_pointer, {"rotation_euler", 1});
1227
1228 ASSERT_EQ(action->layer_array_num, 1);
1229 ASSERT_EQ(action_2->layer_array_num, 1);
1230
1231 Layer *layer_1 = action->layer(0);
1232 Layer *layer_2 = action_2->layer(0);
1233
1234 ASSERT_EQ(layer_1->strip_array_num, 1);
1235 ASSERT_EQ(layer_2->strip_array_num, 1);
1236
1237 StripKeyframeData &strip_data_1 = layer_1->strip(0)->data<StripKeyframeData>(*action);
1238 StripKeyframeData &strip_data_2 = layer_2->strip(0)->data<StripKeyframeData>(*action_2);
1239
1240 ASSERT_EQ(strip_data_1.channelbag_array_num, 1);
1241 ASSERT_EQ(strip_data_2.channelbag_array_num, 1);
1242
1243 Channelbag *bag_1 = strip_data_1.channelbag(0);
1244 Channelbag *bag_2 = strip_data_2.channelbag(0);
1245
1246 ASSERT_EQ(bag_1->fcurve_array_num, 2);
1247 ASSERT_EQ(bag_2->fcurve_array_num, 2);
1248
1249 move_slot(*bmain, slot_suzanne, *action_2, *action);
1250
1251 ASSERT_EQ(strip_data_1.channelbag_array_num, 2);
1252 ASSERT_EQ(strip_data_2.channelbag_array_num, 0);
1253
1254 ASSERT_EQ(action->slot_array_num, 2);
1255 ASSERT_EQ(action_2->slot_array_num, 0);
1256
1257 /* Action should have been reassigned. */
1258 ASSERT_EQ(action, cube->adt->action);
1259 ASSERT_EQ(action, suzanne->adt->action);
1260}
1261
1262TEST_F(ActionLayersTest, action_move_slot_without_channelbag)
1263{
1264 Action *action_2 = BKE_id_new<Action>(bmain, "Action 2");
1265 EXPECT_TRUE(action->is_empty());
1266
1267 Slot &slot_cube = action->slot_add();
1268 Slot &slot_suzanne = action_2->slot_add();
1269 EXPECT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1270 EXPECT_EQ(assign_action_and_slot(action_2, &slot_suzanne, suzanne->id),
1272
1273 PointerRNA cube_rna_pointer = RNA_id_pointer_create(&cube->id);
1274 PointerRNA suzanne_rna_pointer = RNA_id_pointer_create(&suzanne->id);
1275
1276 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"location", 0});
1277 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"rotation_euler", 1});
1278
1279 /* Make sure action_2 has a keyframe strip, but without a channelbag. */
1280 action_2->layer_add("Bagless").strip_add(*action_2, Strip::Type::Keyframe);
1281
1282 ASSERT_EQ(action->layer_array_num, 1);
1283 ASSERT_EQ(action_2->layer_array_num, 1);
1284
1285 Layer *layer_1 = action->layer(0);
1286 Layer *layer_2 = action_2->layer(0);
1287
1288 ASSERT_EQ(layer_1->strip_array_num, 1);
1289 ASSERT_EQ(layer_2->strip_array_num, 1);
1290
1291 StripKeyframeData &strip_data_1 = layer_1->strip(0)->data<StripKeyframeData>(*action);
1292 StripKeyframeData &strip_data_2 = layer_2->strip(0)->data<StripKeyframeData>(*action_2);
1293
1294 ASSERT_EQ(strip_data_1.channelbag_array_num, 1);
1295 ASSERT_EQ(strip_data_2.channelbag_array_num, 0)
1296 << "the keyframe strip of action_2 should NOT have a channelbag in this test";
1297
1298 Channelbag *bag_1 = strip_data_1.channelbag(0);
1299 ASSERT_EQ(bag_1->fcurve_array_num, 2);
1300
1301 move_slot(*bmain, slot_suzanne, *action_2, *action);
1302
1303 ASSERT_EQ(strip_data_1.channelbag_array_num, 1);
1304 ASSERT_EQ(strip_data_2.channelbag_array_num, 0);
1305
1306 ASSERT_EQ(action->slot_array_num, 2);
1307 ASSERT_EQ(action_2->slot_array_num, 0);
1308
1309 /* Action should have been reassigned. */
1310 ASSERT_EQ(action, cube->adt->action);
1311 ASSERT_EQ(action, suzanne->adt->action);
1312}
1313
1314TEST_F(ActionLayersTest, action_duplicate_slot)
1315{
1316 ASSERT_TRUE(action->is_empty());
1317
1318 Slot &slot_cube = action->slot_add();
1319 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1320
1321 PointerRNA cube_rna_pointer = RNA_id_pointer_create(&cube->id);
1322
1323 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"location", 0});
1324 action_fcurve_ensure_ex(bmain, action, "Test", &cube_rna_pointer, {"rotation_euler", 1});
1325
1326 ASSERT_EQ(action->layer_array_num, 1);
1327 Layer *layer = action->layer(0);
1328
1329 ASSERT_EQ(layer->strip_array_num, 1);
1330 StripKeyframeData &strip_data = layer->strip(0)->data<StripKeyframeData>(*action);
1331
1332 ASSERT_EQ(strip_data.channelbag_array_num, 1);
1333 Channelbag *bag = strip_data.channelbag(0);
1334 ASSERT_EQ(bag->fcurve_array_num, 2);
1335
1336 /* Duplicate the slot and check it for uniqueness within the Action. */
1337 Slot &dupli_slot = duplicate_slot(*action, slot_cube);
1338 EXPECT_NE(dupli_slot.identifier, slot_cube.identifier);
1339 EXPECT_NE(dupli_slot.handle, slot_cube.handle);
1340 ASSERT_EQ(action->slot_array_num, 2);
1341 EXPECT_EQ(&dupli_slot, action->slot(1));
1342
1343 /* Check the channelbag has been duplicated correctly. */
1344 ASSERT_EQ(strip_data.channelbag_array_num, 2);
1345 Channelbag *dupli_bag = strip_data.channelbag(1);
1346 EXPECT_EQ(dupli_bag->slot_handle, dupli_slot.handle);
1347 EXPECT_EQ(dupli_bag->fcurve_array_num, 2);
1348
1349 /* Check the original channelbag is untouched. */
1350 EXPECT_EQ(bag->slot_handle, slot_cube.handle);
1351 EXPECT_EQ(bag->fcurve_array_num, 2);
1352
1353 /* The slot should NOT have been reassigned. */
1354 EXPECT_EQ(action, cube->adt->action);
1355 EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
1356}
1357
1358TEST_F(ActionLayersTest, action_duplicate_slot_without_channelbag)
1359{
1360 ASSERT_TRUE(action->is_empty());
1361
1362 Slot &slot_cube = action->slot_add();
1363 ASSERT_EQ(assign_action_and_slot(action, &slot_cube, cube->id), ActionSlotAssignmentResult::OK);
1364
1365 /* Create a keyframe strip, but without any channelbags. */
1366 action->layer_keystrip_ensure();
1367
1368 ASSERT_EQ(action->layer_array_num, 1);
1369 Layer *layer = action->layer(0);
1370
1371 ASSERT_EQ(layer->strip_array_num, 1);
1372 StripKeyframeData &strip_data = layer->strip(0)->data<StripKeyframeData>(*action);
1373
1374 ASSERT_EQ(strip_data.channelbag_array_num, 0);
1375
1376 /* Duplicate the slot and check it for uniqueness within the Action. */
1377 Slot &dupli_slot = duplicate_slot(*action, slot_cube);
1378 EXPECT_NE(dupli_slot.identifier, slot_cube.identifier);
1379 EXPECT_NE(dupli_slot.handle, slot_cube.handle);
1380 ASSERT_EQ(action->slot_array_num, 2);
1381 EXPECT_EQ(&dupli_slot, action->slot(1));
1382
1383 /* Check there are still no channelbags. */
1384 EXPECT_EQ(strip_data.channelbag_array_num, 0);
1385
1386 /* The slot should NOT have been reassigned. */
1387 EXPECT_EQ(action, cube->adt->action);
1388 EXPECT_EQ(slot_cube.handle, cube->adt->slot_handle);
1389}
1390
1391/*-----------------------------------------------------------*/
1392
1393/* Allocate fcu->bezt, and also return a unique_ptr to it for easily freeing the memory. */
1394static void allocate_keyframes(FCurve &fcu, const size_t num_keyframes)
1395{
1396 fcu.bezt = MEM_calloc_arrayN<BezTriple>(num_keyframes, __func__);
1397}
1398
1399/* Append keyframe, assumes that fcu->bezt is allocated and has enough space. */
1400static void add_keyframe(FCurve &fcu, float x, float y)
1401{
1402 /* The insert_keyframe functions are in the editors, so we cannot link to those here. */
1403 BezTriple the_keyframe = {};
1404
1405 /* Copied from insert_vert_fcurve() in `keyframing.cc`. */
1406 the_keyframe.vec[0][0] = x - 1.0f;
1407 the_keyframe.vec[0][1] = y;
1408 the_keyframe.vec[1][0] = x;
1409 the_keyframe.vec[1][1] = y;
1410 the_keyframe.vec[2][0] = x + 1.0f;
1411 the_keyframe.vec[2][1] = y;
1412
1413 memcpy(&fcu.bezt[fcu.totvert], &the_keyframe, sizeof(the_keyframe));
1414 fcu.totvert++;
1415}
1416
1417static void add_fcurve_to_action(Action &action, FCurve &fcu)
1418{
1419 Slot &slot = action.slot_array_num > 0 ? *action.slot(0) : action.slot_add();
1420 action.layer_keystrip_ensure();
1421 StripKeyframeData &strip_data = action.layer(0)->strip(0)->data<StripKeyframeData>(action);
1422 Channelbag &cbag = strip_data.channelbag_for_slot_ensure(slot);
1423 cbag.fcurve_append(fcu);
1424}
1425
1426class ActionQueryTest : public testing::Test {
1427 public:
1429
1430 static void SetUpTestSuite()
1431 {
1432 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
1433 CLG_init();
1434
1435 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
1437 }
1438
1439 static void TearDownTestSuite()
1440 {
1441 CLG_exit();
1442 }
1443
1444 void SetUp() override
1445 {
1446 bmain = BKE_main_new();
1447 }
1448
1449 void TearDown() override
1450 {
1452 }
1453
1455 {
1456 return *BKE_id_new<Action>(bmain, "ACÄnimåtië");
1457 }
1458};
1459
1460TEST_F(ActionQueryTest, BKE_action_frame_range_calc)
1461{
1462 /* No FCurves. */
1463 {
1464 const Action &empty = action_new();
1465 EXPECT_EQ((float2{0.0f, 0.0f}), empty.get_frame_range_of_keys(false));
1466 }
1467
1468 /* One curve with one key. */
1469 {
1470 FCurve &fcu = *MEM_callocN<FCurve>(__func__);
1471 allocate_keyframes(fcu, 1);
1472 add_keyframe(fcu, 1.0f, 2.0f);
1473
1474 Action &action = action_new();
1475 add_fcurve_to_action(action, fcu);
1476
1477 const float2 frame_range = action.get_frame_range_of_keys(false);
1478 EXPECT_FLOAT_EQ(frame_range[0], 1.0f);
1479 EXPECT_FLOAT_EQ(frame_range[1], 1.0f);
1480 }
1481
1482 /* Two curves with one key each on different frames. */
1483 {
1484 FCurve &fcu1 = *MEM_callocN<FCurve>(__func__);
1485 FCurve &fcu2 = *MEM_callocN<FCurve>(__func__);
1486 allocate_keyframes(fcu1, 1);
1487 allocate_keyframes(fcu2, 1);
1488 add_keyframe(fcu1, 1.0f, 2.0f);
1489 add_keyframe(fcu2, 1.5f, 2.0f);
1490
1491 Action &action = action_new();
1492 add_fcurve_to_action(action, fcu1);
1493 add_fcurve_to_action(action, fcu2);
1494
1495 const float2 frame_range = action.get_frame_range_of_keys(false);
1496 EXPECT_FLOAT_EQ(frame_range[0], 1.0f);
1497 EXPECT_FLOAT_EQ(frame_range[1], 1.5f);
1498 }
1499
1500 /* One curve with two keys. */
1501 {
1502 FCurve &fcu = *MEM_callocN<FCurve>(__func__);
1503 allocate_keyframes(fcu, 2);
1504 add_keyframe(fcu, 1.0f, 2.0f);
1505 add_keyframe(fcu, 1.5f, 2.0f);
1506
1507 Action &action = action_new();
1508 add_fcurve_to_action(action, fcu);
1509
1510 const float2 frame_range = action.get_frame_range_of_keys(false);
1511 EXPECT_FLOAT_EQ(frame_range[0], 1.0f);
1512 EXPECT_FLOAT_EQ(frame_range[1], 1.5f);
1513 }
1514
1515 /* TODO: action with fcurve modifiers. */
1516}
1517
1518/*-----------------------------------------------------------*/
1519
1520class ChannelbagTest : public testing::Test {
1521 public:
1523
1524 static void SetUpTestSuite() {}
1525
1526 static void TearDownTestSuite() {}
1527
1528 void SetUp() override
1529 {
1530 channelbag = new Channelbag();
1531 }
1532
1533 void TearDown() override
1534 {
1535 delete channelbag;
1536 }
1537};
1538
1539TEST_F(ChannelbagTest, fcurve_create_many)
1540{
1541 FCurve &existing1 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1542 FCurve &existing2 = channelbag->fcurve_ensure(nullptr, {"fcu0", 1, {}, {}, "group0"});
1543 FCurve &existing3 = channelbag->fcurve_ensure(nullptr, {"fcu1", 1, {}, {}, "group1"});
1544 FCurve &existing4 = channelbag->fcurve_ensure(nullptr, {"fcu_", 0});
1545 ASSERT_EQ(2, channelbag->channel_groups().size());
1546 ASSERT_EQ(4, channelbag->fcurves().size());
1547
1548 FCurveDescriptor desc[] = {
1549 /* New group. */
1550 {"fcu2", 0, {}, {}, "group2"},
1551 {"fcu2", 1, {}, {}, "group2"},
1552 {"fcu2", 2, {}, {}, "group2"},
1553 /* Existing groups. */
1554 {"fcu3", 0, {}, {}, "group1"},
1555 {"fcu4", 0, {}, {}, "group0"},
1556 {"fcu5", 0, {}, {}, "group1"},
1557 {"fcu6", 0, {}, {}, "group0"},
1558 {"fcu7", 0, {}, {}, "group2"},
1559 /* No group. */
1560 {"fcu8", 0},
1561 {"fcu8", 1},
1562 /* Empty rna path, should return null. */
1563 {"", 0, {}, {}, "irrelevant"},
1564 /* Should return null since such curves already exist. */
1565 {"fcu0", 1, {}, {}, "irrelevant"},
1566 {"fcu5", 0, {}, {}, "also unused"},
1567 {"fcu2", 0, {}, {}, "group2"},
1568 {"fcu6", 0},
1569 };
1570 Vector<FCurve *> fcurves = channelbag->fcurve_create_many(nullptr, {desc, ARRAY_SIZE(desc)});
1571 ASSERT_EQ(15, fcurves.size());
1572
1573 EXPECT_STREQ("group2", fcurves[0]->grp->name);
1574 EXPECT_STREQ("group2", fcurves[1]->grp->name);
1575 EXPECT_STREQ("group2", fcurves[2]->grp->name);
1576 EXPECT_STREQ("group1", fcurves[3]->grp->name);
1577 EXPECT_STREQ("group0", fcurves[4]->grp->name);
1578 EXPECT_STREQ("group1", fcurves[5]->grp->name);
1579 EXPECT_STREQ("group0", fcurves[6]->grp->name);
1580 EXPECT_STREQ("group2", fcurves[7]->grp->name);
1581 EXPECT_EQ(nullptr, fcurves[8]->grp);
1582 EXPECT_EQ(nullptr, fcurves[9]->grp);
1583 EXPECT_EQ(nullptr, fcurves[10]);
1584 EXPECT_EQ(nullptr, fcurves[11]);
1585 EXPECT_EQ(nullptr, fcurves[12]);
1586 EXPECT_EQ(nullptr, fcurves[13]);
1587 EXPECT_EQ(nullptr, fcurves[14]);
1588
1589 EXPECT_EQ(3, channelbag->channel_groups().size());
1590 EXPECT_EQ(14, channelbag->fcurves().size());
1591
1592 EXPECT_STREQ("group0", existing1.grp->name);
1593 EXPECT_STREQ("group0", existing2.grp->name);
1594 EXPECT_STREQ("group1", existing3.grp->name);
1595 EXPECT_EQ(nullptr, existing4.grp);
1596}
1597
1598TEST_F(ChannelbagTest, fcurve_move_to_index)
1599{
1600 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1601 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
1602 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
1603 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group1"});
1604 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
1605
1606 ASSERT_EQ(5, channelbag->fcurves().size());
1607 ASSERT_EQ(2, channelbag->channel_groups().size());
1608
1609 bActionGroup &group0 = *channelbag->channel_group(0);
1610 bActionGroup &group1 = *channelbag->channel_group(1);
1611
1612 /* Moving an fcurve to where it already is should be fine. */
1613 channelbag->fcurve_move_to_index(fcu0, 0);
1614 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1615 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
1616 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1617 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1618 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1619 EXPECT_EQ(&group0, fcu0.grp);
1620 EXPECT_EQ(&group0, fcu1.grp);
1621 EXPECT_EQ(&group1, fcu2.grp);
1622 EXPECT_EQ(&group1, fcu3.grp);
1623 EXPECT_EQ(nullptr, fcu4.grp);
1624
1625 /* Move to first. */
1626 channelbag->fcurve_move_to_index(fcu4, 0);
1627 EXPECT_EQ(0, group0.fcurve_range_start);
1628 EXPECT_EQ(2, group0.fcurve_range_length);
1629 EXPECT_EQ(2, group1.fcurve_range_start);
1630 EXPECT_EQ(2, group1.fcurve_range_length);
1631 EXPECT_EQ(&fcu4, channelbag->fcurve(0));
1632 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
1633 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
1634 EXPECT_EQ(&fcu2, channelbag->fcurve(3));
1635 EXPECT_EQ(&fcu3, channelbag->fcurve(4));
1636 EXPECT_EQ(&group0, fcu4.grp);
1637 EXPECT_EQ(&group0, fcu0.grp);
1638 EXPECT_EQ(&group1, fcu1.grp);
1639 EXPECT_EQ(&group1, fcu2.grp);
1640 EXPECT_EQ(nullptr, fcu3.grp);
1641
1642 /* Move to last. */
1643 channelbag->fcurve_move_to_index(fcu1, 4);
1644 EXPECT_EQ(0, group0.fcurve_range_start);
1645 EXPECT_EQ(2, group0.fcurve_range_length);
1646 EXPECT_EQ(2, group1.fcurve_range_start);
1647 EXPECT_EQ(2, group1.fcurve_range_length);
1648 EXPECT_EQ(&fcu4, channelbag->fcurve(0));
1649 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
1650 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1651 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1652 EXPECT_EQ(&fcu1, channelbag->fcurve(4));
1653 EXPECT_EQ(&group0, fcu4.grp);
1654 EXPECT_EQ(&group0, fcu0.grp);
1655 EXPECT_EQ(&group1, fcu2.grp);
1656 EXPECT_EQ(&group1, fcu3.grp);
1657 EXPECT_EQ(nullptr, fcu1.grp);
1658
1659 /* Move to middle. */
1660 channelbag->fcurve_move_to_index(fcu4, 2);
1661 EXPECT_EQ(0, group0.fcurve_range_start);
1662 EXPECT_EQ(2, group0.fcurve_range_length);
1663 EXPECT_EQ(2, group1.fcurve_range_start);
1664 EXPECT_EQ(2, group1.fcurve_range_length);
1665 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1666 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
1667 EXPECT_EQ(&fcu4, channelbag->fcurve(2));
1668 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1669 EXPECT_EQ(&fcu1, channelbag->fcurve(4));
1670 EXPECT_EQ(&group0, fcu0.grp);
1671 EXPECT_EQ(&group0, fcu2.grp);
1672 EXPECT_EQ(&group1, fcu4.grp);
1673 EXPECT_EQ(&group1, fcu3.grp);
1674 EXPECT_EQ(nullptr, fcu1.grp);
1675}
1676
1677TEST_F(ChannelbagTest, channel_group_create)
1678{
1679 ASSERT_TRUE(channelbag->channel_groups().is_empty());
1680
1681 bActionGroup &group0 = channelbag->channel_group_create("Foo");
1682 ASSERT_EQ(channelbag->channel_groups().size(), 1);
1683 EXPECT_EQ(StringRef{group0.name}, StringRef{"Foo"});
1684 EXPECT_EQ(group0.fcurve_range_start, 0);
1685 EXPECT_EQ(group0.fcurve_range_length, 0);
1686 EXPECT_EQ(&group0, channelbag->channel_group(0));
1687
1688 /* Set for testing purposes. Does not reflect actual fcurves in this test. */
1689 group0.fcurve_range_length = 2;
1690
1691 bActionGroup &group1 = channelbag->channel_group_create("Bar");
1692 ASSERT_EQ(channelbag->channel_groups().size(), 2);
1693 EXPECT_EQ(StringRef{group1.name}, StringRef{"Bar"});
1694 EXPECT_EQ(group1.fcurve_range_start, 2);
1695 EXPECT_EQ(group1.fcurve_range_length, 0);
1696 EXPECT_EQ(&group0, channelbag->channel_group(0));
1697 EXPECT_EQ(&group1, channelbag->channel_group(1));
1698
1699 /* Set for testing purposes. Does not reflect actual fcurves in this test. */
1700 group1.fcurve_range_length = 1;
1701
1702 bActionGroup &group2 = channelbag->channel_group_create("Yar");
1703 ASSERT_EQ(channelbag->channel_groups().size(), 3);
1704 EXPECT_EQ(StringRef{group2.name}, StringRef{"Yar"});
1705 EXPECT_EQ(group2.fcurve_range_start, 3);
1706 EXPECT_EQ(group2.fcurve_range_length, 0);
1707 EXPECT_EQ(&group0, channelbag->channel_group(0));
1708 EXPECT_EQ(&group1, channelbag->channel_group(1));
1709 EXPECT_EQ(&group2, channelbag->channel_group(2));
1710}
1711
1712TEST_F(ChannelbagTest, channel_group_remove)
1713{
1714 bActionGroup &group0 = channelbag->channel_group_create("Group0");
1715 bActionGroup &group1 = channelbag->channel_group_create("Group1");
1716 bActionGroup &group2 = channelbag->channel_group_create("Group2");
1717
1718 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "Group0"});
1719 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "Group0"});
1720 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "Group2"});
1721 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "Group2"});
1722 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
1723
1724 ASSERT_EQ(3, channelbag->channel_groups().size());
1725 ASSERT_EQ(5, channelbag->fcurves().size());
1726
1727 /* Attempt to remove a group that's not in the channel bag. Shouldn't do
1728 * anything. */
1729 bActionGroup bogus;
1730 EXPECT_EQ(false, channelbag->channel_group_remove(bogus));
1731 ASSERT_EQ(3, channelbag->channel_groups().size());
1732 ASSERT_EQ(5, channelbag->fcurves().size());
1733 EXPECT_EQ(&group0, channelbag->channel_group(0));
1734 EXPECT_EQ(&group1, channelbag->channel_group(1));
1735 EXPECT_EQ(&group2, channelbag->channel_group(2));
1736 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1737 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
1738 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1739 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1740 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1741 EXPECT_EQ(&group0, fcu0.grp);
1742 EXPECT_EQ(&group0, fcu1.grp);
1743 EXPECT_EQ(&group2, fcu2.grp);
1744 EXPECT_EQ(&group2, fcu3.grp);
1745 EXPECT_EQ(nullptr, fcu4.grp);
1746
1747 /* Removing an empty group shouldn't affect the fcurves at all. */
1748 EXPECT_EQ(true, channelbag->channel_group_remove(group1));
1749 ASSERT_EQ(2, channelbag->channel_groups().size());
1750 ASSERT_EQ(5, channelbag->fcurves().size());
1751 EXPECT_EQ(&group0, channelbag->channel_group(0));
1752 EXPECT_EQ(&group2, channelbag->channel_group(1));
1753 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
1754 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
1755 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1756 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
1757 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1758 EXPECT_EQ(&group0, fcu0.grp);
1759 EXPECT_EQ(&group0, fcu1.grp);
1760 EXPECT_EQ(&group2, fcu2.grp);
1761 EXPECT_EQ(&group2, fcu3.grp);
1762 EXPECT_EQ(nullptr, fcu4.grp);
1763
1764 /* Removing a group that's not at the end of the group array should move its
1765 * fcurves to be just after the grouped fcurves. */
1766 EXPECT_EQ(true, channelbag->channel_group_remove(group0));
1767 ASSERT_EQ(1, channelbag->channel_groups().size());
1768 ASSERT_EQ(5, channelbag->fcurves().size());
1769 EXPECT_EQ(&group2, channelbag->channel_group(0));
1770 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
1771 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1772 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
1773 EXPECT_EQ(&fcu1, channelbag->fcurve(3));
1774 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1775 EXPECT_EQ(nullptr, fcu0.grp);
1776 EXPECT_EQ(nullptr, fcu1.grp);
1777 EXPECT_EQ(&group2, fcu2.grp);
1778 EXPECT_EQ(&group2, fcu3.grp);
1779 EXPECT_EQ(nullptr, fcu4.grp);
1780
1781 /* Removing a group at the end of the group array shouldn't move its
1782 * fcurves. */
1783 EXPECT_EQ(true, channelbag->channel_group_remove(group2));
1784 ASSERT_EQ(0, channelbag->channel_groups().size());
1785 ASSERT_EQ(5, channelbag->fcurves().size());
1786 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
1787 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1788 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
1789 EXPECT_EQ(&fcu1, channelbag->fcurve(3));
1790 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
1791 EXPECT_EQ(nullptr, fcu0.grp);
1792 EXPECT_EQ(nullptr, fcu1.grp);
1793 EXPECT_EQ(nullptr, fcu2.grp);
1794 EXPECT_EQ(nullptr, fcu3.grp);
1795 EXPECT_EQ(nullptr, fcu4.grp);
1796}
1797
1798TEST_F(ChannelbagTest, channel_group_find)
1799{
1800 bActionGroup &group0a = channelbag->channel_group_create("Foo");
1801 bActionGroup &group1a = channelbag->channel_group_create("Bar");
1802 bActionGroup &group2a = channelbag->channel_group_create("Yar");
1803
1804 bActionGroup *group0b = channelbag->channel_group_find("Foo");
1805 bActionGroup *group1b = channelbag->channel_group_find("Bar");
1806 bActionGroup *group2b = channelbag->channel_group_find("Yar");
1807
1808 EXPECT_EQ(&group0a, group0b);
1809 EXPECT_EQ(&group1a, group1b);
1810 EXPECT_EQ(&group2a, group2b);
1811
1812 EXPECT_EQ(nullptr, channelbag->channel_group_find("Wat"));
1813}
1814
1815TEST_F(ChannelbagTest, channel_group_ensure)
1816{
1817 bActionGroup &group0 = channelbag->channel_group_create("Foo");
1818 bActionGroup &group1 = channelbag->channel_group_create("Bar");
1819 EXPECT_EQ(channelbag->channel_groups().size(), 2);
1820
1821 EXPECT_EQ(&group0, &channelbag->channel_group_ensure("Foo"));
1822 EXPECT_EQ(channelbag->channel_groups().size(), 2);
1823
1824 EXPECT_EQ(&group1, &channelbag->channel_group_ensure("Bar"));
1825 EXPECT_EQ(channelbag->channel_groups().size(), 2);
1826
1827 bActionGroup &group2 = channelbag->channel_group_ensure("Yar");
1828 ASSERT_EQ(channelbag->channel_groups().size(), 3);
1829 EXPECT_EQ(&group2, channelbag->channel_group(2));
1830}
1831
1832TEST_F(ChannelbagTest, channel_group_fcurve_creation)
1833{
1834 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0});
1835 EXPECT_EQ(1, channelbag->fcurves().size());
1836 EXPECT_TRUE(channelbag->channel_groups().is_empty());
1837
1838 /* If an fcurve already exists, then ensuring it with a channel group in the
1839 * fcurve descriptor should NOT add it that group, nor should the group be
1840 * created if it doesn't already exist. */
1841 channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1842 EXPECT_EQ(1, channelbag->fcurves().size());
1843 EXPECT_EQ(nullptr, fcu0.grp);
1844 EXPECT_TRUE(channelbag->channel_groups().is_empty());
1845
1846 /* Creating a new fcurve with a channel group in the fcurve descriptor should
1847 * create the group and put the fcurve in it. This also implies that the
1848 * fcurve will be added before any non-grouped fcurves in the array. */
1849 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
1850 ASSERT_EQ(2, channelbag->fcurves().size());
1851 ASSERT_EQ(1, channelbag->channel_groups().size());
1852 bActionGroup &group0 = *channelbag->channel_group(0);
1853 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1854 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
1855 EXPECT_EQ(&group0, fcu1.grp);
1856 EXPECT_EQ(nullptr, fcu0.grp);
1857 EXPECT_EQ(0, group0.fcurve_range_start);
1858 EXPECT_EQ(1, group0.fcurve_range_length);
1859
1860 /* Creating a new fcurve with a second channel group in the fcurve descriptor
1861 * should create the group and put the fcurve in it. This also implies that
1862 * the fcurve will be added before non-grouped fcurves, but after other
1863 * grouped ones. */
1864 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
1865 ASSERT_EQ(3, channelbag->fcurves().size());
1866 ASSERT_EQ(2, channelbag->channel_groups().size());
1867 EXPECT_EQ(&group0, channelbag->channel_group(0));
1868 bActionGroup &group1 = *channelbag->channel_group(1);
1869 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1870 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
1871 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
1872 EXPECT_EQ(&group0, fcu1.grp);
1873 EXPECT_EQ(&group1, fcu2.grp);
1874 EXPECT_EQ(nullptr, fcu0.grp);
1875 EXPECT_EQ(0, group0.fcurve_range_start);
1876 EXPECT_EQ(1, group0.fcurve_range_length);
1877 EXPECT_EQ(1, group1.fcurve_range_start);
1878 EXPECT_EQ(1, group1.fcurve_range_length);
1879
1880 /* Creating a new fcurve with the first channel group again should put it at
1881 * the end of that group. */
1882 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group0"});
1883 ASSERT_EQ(4, channelbag->fcurves().size());
1884 ASSERT_EQ(2, channelbag->channel_groups().size());
1885 EXPECT_EQ(&group0, channelbag->channel_group(0));
1886 EXPECT_EQ(&group1, channelbag->channel_group(1));
1887 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1888 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1889 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1890 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
1891 EXPECT_EQ(&group0, fcu1.grp);
1892 EXPECT_EQ(&group0, fcu3.grp);
1893 EXPECT_EQ(&group1, fcu2.grp);
1894 EXPECT_EQ(nullptr, fcu0.grp);
1895 EXPECT_EQ(0, group0.fcurve_range_start);
1896 EXPECT_EQ(2, group0.fcurve_range_length);
1897 EXPECT_EQ(2, group1.fcurve_range_start);
1898 EXPECT_EQ(1, group1.fcurve_range_length);
1899
1900 /* Finally, creating a new fcurve with the second channel group again should
1901 * also put it at the end of that group. */
1902 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0, {}, {}, "group1"});
1903 ASSERT_EQ(5, channelbag->fcurves().size());
1904 ASSERT_EQ(2, channelbag->channel_groups().size());
1905 EXPECT_EQ(&group0, channelbag->channel_group(0));
1906 EXPECT_EQ(&group1, channelbag->channel_group(1));
1907 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
1908 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
1909 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
1910 EXPECT_EQ(&fcu4, channelbag->fcurve(3));
1911 EXPECT_EQ(&fcu0, channelbag->fcurve(4));
1912 EXPECT_EQ(&group0, fcu1.grp);
1913 EXPECT_EQ(&group0, fcu3.grp);
1914 EXPECT_EQ(&group1, fcu2.grp);
1915 EXPECT_EQ(&group1, fcu4.grp);
1916 EXPECT_EQ(nullptr, fcu0.grp);
1917 EXPECT_EQ(0, group0.fcurve_range_start);
1918 EXPECT_EQ(2, group0.fcurve_range_length);
1919 EXPECT_EQ(2, group1.fcurve_range_start);
1920 EXPECT_EQ(2, group1.fcurve_range_length);
1921}
1922
1923TEST_F(ChannelbagTest, channel_group_fcurve_removal)
1924{
1925 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1926 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
1927 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
1928 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group1"});
1929 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
1930
1931 ASSERT_EQ(5, channelbag->fcurves().size());
1932 ASSERT_EQ(2, channelbag->channel_groups().size());
1933
1934 bActionGroup &group0 = *channelbag->channel_group(0);
1935 bActionGroup &group1 = *channelbag->channel_group(1);
1936
1937 EXPECT_EQ(0, group0.fcurve_range_start);
1938 EXPECT_EQ(2, group0.fcurve_range_length);
1939 EXPECT_EQ(2, group1.fcurve_range_start);
1940 EXPECT_EQ(2, group1.fcurve_range_length);
1941 EXPECT_EQ(&group0, fcu0.grp);
1942 EXPECT_EQ(&group0, fcu1.grp);
1943 EXPECT_EQ(&group1, fcu2.grp);
1944 EXPECT_EQ(&group1, fcu3.grp);
1945 EXPECT_EQ(nullptr, fcu4.grp);
1946
1947 channelbag->fcurve_remove(fcu3);
1948 ASSERT_EQ(4, channelbag->fcurves().size());
1949 ASSERT_EQ(2, channelbag->channel_groups().size());
1950 EXPECT_EQ(&group0, channelbag->channel_group(0));
1951 EXPECT_EQ(&group1, channelbag->channel_group(1));
1952 EXPECT_EQ(0, group0.fcurve_range_start);
1953 EXPECT_EQ(2, group0.fcurve_range_length);
1954 EXPECT_EQ(2, group1.fcurve_range_start);
1955 EXPECT_EQ(1, group1.fcurve_range_length);
1956 EXPECT_EQ(&group0, fcu0.grp);
1957 EXPECT_EQ(&group0, fcu1.grp);
1958 EXPECT_EQ(&group1, fcu2.grp);
1959 EXPECT_EQ(nullptr, fcu4.grp);
1960
1961 channelbag->fcurve_remove(fcu0);
1962 ASSERT_EQ(3, channelbag->fcurves().size());
1963 ASSERT_EQ(2, channelbag->channel_groups().size());
1964 EXPECT_EQ(&group0, channelbag->channel_group(0));
1965 EXPECT_EQ(&group1, channelbag->channel_group(1));
1966 EXPECT_EQ(0, group0.fcurve_range_start);
1967 EXPECT_EQ(1, group0.fcurve_range_length);
1968 EXPECT_EQ(1, group1.fcurve_range_start);
1969 EXPECT_EQ(1, group1.fcurve_range_length);
1970 EXPECT_EQ(&group0, fcu1.grp);
1971 EXPECT_EQ(&group1, fcu2.grp);
1972 EXPECT_EQ(nullptr, fcu4.grp);
1973
1974 channelbag->fcurve_remove(fcu1);
1975 ASSERT_EQ(2, channelbag->fcurves().size());
1976 ASSERT_EQ(1, channelbag->channel_groups().size());
1977 EXPECT_EQ(&group1, channelbag->channel_group(0));
1978 EXPECT_EQ(0, group1.fcurve_range_start);
1979 EXPECT_EQ(1, group1.fcurve_range_length);
1980 EXPECT_EQ(&group1, fcu2.grp);
1981 EXPECT_EQ(nullptr, fcu4.grp);
1982
1983 channelbag->fcurve_remove(fcu4);
1984 ASSERT_EQ(1, channelbag->fcurves().size());
1985 ASSERT_EQ(1, channelbag->channel_groups().size());
1986 EXPECT_EQ(&group1, channelbag->channel_group(0));
1987 EXPECT_EQ(0, group1.fcurve_range_start);
1988 EXPECT_EQ(1, group1.fcurve_range_length);
1989 EXPECT_EQ(&group1, fcu2.grp);
1990
1991 channelbag->fcurve_remove(fcu2);
1992 ASSERT_EQ(0, channelbag->fcurves().size());
1993 ASSERT_EQ(0, channelbag->channel_groups().size());
1994}
1995
1996TEST_F(ChannelbagTest, channel_group_move_to_index)
1997{
1998 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
1999 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group1"});
2000 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
2001 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group2"});
2002 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
2003
2004 ASSERT_EQ(5, channelbag->fcurves().size());
2005 ASSERT_EQ(3, channelbag->channel_groups().size());
2006
2007 bActionGroup &group0 = *channelbag->channel_group(0);
2008 bActionGroup &group1 = *channelbag->channel_group(1);
2009 bActionGroup &group2 = *channelbag->channel_group(2);
2010
2011 channelbag->channel_group_move_to_index(group0, 2);
2012 EXPECT_EQ(&group1, channelbag->channel_group(0));
2013 EXPECT_EQ(&group2, channelbag->channel_group(1));
2014 EXPECT_EQ(&group0, channelbag->channel_group(2));
2015 EXPECT_EQ(0, group1.fcurve_range_start);
2016 EXPECT_EQ(2, group1.fcurve_range_length);
2017 EXPECT_EQ(2, group2.fcurve_range_start);
2018 EXPECT_EQ(1, group2.fcurve_range_length);
2019 EXPECT_EQ(3, group0.fcurve_range_start);
2020 EXPECT_EQ(1, group0.fcurve_range_length);
2021 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2022 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2023 EXPECT_EQ(&fcu3, channelbag->fcurve(2));
2024 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
2025 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
2026 EXPECT_EQ(&group1, fcu1.grp);
2027 EXPECT_EQ(&group1, fcu2.grp);
2028 EXPECT_EQ(&group2, fcu3.grp);
2029 EXPECT_EQ(&group0, fcu0.grp);
2030 EXPECT_EQ(nullptr, fcu4.grp);
2031
2032 channelbag->channel_group_move_to_index(group1, 1);
2033 EXPECT_EQ(&group2, channelbag->channel_group(0));
2034 EXPECT_EQ(&group1, channelbag->channel_group(1));
2035 EXPECT_EQ(&group0, channelbag->channel_group(2));
2036 EXPECT_EQ(0, group2.fcurve_range_start);
2037 EXPECT_EQ(1, group2.fcurve_range_length);
2038 EXPECT_EQ(1, group1.fcurve_range_start);
2039 EXPECT_EQ(2, group1.fcurve_range_length);
2040 EXPECT_EQ(3, group0.fcurve_range_start);
2041 EXPECT_EQ(1, group0.fcurve_range_length);
2042 EXPECT_EQ(&fcu3, channelbag->fcurve(0));
2043 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
2044 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
2045 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
2046 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
2047 EXPECT_EQ(&group2, fcu3.grp);
2048 EXPECT_EQ(&group1, fcu1.grp);
2049 EXPECT_EQ(&group1, fcu2.grp);
2050 EXPECT_EQ(&group0, fcu0.grp);
2051 EXPECT_EQ(nullptr, fcu4.grp);
2052
2053 channelbag->channel_group_move_to_index(group0, 0);
2054 EXPECT_EQ(&group0, channelbag->channel_group(0));
2055 EXPECT_EQ(&group2, channelbag->channel_group(1));
2056 EXPECT_EQ(&group1, channelbag->channel_group(2));
2057 EXPECT_EQ(0, group0.fcurve_range_start);
2058 EXPECT_EQ(1, group0.fcurve_range_length);
2059 EXPECT_EQ(1, group2.fcurve_range_start);
2060 EXPECT_EQ(1, group2.fcurve_range_length);
2061 EXPECT_EQ(2, group1.fcurve_range_start);
2062 EXPECT_EQ(2, group1.fcurve_range_length);
2063 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
2064 EXPECT_EQ(&fcu3, channelbag->fcurve(1));
2065 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
2066 EXPECT_EQ(&fcu2, channelbag->fcurve(3));
2067 EXPECT_EQ(&fcu4, channelbag->fcurve(4));
2068 EXPECT_EQ(&group0, fcu0.grp);
2069 EXPECT_EQ(&group2, fcu3.grp);
2070 EXPECT_EQ(&group1, fcu1.grp);
2071 EXPECT_EQ(&group1, fcu2.grp);
2072 EXPECT_EQ(nullptr, fcu4.grp);
2073}
2074
2075TEST_F(ChannelbagTest, channel_group_move_fcurve_into)
2076{
2077 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0});
2078 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0});
2079 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0});
2080 bActionGroup &group0 = channelbag->channel_group_create("group0");
2081 bActionGroup &group1 = channelbag->channel_group_create("group1");
2082
2083 ASSERT_EQ(3, channelbag->fcurves().size());
2084 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
2085 EXPECT_EQ(&fcu1, channelbag->fcurve(1));
2086 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
2087 ASSERT_EQ(2, channelbag->channel_groups().size());
2088 EXPECT_EQ(&group0, channelbag->channel_group(0));
2089 EXPECT_EQ(&group1, channelbag->channel_group(1));
2090 EXPECT_EQ(0, group0.fcurve_range_start);
2091 EXPECT_EQ(0, group0.fcurve_range_length);
2092 EXPECT_EQ(0, group1.fcurve_range_start);
2093 EXPECT_EQ(0, group1.fcurve_range_length);
2094
2095 channelbag->fcurve_assign_to_channel_group(fcu2, group1);
2096 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
2097 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
2098 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
2099 EXPECT_EQ(0, group0.fcurve_range_start);
2100 EXPECT_EQ(0, group0.fcurve_range_length);
2101 EXPECT_EQ(0, group1.fcurve_range_start);
2102 EXPECT_EQ(1, group1.fcurve_range_length);
2103
2104 channelbag->fcurve_assign_to_channel_group(fcu1, group0);
2105 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2106 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2107 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
2108 EXPECT_EQ(0, group0.fcurve_range_start);
2109 EXPECT_EQ(1, group0.fcurve_range_length);
2110 EXPECT_EQ(1, group1.fcurve_range_start);
2111 EXPECT_EQ(1, group1.fcurve_range_length);
2112
2113 channelbag->fcurve_assign_to_channel_group(fcu0, group1);
2114 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2115 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2116 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
2117 EXPECT_EQ(0, group0.fcurve_range_start);
2118 EXPECT_EQ(1, group0.fcurve_range_length);
2119 EXPECT_EQ(1, group1.fcurve_range_start);
2120 EXPECT_EQ(2, group1.fcurve_range_length);
2121
2122 channelbag->fcurve_assign_to_channel_group(fcu0, group0);
2123 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2124 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
2125 EXPECT_EQ(&fcu2, channelbag->fcurve(2));
2126 EXPECT_EQ(0, group0.fcurve_range_start);
2127 EXPECT_EQ(2, group0.fcurve_range_length);
2128 EXPECT_EQ(2, group1.fcurve_range_start);
2129 EXPECT_EQ(1, group1.fcurve_range_length);
2130
2131 channelbag->fcurve_assign_to_channel_group(fcu1, group1);
2132 EXPECT_EQ(&fcu0, channelbag->fcurve(0));
2133 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2134 EXPECT_EQ(&fcu1, channelbag->fcurve(2));
2135 EXPECT_EQ(0, group0.fcurve_range_start);
2136 EXPECT_EQ(1, group0.fcurve_range_length);
2137 EXPECT_EQ(1, group1.fcurve_range_start);
2138 EXPECT_EQ(2, group1.fcurve_range_length);
2139}
2140
2141TEST_F(ChannelbagTest, channel_group_fcurve_ungroup)
2142{
2143 FCurve &fcu0 = channelbag->fcurve_ensure(nullptr, {"fcu0", 0, {}, {}, "group0"});
2144 FCurve &fcu1 = channelbag->fcurve_ensure(nullptr, {"fcu1", 0, {}, {}, "group0"});
2145 FCurve &fcu2 = channelbag->fcurve_ensure(nullptr, {"fcu2", 0, {}, {}, "group1"});
2146 FCurve &fcu3 = channelbag->fcurve_ensure(nullptr, {"fcu3", 0, {}, {}, "group1"});
2147 FCurve &fcu4 = channelbag->fcurve_ensure(nullptr, {"fcu4", 0});
2148
2149 ASSERT_EQ(5, channelbag->fcurves().size());
2150 ASSERT_EQ(2, channelbag->channel_groups().size());
2151
2152 bActionGroup &group0 = *channelbag->channel_group(0);
2153 bActionGroup &group1 = *channelbag->channel_group(1);
2154
2155 /* Attempting to ungroup an fcurve that's not in the channel bag should fail. */
2156 FCurve bogus = {};
2157 EXPECT_FALSE(channelbag->fcurve_ungroup(bogus));
2158
2159 /* Attempting to ungroup an fcurve that's already ungrouped is fine. */
2160 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu4));
2161
2162 /* Ungroup each fcurve until all are ungrouped. */
2163
2164 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu0));
2165 EXPECT_EQ(0, group0.fcurve_range_start);
2166 EXPECT_EQ(1, group0.fcurve_range_length);
2167 EXPECT_EQ(1, group1.fcurve_range_start);
2168 EXPECT_EQ(2, group1.fcurve_range_length);
2169 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2170 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2171 EXPECT_EQ(&fcu3, channelbag->fcurve(2));
2172 EXPECT_EQ(&fcu4, channelbag->fcurve(3));
2173 EXPECT_EQ(&fcu0, channelbag->fcurve(4));
2174 EXPECT_EQ(&group0, fcu1.grp);
2175 EXPECT_EQ(&group1, fcu2.grp);
2176 EXPECT_EQ(&group1, fcu3.grp);
2177 EXPECT_EQ(nullptr, fcu4.grp);
2178 EXPECT_EQ(nullptr, fcu0.grp);
2179
2180 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu3));
2181 EXPECT_EQ(0, group0.fcurve_range_start);
2182 EXPECT_EQ(1, group0.fcurve_range_length);
2183 EXPECT_EQ(1, group1.fcurve_range_start);
2184 EXPECT_EQ(1, group1.fcurve_range_length);
2185 EXPECT_EQ(&fcu1, channelbag->fcurve(0));
2186 EXPECT_EQ(&fcu2, channelbag->fcurve(1));
2187 EXPECT_EQ(&fcu4, channelbag->fcurve(2));
2188 EXPECT_EQ(&fcu0, channelbag->fcurve(3));
2189 EXPECT_EQ(&fcu3, channelbag->fcurve(4));
2190 EXPECT_EQ(&group0, fcu1.grp);
2191 EXPECT_EQ(&group1, fcu2.grp);
2192 EXPECT_EQ(nullptr, fcu4.grp);
2193 EXPECT_EQ(nullptr, fcu0.grp);
2194 EXPECT_EQ(nullptr, fcu3.grp);
2195
2196 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu1));
2197 EXPECT_EQ(1, channelbag->channel_groups().size());
2198 EXPECT_EQ(&group1, channelbag->channel_group(0));
2199 EXPECT_EQ(0, group1.fcurve_range_start);
2200 EXPECT_EQ(1, group1.fcurve_range_length);
2201 EXPECT_EQ(&fcu2, channelbag->fcurve(0));
2202 EXPECT_EQ(&fcu4, channelbag->fcurve(1));
2203 EXPECT_EQ(&fcu0, channelbag->fcurve(2));
2204 EXPECT_EQ(&fcu3, channelbag->fcurve(3));
2205 EXPECT_EQ(&fcu1, channelbag->fcurve(4));
2206 EXPECT_EQ(&group1, fcu2.grp);
2207 EXPECT_EQ(nullptr, fcu4.grp);
2208 EXPECT_EQ(nullptr, fcu0.grp);
2209 EXPECT_EQ(nullptr, fcu3.grp);
2210 EXPECT_EQ(nullptr, fcu1.grp);
2211
2212 EXPECT_TRUE(channelbag->fcurve_ungroup(fcu2));
2213 EXPECT_EQ(0, channelbag->channel_groups().size());
2214 EXPECT_EQ(&fcu4, channelbag->fcurve(0));
2215 EXPECT_EQ(&fcu0, channelbag->fcurve(1));
2216 EXPECT_EQ(&fcu3, channelbag->fcurve(2));
2217 EXPECT_EQ(&fcu1, channelbag->fcurve(3));
2218 EXPECT_EQ(&fcu2, channelbag->fcurve(4));
2219 EXPECT_EQ(nullptr, fcu4.grp);
2220 EXPECT_EQ(nullptr, fcu0.grp);
2221 EXPECT_EQ(nullptr, fcu3.grp);
2222 EXPECT_EQ(nullptr, fcu1.grp);
2223 EXPECT_EQ(nullptr, fcu2.grp);
2224}
2225
2226/*-----------------------------------------------------------*/
2227
2228class ActionFCurveMoveTest : public testing::Test {
2229 public:
2231
2232 static void SetUpTestSuite()
2233 {
2234 /* BKE_id_free() hits a code path that uses CLOG, which crashes if not initialized properly. */
2235 CLG_init();
2236
2237 /* To make id_can_have_animdata() and friends work, the `id_types` array needs to be set up. */
2239 }
2240
2241 static void TearDownTestSuite()
2242 {
2243 CLG_exit();
2244 }
2245
2246 void SetUp() override
2247 {
2248 bmain = BKE_main_new();
2249 }
2250
2251 void TearDown() override
2252 {
2254 }
2255
2256 static FCurve *fcurve_create(const StringRefNull rna_path, const int array_index)
2257 {
2258 FCurve *fcurve = BKE_fcurve_create();
2259 fcurve->rna_path = BLI_strdupn(rna_path.c_str(), array_index);
2260 return fcurve;
2261 };
2262};
2263
2264TEST_F(ActionFCurveMoveTest, test_fcurve_move_legacy)
2265{
2266 Action &action_src = action_add(*this->bmain, "SourceAction");
2267 Action &action_dst = action_add(*this->bmain, "DestinationAction");
2268
2269 /* Add F-Curves to source Action. */
2270 BLI_addtail(&action_src.curves, fcurve_create("source_prop", 0));
2271 FCurve *fcurve_to_move = fcurve_create("source_prop", 2);
2272 BLI_addtail(&action_src.curves, fcurve_to_move);
2273
2274 /* Add F-Curves to destination Action. */
2275 BLI_addtail(&action_dst.curves, fcurve_create("dest_prop", 0));
2276
2277 ASSERT_TRUE(action_src.is_action_legacy());
2278 ASSERT_TRUE(action_dst.is_action_legacy());
2279
2280 action_fcurve_move(action_dst, Slot::unassigned, action_src, *fcurve_to_move);
2281
2282 EXPECT_TRUE(action_src.is_action_legacy());
2283 EXPECT_TRUE(action_dst.is_action_legacy());
2284
2285 EXPECT_EQ(-1, BLI_findindex(&action_src.curves, fcurve_to_move))
2286 << "F-Curve should no longer exist in source Action";
2287 EXPECT_EQ(1, BLI_findindex(&action_dst.curves, fcurve_to_move))
2288 << "F-Curve should exist in destination Action";
2289
2290 EXPECT_EQ(1, BLI_listbase_count(&action_src.curves))
2291 << "Source Action should still have the other F-Curve";
2292 EXPECT_EQ(2, BLI_listbase_count(&action_dst.curves))
2293 << "Destination Action should have its original and the moved F-Curve";
2294}
2295
2296TEST_F(ActionFCurveMoveTest, test_fcurve_move_layered)
2297{
2298 Action &action_src = action_add(*this->bmain, "SourceAction");
2299 Action &action_dst = action_add(*this->bmain, "DestinationAction");
2300
2301 /* Add F-Curves to source Action. */
2302 Slot &slot_src = action_src.slot_add();
2303 action_src.layer_keystrip_ensure();
2304 StripKeyframeData &strip_data_src = action_src.layer(0)->strip(0)->data<StripKeyframeData>(
2305 action_src);
2306 Channelbag &cbag_src = strip_data_src.channelbag_for_slot_ensure(slot_src);
2307
2308 cbag_src.fcurve_ensure(this->bmain, {"source_prop", 0});
2309 FCurve &fcurve_to_move = cbag_src.fcurve_ensure(this->bmain, {"source_prop", 2});
2310 bActionGroup &group_src = cbag_src.channel_group_create("Gröpje");
2311 cbag_src.fcurve_assign_to_channel_group(fcurve_to_move, group_src);
2312
2313 /* Add F-Curves to destination Action. */
2314 Slot &slot_dst = action_dst.slot_add();
2315 action_dst.layer_keystrip_ensure();
2316 StripKeyframeData &strip_data_dst = action_dst.layer(0)->strip(0)->data<StripKeyframeData>(
2317 action_dst);
2318 Channelbag &cbag_dst = strip_data_dst.channelbag_for_slot_ensure(slot_dst);
2319
2320 cbag_dst.fcurve_ensure(this->bmain, {"dest_prop", 0});
2321
2322 ASSERT_TRUE(action_src.is_action_layered());
2323 ASSERT_TRUE(action_dst.is_action_layered());
2324
2325 action_fcurve_move(action_dst, slot_dst.handle, action_src, fcurve_to_move);
2326
2327 EXPECT_TRUE(action_src.is_action_layered());
2328 EXPECT_TRUE(action_dst.is_action_layered());
2329
2330 EXPECT_EQ(nullptr, cbag_src.fcurve_find({fcurve_to_move.rna_path, fcurve_to_move.array_index}))
2331 << "F-Curve should no longer exist in source Action";
2332 EXPECT_EQ(&fcurve_to_move,
2333 cbag_dst.fcurve_find({fcurve_to_move.rna_path, fcurve_to_move.array_index}))
2334 << "F-Curve should exist in destination Action";
2335
2336 EXPECT_EQ(1, cbag_src.fcurves().size()) << "Source Action should still have the other F-Curve";
2337 EXPECT_EQ(2, cbag_dst.fcurves().size())
2338 << "Destination Action should have its original and the moved F-Curve";
2339
2340 bActionGroup *group_dst = cbag_dst.channel_group_find("Gröpje");
2341 ASSERT_NE(nullptr, group_dst) << "Expected channel group to be created";
2342 ASSERT_EQ(group_dst, fcurve_to_move.grp) << "Expected group membership to move as well";
2343}
2344
2345} // namespace blender::animrig::tests
Functions and classes to work with Actions.
Blender kernel action and pose functionality.
AnimData * BKE_animdata_ensure_id(ID *id)
Definition anim_data.cc:97
FCurve * BKE_fcurve_create()
FModifier * add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu)
float evaluate_fcurve(const FCurve *fcu, float evaltime)
void BKE_idtype_init()
Definition idtype.cc:121
void BKE_id_free(Main *bmain, void *idv)
void * BKE_id_new(Main *bmain, short type, const char *name)
Definition lib_id.cc:1514
const char * BKE_id_name(const ID &id)
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1519
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.
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:83
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
void * BLI_poptail(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:261
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
#define STRNCPY_UTF8(dst, src)
#define ARRAY_SIZE(arr)
void CLG_exit()
Definition clog.cc:880
void CLG_init()
Definition clog.cc:873
#define MAX_ID_NAME
Definition DNA_ID.h:373
@ ID_CA
@ ID_ME
@ ID_OB
#define DNA_DEFAULT_ACTION_LAST_SLOT_HANDLE
@ INSERTKEY_NOFLAGS
@ FMODIFIER_TYPE_NOISE
@ HD_AUTO
@ BEZT_IPO_BEZ
@ BEZT_KEYTYPE_KEYFRAME
Object is a sort of wrapper for general info.
@ OB_EMPTY
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
bool contains(const T &value) const
constexpr const char * c_str() const
int64_t size() const
Slot & slot_add_for_id(const ID &animated_id)
const Layer * layer(int64_t index) const
const Slot * slot(int64_t index) const
float2 get_frame_range_of_keys(bool include_modifiers) const ATTR_WARN_UNUSED_RESULT
void slot_identifier_define(Slot &slot, StringRefNull new_identifier)
Layer & layer_add(std::optional< StringRefNull > name)
const bActionGroup * channel_group(int64_t index) const
FCurve & fcurve_ensure(Main *bmain, const FCurveDescriptor &fcurve_descriptor)
FCurve * fcurve_create_unique(Main *bmain, const FCurveDescriptor &fcurve_descriptor)
blender::Span< const FCurve * > fcurves() const
blender::Span< const bActionGroup * > channel_groups() const
bool strip_remove(Action &owning_action, Strip &strip)
blender::Span< const Strip * > strips() const
const Strip * strip(int64_t index) const
Strip & strip_add(Action &owning_action, Strip::Type strip_type)
std::string idtype_string() const
Span< ID * > users(Main &bmain) const
static constexpr slot_handle_t unassigned
const Channelbag * channelbag_for_slot(const Slot &slot) const
SingleKeyingResult keyframe_insert(Main *bmain, const Slot &slot, const FCurveDescriptor &fcurve_descriptor, float2 time_value, const KeyframeSettings &settings, eInsertKeyFlags insert_key_flags=INSERTKEY_NOFLAGS, std::optional< float2 > cycle_range=std::nullopt)
Channelbag & channelbag_for_slot_ensure(const Slot &slot)
blender::Span< const Channelbag * > channelbags() const
const Channelbag * channelbag(int64_t index) const
bool is_last_frame(float frame_time) const
void resize(float frame_start, float frame_end)
const T & data(const Action &owning_action) const
bool contains_frame(float frame_time) const
static FCurve * fcurve_create(const StringRefNull rna_path, const int array_index)
#define GS(x)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
static void add_fcurve_to_action(Action &action, FCurve &fcu)
static void allocate_keyframes(FCurve &fcu, const size_t num_keyframes)
TEST_F(ActionIteratorsTest, iterate_all_fcurves_of_slot)
TEST(action, low_level_initialisation)
static void add_keyframe(FCurve &fcu, float x, float y)
KeyframeSettings get_keyframe_settings(bool from_userprefs)
Slot * assign_action_ensure_slot_for_keying(Action &action, ID &animated_id)
void action_fcurve_move(Action &action_dst, slot_handle_t action_slot_dst, Action &action_src, FCurve &fcurve)
Action & action_add(Main &bmain, StringRefNull name)
Slot & duplicate_slot(Action &action, const Slot &slot)
FCurve * action_fcurve_ensure_legacy(Main *bmain, bAction *act, const char group[], PointerRNA *ptr, const FCurveDescriptor &fcurve_descriptor)
ActionSlotAssignmentResult assign_action_and_slot(Action *action, Slot *slot_to_assign, ID &animated_id)
decltype(::ActionSlot::handle) slot_handle_t
bool is_action_assignable_to(const bAction *dna_action, ID_Type id_code)
SingleKeyingResult insert_vert_fcurve(FCurve *fcu, const float2 position, const KeyframeSettings &settings, eInsertKeyFlags flag)
Main Key-framing API call.
ID * action_slot_get_id_for_keying(Main &bmain, Action &action, slot_handle_t slot_handle, ID *primary_id)
FCurve * action_fcurve_ensure_ex(Main *bmain, bAction *act, const char group[], PointerRNA *ptr, const FCurveDescriptor &fcurve_descriptor)
Action * convert_to_layered_action(Main &bmain, const Action &legacy_action)
bool unassign_action(ID &animated_id)
bool assign_action(bAction *action, ID &animated_id)
ActionSlotAssignmentResult assign_action_slot(Slot *slot_to_assign, ID &animated_id)
void move_slot(Main &bmain, Slot &slot, Action &from_action, Action &to_action)
Slot * generic_slot_for_autoassign(const ID &animated_id, Action &action, StringRefNull last_slot_identifier)
VecBase< float, 2 > float2
PointerRNA RNA_id_pointer_create(ID *id)
struct FCurve ** fcurve_array
char identifier[258]
ActionSlotRuntimeHandle * runtime
bAction * action
int32_t slot_handle
char last_slot_identifier[258]
float vec[3][3]
bActionGroup * grp
char * rna_path
BezTriple * bezt
unsigned int totvert
ListBase modifiers
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
void * first
ListBase curves
int32_t last_slot_handle
ListBase groups