Blender V4.5
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 "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_collection.hh"
19#include "BKE_context.hh"
20#include "BKE_global.hh"
21#include "BKE_idprop.hh"
22#include "BKE_idtype.hh"
23#include "BKE_lib_id.hh"
24#include "BKE_lib_query.hh"
25#include "BKE_main.hh"
26#include "BKE_material.hh"
27#include "BKE_mesh.h"
28#include "BKE_node.hh"
29#include "BKE_object.hh"
30#include "BKE_scene.hh"
31
32#include "IMB_imbuf.hh"
33
34#include "ED_node.hh"
35
36namespace blender::bke::tests {
37
38class TestData {
39 public:
40 Main *bmain = nullptr;
41 bContext *C = nullptr;
42
44 {
45 if (this->bmain == nullptr) {
46 this->bmain = BKE_main_new();
47 G.main = this->bmain;
48 }
49
50 if (this->C == nullptr) {
51 this->C = CTX_create();
52 CTX_data_main_set(this->C, this->bmain);
53 }
54 }
55
57 {
58 if (this->bmain != nullptr) {
59 BKE_main_free(this->bmain);
60 this->bmain = nullptr;
61 G.main = nullptr;
62 }
63
64 if (this->C != nullptr) {
65 CTX_free(this->C);
66 this->C = nullptr;
67 }
68 }
69};
70
71class LibQueryTest : public ::testing::Test {
72
73 protected:
74 static void SetUpTestSuite()
75 {
76 CLG_init();
78 RNA_init();
81 IMB_init();
83 }
84
95};
96
97class WholeIDTestData : public TestData {
98 public:
99 Scene *scene = nullptr;
100 Object *object = nullptr;
101 Object *target = nullptr;
102 Mesh *mesh = nullptr;
103 Material *material = nullptr;
104
106 {
107 this->scene = BKE_scene_add(this->bmain, "IDLibQueryScene");
108 CTX_data_scene_set(this->C, this->scene);
109
110 this->object = BKE_object_add_only_object(this->bmain, OB_MESH, "IDLibQueryObject");
111 this->target = BKE_object_add_only_object(this->bmain, OB_EMPTY, "IDLibQueryTarget");
112
113 this->mesh = BKE_mesh_add(this->bmain, "IDLibQueryMesh");
114 this->object->data = this->mesh;
115
116 BKE_collection_object_add(this->bmain, this->scene->master_collection, this->object);
117 BKE_collection_object_add(this->bmain, this->scene->master_collection, this->target);
118 }
119};
120
122 public:
123 bNode *node = nullptr;
124
126 {
127 /* Add a material that contains an embedded nodetree and assign a custom property to one of
128 * its nodes. */
129 this->material = BKE_material_add(this->bmain, "Material");
130 ED_node_shader_default(this->C, &this->material->id);
131
133 this->bmain, this->object, this->material, this->object->actcol, BKE_MAT_ASSIGN_OBJECT);
134
135 this->node = static_cast<bNode *>(this->material->nodetree->nodes.first);
136
137 this->node->prop = bke::idprop::create_group("Node Custom Properties").release();
138 IDP_AddToGroup(this->node->prop,
139 bke::idprop::create("ID Pointer", &this->target->id).release());
140 }
142 {
143 BKE_id_free(this->bmain, &this->material->id);
144 }
145};
146
147/* -------------------------------------------------------------------- */
150
151TEST_F(LibQueryTest, libquery_basic)
152{
153 WholeIDTestData context;
154
155 ASSERT_NE(context.scene, nullptr);
156 ASSERT_NE(context.object, nullptr);
157 ASSERT_NE(context.target, nullptr);
158 ASSERT_NE(context.mesh, nullptr);
159
160 /* Reset all ID user-count to 0. */
161 ID *id_iter;
162 FOREACH_MAIN_ID_BEGIN (context.bmain, id_iter) {
163 id_iter->us = 0;
164 }
166
167 /* Set an invalid user-count value to IDs directly used by the scene.
168 * This includes these used by its embedded IDs, like the master collection, and the scene
169 itself
170 * (through the loop-back pointers of embedded IDs to their owner). */
171 auto set_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
172 if (*(cb_data->id_pointer)) {
173 (*(cb_data->id_pointer))->us = 42;
174 }
175 return IDWALK_RET_NOP;
176 };
178 context.bmain, &context.scene->id, set_count, nullptr, IDWALK_READONLY);
179 EXPECT_EQ(context.scene->id.us, 42);
180 EXPECT_EQ(context.object->id.us, 42);
181 EXPECT_EQ(context.target->id.us, 42);
182 EXPECT_EQ(context.mesh->id.us, 0);
183
184 /* Clear object's obdata mesh pointer. */
185 auto clear_mesh_pointer = [](LibraryIDLinkCallbackData *cb_data) -> int {
186 WholeIDTestData *test_data = static_cast<WholeIDTestData *>(cb_data->user_data);
187 if (*(cb_data->id_pointer) == &test_data->mesh->id) {
188 *(cb_data->id_pointer) = nullptr;
189 }
190 return IDWALK_RET_NOP;
191 };
193 context.bmain, &context.object->id, clear_mesh_pointer, &context, IDWALK_NOP);
194 EXPECT_EQ(context.object->data, nullptr);
195
196#if 0 /* Does not work. */
197 /* Modifying data when IDWALK_READONLY is set is forbidden. */
198 context.object->data = context.mesh;
199 EXPECT_BLI_ASSERT(BKE_library_foreach_ID_link(context.bmain,
200 &context.scene->id,
201 clear_mesh_pointer,
202 &context.test_data,
204 "");
205#endif
206}
207
208TEST_F(LibQueryTest, libquery_recursive)
209{
210 IDSubDataTestData context;
211
212 EXPECT_NE(context.scene, nullptr);
213 EXPECT_NE(context.object, nullptr);
214 EXPECT_NE(context.target, nullptr);
215 EXPECT_NE(context.mesh, nullptr);
216
217 /* Reset all ID user-count to 0. */
218 ID *id_iter;
219 FOREACH_MAIN_ID_BEGIN (context.bmain, id_iter) {
220 id_iter->us = 0;
221 }
223
224 /* Set an invalid user-count value to all IDs used by the scene, recursively.
225 * Here, it should mean all IDs in Main, including the scene itself
226 * (because of the loop-back pointer from the embedded master collection to its scene owner). */
227 auto set_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
228 if (*(cb_data->id_pointer)) {
229 (*(cb_data->id_pointer))->us = 42;
230 }
231 return IDWALK_RET_NOP;
232 };
234 context.bmain, &context.scene->id, set_count, nullptr, IDWALK_RECURSE);
235 FOREACH_MAIN_ID_BEGIN (context.bmain, id_iter) {
236 EXPECT_EQ(id_iter->us, 42);
237 }
239
240 /* Reset all ID user-count to 0. */
241 FOREACH_MAIN_ID_BEGIN (context.bmain, id_iter) {
242 id_iter->us = 0;
243 }
245
246 /* Recompute valid user counts for all IDs used by the scene, recursively. */
247 auto compute_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
248 if (*(cb_data->id_pointer) && (cb_data->cb_flag & IDWALK_CB_USER) != 0) {
249 (*(cb_data->id_pointer))->us++;
250 }
251 return IDWALK_RET_NOP;
252 };
254 context.bmain, &context.scene->id, compute_count, nullptr, IDWALK_RECURSE);
255 EXPECT_EQ(context.scene->id.us, 0);
256 EXPECT_EQ(context.object->id.us, 1);
257 /* Scene's master collection, and scene's compositor node IDProperty. Note that object constraint
258 * is _not_ a reference-counting usage. */
259 EXPECT_EQ(context.target->id.us, 2);
260 EXPECT_EQ(context.mesh->id.us, 1);
261}
262
263TEST_F(LibQueryTest, libquery_subdata)
264{
265 IDSubDataTestData context;
266
267 ASSERT_NE(context.scene, nullptr);
268 ASSERT_NE(context.object, nullptr);
269 ASSERT_NE(context.target, nullptr);
270 ASSERT_NE(context.mesh, nullptr);
271 ASSERT_NE(context.material, nullptr);
272
273 /* Reset all ID user-count to 0. */
274 ID *id_iter;
275 FOREACH_MAIN_ID_BEGIN (context.bmain, id_iter) {
276 id_iter->us = 0;
277 }
279
280 /* Set an invalid user-count value to all IDs used by one of the material's nodes. */
281 auto set_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
282 if (*(cb_data->id_pointer)) {
283 (*(cb_data->id_pointer))->us = 42;
284 }
285 return IDWALK_RET_NOP;
286 };
287 auto node_foreach_id = [&context](LibraryForeachIDData *data) {
288 bke::node_node_foreach_id(context.node, data);
289 };
290
291 BKE_library_foreach_subdata_id(context.bmain,
292 &context.material->id,
293 &context.material->nodetree->id,
295 set_count,
296 nullptr,
297 IDWALK_NOP);
298
299 EXPECT_EQ(context.scene->id.us, 0);
300 EXPECT_EQ(context.object->id.us, 0);
301 /* The material's node-tree input node IDProperty uses the target object. */
302 EXPECT_EQ(context.target->id.us, 42);
303 EXPECT_EQ(context.mesh->id.us, 0);
304}
305
307
308} // 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:725
void BKE_idtype_init()
Definition idtype.cc:122
void BKE_id_free(Main *bmain, void *idv)
@ 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 LibraryForeachIDFlag flag)
Definition lib_query.cc:451
void BKE_library_foreach_ID_link(Main *bmain, ID *id, blender::FunctionRef< LibraryIDLinkCallback > callback, void *user_data, LibraryForeachIDFlag flag)
Definition lib_query.cc:431
@ IDWALK_RET_NOP
@ IDWALK_RECURSE
@ IDWALK_NOP
@ IDWALK_READONLY
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:563
Main * BKE_main_new()
Definition main.cc:48
void BKE_main_free(Main *bmain)
Definition main.cc:175
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:557
General operations, lookup, etc. for materials.
void BKE_materials_init()
@ BKE_MAT_ASSIGN_OBJECT
Material * BKE_material_add(Main *bmain, const char *name)
void BKE_object_material_assign(Main *bmain, Object *ob, Material *ma, short act, int assign_type)
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
Scene * BKE_scene_add(Main *bmain, const char *name)
Definition scene.cc:2010
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
void CLG_exit(void)
Definition clog.c:704
void CLG_init(void)
Definition clog.c:697
Object is a sort of wrapper for general info.
@ OB_EMPTY
@ OB_MESH
void ED_node_shader_default(const bContext *C, ID *id)
Definition node_edit.cc:574
GHOST_TSuccess GHOST_DisposeSystemPaths()
void IMB_exit()
Definition module.cc:23
void IMB_init()
Definition module.cc:16
BMesh const char void * data
#define this
#define G(x, y, z)
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRef 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(StringRef prop_name, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_GROUP.
TEST_F(BKE_armature_find_selected_bones_test, some_bones_selected)
void node_node_foreach_id(bNode *node, LibraryForeachIDData *data)
Definition node.cc:352
void node_system_exit()
Definition node.cc:5620
static void node_foreach_id(ID *id, LibraryForeachIDData *data)
Definition node.cc:371
void node_system_init()
Definition node.cc:5615
void RNA_exit()
void RNA_init()
Definition rna_access.cc:80
Definition DNA_ID.h:404
int us
Definition DNA_ID.h:425
struct Collection * master_collection
IDProperty * prop