Blender V4.3
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 "BLI_utildefines.h"
7
8#include "CLG_log.h"
9
10#include "GHOST_Path-api.hh"
11
12#include "DNA_mesh_types.h"
13#include "DNA_node_types.h"
14#include "DNA_object_types.h"
15#include "DNA_scene_types.h"
16
17#include "RNA_define.hh"
18
19#include "BKE_appdir.hh"
20#include "BKE_context.hh"
21#include "BKE_global.hh"
22#include "BKE_idtype.hh"
23#include "BKE_lib_id.hh"
24#include "BKE_lib_remap.hh"
25#include "BKE_main.hh"
26#include "BKE_mesh.hh"
27#include "BKE_node.hh"
28#include "BKE_object.hh"
29#include "BKE_scene.hh"
30
31#include "IMB_imbuf.hh"
32
33#include "ED_node.hh"
34
35#include "MEM_guardedalloc.h"
36
37using namespace blender::bke::id;
38
39namespace blender::bke::tests {
40
41class TestData {
42 public:
43 Main *bmain = nullptr;
44 bContext *C = nullptr;
45
46 virtual void setup()
47 {
48 if (bmain == nullptr) {
50 G.main = bmain;
51 }
52
53 if (C == nullptr) {
54 C = CTX_create();
56 }
57 }
58
59 virtual void teardown()
60 {
61 if (bmain != nullptr) {
63 bmain = nullptr;
64 G.main = nullptr;
65 }
66
67 if (C != nullptr) {
68 CTX_free(C);
69 C = nullptr;
70 }
71 }
72};
73
74class SceneTestData : public TestData {
75 public:
76 Scene *scene = nullptr;
77 void setup() override
78 {
80 scene = BKE_scene_add(bmain, "IDRemapScene");
81 CTX_data_scene_set(C, scene);
82 }
83};
84
86 public:
88 void setup() override
89 {
92 compositor_nodetree = scene->nodetree;
93 }
94};
95
96class MeshTestData : public TestData {
97 public:
98 Mesh *mesh = nullptr;
99
100 void setup() override
101 {
103 mesh = BKE_mesh_add(bmain, nullptr);
104 }
105};
106
108 public:
109 Mesh *other_mesh = nullptr;
110
111 void setup() override
112 {
114 other_mesh = BKE_mesh_add(bmain, nullptr);
115 }
116};
117
119 public:
121 void setup() override
122 {
124
125 object = BKE_object_add_only_object(bmain, OB_MESH, nullptr);
126 object->data = mesh;
127 }
128};
129
130template<typename TestData> class Context {
131 public:
133
135 {
136 CLG_init();
138 RNA_init();
141 IMB_init();
142
144 }
145
147 {
149
151 RNA_exit();
152 IMB_exit();
155 CLG_exit();
156 }
157};
158
159/* -------------------------------------------------------------------- */
163TEST(lib_remap, embedded_ids_can_not_be_remapped)
164{
166 bNodeTree *other_tree = static_cast<bNodeTree *>(BKE_id_new_nomain(ID_NT, nullptr));
167
168 EXPECT_NE(context.test_data.scene, nullptr);
169 EXPECT_NE(context.test_data.compositor_nodetree, nullptr);
170 EXPECT_EQ(context.test_data.compositor_nodetree, context.test_data.scene->nodetree);
171
173 context.test_data.bmain, context.test_data.compositor_nodetree, other_tree, 0);
174
175 EXPECT_EQ(context.test_data.compositor_nodetree, context.test_data.scene->nodetree);
176 EXPECT_NE(context.test_data.scene->nodetree, other_tree);
177
178 BKE_id_free(nullptr, other_tree);
179}
180
181TEST(lib_remap, embedded_ids_can_not_be_deleted)
182{
184
185 EXPECT_NE(context.test_data.scene, nullptr);
186 EXPECT_NE(context.test_data.compositor_nodetree, nullptr);
187 EXPECT_EQ(context.test_data.compositor_nodetree, context.test_data.scene->nodetree);
188
189 BKE_libblock_remap(context.test_data.bmain,
190 context.test_data.compositor_nodetree,
191 nullptr,
193
194 EXPECT_EQ(context.test_data.compositor_nodetree, context.test_data.scene->nodetree);
195 EXPECT_NE(context.test_data.scene->nodetree, nullptr);
196}
197
200/* -------------------------------------------------------------------- */
204TEST(lib_remap, delete_when_remap_to_self_not_allowed)
205{
207
208 EXPECT_NE(context.test_data.mesh, nullptr);
209 EXPECT_NE(context.test_data.other_mesh, nullptr);
210 context.test_data.mesh->texcomesh = context.test_data.other_mesh;
211
213 context.test_data.bmain, context.test_data.other_mesh, context.test_data.mesh, 0);
214
215 EXPECT_EQ(context.test_data.mesh->texcomesh, nullptr);
216}
217
220/* -------------------------------------------------------------------- */
224TEST(lib_remap, users_are_decreased_when_not_skipping_never_null)
225{
227
228 EXPECT_NE(context.test_data.object, nullptr);
229 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
230 EXPECT_EQ(context.test_data.object->id.tag & ID_TAG_DOIT, 0);
231 EXPECT_EQ(context.test_data.mesh->id.us, 1);
232
233 /* This is an invalid situation, test case tests this in between value until we have a better
234 * solution. */
235 BKE_libblock_remap(context.test_data.bmain, context.test_data.mesh, nullptr, 0);
236 EXPECT_EQ(context.test_data.mesh->id.us, 0);
237 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
238 EXPECT_NE(context.test_data.object->data, nullptr);
239 EXPECT_EQ(context.test_data.object->id.tag & ID_TAG_DOIT, 0);
240}
241
242TEST(lib_remap, users_are_same_when_skipping_never_null)
243{
245
246 EXPECT_NE(context.test_data.object, nullptr);
247 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
248 EXPECT_EQ(context.test_data.object->id.tag & ID_TAG_DOIT, 0);
249 EXPECT_EQ(context.test_data.mesh->id.us, 1);
250
252 context.test_data.bmain, context.test_data.mesh, nullptr, ID_REMAP_SKIP_NEVER_NULL_USAGE);
253 EXPECT_EQ(context.test_data.mesh->id.us, 1);
254 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
255 EXPECT_NE(context.test_data.object->data, nullptr);
256 EXPECT_EQ(context.test_data.object->id.tag & ID_TAG_DOIT, 0);
257}
258
261/* -------------------------------------------------------------------- */
265TEST(lib_remap, do_not_delete_when_cannot_unset)
266{
268
269 EXPECT_NE(context.test_data.object, nullptr);
270 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
271
273 context.test_data.bmain, context.test_data.mesh, nullptr, ID_REMAP_SKIP_NEVER_NULL_USAGE);
274 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
275 EXPECT_NE(context.test_data.object->data, nullptr);
276}
277
278TEST(lib_remap, force_never_null_usage)
279{
281
282 EXPECT_NE(context.test_data.object, nullptr);
283 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
284
286 context.test_data.bmain, context.test_data.mesh, nullptr, ID_REMAP_FORCE_NEVER_NULL_USAGE);
287 EXPECT_EQ(context.test_data.object->data, nullptr);
288}
289
290TEST(lib_remap, never_null_usage_flag_not_requested_on_delete)
291{
293
294 EXPECT_NE(context.test_data.object, nullptr);
295 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
296 EXPECT_EQ(context.test_data.object->id.tag & ID_TAG_DOIT, 0);
297
298 /* Never null usage isn't requested so the flag should not be set. */
300 context.test_data.bmain, context.test_data.mesh, nullptr, ID_REMAP_SKIP_NEVER_NULL_USAGE);
301 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
302 EXPECT_NE(context.test_data.object->data, nullptr);
303 EXPECT_EQ(context.test_data.object->id.tag & ID_TAG_DOIT, 0);
304}
305
306TEST(lib_remap, never_null_usage_storage_requested_on_delete)
307{
309
310 EXPECT_NE(context.test_data.object, nullptr);
311 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
312 EXPECT_EQ(context.test_data.object->id.tag & ID_TAG_DOIT, 0);
313
314 /* Never null usage is requested so the owner ID (the Object) should be added to the set. */
315 IDRemapper remapper;
316 remapper.add(&context.test_data.mesh->id, nullptr);
318 context.test_data.bmain,
319 remapper,
321
322 /* Never null usages un-assignment is not enforced (no #ID_REMAP_FORCE_NEVER_NULL_USAGE),
323 * so the object-data should still use the original mesh. */
324 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
325 EXPECT_NE(context.test_data.object->data, nullptr);
326 EXPECT_TRUE(remapper.never_null_users().contains(&context.test_data.object->id));
327}
328
329TEST(lib_remap, never_null_usage_flag_not_requested_on_remap)
330{
332 Mesh *other_mesh = BKE_mesh_add(context.test_data.bmain, nullptr);
333
334 EXPECT_NE(context.test_data.object, nullptr);
335 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
336 EXPECT_EQ(context.test_data.object->id.tag & ID_TAG_DOIT, 0);
337
338 /* Never null usage isn't requested so the flag should not be set. */
340 context.test_data.bmain, context.test_data.mesh, other_mesh, ID_REMAP_SKIP_NEVER_NULL_USAGE);
341 EXPECT_EQ(context.test_data.object->data, other_mesh);
342 EXPECT_EQ(context.test_data.object->id.tag & ID_TAG_DOIT, 0);
343}
344
345TEST(lib_remap, never_null_usage_storage_requested_on_remap)
346{
348 Mesh *other_mesh = BKE_mesh_add(context.test_data.bmain, nullptr);
349
350 EXPECT_NE(context.test_data.object, nullptr);
351 EXPECT_EQ(context.test_data.object->data, context.test_data.mesh);
352 EXPECT_EQ(context.test_data.object->id.tag & ID_TAG_DOIT, 0);
353
354 /* Never null usage is requested, but the obdata is remapped to another Mesh, not to `nullptr`,
355 * so the `never_null_users` set should remain empty. */
356 IDRemapper remapper;
357 remapper.add(&context.test_data.mesh->id, &other_mesh->id);
359 context.test_data.bmain,
360 remapper,
362 EXPECT_EQ(context.test_data.object->data, other_mesh);
363 EXPECT_TRUE(remapper.never_null_users().is_empty());
364}
365
368} // 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)
void CTX_data_scene_set(bContext *C, Scene *scene)
bContext * CTX_create()
void BKE_idtype_init()
Definition idtype.cc:127
void BKE_id_free(Main *bmain, void *idv)
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1487
void BKE_libblock_remap_multiple_locked(Main *bmain, blender::bke::id::IDRemapper &mappings, const int remap_flags)
Definition lib_remap.cc:645
@ ID_REMAP_SKIP_NEVER_NULL_USAGE
@ ID_REMAP_FORCE_NEVER_NULL_USAGE
@ ID_REMAP_STORE_NEVER_NULL_USAGE
void void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, int remap_flags) ATTR_NONNULL(1
Main * BKE_main_new(void)
Definition main.cc:45
void BKE_main_free(Main *bmain)
Definition main.cc:175
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
Scene * BKE_scene_add(Main *bmain, const char *name)
Definition scene.cc:1954
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
void CLG_exit(void)
Definition clog.c:706
void CLG_init(void)
Definition clog.c:699
@ ID_TAG_DOIT
Definition DNA_ID.h:1003
@ ID_NT
Object is a sort of wrapper for general info.
@ OB_MESH
void ED_node_composit_default(const bContext *C, Scene *scene)
Definition node_edit.cc:618
GHOST_TSuccess GHOST_DisposeSystemPaths()
void IMB_exit()
Definition module.cc:26
void IMB_init()
Definition module.cc:18
Read Guarded memory(de)allocation.
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool is_empty() const
Definition BLI_set.hh:572
void add(ID *old_id, ID *new_id)
const Set< ID * > & never_null_users() const
#define G(x, y, z)
TEST(action_groups, ReconstructGroupsWithReordering)
void node_system_exit()
Definition node.cc:4659
void node_system_init()
Definition node.cc:4649
void RNA_exit()
Definition rna_access.cc:98
void RNA_init()
Definition rna_access.cc:73