Files
test/source/blender/animrig/intern/bone_collections_test.cc
Brecht Van Lommel 920e709069 Refactor: Make header files more clangd and clang-tidy friendly
When using clangd or running clang-tidy on headers there are
currently many errors. These are noisy in IDEs, make auto fixes
impossible, and break features like code completion, refactoring
and navigation.

This makes source/blender headers work by themselves, which is
generally the goal anyway. But #includes and forward declarations
were often incomplete.

* Add #includes and forward declarations
* Add IWYU pragma: export in a few places
* Remove some unused #includes (but there are many more)
* Tweak ShaderCreateInfo macros to work better with clangd

Some types of headers still have errors, these could be fixed or
worked around with more investigation. Mostly preprocessor
template headers like NOD_static_types.h.

Note that that disabling WITH_UNITY_BUILD is required for clangd to
work properly, otherwise compile_commands.json does not contain
the information for the relevant source files.

For more details see the developer docs:
https://developer.blender.org/docs/handbook/tooling/clangd/

Pull Request: https://projects.blender.org/blender/blender/pulls/132608
2025-01-07 12:39:13 +01:00

1623 lines
75 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLT_translation.hh"
#include "BKE_idtype.hh"
#include "BKE_lib_id.hh"
#include "ANIM_bone_collections.hh"
#include "intern/bone_collections_internal.hh"
#include "testing/testing.h"
namespace blender::animrig::tests {
TEST(ANIM_bone_collections, bonecoll_new_free)
{
BoneCollection *bcoll = ANIM_bonecoll_new("some name");
EXPECT_NE(nullptr, bcoll);
EXPECT_EQ("some name", std::string(bcoll->name));
EXPECT_TRUE(BLI_listbase_is_empty(&bcoll->bones));
EXPECT_EQ(BONE_COLLECTION_VISIBLE | BONE_COLLECTION_SELECTABLE |
BONE_COLLECTION_ANCESTORS_VISIBLE,
bcoll->flags);
ANIM_bonecoll_free(bcoll);
}
TEST(ANIM_bone_collections, bonecoll_default_name)
{
{
BoneCollection *bcoll = ANIM_bonecoll_new("");
EXPECT_EQ(DATA_("Bones"), std::string(bcoll->name));
ANIM_bonecoll_free(bcoll);
}
{
BoneCollection *bcoll = ANIM_bonecoll_new(nullptr);
EXPECT_EQ(DATA_("Bones"), std::string(bcoll->name));
ANIM_bonecoll_free(bcoll);
}
}
class ArmatureBoneCollections : public testing::Test {
protected:
bArmature arm;
Bone bone1, bone2, bone3;
void SetUp() override
{
memset(&arm, 0, sizeof(arm));
memset(&bone1, 0, sizeof(Bone));
memset(&bone2, 0, sizeof(Bone));
memset(&bone3, 0, sizeof(Bone));
STRNCPY(arm.id.name, "ARArmature");
STRNCPY(bone1.name, "bone1");
STRNCPY(bone2.name, "bone2");
STRNCPY(bone3.name, "bone3");
BLI_addtail(&arm.bonebase, &bone1); /* bone1 is root bone. */
BLI_addtail(&arm.bonebase, &bone2); /* bone2 is root bone. */
BLI_addtail(&bone2.childbase, &bone3); /* bone3 has bone2 as parent. */
BKE_armature_bone_hash_make(&arm);
}
void TearDown() override
{
/* Avoid freeing the bones, as they are part of this struct and not owned by
* the armature. */
BLI_listbase_clear(&arm.bonebase);
BKE_idtype_init();
BKE_libblock_free_datablock(&arm.id, 0);
}
};
TEST_F(ArmatureBoneCollections, armature_owned_collections)
{
BoneCollection *bcoll1 = ANIM_armature_bonecoll_new(&arm, "collection");
BoneCollection *bcoll2 = ANIM_armature_bonecoll_new(&arm, "collection");
EXPECT_EQ(std::string("collection"), std::string(bcoll1->name));
EXPECT_EQ(std::string("collection.001"), std::string(bcoll2->name));
ANIM_armature_bonecoll_remove(&arm, bcoll1);
ANIM_armature_bonecoll_remove(&arm, bcoll2);
}
TEST_F(ArmatureBoneCollections, collection_hierarchy_creation)
{
/* Implicit root: */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "wortel");
/* Explicit root: */
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "wortel", -1);
ASSERT_EQ(0, armature_bonecoll_find_index(&arm, bcoll_root_0));
ASSERT_EQ(1, armature_bonecoll_find_index(&arm, bcoll_root_1));
/* Child of bcoll at index 0: */
BoneCollection *bcoll_child_of_0 = ANIM_armature_bonecoll_new(&arm, "koter", 0);
/* Child of bcoll at index 1: */
BoneCollection *bcoll_child_of_1 = ANIM_armature_bonecoll_new(&arm, "koter", 1);
ASSERT_EQ(4, arm.collection_array_num);
EXPECT_EQ(0, armature_bonecoll_find_index(&arm, bcoll_root_0));
EXPECT_EQ(1, armature_bonecoll_find_index(&arm, bcoll_root_1));
EXPECT_EQ(2, armature_bonecoll_find_index(&arm, bcoll_child_of_0));
EXPECT_EQ(3, armature_bonecoll_find_index(&arm, bcoll_child_of_1));
/* Add another child of bcoll_root_0, which should push bcoll_child_of_1 further down the array.
*/
BoneCollection *bcoll_another_child_of_0 = ANIM_armature_bonecoll_new(&arm, "koter", 0);
ASSERT_EQ(5, arm.collection_array_num);
EXPECT_EQ(0, armature_bonecoll_find_index(&arm, bcoll_root_0));
EXPECT_EQ(1, armature_bonecoll_find_index(&arm, bcoll_root_1));
EXPECT_EQ(2, armature_bonecoll_find_index(&arm, bcoll_child_of_0));
EXPECT_EQ(3, armature_bonecoll_find_index(&arm, bcoll_another_child_of_0));
EXPECT_EQ(4, armature_bonecoll_find_index(&arm, bcoll_child_of_1));
/* Make sure the names remain unique within the entire Armature, and not just between siblings
* (i.e. a unique 'path' is not strong enough). */
EXPECT_EQ(std::string("wortel"), std::string(bcoll_root_0->name));
EXPECT_EQ(std::string("wortel.001"), std::string(bcoll_root_1->name));
EXPECT_EQ(std::string("koter"), std::string(bcoll_child_of_0->name));
EXPECT_EQ(std::string("koter.001"), std::string(bcoll_child_of_1->name));
EXPECT_EQ(std::string("koter.002"), std::string(bcoll_another_child_of_0->name));
/* Test the internal hierarchy bookkeeping. */
EXPECT_EQ(2, arm.collection_root_count);
EXPECT_EQ(2, bcoll_root_0->child_count);
EXPECT_EQ(1, bcoll_root_1->child_count);
EXPECT_EQ(0, bcoll_child_of_0->child_count);
EXPECT_EQ(0, bcoll_another_child_of_0->child_count);
EXPECT_EQ(0, bcoll_child_of_1->child_count);
EXPECT_EQ(2, bcoll_root_0->child_index);
EXPECT_EQ(4, bcoll_root_1->child_index);
EXPECT_EQ(0, bcoll_child_of_0->child_index);
EXPECT_EQ(0, bcoll_another_child_of_0->child_index);
EXPECT_EQ(0, bcoll_child_of_1->child_index);
/* TODO: test with deeper hierarchy. */
}
TEST_F(ArmatureBoneCollections, collection_hierarchy_removal)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(5, arm.collection_array[1]->child_index);
ASSERT_EQ(0, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(3, arm.collection_array[0]->child_count);
ASSERT_EQ(1, arm.collection_array[1]->child_count);
ASSERT_EQ(0, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
/* Remove the middle child of root_0. */
ANIM_armature_bonecoll_remove(&arm, bcoll_r0_child1);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(5, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(4, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(2, arm.collection_array[0]->child_count);
EXPECT_EQ(1, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
/* Remove the first child of root_0. */
ANIM_armature_bonecoll_remove(&arm, bcoll_r0_child0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(4, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[3]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(3, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(1, arm.collection_array[0]->child_count);
EXPECT_EQ(1, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
/* Remove root_1 itself, which should make its only child a new root. */
ANIM_armature_bonecoll_remove(&arm, bcoll_root_1);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(3, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[2]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(1, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
}
TEST_F(ArmatureBoneCollections, collection_hierarchy_removal__more_complex_remove_inner_child)
{
/* Set up a slightly bigger hierarchy. Contrary to the other tests these are
* actually declared in array order. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
BoneCollection *bcoll_r0c0_child0 = ANIM_armature_bonecoll_new(&arm, "r0c0_child0", 2);
BoneCollection *bcoll_r0c0_child1 = ANIM_armature_bonecoll_new(&arm, "r0c0_child1", 2);
BoneCollection *bcoll_r0c0_child2 = ANIM_armature_bonecoll_new(&arm, "r0c0_child2", 2);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(8, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children of root_0. */
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[5]->name); /* Kids of r0_child0. */
ASSERT_STREQ(bcoll_r0c0_child1->name, arm.collection_array[6]->name);
ASSERT_STREQ(bcoll_r0c0_child2->name, arm.collection_array[7]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(0, arm.collection_array[1]->child_index);
ASSERT_EQ(5, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(0, arm.collection_array[6]->child_index);
ASSERT_EQ(0, arm.collection_array[7]->child_index);
ASSERT_EQ(3, arm.collection_array[0]->child_count);
ASSERT_EQ(0, arm.collection_array[1]->child_count);
ASSERT_EQ(3, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
ASSERT_EQ(0, arm.collection_array[6]->child_count);
ASSERT_EQ(0, arm.collection_array[7]->child_count);
/* Remove bcoll_r0_child0, which should make all of its children a child of root_0. */
ANIM_armature_bonecoll_remove(&arm, bcoll_r0_child0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[2]->name); /* Children of root_0. */
EXPECT_STREQ(bcoll_r0c0_child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0c0_child2->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[5]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[6]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
EXPECT_EQ(5, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* Remove root_0, which should make all of its children new roots. */
ANIM_armature_bonecoll_remove(&arm, bcoll_root_0);
ASSERT_EQ(6, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_r0c0_child1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0c0_child2->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[5]->name);
EXPECT_EQ(0, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
}
TEST_F(ArmatureBoneCollections, collection_hierarchy_removal__more_complex_remove_root)
{
/* Set up a slightly bigger hierarchy. Contrary to the other tests these are
* actually declared in array order. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
BoneCollection *bcoll_r0c0_child0 = ANIM_armature_bonecoll_new(&arm, "r0c0_child0", 2);
BoneCollection *bcoll_r0c0_child1 = ANIM_armature_bonecoll_new(&arm, "r0c0_child1", 2);
BoneCollection *bcoll_r0c0_child2 = ANIM_armature_bonecoll_new(&arm, "r0c0_child2", 2);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(8, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children of root_0. */
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[5]->name); /* Kids of r0_child0. */
ASSERT_STREQ(bcoll_r0c0_child1->name, arm.collection_array[6]->name);
ASSERT_STREQ(bcoll_r0c0_child2->name, arm.collection_array[7]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(0, arm.collection_array[1]->child_index);
ASSERT_EQ(5, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(0, arm.collection_array[6]->child_index);
ASSERT_EQ(0, arm.collection_array[7]->child_index);
ASSERT_EQ(3, arm.collection_array[0]->child_count);
ASSERT_EQ(0, arm.collection_array[1]->child_count);
ASSERT_EQ(3, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
ASSERT_EQ(0, arm.collection_array[6]->child_count);
ASSERT_EQ(0, arm.collection_array[7]->child_count);
/* Remove root_0, which should make all of its children new roots. */
ANIM_armature_bonecoll_remove(&arm, bcoll_root_0);
ASSERT_EQ(4, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[4]->name); /* Kids of r0_child0. */
ASSERT_STREQ(bcoll_r0c0_child1->name, arm.collection_array[5]->name);
ASSERT_STREQ(bcoll_r0c0_child2->name, arm.collection_array[6]->name);
EXPECT_EQ(4, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
}
TEST_F(ArmatureBoneCollections, find_parent_index)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0c0_child0 = ANIM_armature_bonecoll_new(&arm, "r0c0_child0", 2);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r0c0_child0->name, arm.collection_array[5]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(4, arm.collection_array[1]->child_index);
ASSERT_EQ(5, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(2, arm.collection_array[0]->child_count);
ASSERT_EQ(1, arm.collection_array[1]->child_count);
ASSERT_EQ(1, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, -1));
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, 500000));
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, 0));
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, 1));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 2));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 3));
EXPECT_EQ(1, armature_bonecoll_find_parent_index(&arm, 4));
EXPECT_EQ(2, armature_bonecoll_find_parent_index(&arm, 5));
}
TEST_F(ArmatureBoneCollections, collection_hierarchy_visibility)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root0 = ANIM_armature_bonecoll_new(&arm, "root0");
BoneCollection *bcoll_root1 = ANIM_armature_bonecoll_new(&arm, "root1");
const int root0_index = armature_bonecoll_find_index(&arm, bcoll_root0);
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", root0_index);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", root0_index);
const int child0_index = armature_bonecoll_find_index(&arm, bcoll_r0_child0);
BoneCollection *bcoll_c0_child0 = ANIM_armature_bonecoll_new(&arm, "c0_child0", child0_index);
/* Initially, all bone collections should be marked as visible. */
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_VISIBLE);
/* Initially, all bone collections should have visible ancestors. */
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
/* Mark root_0 as invisible, this should also update its children. */
ANIM_bonecoll_hide(&arm, bcoll_root0);
EXPECT_FALSE(bcoll_root0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_FALSE(bcoll_r0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_FALSE(bcoll_r0_child1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_FALSE(bcoll_c0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
/* Move r0_child0 to root1, that should change its BONE_COLLECTION_ANCESTORS_VISIBLE */
const int root1_index = armature_bonecoll_find_index(&arm, bcoll_root1);
armature_bonecoll_move_to_parent(&arm, child0_index, 0, root0_index, root1_index);
EXPECT_FALSE(bcoll_root0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_VISIBLE);
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE)
<< "The child that was moved to a visible root should be affected";
EXPECT_FALSE(bcoll_r0_child1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE)
<< "The child that wasn't moved should not be affected.";
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE)
<< "The grandchild that was indirectly moved to a visible root should be affected";
/* Add a new child to root0, it should have the right flags. */
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", root0_index);
EXPECT_TRUE(bcoll_r0_child2->flags & BONE_COLLECTION_VISIBLE);
EXPECT_FALSE(bcoll_r0_child2->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
}
TEST_F(ArmatureBoneCollections, bones_assign_unassign)
{
BoneCollection *bcoll = ANIM_armature_bonecoll_new(&arm, "collection");
ANIM_armature_bonecoll_assign(bcoll, &bone1);
ANIM_armature_bonecoll_assign(bcoll, &bone2);
ASSERT_EQ(2, BLI_listbase_count(&bcoll->bones)) << "expecting two bones in collection";
EXPECT_EQ(&bone1, static_cast<BoneCollectionMember *>(BLI_findlink(&bcoll->bones, 0))->bone);
EXPECT_EQ(&bone2, static_cast<BoneCollectionMember *>(BLI_findlink(&bcoll->bones, 1))->bone);
EXPECT_EQ(bcoll, static_cast<BoneCollectionReference *>(bone1.runtime.collections.first)->bcoll)
<< "expecting back-reference to collection in bone1 runtime data";
EXPECT_EQ(bcoll, static_cast<BoneCollectionReference *>(bone2.runtime.collections.first)->bcoll)
<< "expecting back-reference to collection in bone2 runtime data";
ANIM_armature_bonecoll_unassign(bcoll, &bone1);
ANIM_armature_bonecoll_unassign(bcoll, &bone2);
EXPECT_EQ(0, BLI_listbase_count(&bone1.runtime.collections))
<< "expecting back-references in bone1 runtime data to be cleared when unassigned";
EXPECT_EQ(0, BLI_listbase_count(&bone2.runtime.collections))
<< "expecting back-references in bone2 runtime data to be cleared when unassigned";
ANIM_armature_bonecoll_remove(&arm, bcoll);
}
TEST_F(ArmatureBoneCollections, bones_assign_remove)
{
BoneCollection *bcoll = ANIM_armature_bonecoll_new(&arm, "collection");
ANIM_armature_bonecoll_assign(bcoll, &bone1);
ANIM_armature_bonecoll_assign(bcoll, &bone2);
ANIM_armature_bonecoll_remove(&arm, bcoll);
EXPECT_EQ(0, BLI_listbase_count(&bone1.runtime.collections))
<< "expecting back-references in bone1 runtime data to be cleared when the collection is "
"removed";
EXPECT_EQ(0, BLI_listbase_count(&bone2.runtime.collections))
<< "expecting back-references in bone2 runtime data to be cleared when the collection is "
"removed";
}
TEST_F(ArmatureBoneCollections, active_set_clear_by_pointer)
{
BoneCollection *bcoll1 = ANIM_armature_bonecoll_new(&arm, "Bones 1");
BoneCollection *bcoll2 = ANIM_armature_bonecoll_new(&arm, "Bones 2");
BoneCollection *bcoll3 = ANIM_bonecoll_new("Alien Bones");
ANIM_armature_bonecoll_active_set(&arm, bcoll1);
EXPECT_EQ(0, arm.runtime.active_collection_index);
EXPECT_EQ(bcoll1, arm.runtime.active_collection);
EXPECT_STREQ(bcoll1->name, arm.active_collection_name);
ANIM_armature_bonecoll_active_set(&arm, nullptr);
EXPECT_EQ(-1, arm.runtime.active_collection_index);
EXPECT_EQ(nullptr, arm.runtime.active_collection);
EXPECT_STREQ("", arm.active_collection_name);
ANIM_armature_bonecoll_active_set(&arm, bcoll2);
EXPECT_EQ(1, arm.runtime.active_collection_index);
EXPECT_EQ(bcoll2, arm.runtime.active_collection);
EXPECT_STREQ(bcoll2->name, arm.active_collection_name);
ANIM_armature_bonecoll_active_set(&arm, bcoll3);
EXPECT_EQ(-1, arm.runtime.active_collection_index);
EXPECT_EQ(nullptr, arm.runtime.active_collection);
EXPECT_STREQ("", arm.active_collection_name);
ANIM_bonecoll_free(bcoll3);
}
TEST_F(ArmatureBoneCollections, active_set_clear_by_index)
{
BoneCollection *bcoll1 = ANIM_armature_bonecoll_new(&arm, "Bones 1");
BoneCollection *bcoll2 = ANIM_armature_bonecoll_new(&arm, "Bones 2");
ANIM_armature_bonecoll_active_index_set(&arm, 0);
EXPECT_EQ(0, arm.runtime.active_collection_index);
EXPECT_EQ(bcoll1, arm.runtime.active_collection);
EXPECT_STREQ(bcoll1->name, arm.active_collection_name);
ANIM_armature_bonecoll_active_index_set(&arm, -1);
EXPECT_EQ(-1, arm.runtime.active_collection_index);
EXPECT_EQ(nullptr, arm.runtime.active_collection);
EXPECT_STREQ("", arm.active_collection_name);
ANIM_armature_bonecoll_active_index_set(&arm, 1);
EXPECT_EQ(1, arm.runtime.active_collection_index);
EXPECT_EQ(bcoll2, arm.runtime.active_collection);
EXPECT_STREQ(bcoll2->name, arm.active_collection_name);
ANIM_armature_bonecoll_active_index_set(&arm, 47);
EXPECT_EQ(-1, arm.runtime.active_collection_index);
EXPECT_EQ(nullptr, arm.runtime.active_collection);
EXPECT_STREQ("", arm.active_collection_name);
}
TEST_F(ArmatureBoneCollections, bcoll_is_editable)
{
BoneCollection *bcoll1 = ANIM_armature_bonecoll_new(&arm, "Bones 1");
BoneCollection *bcoll2 = ANIM_armature_bonecoll_new(&arm, "Bones 2");
EXPECT_EQ(0, bcoll1->flags & BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL);
EXPECT_EQ(0, bcoll2->flags & BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL);
EXPECT_TRUE(ANIM_armature_bonecoll_is_editable(&arm, bcoll1))
<< "Expecting local armature to be editable";
/* Fake that the armature is linked from another blend file. */
Library fake_lib = {};
arm.id.lib = &fake_lib;
EXPECT_FALSE(ANIM_armature_bonecoll_is_editable(&arm, bcoll1))
<< "Expecting local armature to not be editable";
/* Fake that the armature is an override, but linked from another blend file. */
IDOverrideLibrary fake_override = {};
bArmature fake_reference = {};
fake_override.reference = &fake_reference.id;
arm.id.override_library = &fake_override;
EXPECT_FALSE(ANIM_armature_bonecoll_is_editable(&arm, bcoll1))
<< "Expecting linked armature override to not be editable";
/* Fake that the armature is a local override. */
arm.id.lib = nullptr;
bcoll2->flags |= BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL;
EXPECT_FALSE(ANIM_armature_bonecoll_is_editable(&arm, bcoll1))
<< "Expecting linked bone collection on local armature override to not be editable";
EXPECT_TRUE(ANIM_armature_bonecoll_is_editable(&arm, bcoll2))
<< "Expecting local bone collection on local armature override to be editable";
}
TEST_F(ArmatureBoneCollections, bcoll_move_to_index__roots)
{
BoneCollection *bcoll1 = ANIM_armature_bonecoll_new(&arm, "collection");
BoneCollection *bcoll2 = ANIM_armature_bonecoll_new(&arm, "collection");
BoneCollection *bcoll3 = ANIM_armature_bonecoll_new(&arm, "collection");
BoneCollection *bcoll4 = ANIM_armature_bonecoll_new(&arm, "collection");
EXPECT_EQ(arm.collection_array[0], bcoll1);
EXPECT_EQ(arm.collection_array[1], bcoll2);
EXPECT_EQ(arm.collection_array[2], bcoll3);
EXPECT_EQ(arm.collection_array[3], bcoll4);
EXPECT_TRUE(ANIM_armature_bonecoll_move_to_index(&arm, 2, 1));
EXPECT_EQ(arm.collection_array[0], bcoll1);
EXPECT_EQ(arm.collection_array[1], bcoll3);
EXPECT_EQ(arm.collection_array[2], bcoll2);
EXPECT_EQ(arm.collection_array[3], bcoll4);
EXPECT_TRUE(ANIM_armature_bonecoll_move_to_index(&arm, 0, 3));
EXPECT_EQ(arm.collection_array[0], bcoll3);
EXPECT_EQ(arm.collection_array[1], bcoll2);
EXPECT_EQ(arm.collection_array[2], bcoll4);
EXPECT_EQ(arm.collection_array[3], bcoll1);
/* Out of bounds should not be accepted. */
EXPECT_FALSE(ANIM_armature_bonecoll_move_to_index(&arm, 0, 327));
EXPECT_EQ(arm.collection_array[0], bcoll3);
EXPECT_EQ(arm.collection_array[1], bcoll2);
EXPECT_EQ(arm.collection_array[2], bcoll4);
EXPECT_EQ(arm.collection_array[3], bcoll1);
}
TEST_F(ArmatureBoneCollections, bcoll_move_to_index__siblings)
{
BoneCollection *root = ANIM_armature_bonecoll_new(&arm, "root");
BoneCollection *child0 = ANIM_armature_bonecoll_new(&arm, "child0", 0);
BoneCollection *child1 = ANIM_armature_bonecoll_new(&arm, "child1", 0);
BoneCollection *child2 = ANIM_armature_bonecoll_new(&arm, "child2", 0);
BoneCollection *child1_0 = ANIM_armature_bonecoll_new(&arm, "child1_0", 2);
ASSERT_STREQ(root->name, arm.collection_array[0]->name);
ASSERT_STREQ(child0->name, arm.collection_array[1]->name);
ASSERT_STREQ(child1->name, arm.collection_array[2]->name);
ASSERT_STREQ(child2->name, arm.collection_array[3]->name);
ASSERT_STREQ(child1_0->name, arm.collection_array[4]->name);
/* Move child2 to child0, i.e. a move 'to the left'. */
EXPECT_TRUE(ANIM_armature_bonecoll_move_to_index(&arm, 3, 1));
EXPECT_STREQ(root->name, arm.collection_array[0]->name);
EXPECT_STREQ(child2->name, arm.collection_array[1]->name);
EXPECT_STREQ(child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(child1_0->name, arm.collection_array[4]->name);
/* Move child2 to child1, i.e. a move 'to the right'. */
EXPECT_TRUE(ANIM_armature_bonecoll_move_to_index(&arm, 1, 3));
EXPECT_STREQ(root->name, arm.collection_array[0]->name);
EXPECT_STREQ(child0->name, arm.collection_array[1]->name);
EXPECT_STREQ(child1->name, arm.collection_array[2]->name);
EXPECT_STREQ(child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(child1_0->name, arm.collection_array[4]->name);
/* Move child2 to root, should not be allowed. */
EXPECT_FALSE(ANIM_armature_bonecoll_move_to_index(&arm, 3, 0));
EXPECT_STREQ(root->name, arm.collection_array[0]->name);
EXPECT_STREQ(child0->name, arm.collection_array[1]->name);
EXPECT_STREQ(child1->name, arm.collection_array[2]->name);
EXPECT_STREQ(child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(child1_0->name, arm.collection_array[4]->name);
/* Move child1_0 to child_2, should not be allowed. */
EXPECT_FALSE(ANIM_armature_bonecoll_move_to_index(&arm, 4, 3));
EXPECT_STREQ(root->name, arm.collection_array[0]->name);
EXPECT_STREQ(child0->name, arm.collection_array[1]->name);
EXPECT_STREQ(child1->name, arm.collection_array[2]->name);
EXPECT_STREQ(child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(child1_0->name, arm.collection_array[4]->name);
}
TEST_F(ArmatureBoneCollections, bcoll_move_to_parent)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(5, arm.collection_array[1]->child_index);
ASSERT_EQ(0, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(3, arm.collection_array[0]->child_count);
ASSERT_EQ(1, arm.collection_array[1]->child_count);
ASSERT_EQ(0, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
/* Move the middle child of root_0 to root_1. */
EXPECT_EQ(5, armature_bonecoll_move_to_parent(&arm, 3, bcoll_root_1->child_count, 0, 1));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[5]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(4, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(2, arm.collection_array[0]->child_count);
EXPECT_EQ(2, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
/* Move the first child of root_1 to root_0. This shouldn't change its index. */
EXPECT_EQ(4, armature_bonecoll_move_to_parent(&arm, 4, bcoll_root_0->child_count, 1, 0));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[5]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(5, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(1, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
/* Move the final child of root_1 to root_0. This shouldn't change its index
* again, but leave root_1 without children. */
EXPECT_EQ(5, armature_bonecoll_move_to_parent(&arm, 5, bcoll_root_0->child_count, 1, 0));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[5]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(4, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
/* Move the first child of root_0 (bcoll_r0_child0) to bcoll_r0_child2. */
EXPECT_EQ(5, armature_bonecoll_move_to_parent(&arm, 2, bcoll_r0_child2->child_count, 0, 3));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[5]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(5, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(1, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
}
TEST_F(ArmatureBoneCollections, bcoll_move_to_parent__root_unroot)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(5, arm.collection_array[1]->child_index);
ASSERT_EQ(0, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(3, arm.collection_array[0]->child_count);
ASSERT_EQ(1, arm.collection_array[1]->child_count);
ASSERT_EQ(0, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
/* Make a leaf node (bcoll_r0_child1) a root. */
EXPECT_EQ(2, armature_bonecoll_move_to_parent(&arm, 3, arm.collection_root_count, 0, -1));
ASSERT_EQ(3, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[2]->name); /* Became a root. */
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
EXPECT_EQ(3, arm.collection_array[0]->child_index);
EXPECT_EQ(5, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(2, arm.collection_array[0]->child_count);
EXPECT_EQ(1, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
/* Make a root node (root_1) a child of root_0. */
EXPECT_EQ(4, armature_bonecoll_move_to_parent(&arm, 1, bcoll_root_0->child_count, -1, 0));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(6, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[1]->name); /* Actually a root. */
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[4]->name); /* Became a child. */
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
EXPECT_EQ(2, arm.collection_array[0]->child_index);
EXPECT_EQ(0, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(5, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(0, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(1, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
/* TODO: test with circular parenthood. */
}
TEST_F(ArmatureBoneCollections, bcoll_move_to_parent__within_siblings)
{
/* Set up a small hierarchy. */
auto bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
auto bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
auto bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
auto bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
auto bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
auto bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
auto bcoll_r0_child3 = ANIM_armature_bonecoll_new(&arm, "r0_child3", 0);
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[2]->name); /* Children root_1. */
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[3]->name); /* Children root_0. */
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[5]->name);
ASSERT_STREQ(bcoll_r0_child3->name, arm.collection_array[6]->name);
ASSERT_EQ(3, arm.collection_array[0]->child_index);
ASSERT_EQ(2, arm.collection_array[1]->child_index);
ASSERT_EQ(0, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
ASSERT_EQ(0, arm.collection_array[6]->child_index);
ASSERT_EQ(4, arm.collection_array[0]->child_count);
ASSERT_EQ(1, arm.collection_array[1]->child_count);
ASSERT_EQ(0, arm.collection_array[2]->child_count);
ASSERT_EQ(0, arm.collection_array[3]->child_count);
ASSERT_EQ(0, arm.collection_array[4]->child_count);
ASSERT_EQ(0, arm.collection_array[5]->child_count);
ASSERT_EQ(0, arm.collection_array[6]->child_count);
/* First half of the test, move 3 children from root_1 to root_0. */
/* Move r0_child0 to become 1st child of root_1, before r1_child0. */
EXPECT_EQ(2,
armature_bonecoll_move_to_parent(&arm,
3, /* From index. */
0, /* To child number. */
0, /* From parent. */
1 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[4]->name); /* Children root_0. */
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[5]->name);
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[6]->name);
EXPECT_EQ(4, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(2, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* Move r0_child1 to become the 2nd child of root_1. */
EXPECT_EQ(3,
armature_bonecoll_move_to_parent(&arm,
4, /* From index. */
1, /* To child number. */
0, /* From parent. */
1 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[5]->name); /* Children root_0. */
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[6]->name);
EXPECT_EQ(5, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(2, arm.collection_array[0]->child_count);
EXPECT_EQ(3, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* Move r0_child3 to become the last child of root_1. */
EXPECT_EQ(5,
armature_bonecoll_move_to_parent(&arm,
6, /* From index. */
3, /* To child number. */
0, /* From parent. */
1 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[5]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[6]->name); /* Children root_0. */
EXPECT_EQ(6, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(1, arm.collection_array[0]->child_count);
EXPECT_EQ(4, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* 2nd half of the test: move the children back to root_0 to test moving in
* the other direction. */
/* Move r0_child3 to become the first child of root_0. */
EXPECT_EQ(5,
armature_bonecoll_move_to_parent(&arm,
5, /* From index. */
0, /* To child number. */
1, /* From parent. */
0 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[5]->name); /* Children root_0. */
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[6]->name);
EXPECT_EQ(5, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(2, arm.collection_array[0]->child_count);
EXPECT_EQ(3, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* Move r0_child0 to become the last child of root_0. */
EXPECT_EQ(6,
armature_bonecoll_move_to_parent(&arm,
2, /* From index. */
2, /* To child number. */
1, /* From parent. */
0 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[4]->name); /* Children root_0. */
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[5]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[6]->name);
EXPECT_EQ(4, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(3, arm.collection_array[0]->child_count);
EXPECT_EQ(2, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
/* Move r0_child1 to become the 3nd child of root_0. */
EXPECT_EQ(5,
armature_bonecoll_move_to_parent(&arm,
2, /* From index. */
2, /* To child number. */
1, /* From parent. */
0 /* To parent. */
));
ASSERT_EQ(2, arm.collection_root_count);
ASSERT_EQ(7, arm.collection_array_num);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[2]->name); /* Children root_1. */
EXPECT_STREQ(bcoll_r0_child3->name, arm.collection_array[3]->name); /* Children root_0. */
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[5]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[6]->name);
EXPECT_EQ(3, arm.collection_array[0]->child_index);
EXPECT_EQ(2, arm.collection_array[1]->child_index);
EXPECT_EQ(0, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
EXPECT_EQ(0, arm.collection_array[6]->child_index);
EXPECT_EQ(4, arm.collection_array[0]->child_count);
EXPECT_EQ(1, arm.collection_array[1]->child_count);
EXPECT_EQ(0, arm.collection_array[2]->child_count);
EXPECT_EQ(0, arm.collection_array[3]->child_count);
EXPECT_EQ(0, arm.collection_array[4]->child_count);
EXPECT_EQ(0, arm.collection_array[5]->child_count);
EXPECT_EQ(0, arm.collection_array[6]->child_count);
}
TEST_F(ArmatureBoneCollections, internal__bonecolls_rotate_block)
{
/* Set up a small hierarchy. */
BoneCollection *bcoll_root_0 = ANIM_armature_bonecoll_new(&arm, "root_0");
BoneCollection *bcoll_root_1 = ANIM_armature_bonecoll_new(&arm, "root_1");
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", 0);
BoneCollection *bcoll_r1_child0 = ANIM_armature_bonecoll_new(&arm, "r1_child0", 1);
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", 0);
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", 0);
/* The tests below compare the collection names, instead of their pointers, so
* that we get human-readable messages on failure. */
/* Unnecessary assertions, just to make it easier to understand in which order
* the array starts out. */
ASSERT_EQ(6, arm.collection_array_num);
ASSERT_STREQ(bcoll_root_0->name, arm.collection_array[0]->name);
ASSERT_STREQ(bcoll_root_1->name, arm.collection_array[1]->name);
ASSERT_STREQ(bcoll_r0_child0->name, arm.collection_array[2]->name);
ASSERT_STREQ(bcoll_r0_child1->name, arm.collection_array[3]->name);
ASSERT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
ASSERT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
ASSERT_EQ(2, arm.collection_array[0]->child_index);
ASSERT_EQ(5, arm.collection_array[1]->child_index);
ASSERT_EQ(0, arm.collection_array[2]->child_index);
ASSERT_EQ(0, arm.collection_array[3]->child_index);
ASSERT_EQ(0, arm.collection_array[4]->child_index);
ASSERT_EQ(0, arm.collection_array[5]->child_index);
/* Move [0,1,2] to [1,2,3]. */
internal::bonecolls_rotate_block(&arm, 0, 3, 1);
ASSERT_EQ(6, arm.collection_array_num) << "array size should not change";
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[5]->name);
EXPECT_EQ(0, arm.collection_array[0]->child_index);
EXPECT_EQ(3, arm.collection_array[1]->child_index);
EXPECT_EQ(5, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
/* Move [4,5] to [3,4]. */
internal::bonecolls_rotate_block(&arm, 4, 2, -1);
ASSERT_EQ(6, arm.collection_array_num) << "array size should not change";
EXPECT_STREQ(bcoll_r0_child1->name, arm.collection_array[0]->name);
EXPECT_STREQ(bcoll_root_0->name, arm.collection_array[1]->name);
EXPECT_STREQ(bcoll_root_1->name, arm.collection_array[2]->name);
EXPECT_STREQ(bcoll_r0_child2->name, arm.collection_array[3]->name);
EXPECT_STREQ(bcoll_r1_child0->name, arm.collection_array[4]->name);
EXPECT_STREQ(bcoll_r0_child0->name, arm.collection_array[5]->name);
EXPECT_EQ(0, arm.collection_array[0]->child_index);
EXPECT_EQ(3, arm.collection_array[1]->child_index);
EXPECT_EQ(4, arm.collection_array[2]->child_index);
EXPECT_EQ(0, arm.collection_array[3]->child_index);
EXPECT_EQ(0, arm.collection_array[4]->child_index);
EXPECT_EQ(0, arm.collection_array[5]->child_index);
}
class ArmatureBoneCollectionsTestList : public testing::Test {
protected:
bArmature arm;
BoneCollection *root;
BoneCollection *child0;
BoneCollection *child1;
BoneCollection *child2;
BoneCollection *child1_0;
void SetUp() override
{
memset(&arm, 0, sizeof(arm));
STRNCPY(arm.id.name, "ARArmature");
root = ANIM_armature_bonecoll_new(&arm, "root");
child0 = ANIM_armature_bonecoll_new(&arm, "child0", 0);
child1 = ANIM_armature_bonecoll_new(&arm, "child1", 0);
child2 = ANIM_armature_bonecoll_new(&arm, "child2", 0);
child1_0 = ANIM_armature_bonecoll_new(&arm, "child1_0", 2);
ASSERT_STREQ(root->name, arm.collection_array[0]->name);
ASSERT_STREQ(child0->name, arm.collection_array[1]->name);
ASSERT_STREQ(child1->name, arm.collection_array[2]->name);
ASSERT_STREQ(child2->name, arm.collection_array[3]->name);
ASSERT_STREQ(child1_0->name, arm.collection_array[4]->name);
}
void TearDown() override
{
BKE_idtype_init();
BKE_libblock_free_datablock(&arm.id, 0);
}
testing::AssertionResult expect_bcolls(std::vector<std::string> expect_names)
{
std::vector<std::string> actual_names;
for (const BoneCollection *bcoll : arm.collections_span()) {
actual_names.push_back(bcoll->name);
}
if (expect_names == actual_names) {
return testing::AssertionSuccess();
}
testing::AssertionResult failure = testing::AssertionFailure();
failure << "Expected bone collections differ from actual ones:" << std::endl;
/* This is what you get when C++ doesn't even have a standard library
* function to do something like `expect_names.join(", ")`. */
failure << "Expected collections: [";
for (int i = 0; i < expect_names.size() - 1; i++) {
failure << expect_names[i] << ", ";
}
failure << expect_names.back() << "]" << std::endl;
failure << "Actual collections : [";
for (int i = 0; i < actual_names.size() - 1; i++) {
failure << actual_names[i] << ", ";
}
failure << actual_names.back() << "]" << std::endl;
internal::bonecolls_debug_list(&arm);
return failure;
}
};
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__before_first_sibling)
{
/* Set the active index to be one of the affected bone collections. */
ANIM_armature_bonecoll_active_name_set(&arm, "child2");
ASSERT_EQ(3, arm.runtime.active_collection_index);
EXPECT_EQ(1, ANIM_armature_bonecoll_move_before_after_index(&arm, 3, 1, MoveLocation::Before));
EXPECT_TRUE(expect_bcolls({"root", "child2", "child0", "child1", "child1_0"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 1));
/* The three indicators of the active collection should still be in sync. */
EXPECT_EQ(1, arm.runtime.active_collection_index);
EXPECT_EQ(child2, arm.runtime.active_collection);
EXPECT_STREQ("child2", arm.active_collection_name);
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__after_first_sibling)
{
EXPECT_EQ(2, ANIM_armature_bonecoll_move_before_after_index(&arm, 3, 1, MoveLocation::After));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child2", "child1", "child1_0"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 2));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__before_last_sibling)
{
/* Set the active index to be one of the affected bone collections. */
ANIM_armature_bonecoll_active_name_set(&arm, "child1");
ASSERT_EQ(2, arm.runtime.active_collection_index);
EXPECT_EQ(2, ANIM_armature_bonecoll_move_before_after_index(&arm, 1, 3, MoveLocation::Before));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child0", "child2", "child1_0"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 2));
/* The three indicators of the active collection should still be in sync. */
EXPECT_EQ(1, arm.runtime.active_collection_index);
EXPECT_EQ(child1, arm.runtime.active_collection);
EXPECT_STREQ("child1", arm.active_collection_name);
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__after_last_sibling)
{
EXPECT_EQ(3, ANIM_armature_bonecoll_move_before_after_index(&arm, 1, 3, MoveLocation::After));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child2", "child0", "child1_0"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 3));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__other_parent_before__move_left)
{
EXPECT_EQ(1, ANIM_armature_bonecoll_move_before_after_index(&arm, 4, 1, MoveLocation::Before));
EXPECT_TRUE(expect_bcolls({"root", "child1_0", "child0", "child1", "child2"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 1));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__other_parent_after__move_left)
{
EXPECT_EQ(2, ANIM_armature_bonecoll_move_before_after_index(&arm, 4, 1, MoveLocation::After));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1_0", "child1", "child2"}));
EXPECT_EQ(0, armature_bonecoll_find_parent_index(&arm, 2));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__other_parent_before__move_right)
{
EXPECT_EQ(3, ANIM_armature_bonecoll_move_before_after_index(&arm, 1, 4, MoveLocation::Before));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child2", "child0", "child1_0"}));
EXPECT_EQ(1, armature_bonecoll_find_parent_index(&arm, 3));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__other_parent_after__move_right)
{
EXPECT_EQ(4, ANIM_armature_bonecoll_move_before_after_index(&arm, 1, 4, MoveLocation::After));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child2", "child1_0", "child0"}));
EXPECT_EQ(1, armature_bonecoll_find_parent_index(&arm, 4));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__to_root__before)
{
EXPECT_EQ(0, ANIM_armature_bonecoll_move_before_after_index(&arm, 4, 0, MoveLocation::Before));
EXPECT_TRUE(expect_bcolls({"child1_0", "root", "child0", "child1", "child2"}));
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, 0));
}
TEST_F(ArmatureBoneCollectionsTestList, move_before_after_index__to_root__after)
{
EXPECT_EQ(1, ANIM_armature_bonecoll_move_before_after_index(&arm, 4, 0, MoveLocation::After));
EXPECT_TRUE(expect_bcolls({"root", "child1_0", "child0", "child1", "child2"}));
EXPECT_EQ(-1, armature_bonecoll_find_parent_index(&arm, 1));
}
TEST_F(ArmatureBoneCollectionsTestList, child_number_set__roots)
{
/* Test with only one root. */
EXPECT_EQ(0, armature_bonecoll_child_number_set(&arm, root, 0));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Move to "after the last child", which is the one root itself. */
EXPECT_EQ(0, armature_bonecoll_child_number_set(&arm, root, -1));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
EXPECT_EQ(0, armature_bonecoll_child_number_set(&arm, root, 0));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Going beyond the number of children is not allowed. */
EXPECT_EQ(-1, armature_bonecoll_child_number_set(&arm, root, 1));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Add two roots to be able to play. */
ANIM_armature_bonecoll_new(&arm, "root1");
ANIM_armature_bonecoll_new(&arm, "root2");
EXPECT_TRUE(expect_bcolls({"root", "root1", "root2", "child0", "child1", "child2", "child1_0"}));
/* Move the old root in between the two new ones. */
EXPECT_EQ(1, armature_bonecoll_child_number_set(&arm, root, 1));
EXPECT_TRUE(expect_bcolls({"root1", "root", "root2", "child0", "child1", "child2", "child1_0"}));
/* And to the last one. */
EXPECT_EQ(2, armature_bonecoll_child_number_set(&arm, root, 2));
EXPECT_TRUE(expect_bcolls({"root1", "root2", "root", "child0", "child1", "child2", "child1_0"}));
}
TEST_F(ArmatureBoneCollectionsTestList, child_number_set__siblings)
{
/* Move child0 to itself. */
EXPECT_EQ(1, armature_bonecoll_child_number_set(&arm, child0, 0));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Move child2 to itself. */
EXPECT_EQ(3, armature_bonecoll_child_number_set(&arm, child2, -1));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Going beyond the number of children is not allowed. */
EXPECT_EQ(-1, armature_bonecoll_child_number_set(&arm, child0, 3));
EXPECT_TRUE(expect_bcolls({"root", "child0", "child1", "child2", "child1_0"}));
/* Move child0 to in between child1 and child2. */
EXPECT_EQ(2, armature_bonecoll_child_number_set(&arm, child0, 1));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child0", "child2", "child1_0"}));
/* Move child0 to the last spot. */
EXPECT_EQ(3, armature_bonecoll_child_number_set(&arm, child0, 2));
EXPECT_TRUE(expect_bcolls({"root", "child1", "child2", "child0", "child1_0"}));
}
TEST_F(ArmatureBoneCollectionsTestList, bone_collection_solo)
{
EXPECT_FALSE(arm.flag & ARM_BCOLL_SOLO_ACTIVE) << "By default no solo'ing should be active";
/* Enable solo. */
EXPECT_FALSE(child1->flags & BONE_COLLECTION_SOLO);
ANIM_armature_bonecoll_solo_set(&arm, child1, true);
EXPECT_TRUE(child1->flags & BONE_COLLECTION_SOLO);
EXPECT_TRUE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
/* Enable solo on another bone collection. */
EXPECT_FALSE(child1_0->flags & BONE_COLLECTION_SOLO);
ANIM_armature_bonecoll_solo_set(&arm, child1_0, true);
EXPECT_TRUE(child1_0->flags & BONE_COLLECTION_SOLO);
EXPECT_TRUE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
/* Disable the first solo flag. */
EXPECT_TRUE(child1->flags & BONE_COLLECTION_SOLO);
ANIM_armature_bonecoll_solo_set(&arm, child1, false);
EXPECT_FALSE(child1->flags & BONE_COLLECTION_SOLO);
EXPECT_TRUE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
/* Disable the second solo flag. This should also disable the ARM_BCOLL_SOLO_ACTIVE flag. */
EXPECT_TRUE(child1_0->flags & BONE_COLLECTION_SOLO);
ANIM_armature_bonecoll_solo_set(&arm, child1_0, false);
EXPECT_FALSE(child1_0->flags & BONE_COLLECTION_SOLO);
EXPECT_FALSE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
}
class ArmatureBoneCollectionsLiboverrides : public ArmatureBoneCollectionsTestList {
protected:
bArmature dst_arm;
BoneCollection *dst_root;
BoneCollection *dst_child0;
BoneCollection *dst_child1;
BoneCollection *dst_child2;
BoneCollection *dst_child1_0;
void SetUp() override
{
ArmatureBoneCollectionsTestList::SetUp();
/* TODO: make this clone `arm` into `dst_arm`, instead of assuming the below
* code is still in sync with the super-class. */
memset(&dst_arm, 0, sizeof(dst_arm));
STRNCPY(dst_arm.id.name, "ARArmatureDST");
dst_root = ANIM_armature_bonecoll_new(&dst_arm, "root");
dst_child0 = ANIM_armature_bonecoll_new(&dst_arm, "child0", 0);
dst_child1 = ANIM_armature_bonecoll_new(&dst_arm, "child1", 0);
dst_child2 = ANIM_armature_bonecoll_new(&dst_arm, "child2", 0);
dst_child1_0 = ANIM_armature_bonecoll_new(&dst_arm, "child1_0", 2);
ASSERT_STREQ(dst_root->name, dst_arm.collection_array[0]->name);
ASSERT_STREQ(dst_child0->name, dst_arm.collection_array[1]->name);
ASSERT_STREQ(dst_child1->name, dst_arm.collection_array[2]->name);
ASSERT_STREQ(dst_child2->name, dst_arm.collection_array[3]->name);
ASSERT_STREQ(dst_child1_0->name, dst_arm.collection_array[4]->name);
BKE_armature_bone_hash_make(&arm);
BKE_armature_bone_hash_make(&dst_arm);
}
void TearDown() override
{
ArmatureBoneCollectionsTestList::TearDown();
BKE_libblock_free_datablock(&dst_arm.id, 0);
}
};
TEST_F(ArmatureBoneCollectionsLiboverrides, bcoll_insert_copy_after)
{
/* Mimic that a new root, two children, and two grandchildren were added via library overrides.
* These were saved in `arm`, and now need to be copied into `dst_arm`. */
BoneCollection *src_root = ANIM_armature_bonecoll_new(&arm, "new_root");
const int root_index = armature_bonecoll_find_index(&arm, src_root);
BoneCollection *src_child1 = ANIM_armature_bonecoll_new(&arm, "new_child1", root_index);
ANIM_armature_bonecoll_new(&arm, "new_child2", root_index);
const int child1_index = armature_bonecoll_find_index(&arm, src_child1);
ANIM_armature_bonecoll_new(&arm, "new_gchild1", child1_index);
ANIM_armature_bonecoll_new(&arm, "new_gchild2", child1_index);
/* Copy the root. This should be the only change that's recorded by a library override operation.
* It should also copy the entire subtree of that root. */
const BoneCollection *anchor = dst_arm.collection_array[0];
ASSERT_STREQ("root", anchor->name);
BoneCollection *copy_root = ANIM_armature_bonecoll_insert_copy_after(
&dst_arm, &arm, anchor, src_root);
/* Check the array order. */
EXPECT_TRUE(expect_bcolls({"root",
"new_root",
"child0",
"child1",
"child2",
"child1_0",
"new_child1",
"new_child2",
"new_gchild1",
"new_gchild2"}));
/* Check that the copied root is actually stored in the destination armature array. */
const int new_root_index = armature_bonecoll_find_index(&dst_arm, copy_root);
EXPECT_EQ(1, new_root_index);
/* Check the hierarchy. */
const int new_child1_index = ANIM_armature_bonecoll_get_index_by_name(&dst_arm, "new_child1");
EXPECT_TRUE(armature_bonecoll_is_root(&dst_arm, new_root_index));
EXPECT_TRUE(armature_bonecoll_is_child_of(&dst_arm, new_root_index, new_child1_index));
EXPECT_TRUE(armature_bonecoll_is_child_of(
&dst_arm, new_root_index, ANIM_armature_bonecoll_get_index_by_name(&dst_arm, "new_child2")));
EXPECT_TRUE(armature_bonecoll_is_child_of(
&dst_arm,
new_child1_index,
ANIM_armature_bonecoll_get_index_by_name(&dst_arm, "new_gchild1")));
EXPECT_TRUE(armature_bonecoll_is_child_of(
&dst_arm,
new_child1_index,
ANIM_armature_bonecoll_get_index_by_name(&dst_arm, "new_gchild2")));
if (HasFailure()) {
internal::bonecolls_debug_list(&dst_arm);
}
}
} // namespace blender::animrig::tests