Blender V5.0
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->bmain, &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 itself
169 * (through the loop-back pointers of embedded IDs to their owner). */
170 auto set_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
171 if (*(cb_data->id_pointer)) {
172 (*(cb_data->id_pointer))->us = 42;
173 }
174 return IDWALK_RET_NOP;
175 };
177 context.bmain, &context.scene->id, set_count, nullptr, IDWALK_READONLY);
178 EXPECT_EQ(context.scene->id.us, 42);
179 EXPECT_EQ(context.object->id.us, 42);
180 EXPECT_EQ(context.target->id.us, 42);
181 EXPECT_EQ(context.mesh->id.us, 0);
182
183 /* Clear object's obdata mesh pointer. */
184 auto clear_mesh_pointer = [](LibraryIDLinkCallbackData *cb_data) -> int {
185 WholeIDTestData *test_data = static_cast<WholeIDTestData *>(cb_data->user_data);
186 if (*(cb_data->id_pointer) == &test_data->mesh->id) {
187 *(cb_data->id_pointer) = nullptr;
188 }
189 return IDWALK_RET_NOP;
190 };
192 context.bmain, &context.object->id, clear_mesh_pointer, &context, IDWALK_NOP);
193 EXPECT_EQ(context.object->data, nullptr);
194
195#if 0 /* Does not work. */
196 /* Modifying data when IDWALK_READONLY is set is forbidden. */
197 context.object->data = context.mesh;
198 EXPECT_BLI_ASSERT(BKE_library_foreach_ID_link(context.bmain,
199 &context.scene->id,
200 clear_mesh_pointer,
201 &context.test_data,
203 "");
204#endif
205}
206
207TEST_F(LibQueryTest, libquery_recursive)
208{
209 IDSubDataTestData context;
210
211 EXPECT_NE(context.scene, nullptr);
212 EXPECT_NE(context.object, nullptr);
213 EXPECT_NE(context.target, nullptr);
214 EXPECT_NE(context.mesh, nullptr);
215
216 /* Reset all ID user-count to 0. */
217 ID *id_iter;
218 FOREACH_MAIN_ID_BEGIN (context.bmain, id_iter) {
219 id_iter->us = 0;
220 }
222
223 /* Set an invalid user-count value to all IDs used by the scene, recursively.
224 * Here, it should mean all IDs in Main, including the scene itself
225 * (because of the loop-back pointer from the embedded master collection to its scene owner). */
226 auto set_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
227 if (*(cb_data->id_pointer)) {
228 (*(cb_data->id_pointer))->us = 42;
229 }
230 return IDWALK_RET_NOP;
231 };
233 context.bmain, &context.scene->id, set_count, nullptr, IDWALK_RECURSE);
234 FOREACH_MAIN_ID_BEGIN (context.bmain, id_iter) {
235 EXPECT_EQ(id_iter->us, 42);
236 }
238
239 /* Reset all ID user-count to 0. */
240 FOREACH_MAIN_ID_BEGIN (context.bmain, id_iter) {
241 id_iter->us = 0;
242 }
244
245 /* Recompute valid user counts for all IDs used by the scene, recursively. */
246 auto compute_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
247 if (*(cb_data->id_pointer) && (cb_data->cb_flag & IDWALK_CB_USER) != 0) {
248 (*(cb_data->id_pointer))->us++;
249 }
250 return IDWALK_RET_NOP;
251 };
253 context.bmain, &context.scene->id, compute_count, nullptr, IDWALK_RECURSE);
254 EXPECT_EQ(context.scene->id.us, 0);
255 EXPECT_EQ(context.object->id.us, 1);
256 /* Scene's master collection, and scene's compositor node IDProperty. Note that object constraint
257 * is _not_ a reference-counting usage. */
258 EXPECT_EQ(context.target->id.us, 2);
259 EXPECT_EQ(context.mesh->id.us, 1);
260}
261
262TEST_F(LibQueryTest, libquery_subdata)
263{
264 IDSubDataTestData context;
265
266 ASSERT_NE(context.scene, nullptr);
267 ASSERT_NE(context.object, nullptr);
268 ASSERT_NE(context.target, nullptr);
269 ASSERT_NE(context.mesh, nullptr);
270 ASSERT_NE(context.material, nullptr);
271
272 /* Reset all ID user-count to 0. */
273 ID *id_iter;
274 FOREACH_MAIN_ID_BEGIN (context.bmain, id_iter) {
275 id_iter->us = 0;
276 }
278
279 /* Set an invalid user-count value to all IDs used by one of the material's nodes. */
280 auto set_count = [](LibraryIDLinkCallbackData *cb_data) -> int {
281 if (*(cb_data->id_pointer)) {
282 (*(cb_data->id_pointer))->us = 42;
283 }
284 return IDWALK_RET_NOP;
285 };
286 auto node_foreach_id = [&context](LibraryForeachIDData *data) {
287 bke::node_node_foreach_id(context.node, data);
288 };
289
290 BKE_library_foreach_subdata_id(context.bmain,
291 &context.material->id,
292 &context.material->nodetree->id,
294 set_count,
295 nullptr,
296 IDWALK_NOP);
297
298 EXPECT_EQ(context.scene->id.us, 0);
299 EXPECT_EQ(context.object->id.us, 0);
300 /* The material's node-tree input node IDProperty uses the target object. */
301 EXPECT_EQ(context.target->id.us, 42);
302 EXPECT_EQ(context.mesh->id.us, 0);
303}
304
306
307} // 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:717
void BKE_idtype_init()
Definition idtype.cc:121
void BKE_id_free(Main *bmain, void *idv)
@ 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 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_RECURSE
@ IDWALK_NOP
@ IDWALK_READONLY
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:583
Main * BKE_main_new()
Definition main.cc:89
void BKE_main_free(Main *bmain)
Definition main.cc:192
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:577
General operations, lookup, etc. for materials.
@ BKE_MAT_ASSIGN_OBJECT
void BKE_materials_init()
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:2001
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
Object is a sort of wrapper for general info.
@ OB_EMPTY
@ 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
BMesh const char void * data
#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(ArmatureDeformTest, MeshDeform)
void node_node_foreach_id(bNode *node, LibraryForeachIDData *data)
Definition node.cc:357
void node_system_exit()
Definition node.cc:5431
static void node_foreach_id(ID *id, LibraryForeachIDData *data)
Definition node.cc:381
void node_system_init()
Definition node.cc:5426
void RNA_exit()
void RNA_init()
Definition rna_access.cc:88
Definition DNA_ID.h:414
int us
Definition DNA_ID.h:443
struct Collection * master_collection
IDProperty * prop