Blender V5.0
lib_remap_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4#include "testing/testing.h"
5
6#include "CLG_log.h"
7
8#include "GHOST_Path-api.hh"
9
10#include "DNA_material_types.h"
11#include "DNA_mesh_types.h"
12#include "DNA_node_types.h"
13#include "DNA_object_types.h"
14
15#include "RNA_define.hh"
16
17#include "BKE_appdir.hh"
18#include "BKE_context.hh"
19#include "BKE_global.hh"
20#include "BKE_idtype.hh"
21#include "BKE_lib_id.hh"
22#include "BKE_lib_remap.hh"
23#include "BKE_main.hh"
24#include "BKE_material.hh"
25#include "BKE_mesh.h"
26#include "BKE_node.hh"
27#include "BKE_object.hh"
28
29#include "IMB_imbuf.hh"
30
31#include "ED_node.hh"
32
33using namespace blender::bke::id;
34
35namespace blender::bke::tests {
36
37class TestData {
38 public:
39 Main *bmain = nullptr;
40 bContext *C = nullptr;
41
43 {
44 if (this->bmain == nullptr) {
45 this->bmain = BKE_main_new();
46 G.main = this->bmain;
47 }
48
49 if (this->C == nullptr) {
50 this->C = CTX_create();
51 CTX_data_main_set(this->C, this->bmain);
52 }
53 }
54
56 {
57 if (this->bmain != nullptr) {
58 BKE_main_free(this->bmain);
59 this->bmain = nullptr;
60 G.main = nullptr;
61 }
62
63 if (this->C != nullptr) {
64 CTX_free(C);
65 this->C = nullptr;
66 }
67 }
68};
69
70class LibRemapTest : public ::testing::Test {
71
72 protected:
73 static void SetUpTestSuite()
74 {
75 CLG_init();
77 RNA_init();
80 IMB_init();
82 }
83
94};
95
96class MaterialTestData : public TestData {
97 public:
98 Material *material = nullptr;
101 {
102 material = BKE_material_add(this->bmain, "Material");
103 ED_node_shader_default(this->C, this->bmain, &this->material->id);
104 this->material_nodetree = this->material->nodetree;
105 }
106};
107
108class MeshTestData : public TestData {
109 public:
110 Mesh *mesh = nullptr;
112 {
113 this->mesh = BKE_mesh_add(this->bmain, nullptr);
114 }
115};
116
118 public:
119 Mesh *other_mesh = nullptr;
120
122 {
123 this->other_mesh = BKE_mesh_add(this->bmain, nullptr);
124 }
125};
126
128 public:
131 {
132 this->object = BKE_object_add_only_object(this->bmain, OB_MESH, nullptr);
133 this->object->data = this->mesh;
134 }
135};
136
137/* -------------------------------------------------------------------- */
140
141TEST_F(LibRemapTest, embedded_ids_can_not_be_remapped)
142{
143 MaterialTestData context;
144 bNodeTree *other_tree = BKE_id_new_nomain<bNodeTree>(nullptr);
145
146 ASSERT_NE(context.material, nullptr);
147 ASSERT_EQ(context.material_nodetree, context.material->nodetree);
148
149 BKE_libblock_remap(context.bmain, context.material_nodetree, other_tree, 0);
150
151 EXPECT_EQ(context.material_nodetree, context.material->nodetree);
152 EXPECT_NE(context.material->nodetree, other_tree);
153
154 BKE_id_free(nullptr, other_tree);
155}
156
157TEST_F(LibRemapTest, embedded_ids_can_not_be_deleted)
158{
159 MaterialTestData context;
160
161 ASSERT_NE(context.material_nodetree, nullptr);
162 ASSERT_EQ(context.material_nodetree, context.material->nodetree);
163
165 context.bmain, context.material_nodetree, nullptr, ID_REMAP_SKIP_NEVER_NULL_USAGE);
166
167 EXPECT_EQ(context.material_nodetree, context.material->nodetree);
168 EXPECT_NE(context.material->nodetree, nullptr);
169}
170
172
173/* -------------------------------------------------------------------- */
176
177TEST_F(LibRemapTest, delete_when_remap_to_self_not_allowed)
178{
179 TwoMeshesTestData context;
180
181 ASSERT_NE(context.mesh, nullptr);
182 ASSERT_NE(context.other_mesh, nullptr);
183 context.mesh->texcomesh = context.other_mesh;
184
185 BKE_libblock_remap(context.bmain, context.other_mesh, context.mesh, 0);
186
187 EXPECT_EQ(context.mesh->texcomesh, nullptr);
188}
189
191
192/* -------------------------------------------------------------------- */
195
196TEST_F(LibRemapTest, users_are_decreased_when_not_skipping_never_null)
197{
198 MeshObjectTestData context;
199
200 ASSERT_NE(context.object, nullptr);
201 ASSERT_EQ(context.object->data, context.mesh);
202 ASSERT_EQ(context.object->id.tag & ID_TAG_DOIT, 0);
203 ASSERT_EQ(context.mesh->id.us, 1);
204
205 /* This is an invalid situation, test case tests this in between value until we have a better
206 * solution. */
207 BKE_libblock_remap(context.bmain, context.mesh, nullptr, 0);
208 EXPECT_EQ(context.mesh->id.us, 0);
209 EXPECT_EQ(context.object->data, context.mesh);
210 EXPECT_NE(context.object->data, nullptr);
211 EXPECT_EQ(context.object->id.tag & ID_TAG_DOIT, 0);
212}
213
214TEST_F(LibRemapTest, users_are_same_when_skipping_never_null)
215{
216 MeshObjectTestData context;
217
218 ASSERT_NE(context.object, nullptr);
219 ASSERT_EQ(context.object->data, context.mesh);
220 ASSERT_EQ(context.object->id.tag & ID_TAG_DOIT, 0);
221 ASSERT_EQ(context.mesh->id.us, 1);
222
223 BKE_libblock_remap(context.bmain, context.mesh, nullptr, ID_REMAP_SKIP_NEVER_NULL_USAGE);
224 EXPECT_EQ(context.mesh->id.us, 1);
225 EXPECT_EQ(context.object->data, context.mesh);
226 EXPECT_NE(context.object->data, nullptr);
227 EXPECT_EQ(context.object->id.tag & ID_TAG_DOIT, 0);
228}
229
231
232/* -------------------------------------------------------------------- */
235
236TEST_F(LibRemapTest, do_not_delete_when_cannot_unset)
237{
238 MeshObjectTestData context;
239
240 ASSERT_NE(context.object, nullptr);
241 ASSERT_EQ(context.object->data, context.mesh);
242
243 BKE_libblock_remap(context.bmain, context.mesh, nullptr, ID_REMAP_SKIP_NEVER_NULL_USAGE);
244 EXPECT_EQ(context.object->data, context.mesh);
245 EXPECT_NE(context.object->data, nullptr);
246}
247
248TEST_F(LibRemapTest, force_never_null_usage)
249{
250 MeshObjectTestData context;
251
252 ASSERT_NE(context.object, nullptr);
253 ASSERT_EQ(context.object->data, context.mesh);
254
255 BKE_libblock_remap(context.bmain, context.mesh, nullptr, ID_REMAP_FORCE_NEVER_NULL_USAGE);
256 EXPECT_EQ(context.object->data, nullptr);
257}
258
259TEST_F(LibRemapTest, never_null_usage_flag_not_requested_on_delete)
260{
261 MeshObjectTestData context;
262
263 ASSERT_NE(context.object, nullptr);
264 ASSERT_EQ(context.object->data, context.mesh);
265 ASSERT_EQ(context.object->id.tag & ID_TAG_DOIT, 0);
266
267 /* Never null usage isn't requested so the flag should not be set. */
268 BKE_libblock_remap(context.bmain, context.mesh, nullptr, ID_REMAP_SKIP_NEVER_NULL_USAGE);
269 EXPECT_EQ(context.object->data, context.mesh);
270 EXPECT_NE(context.object->data, nullptr);
271 EXPECT_EQ(context.object->id.tag & ID_TAG_DOIT, 0);
272}
273
274TEST_F(LibRemapTest, never_null_usage_storage_requested_on_delete)
275{
276 MeshObjectTestData context;
277
278 ASSERT_NE(context.object, nullptr);
279 ASSERT_EQ(context.object->data, context.mesh);
280 ASSERT_EQ(context.object->id.tag & ID_TAG_DOIT, 0);
281
282 /* Never null usage is requested so the owner ID (the Object) should be added to the set. */
283 IDRemapper remapper;
284 remapper.add(&context.mesh->id, nullptr);
287
288 /* Never null usages un-assignment is not enforced (no #ID_REMAP_FORCE_NEVER_NULL_USAGE),
289 * so the object-data should still use the original mesh. */
290 EXPECT_EQ(context.object->data, context.mesh);
291 EXPECT_NE(context.object->data, nullptr);
292 EXPECT_TRUE(remapper.never_null_users().contains(&context.object->id));
293}
294
295TEST_F(LibRemapTest, never_null_usage_flag_not_requested_on_remap)
296{
297 MeshObjectTestData context;
298 Mesh *other_mesh = BKE_mesh_add(context.bmain, nullptr);
299
300 ASSERT_NE(context.object, nullptr);
301 ASSERT_EQ(context.object->data, context.mesh);
302 ASSERT_EQ(context.object->id.tag & ID_TAG_DOIT, 0);
303
304 /* Never null usage isn't requested so the flag should not be set. */
305 BKE_libblock_remap(context.bmain, context.mesh, other_mesh, ID_REMAP_SKIP_NEVER_NULL_USAGE);
306 EXPECT_EQ(context.object->data, other_mesh);
307 EXPECT_EQ(context.object->id.tag & ID_TAG_DOIT, 0);
308}
309
310TEST_F(LibRemapTest, never_null_usage_storage_requested_on_remap)
311{
312 MeshObjectTestData context;
313 Mesh *other_mesh = BKE_mesh_add(context.bmain, nullptr);
314
315 ASSERT_NE(context.object, nullptr);
316 ASSERT_EQ(context.object->data, context.mesh);
317 ASSERT_EQ(context.object->id.tag & ID_TAG_DOIT, 0);
318
319 /* Never null usage is requested, but the obdata is remapped to another Mesh, not to `nullptr`,
320 * so the `never_null_users` set should remain empty. */
321 IDRemapper remapper;
322 remapper.add(&context.mesh->id, &other_mesh->id);
325 EXPECT_EQ(context.object->data, other_mesh);
326 EXPECT_TRUE(remapper.never_null_users().is_empty());
327}
328
330
331} // namespace blender::bke::tests
void BKE_appdir_init()
Definition appdir.cc:93
void BKE_appdir_exit()
Definition appdir.cc:101
void CTX_data_main_set(bContext *C, Main *bmain)
void CTX_free(bContext *C)
bContext * CTX_create()
void BKE_idtype_init()
Definition idtype.cc:121
void BKE_id_free(Main *bmain, void *idv)
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1519
void BKE_libblock_remap_multiple_locked(Main *bmain, blender::bke::id::IDRemapper &mappings, const int remap_flags)
Definition lib_remap.cc:655
void void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, int remap_flags) ATTR_NONNULL(1
@ ID_REMAP_SKIP_NEVER_NULL_USAGE
@ ID_REMAP_FORCE_NEVER_NULL_USAGE
@ ID_REMAP_STORE_NEVER_NULL_USAGE
Main * BKE_main_new()
Definition main.cc:89
void BKE_main_free(Main *bmain)
Definition main.cc:192
General operations, lookup, etc. for materials.
void BKE_materials_init()
Material * BKE_material_add(Main *bmain, const char *name)
void BKE_materials_exit()
Mesh * BKE_mesh_add(Main *bmain, const char *name)
General operations, lookup, etc. for blender objects.
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
void CLG_exit()
Definition clog.cc:880
void CLG_init()
Definition clog.cc:873
@ ID_TAG_DOIT
Definition DNA_ID.h:1036
Object is a sort of wrapper for general info.
@ OB_MESH
void ED_node_shader_default(const bContext *C, Main *bmain, ID *id)
Definition node_edit.cc:513
GHOST_TSuccess GHOST_DisposeSystemPaths()
void IMB_exit()
Definition module.cc:21
void IMB_init()
Definition module.cc:15
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool is_empty() const
Definition BLI_set.hh:595
void add(ID *old_id, ID *new_id)
const Set< ID * > & never_null_users() const
#define G(x, y, z)
TEST_F(ArmatureDeformTest, MeshDeform)
void node_system_exit()
Definition node.cc:5431
void node_system_init()
Definition node.cc:5426
void RNA_exit()
void RNA_init()
Definition rna_access.cc:88
struct bNodeTree * nodetree