Blender V4.3
main_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4#include "testing/testing.h"
5
6#include "MEM_guardedalloc.h"
7
8#include "CLG_log.h"
9
10#include "BLI_listbase.h"
11#include "BLI_path_utils.hh"
12#include "BLI_string.h"
13
14#include "BKE_collection.hh"
15#include "BKE_idtype.hh"
16#include "BKE_lib_id.hh"
17#include "BKE_library.hh"
18#include "BKE_main.hh"
19
20#include "DNA_ID.h"
22#include "DNA_object_types.h"
23
24namespace blender::bke::tests {
25
26class BMainTest : public testing::Test {
27 public:
28 static void SetUpTestSuite()
29 {
30 CLG_init();
32 }
33 static void TearDownTestSuite()
34 {
35 CLG_exit();
36 }
37};
38
39class BMainMergeTest : public BMainTest {
40 public:
41 void SetUp() override
42 {
45 }
46
47 void TearDown() override
48 {
49 if (bmain_src) {
51 }
52 if (bmain_dst) {
54 }
55 }
56
59};
60
62{
63 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_dst->libraries));
64 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_dst->collections));
65 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_dst->objects));
66
67 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_src->libraries));
68 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_src->collections));
69 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_src->objects));
70
71 BKE_id_new(bmain_dst, ID_GR, "Coll_dst");
72 Collection *coll = static_cast<Collection *>(BKE_id_new(bmain_src, ID_GR, "Coll_src"));
73 Object *ob = static_cast<Object *>(BKE_id_new(bmain_src, ID_OB, "Ob_src"));
74 BKE_collection_object_add(bmain_src, coll, ob);
75
76 EXPECT_EQ(1, BLI_listbase_count(&bmain_dst->collections));
77 EXPECT_EQ(0, BLI_listbase_count(&bmain_dst->objects));
78 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->collections));
79 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->objects));
80
81 MainMergeReport reports = {};
82 BKE_main_merge(bmain_dst, &bmain_src, reports);
83
84 EXPECT_EQ(2, BLI_listbase_count(&bmain_dst->collections));
85 EXPECT_EQ(1, BLI_listbase_count(&bmain_dst->objects));
86 EXPECT_EQ(2, reports.num_merged_ids);
87 EXPECT_EQ(0, reports.num_unknown_ids);
88 EXPECT_EQ(0, reports.num_remapped_ids);
90 EXPECT_EQ(nullptr, bmain_src);
91
92 bmain_src = BKE_main_new();
93 Collection *coll_2 = static_cast<Collection *>(BKE_id_new(bmain_src, ID_GR, "Coll_src_2"));
94 Object *ob_2 = static_cast<Object *>(BKE_id_new(bmain_src, ID_OB, "Ob_src"));
95 BKE_collection_object_add(bmain_src, coll_2, ob_2);
96
97 EXPECT_EQ(2, BLI_listbase_count(&bmain_dst->collections));
98 EXPECT_EQ(1, BLI_listbase_count(&bmain_dst->objects));
99 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->collections));
100 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->objects));
101
102 reports = {};
103 BKE_main_merge(bmain_dst, &bmain_src, reports);
104
105 /* The second `Ob_src` object in `bmain_src` cannot be merged in `bmain_dst`, since its name
106 * would collide with the first object. */
107 EXPECT_EQ(3, BLI_listbase_count(&bmain_dst->collections));
108 EXPECT_EQ(1, BLI_listbase_count(&bmain_dst->objects));
109 EXPECT_EQ(1, reports.num_merged_ids);
110 EXPECT_EQ(0, reports.num_unknown_ids);
111 EXPECT_EQ(1, reports.num_remapped_ids);
113 EXPECT_EQ(nullptr, bmain_src);
114
115 /* `Coll_src_2` should have been remapped to using `Ob_src` in `bmain_dst`, instead of `Ob_src`
116 * in `bmain_src`. */
118 EXPECT_EQ(ob, static_cast<CollectionObject *>(coll_2->gobject.first)->ob);
119}
120
122{
123#ifdef WIN32
124# define ABS_ROOT "C:" SEP_STR
125#else
126# define ABS_ROOT SEP_STR
127#endif
128 constexpr char DST_PATH[] = ABS_ROOT "tmp" SEP_STR "dst" SEP_STR "dst.blend";
129 constexpr char SRC_PATH[] = ABS_ROOT "tmp" SEP_STR "src" SEP_STR "src.blend";
130 constexpr char LIB_PATH[] = ABS_ROOT "tmp" SEP_STR "lib" SEP_STR "lib.blend";
131
132 constexpr char LIB_PATH_RELATIVE[] = "//lib" SEP_STR "lib.blend";
133 constexpr char LIB_PATH_RELATIVE_ABS_SRC[] = ABS_ROOT "tmp" SEP_STR "src" SEP_STR "lib" SEP_STR
134 "lib.blend";
135
136 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_dst->libraries));
137 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_dst->collections));
138 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_dst->objects));
139
140 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_src->libraries));
141 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_src->collections));
142 EXPECT_TRUE(BLI_listbase_is_empty(&bmain_src->objects));
143
144 STRNCPY(bmain_dst->filepath, DST_PATH);
145 STRNCPY(bmain_src->filepath, SRC_PATH);
146
147 BKE_id_new(bmain_dst, ID_GR, "Coll_dst");
148
149 Collection *coll_1 = static_cast<Collection *>(BKE_id_new(bmain_src, ID_GR, "Coll_src"));
150 Object *ob_1 = static_cast<Object *>(BKE_id_new(bmain_src, ID_OB, "Ob_src"));
151 BKE_collection_object_add(bmain_src, coll_1, ob_1);
152 Library *lib_src_1 = static_cast<Library *>(BKE_id_new(bmain_src, ID_LI, LIB_PATH));
153 BKE_library_filepath_set(bmain_src, lib_src_1, LIB_PATH);
154 ob_1->id.lib = lib_src_1;
155
156 EXPECT_EQ(1, BLI_listbase_count(&bmain_dst->collections));
157 EXPECT_EQ(0, BLI_listbase_count(&bmain_dst->objects));
158 EXPECT_EQ(0, BLI_listbase_count(&bmain_dst->libraries));
159 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->collections));
160 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->objects));
161 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->libraries));
162
163 MainMergeReport reports = {};
164 BKE_main_merge(bmain_dst, &bmain_src, reports);
165
166 EXPECT_EQ(2, BLI_listbase_count(&bmain_dst->collections));
167 EXPECT_EQ(1, BLI_listbase_count(&bmain_dst->objects));
168 EXPECT_EQ(1, BLI_listbase_count(&bmain_dst->libraries));
169 EXPECT_EQ(ob_1, bmain_dst->objects.first);
170 EXPECT_EQ(lib_src_1, bmain_dst->libraries.first);
171 EXPECT_EQ(ob_1->id.lib, lib_src_1);
172 EXPECT_EQ(3, reports.num_merged_ids);
173 EXPECT_EQ(0, reports.num_unknown_ids);
174 EXPECT_EQ(0, reports.num_remapped_ids);
176 EXPECT_EQ(nullptr, bmain_src);
177
178 /* Try another merge, with the same library path - second library should be skipped, destination
179 * merge should still have only one library ID. */
180 bmain_src = BKE_main_new();
181 STRNCPY(bmain_src->filepath, SRC_PATH);
182
183 Collection *coll_2 = static_cast<Collection *>(BKE_id_new(bmain_src, ID_GR, "Coll_src_2"));
184 Object *ob_2 = static_cast<Object *>(BKE_id_new(bmain_src, ID_OB, "Ob_src_2"));
185 BKE_collection_object_add(bmain_src, coll_2, ob_2);
186 Library *lib_src_2 = static_cast<Library *>(BKE_id_new(bmain_src, ID_LI, LIB_PATH));
187 BKE_library_filepath_set(bmain_src, lib_src_2, LIB_PATH);
188 std::cout << lib_src_1->runtime.filepath_abs << "\n";
189 std::cout << lib_src_2->runtime.filepath_abs << "\n";
190 ob_2->id.lib = lib_src_2;
191
192 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->collections));
193 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->objects));
194 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->libraries));
195
196 reports = {};
197 BKE_main_merge(bmain_dst, &bmain_src, reports);
198
199 EXPECT_EQ(3, BLI_listbase_count(&bmain_dst->collections));
200 EXPECT_EQ(2, BLI_listbase_count(&bmain_dst->objects));
201 EXPECT_EQ(1, BLI_listbase_count(&bmain_dst->libraries));
202 EXPECT_EQ(ob_1, bmain_dst->objects.first);
203 EXPECT_EQ(ob_2, bmain_dst->objects.last);
204 EXPECT_EQ(lib_src_1, bmain_dst->libraries.first);
205 EXPECT_EQ(ob_1->id.lib, lib_src_1);
206 EXPECT_EQ(ob_2->id.lib, lib_src_1);
207 EXPECT_EQ(2, reports.num_merged_ids);
208 EXPECT_EQ(0, reports.num_unknown_ids);
209 EXPECT_EQ(0, reports.num_remapped_ids);
211 EXPECT_EQ(nullptr, bmain_src);
212
213 /* Use a relative library path. Since this is a different library, even though the object re-use
214 * the same name, it should still be moved into `bmain_dst`. The library filepath should also be
215 * updated and become relative the path of bmain_dst too. */
216 bmain_src = BKE_main_new();
217 STRNCPY(bmain_src->filepath, SRC_PATH);
218
219 Collection *coll_3 = static_cast<Collection *>(BKE_id_new(bmain_src, ID_GR, "Coll_src_3"));
220 Object *ob_3 = static_cast<Object *>(BKE_id_new(bmain_src, ID_OB, "Ob_src"));
221 BKE_collection_object_add(bmain_src, coll_3, ob_3);
222 Library *lib_src_3 = static_cast<Library *>(BKE_id_new(bmain_src, ID_LI, LIB_PATH_RELATIVE));
223 BKE_library_filepath_set(bmain_src, lib_src_3, LIB_PATH_RELATIVE);
224 ob_3->id.lib = lib_src_3;
225
226 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->collections));
227 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->objects));
228 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->libraries));
229 EXPECT_TRUE(STREQ(lib_src_3->filepath, LIB_PATH_RELATIVE));
230 EXPECT_TRUE(STREQ(lib_src_3->runtime.filepath_abs, LIB_PATH_RELATIVE_ABS_SRC));
231
232 reports = {};
233 BKE_main_merge(bmain_dst, &bmain_src, reports);
234
235 EXPECT_EQ(4, BLI_listbase_count(&bmain_dst->collections));
236 EXPECT_EQ(3, BLI_listbase_count(&bmain_dst->objects));
237 EXPECT_EQ(2, BLI_listbase_count(&bmain_dst->libraries));
238 EXPECT_EQ(ob_1, bmain_dst->objects.first);
239 EXPECT_EQ(ob_3, bmain_dst->objects.last);
240 EXPECT_EQ(lib_src_3, bmain_dst->libraries.first);
241 EXPECT_EQ(lib_src_1, bmain_dst->libraries.last);
242 EXPECT_EQ(ob_1->id.lib, lib_src_1);
243 EXPECT_EQ(ob_2->id.lib, lib_src_1);
244 EXPECT_EQ(ob_3->id.lib, lib_src_3);
245 EXPECT_FALSE(STREQ(lib_src_3->filepath, LIB_PATH_RELATIVE));
246 EXPECT_TRUE(STREQ(lib_src_3->runtime.filepath_abs, LIB_PATH_RELATIVE_ABS_SRC));
247 EXPECT_EQ(3, reports.num_merged_ids);
248 EXPECT_EQ(0, reports.num_unknown_ids);
249 EXPECT_EQ(0, reports.num_remapped_ids);
251 EXPECT_EQ(nullptr, bmain_src);
252
253 /* Try another merge, with the library path set to the path of the destination bmain. That source
254 * library should also be skipped, and the 'linked' object in source bmain should become a local
255 * object in destination bmain. */
256 bmain_src = BKE_main_new();
257 STRNCPY(bmain_src->filepath, SRC_PATH);
258
259 Collection *coll_4 = static_cast<Collection *>(BKE_id_new(bmain_src, ID_GR, "Coll_src"));
260 Object *ob_4 = static_cast<Object *>(BKE_id_new(bmain_src, ID_OB, "Ob_src_4"));
261 BKE_collection_object_add(bmain_src, coll_4, ob_4);
262 Library *lib_src_4 = static_cast<Library *>(BKE_id_new(bmain_src, ID_LI, DST_PATH));
263 BKE_library_filepath_set(bmain_src, lib_src_4, DST_PATH);
264 coll_4->id.lib = lib_src_4;
265 ob_4->id.lib = lib_src_4;
266
267 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->collections));
268 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->objects));
269 EXPECT_EQ(1, BLI_listbase_count(&bmain_src->libraries));
270
271 reports = {};
272 BKE_main_merge(bmain_dst, &bmain_src, reports);
273
274 /* `bmain_dst` is unchanged, since both `coll_4` and `ob_4` were defined as linked from
275 * `bmain_dst`. */
276 EXPECT_EQ(4, BLI_listbase_count(&bmain_dst->collections));
277 EXPECT_EQ(3, BLI_listbase_count(&bmain_dst->objects));
278 EXPECT_EQ(2, BLI_listbase_count(&bmain_dst->libraries));
279 EXPECT_EQ(lib_src_3, bmain_dst->libraries.first);
280 EXPECT_EQ(lib_src_1, bmain_dst->libraries.last);
281 EXPECT_EQ(ob_1->id.lib, lib_src_1);
282 EXPECT_EQ(ob_2->id.lib, lib_src_1);
283 EXPECT_EQ(ob_3->id.lib, lib_src_3);
284 EXPECT_EQ(0, reports.num_merged_ids);
285 EXPECT_EQ(1, reports.num_unknown_ids);
286 EXPECT_EQ(1, reports.num_remapped_ids);
288 EXPECT_EQ(nullptr, bmain_src);
289}
290
291} // namespace blender::bke::tests
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
void BKE_idtype_init()
Definition idtype.cc:127
void * BKE_id_new(Main *bmain, short type, const char *name)
Definition lib_id.cc:1482
void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath)
Definition library.cc:135
void BKE_main_merge(Main *bmain_dst, Main **r_bmain_src, MainMergeReport &reports)
Definition main.cc:321
Main * BKE_main_new(void)
Definition main.cc:45
void BKE_main_free(Main *bmain)
Definition main.cc:175
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define STREQ(a, b)
void CLG_exit(void)
Definition clog.c:706
void CLG_init(void)
Definition clog.c:699
ID and Library types, which are fundamental for SDNA.
@ ID_LI
@ ID_GR
@ ID_OB
Object groups, one object can be in many groups at once.
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
#define ABS_ROOT
TEST_F(BKE_armature_find_selected_bones_test, some_bones_selected)
struct Library * lib
Definition DNA_ID.h:419
char filepath_abs[1024]
Definition DNA_ID.h:509
char filepath[1024]
Definition DNA_ID.h:531
struct Library_Runtime runtime
Definition DNA_ID.h:535
void * first
int num_remapped_libraries
Definition BKE_main.hh:329
#define SEP_STR
Definition unit.cc:39