Blender V4.3
lib_query_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
13#include "DNA_mesh_types.h"
14#include "DNA_node_types.h"
15#include "DNA_object_types.h"
16#include "DNA_scene_types.h"
17
18#include "RNA_define.hh"
19
20#include "BKE_appdir.hh"
21#include "BKE_collection.hh"
22#include "BKE_constraint.h"
23#include "BKE_context.hh"
24#include "BKE_global.hh"
25#include "BKE_idprop.hh"
26#include "BKE_idtype.hh"
27#include "BKE_lib_id.hh"
28#include "BKE_lib_query.hh"
29#include "BKE_main.hh"
30#include "BKE_mesh.hh"
31#include "BKE_node.hh"
32#include "BKE_object.hh"
33#include "BKE_scene.hh"
34
35#include "IMB_imbuf.hh"
36
37#include "ED_node.hh"
38
39#include "MEM_guardedalloc.h"
40
41namespace blender::bke::tests {
42
43class TestData {
44 public:
45 Main *bmain = nullptr;
46 bContext *C = nullptr;
47
48 virtual void setup()
49 {
50 if (this->bmain == nullptr) {
51 this->bmain = BKE_main_new();
52 }
53 G.main = this->bmain;
54
55 if (this->C == nullptr) {
56 this->C = CTX_create();
57 CTX_data_main_set(this->C, bmain);
58 }
59 }
60
61 virtual void teardown()
62 {
63 if (this->C != nullptr) {
64 CTX_free(this->C);
65 this->C = nullptr;
66 }
67
68 G.main = nullptr;
69 if (this->bmain != nullptr) {
70 BKE_main_free(this->bmain);
71 this->bmain = nullptr;
72 }
73 }
74};
75
76class WholeIDTestData : public TestData {
77 public:
78 Scene *scene = nullptr;
79 Object *object = nullptr;
80 Object *target = nullptr;
81 Mesh *mesh = nullptr;
82
83 void setup() override
84 {
86
87 this->scene = BKE_scene_add(this->bmain, "IDLibQueryScene");
88 CTX_data_scene_set(this->C, this->scene);
89
90 this->object = BKE_object_add_only_object(this->bmain, OB_MESH, "IDLibQueryObject");
91 this->target = BKE_object_add_only_object(this->bmain, OB_EMPTY, "IDLibQueryTarget");
92
93 this->mesh = BKE_mesh_add(this->bmain, "IDLibQueryMesh");
94 this->object->data = this->mesh;
95
96 BKE_collection_object_add(this->bmain, this->scene->master_collection, this->object);
97 BKE_collection_object_add(this->bmain, this->scene->master_collection, this->target);
98 }
99};
100
102 public:
104 bNode *node = nullptr;
105
106 void setup() override
107 {
109
110 /* Add a default Compositor nodetree to the scene, and an ID pointer custom property to one of
111 * its nodes. */
112 ED_node_composit_default(C, scene);
113 this->compositor_nodetree = scene->nodetree;
114 this->node = static_cast<bNode *>(compositor_nodetree->nodes.first);
115
116 this->node->prop = bke::idprop::create_group("Node Custom Properties").release();
117 IDP_AddToGroup(this->node->prop,
118 bke::idprop::create("ID Pointer", &this->target->id).release());
119 }
120};
121
122template<typename TestData> class Context {
123 public:
125
127 {
128 CLG_init();
130 RNA_init();
133 IMB_init();
134
136 }
137
139 {
141
143 RNA_exit();
144 IMB_exit();
147 CLG_exit();
148 }
149};
150
151/* -------------------------------------------------------------------- */
155TEST(lib_query, libquery_basic)
156{
158
159 EXPECT_NE(context.test_data.scene, nullptr);
160 EXPECT_NE(context.test_data.object, nullptr);
161 EXPECT_NE(context.test_data.target, nullptr);
162 EXPECT_NE(context.test_data.mesh, nullptr);
163
164 /* Reset all ID user-count to 0. */
165 ID *id_iter;
166 FOREACH_MAIN_ID_BEGIN (context.test_data.bmain, id_iter) {
167 id_iter->us = 0;
168 }
170
171 /* Set an invalid user-count value to IDs directly used by the scene.
172 * This includes these used by its embedded IDs, like the master collection, and the scene itself
173 * (through the loop-back pointers of embedded IDs to their owner). */
174 auto set_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
175 if (*(cb_data->id_pointer)) {
176 (*(cb_data->id_pointer))->us = 42;
177 }
178 return IDWALK_RET_NOP;
179 };
181 context.test_data.bmain, &context.test_data.scene->id, set_count, nullptr, IDWALK_READONLY);
182 EXPECT_EQ(context.test_data.scene->id.us, 42);
183 EXPECT_EQ(context.test_data.object->id.us, 42);
184 EXPECT_EQ(context.test_data.target->id.us, 42);
185 EXPECT_EQ(context.test_data.mesh->id.us, 0);
186
187 /* Clear object's obdata mesh pointer. */
188 auto clear_mesh_pointer = [](LibraryIDLinkCallbackData *cb_data) -> int {
189 WholeIDTestData *test_data = static_cast<WholeIDTestData *>(cb_data->user_data);
190 if (*(cb_data->id_pointer) == &test_data->mesh->id) {
191 *(cb_data->id_pointer) = nullptr;
192 }
193 return IDWALK_RET_NOP;
194 };
195 BKE_library_foreach_ID_link(context.test_data.bmain,
196 &context.test_data.object->id,
197 clear_mesh_pointer,
198 &context.test_data,
199 IDWALK_NOP);
200 EXPECT_EQ(context.test_data.object->data, nullptr);
201
202#if 0 /* Does not work. */
203 /* Modifying data when IDWALK_READONLY is set is forbidden. */
204 context.test_data.object->data = context.test_data.mesh;
205 EXPECT_BLI_ASSERT(BKE_library_foreach_ID_link(context.test_data.bmain,
206 &context.test_data.scene->id,
207 clear_mesh_pointer,
208 &context.test_data,
210 "");
211#endif
212}
213
214TEST(lib_query, libquery_recursive)
215{
217
218 EXPECT_NE(context.test_data.scene, nullptr);
219 EXPECT_NE(context.test_data.object, nullptr);
220 EXPECT_NE(context.test_data.target, nullptr);
221 EXPECT_NE(context.test_data.mesh, nullptr);
222
223 /* Reset all ID user-count to 0. */
224 ID *id_iter;
225 FOREACH_MAIN_ID_BEGIN (context.test_data.bmain, id_iter) {
226 id_iter->us = 0;
227 }
229
230 /* Set an invalid user-count value to all IDs used by the scene, recursively.
231 * Here, it should mean all IDs in Main, including the scene itself
232 * (because of the loop-back pointer from the embedded master collection to its scene owner). */
233 auto set_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
234 if (*(cb_data->id_pointer)) {
235 (*(cb_data->id_pointer))->us = 42;
236 }
237 return IDWALK_RET_NOP;
238 };
240 context.test_data.bmain, &context.test_data.scene->id, set_count, nullptr, IDWALK_RECURSE);
241 FOREACH_MAIN_ID_BEGIN (context.test_data.bmain, id_iter) {
242 EXPECT_EQ(id_iter->us, 42);
243 }
245
246 /* Reset all ID user-count to 0. */
247 FOREACH_MAIN_ID_BEGIN (context.test_data.bmain, id_iter) {
248 id_iter->us = 0;
249 }
251
252 /* Recompute valid user counts for all IDs used by the scene, recursively. */
253 auto compute_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
254 if (*(cb_data->id_pointer) && (cb_data->cb_flag & IDWALK_CB_USER) != 0) {
255 (*(cb_data->id_pointer))->us++;
256 }
257 return IDWALK_RET_NOP;
258 };
259 BKE_library_foreach_ID_link(context.test_data.bmain,
260 &context.test_data.scene->id,
261 compute_count,
262 nullptr,
264 /* The render layer output node of compositing node-tree uses the scene. */
265 EXPECT_EQ(context.test_data.scene->id.us, 1);
266 EXPECT_EQ(context.test_data.object->id.us, 1);
267 /* Scene's master collection, and scene's compositor node IDProperty. Note that object constraint
268 * is _not_ a reference-counting usage. */
269 EXPECT_EQ(context.test_data.target->id.us, 2);
270 EXPECT_EQ(context.test_data.mesh->id.us, 1);
271}
272
273TEST(lib_query, libquery_subdata)
274{
276
277 EXPECT_NE(context.test_data.scene, nullptr);
278 EXPECT_NE(context.test_data.object, nullptr);
279 EXPECT_NE(context.test_data.target, nullptr);
280 EXPECT_NE(context.test_data.mesh, nullptr);
281
282 /* Reset all ID user-count to 0. */
283 ID *id_iter;
284 FOREACH_MAIN_ID_BEGIN (context.test_data.bmain, id_iter) {
285 id_iter->us = 0;
286 }
288
289 /* Set an invalid user-count value to all IDs used by one of the scene's compositor nodes. */
290 auto set_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
291 if (*(cb_data->id_pointer)) {
292 (*(cb_data->id_pointer))->us = 42;
293 }
294 return IDWALK_RET_NOP;
295 };
296 auto node_foreach_id = [&context](LibraryForeachIDData *data) {
297 bke::node_node_foreach_id(context.test_data.node, data);
298 };
299
300 BKE_library_foreach_subdata_id(context.test_data.bmain,
301 &context.test_data.scene->id,
302 &context.test_data.scene->nodetree->id,
304 set_count,
305 nullptr,
306 IDWALK_NOP);
307
308 EXPECT_EQ(context.test_data.scene->id.us, 0);
309 EXPECT_EQ(context.test_data.object->id.us, 0);
310 /* The scene's compositor input node IDProperty uses the target object. */
311 EXPECT_EQ(context.test_data.target->id.us, 42);
312 EXPECT_EQ(context.test_data.mesh->id.us, 0);
313}
314
317} // namespace blender::bke::tests
void BKE_appdir_init()
Definition appdir.cc:93
void BKE_appdir_exit()
Definition appdir.cc:101
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
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()
bool IDP_AddToGroup(IDProperty *group, IDProperty *prop) ATTR_NONNULL()
Definition idprop.cc:722
void BKE_idtype_init()
Definition idtype.cc:127
@ IDWALK_RET_NOP
@ IDWALK_CB_USER
void BKE_library_foreach_subdata_id(Main *bmain, ID *owner_id, ID *self_id, blender::FunctionRef< void(LibraryForeachIDData *data)> subdata_foreach_id, blender::FunctionRef< LibraryIDLinkCallback > callback, void *user_data, const int flag)
Definition lib_query.cc:436
void BKE_library_foreach_ID_link(Main *bmain, ID *id, blender::FunctionRef< LibraryIDLinkCallback > callback, void *user_data, int flag)
Definition lib_query.cc:416
@ IDWALK_RECURSE
@ IDWALK_NOP
@ IDWALK_READONLY
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:500
Main * BKE_main_new(void)
Definition main.cc:45
void BKE_main_free(Main *bmain)
Definition main.cc:175
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:494
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
Object is a sort of wrapper for general info.
@ OB_EMPTY
@ 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.
#define G(x, y, z)
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_INT, set its name and value.
std::unique_ptr< IDProperty, IDPropertyDeleter > create_group(StringRefNull prop_name, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_GROUP.
TEST(action_groups, ReconstructGroupsWithReordering)
void node_node_foreach_id(bNode *node, LibraryForeachIDData *data)
Definition node.cc:378
void node_system_exit()
Definition node.cc:4659
static void node_foreach_id(ID *id, LibraryForeachIDData *data)
Definition node.cc:397
void node_system_init()
Definition node.cc:4649
void RNA_exit()
Definition rna_access.cc:98
void RNA_init()
Definition rna_access.cc:73
Definition DNA_ID.h:413
int us
Definition DNA_ID.h:435
void * first
struct Collection * master_collection
ListBase nodes
IDProperty * prop