Use Blender Containers in the AbstractHierarchyIterator system. A future bug fix in the area is looking like it will require another map so it seems like a good time to replace the old containers before that lands. The conversion is complicated due to how the prior code leveraged `operator[]` to both add and update entries; it was never clear which operation was being performed. This leads to a somewhat confusing mix of `add_new`, `add`, and `lookup_or_add` calls. The calls in place now are what was required based on our existing tests. Additionally, pointer stability guarantees were a little bit different, affecting the usage of the `graph_children` function the most. Pull Request: https://projects.blender.org/blender/blender/pulls/134698
210 lines
7.4 KiB
C++
210 lines
7.4 KiB
C++
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
#include "IO_abstract_hierarchy_iterator.h"
|
|
|
|
#include "testing/testing.h"
|
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include <climits>
|
|
|
|
namespace blender::io {
|
|
|
|
namespace {
|
|
|
|
/* Return object pointer for use in tests. This makes it possible to reliably test for
|
|
* order/equality functions while using hard-coded values for simplicity. */
|
|
Object *fake_pointer(int value)
|
|
{
|
|
return static_cast<Object *>(POINTER_FROM_INT(value));
|
|
}
|
|
|
|
/* PersistentID subclass for use in tests, making it easier to construct test values. */
|
|
class TestPersistentID : public PersistentID {
|
|
public:
|
|
TestPersistentID(int value0,
|
|
int value1,
|
|
int value2,
|
|
int value3,
|
|
int value4,
|
|
int value5,
|
|
int value6,
|
|
int value7)
|
|
{
|
|
persistent_id_[0] = value0;
|
|
persistent_id_[1] = value1;
|
|
persistent_id_[2] = value2;
|
|
persistent_id_[3] = value3;
|
|
persistent_id_[4] = value4;
|
|
persistent_id_[5] = value5;
|
|
persistent_id_[6] = value6;
|
|
persistent_id_[7] = value7;
|
|
}
|
|
TestPersistentID(int value0, int value1, int value2)
|
|
: TestPersistentID(value0, value1, value2, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX)
|
|
{
|
|
}
|
|
TestPersistentID(int value0, int value1) : TestPersistentID(value0, value1, INT_MAX) {}
|
|
explicit TestPersistentID(int value0) : TestPersistentID(value0, INT_MAX) {}
|
|
};
|
|
|
|
/* ObjectIdentifier subclass for use in tests, making it easier to construct test values. */
|
|
class TestObjectIdentifier : public ObjectIdentifier {
|
|
public:
|
|
TestObjectIdentifier(Object *object, Object *duplicated_by, const PersistentID &persistent_id)
|
|
: ObjectIdentifier(object, duplicated_by, persistent_id)
|
|
{
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
class ObjectIdentifierOrderTest : public testing::Test {};
|
|
|
|
TEST_F(ObjectIdentifierOrderTest, graph_root)
|
|
{
|
|
ObjectIdentifier id_root_1 = ObjectIdentifier::for_graph_root();
|
|
ObjectIdentifier id_root_2 = ObjectIdentifier::for_graph_root();
|
|
EXPECT_TRUE(id_root_1 == id_root_2);
|
|
EXPECT_TRUE(id_root_1.hash() == id_root_2.hash());
|
|
|
|
ObjectIdentifier id_a = ObjectIdentifier::for_real_object(fake_pointer(1));
|
|
EXPECT_FALSE(id_root_1 == id_a);
|
|
|
|
ObjectIdentifier id_accidental_root = ObjectIdentifier::for_real_object(nullptr);
|
|
EXPECT_TRUE(id_root_1 == id_accidental_root);
|
|
EXPECT_TRUE(id_root_1.hash() == id_accidental_root.hash());
|
|
}
|
|
|
|
TEST_F(ObjectIdentifierOrderTest, real_objects)
|
|
{
|
|
ObjectIdentifier id_a = ObjectIdentifier::for_real_object(fake_pointer(1));
|
|
ObjectIdentifier id_b = ObjectIdentifier::for_real_object(fake_pointer(2));
|
|
EXPECT_FALSE(id_a == id_b);
|
|
|
|
ObjectIdentifier id_c = ObjectIdentifier::for_real_object(fake_pointer(1));
|
|
EXPECT_TRUE(id_a == id_c);
|
|
EXPECT_TRUE(id_a.hash() == id_c.hash());
|
|
}
|
|
|
|
TEST_F(ObjectIdentifierOrderTest, duplicated_objects)
|
|
{
|
|
ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1));
|
|
TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0));
|
|
TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
|
|
TestObjectIdentifier id_same_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0));
|
|
TestObjectIdentifier id_different_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(1));
|
|
|
|
EXPECT_FALSE(id_real_a == id_dupli_a);
|
|
EXPECT_FALSE(id_dupli_a == id_dupli_b);
|
|
|
|
EXPECT_FALSE(id_dupli_b == id_different_dupli_b);
|
|
EXPECT_FALSE(id_dupli_a == id_different_dupli_b);
|
|
|
|
EXPECT_TRUE(id_dupli_a == id_same_dupli_a);
|
|
EXPECT_TRUE(id_dupli_a.hash() == id_same_dupli_a.hash());
|
|
}
|
|
|
|
TEST_F(ObjectIdentifierOrderTest, behavior_as_map_keys)
|
|
{
|
|
ObjectIdentifier id_root = ObjectIdentifier::for_graph_root();
|
|
ObjectIdentifier id_another_root = ObjectIdentifier::for_graph_root();
|
|
ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1));
|
|
TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0));
|
|
TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
|
|
AbstractHierarchyIterator::ExportGraph graph;
|
|
|
|
/* This inserts the keys with default values. */
|
|
graph.add_new(id_root, {});
|
|
graph.add_new(id_real_a, {});
|
|
graph.add_new(id_dupli_a, {});
|
|
graph.add_new(id_dupli_b, {});
|
|
graph.add(id_another_root, {});
|
|
|
|
EXPECT_EQ(4, graph.size());
|
|
|
|
graph.remove_contained(id_another_root);
|
|
EXPECT_EQ(3, graph.size());
|
|
|
|
TestObjectIdentifier id_another_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
|
|
graph.remove_contained(id_another_dupli_b);
|
|
EXPECT_EQ(2, graph.size());
|
|
}
|
|
|
|
TEST_F(ObjectIdentifierOrderTest, map_copy_and_update)
|
|
{
|
|
ObjectIdentifier id_root = ObjectIdentifier::for_graph_root();
|
|
ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1));
|
|
TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0));
|
|
TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0));
|
|
TestObjectIdentifier id_dupli_c(fake_pointer(1), fake_pointer(3), TestPersistentID(1));
|
|
AbstractHierarchyIterator::ExportGraph graph;
|
|
|
|
/* This inserts the keys with default values. */
|
|
graph.add_new(id_root, {});
|
|
graph.add_new(id_real_a, {});
|
|
graph.add_new(id_dupli_a, {});
|
|
graph.add_new(id_dupli_b, {});
|
|
graph.add_new(id_dupli_c, {});
|
|
EXPECT_EQ(5, graph.size());
|
|
|
|
AbstractHierarchyIterator::ExportGraph graph_copy = graph;
|
|
EXPECT_EQ(5, graph_copy.size());
|
|
|
|
/* Updating a value in a copy should not update the original. */
|
|
HierarchyContext ctx1;
|
|
HierarchyContext ctx2;
|
|
ctx1.object = fake_pointer(1);
|
|
ctx2.object = fake_pointer(2);
|
|
|
|
graph_copy.lookup(id_root).add_new(&ctx1);
|
|
EXPECT_EQ(0, graph.lookup(id_root).size());
|
|
|
|
/* Deleting a key in the copy should not update the original. */
|
|
graph_copy.remove_contained(id_dupli_c);
|
|
EXPECT_EQ(4, graph_copy.size());
|
|
EXPECT_EQ(5, graph.size());
|
|
}
|
|
|
|
class PersistentIDTest : public testing::Test {};
|
|
|
|
TEST_F(PersistentIDTest, is_from_same_instancer)
|
|
{
|
|
PersistentID child_id_a = TestPersistentID(42, 327);
|
|
PersistentID child_id_b = TestPersistentID(17, 327);
|
|
PersistentID child_id_c = TestPersistentID(17);
|
|
|
|
EXPECT_TRUE(child_id_a.is_from_same_instancer_as(child_id_b));
|
|
EXPECT_FALSE(child_id_a.is_from_same_instancer_as(child_id_c));
|
|
}
|
|
|
|
TEST_F(PersistentIDTest, instancer_id)
|
|
{
|
|
PersistentID child_id = TestPersistentID(42, 327);
|
|
|
|
PersistentID expect_instancer_id = TestPersistentID(327);
|
|
EXPECT_EQ(expect_instancer_id, child_id.instancer_pid());
|
|
EXPECT_EQ(expect_instancer_id.hash(), child_id.instancer_pid().hash());
|
|
|
|
PersistentID empty_id;
|
|
EXPECT_EQ(empty_id, child_id.instancer_pid().instancer_pid());
|
|
EXPECT_EQ(empty_id.hash(), child_id.instancer_pid().instancer_pid().hash());
|
|
}
|
|
|
|
TEST_F(PersistentIDTest, as_object_name_suffix)
|
|
{
|
|
EXPECT_EQ("", PersistentID().as_object_name_suffix());
|
|
EXPECT_EQ("47", TestPersistentID(47).as_object_name_suffix());
|
|
EXPECT_EQ("327-47", TestPersistentID(47, 327).as_object_name_suffix());
|
|
EXPECT_EQ("42-327-47", TestPersistentID(47, 327, 42).as_object_name_suffix());
|
|
|
|
EXPECT_EQ("7-6-5-4-3-2-1-0", TestPersistentID(0, 1, 2, 3, 4, 5, 6, 7).as_object_name_suffix());
|
|
|
|
EXPECT_EQ("0-0-0", TestPersistentID(0, 0, 0).as_object_name_suffix());
|
|
EXPECT_EQ("0-0", TestPersistentID(0, 0).as_object_name_suffix());
|
|
EXPECT_EQ("-3--2--1", TestPersistentID(-1, -2, -3).as_object_name_suffix());
|
|
}
|
|
|
|
} // namespace blender::io
|