Blender V4.3
BLI_listbase_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "testing/testing.h"
6
7#include "MEM_guardedalloc.h"
8
9#include "BLI_array_utils.h"
10#include "BLI_listbase.h"
11#include "BLI_path_utils.hh"
13#include "BLI_string.h"
14
15/* local validation function */
16static bool listbase_is_valid(const ListBase *listbase)
17{
18#define TESTFAIL(test) \
19 if (!(test)) { \
20 goto fail; \
21 } \
22 ((void)0)
23
24 if (listbase->first) {
25 const Link *prev, *link;
26 link = (Link *)listbase->first;
27 TESTFAIL(link->prev == nullptr);
28
29 link = (Link *)listbase->last;
30 TESTFAIL(link->next == nullptr);
31
32 prev = nullptr;
33 link = (Link *)listbase->first;
34 do {
35 TESTFAIL(link->prev == prev);
36 } while ((void)(prev = link), (link = link->next));
37 TESTFAIL(prev == listbase->last);
38
39 prev = nullptr;
40 link = (Link *)listbase->last;
41 do {
42 TESTFAIL(link->next == prev);
43 } while ((void)(prev = link), (link = link->prev));
44 TESTFAIL(prev == listbase->first);
45 }
46 else {
47 TESTFAIL(listbase->last == nullptr);
48 }
49#undef TESTFAIL
50
51 return true;
52
53fail:
54 return false;
55}
56
57static int char_switch(char *string, char ch_src, char ch_dst)
58{
59 int tot = 0;
60 while (*string != 0) {
61 if (*string == ch_src) {
62 *string = ch_dst;
63 tot++;
64 }
65 string++;
66 }
67 return tot;
68}
69
70TEST(listbase, FindLinkOrIndex)
71{
72 ListBase lb;
73 void *link1 = MEM_callocN(sizeof(Link), "link1");
74 void *link2 = MEM_callocN(sizeof(Link), "link2");
75
76 /* Empty list */
78 EXPECT_EQ(BLI_findlink(&lb, -1), (void *)nullptr);
79 EXPECT_EQ(BLI_findlink(&lb, 0), (void *)nullptr);
80 EXPECT_EQ(BLI_findlink(&lb, 1), (void *)nullptr);
81 EXPECT_EQ(BLI_rfindlink(&lb, -1), (void *)nullptr);
82 EXPECT_EQ(BLI_rfindlink(&lb, 0), (void *)nullptr);
83 EXPECT_EQ(BLI_rfindlink(&lb, 1), (void *)nullptr);
84 EXPECT_EQ(BLI_findindex(&lb, link1), -1);
85 EXPECT_EQ(BLI_findlinkfrom((Link *)lb.first, -1), (void *)nullptr);
86 EXPECT_EQ(BLI_findlinkfrom((Link *)lb.first, 0), (void *)nullptr);
87 EXPECT_EQ(BLI_findlinkfrom((Link *)lb.first, 1), (void *)nullptr);
88
89 /* One link */
90 BLI_addtail(&lb, link1);
91 EXPECT_EQ(BLI_findlink(&lb, 0), link1);
92 EXPECT_EQ(BLI_rfindlink(&lb, 0), link1);
93 EXPECT_EQ(BLI_findindex(&lb, link1), 0);
94 EXPECT_EQ(BLI_findlinkfrom((Link *)lb.first, 0), link1);
95
96 /* Two links */
97 BLI_addtail(&lb, link2);
98 EXPECT_EQ(BLI_findlink(&lb, 1), link2);
99 EXPECT_EQ(BLI_rfindlink(&lb, 0), link2);
100 EXPECT_EQ(BLI_findindex(&lb, link2), 1);
101 EXPECT_EQ(BLI_findlinkfrom((Link *)lb.first, 1), link2);
102
103 /* After end of list */
104 EXPECT_EQ(BLI_findlinkfrom((Link *)lb.first, 2), (void *)nullptr);
105
106 BLI_freelistN(&lb);
107}
108
109TEST(listbase, FindLinkFromStringOrPointer)
110{
111 struct TestLink {
112 TestLink *next, *prev;
113 char name[64];
114 const void *ptr;
115 };
116
117 const char *const link1_name = "Link1";
118 const char *const link2_name = "Link2";
119 const void *const link1_ptr = nullptr;
120 const void *const link2_ptr = link2_name;
121
122 const size_t name_offset = offsetof(TestLink, name);
123 const size_t ptr_offset = offsetof(TestLink, ptr);
124
125 ListBase lb;
126 TestLink *link1 = (TestLink *)MEM_callocN(sizeof(TestLink), "link1");
127 STRNCPY(link1->name, link1_name);
128 link1->ptr = link1_ptr;
129 TestLink *link2 = (TestLink *)MEM_callocN(sizeof(TestLink), "link2");
130 STRNCPY(link2->name, link2_name);
131 link2->ptr = link2_ptr;
132
133 /* Empty list */
135 EXPECT_EQ(BLI_findptr(&lb, link1_ptr, ptr_offset), (void *)nullptr);
136 EXPECT_EQ(BLI_findstring(&lb, link1_name, name_offset), (void *)nullptr);
137 EXPECT_EQ(BLI_rfindptr(&lb, link1_ptr, ptr_offset), (void *)nullptr);
138 EXPECT_EQ(BLI_rfindstring(&lb, link1_name, name_offset), (void *)nullptr);
139 EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, link1_name, name_offset, 0), (void *)nullptr);
140
141 /* One link */
142 BLI_addtail(&lb, link1);
143 EXPECT_EQ(BLI_findptr(&lb, link1_ptr, ptr_offset), (void *)link1);
144 EXPECT_EQ(BLI_findstring(&lb, link1_name, name_offset), (void *)link1);
145 EXPECT_EQ(BLI_rfindptr(&lb, link1_ptr, ptr_offset), (void *)link1);
146 EXPECT_EQ(BLI_rfindstring(&lb, link1_name, name_offset), (void *)link1);
147 EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, link1_name, name_offset, 0), (void *)link1);
148 EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, "", name_offset, 0), (void *)link1);
149 EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, nullptr, name_offset, 0), (void *)link1);
150 EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, nullptr, name_offset, 1), (void *)nullptr);
151
152 /* Two links */
153 BLI_addtail(&lb, link2);
154 EXPECT_EQ(BLI_findptr(&lb, link1_ptr, ptr_offset), (void *)link1);
155 EXPECT_EQ(BLI_findstring(&lb, link1_name, name_offset), (void *)link1);
156 EXPECT_EQ(BLI_rfindptr(&lb, link1_ptr, ptr_offset), (void *)link1);
157 EXPECT_EQ(BLI_rfindstring(&lb, link1_name, name_offset), (void *)link1);
158 EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, link1_name, name_offset, 0), (void *)link1);
159 EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, link2_name, name_offset, 0), (void *)link2);
160 EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, nullptr, name_offset, 0), (void *)link1);
161 EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, nullptr, name_offset, 1), (void *)link2);
162 EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, nullptr, name_offset, -1), (void *)nullptr);
163
164 BLI_freelistN(&lb);
165}
166
167TEST(listbase, FromLink)
168{
169 ListBase lb = {nullptr, nullptr};
170 Link *link1 = static_cast<Link *>(MEM_callocN(sizeof(Link), "link1"));
171 Link *link2 = static_cast<Link *>(MEM_callocN(sizeof(Link), "link2"));
172 Link *link3 = static_cast<Link *>(MEM_callocN(sizeof(Link), "link3"));
173
174 /* Null safety. */
175 EXPECT_EQ(lb, BLI_listbase_from_link(nullptr));
176
177 /* One link. */
178 BLI_addtail(&lb, link1);
180
181 /* Two links. */
182 BLI_addtail(&lb, link2);
184
185 /* Three links, search from middle. */
186 BLI_addtail(&lb, link3);
188
189 BLI_freelistN(&lb);
190}
191
192TEST(listbase, SplitAfter)
193{
194 ListBase lb;
195 ListBase split_after_lb;
196 void *link1 = MEM_callocN(sizeof(Link), "link1");
197 void *link2 = MEM_callocN(sizeof(Link), "link2");
198
199 /* Empty list */
201 BLI_listbase_clear(&split_after_lb);
202
203 BLI_listbase_split_after(&lb, &split_after_lb, nullptr);
204 EXPECT_EQ(BLI_listbase_is_empty(&split_after_lb), true);
205
206 /* One link */
208 BLI_listbase_clear(&split_after_lb);
209 BLI_addtail(&lb, link1);
210
211 BLI_listbase_split_after(&lb, &split_after_lb, nullptr);
213 EXPECT_EQ(BLI_listbase_count(&split_after_lb), 1);
214 EXPECT_EQ(BLI_findindex(&split_after_lb, link1), 0);
215 EXPECT_EQ(split_after_lb.first, link1);
216 EXPECT_EQ(split_after_lb.last, link1);
217
219 BLI_listbase_clear(&split_after_lb);
220 BLI_addtail(&lb, link1);
221
222 BLI_listbase_split_after(&lb, &split_after_lb, link1);
224 EXPECT_EQ(BLI_findindex(&lb, link1), 0);
225 EXPECT_EQ(lb.first, link1);
226 EXPECT_EQ(lb.last, link1);
227 EXPECT_EQ(BLI_listbase_is_empty(&split_after_lb), true);
228
229 /* Two links */
231 BLI_listbase_clear(&split_after_lb);
232 BLI_addtail(&lb, link1);
233 BLI_addtail(&lb, link2);
234
235 BLI_listbase_split_after(&lb, &split_after_lb, nullptr);
237 EXPECT_EQ(BLI_listbase_count(&split_after_lb), 2);
238 EXPECT_EQ(BLI_findindex(&split_after_lb, link1), 0);
239 EXPECT_EQ(BLI_findindex(&split_after_lb, link2), 1);
240 EXPECT_EQ(split_after_lb.first, link1);
241 EXPECT_EQ(split_after_lb.last, link2);
242
244 BLI_listbase_clear(&split_after_lb);
245 BLI_addtail(&lb, link1);
246 BLI_addtail(&lb, link2);
247
248 BLI_listbase_split_after(&lb, &split_after_lb, link1);
250 EXPECT_EQ(BLI_findindex(&lb, link1), 0);
251 EXPECT_EQ(lb.first, link1);
252 EXPECT_EQ(lb.last, link1);
253 EXPECT_EQ(BLI_listbase_count(&split_after_lb), 1);
254 EXPECT_EQ(BLI_findindex(&split_after_lb, link2), 0);
255 EXPECT_EQ(split_after_lb.first, link2);
256 EXPECT_EQ(split_after_lb.last, link2);
257
258 BLI_freelistN(&lb);
259 BLI_freelistN(&split_after_lb);
260}
261
262/* -------------------------------------------------------------------- */
263/* Sort utilities & test */
264
265static int testsort_array_str_cmp(const void *a, const void *b)
266{
267 int i = strcmp(*(const char **)a, *(const char **)b);
268 return (i > 0) ? 1 : (i < 0) ? -1 : 0;
269}
270
271static int testsort_listbase_str_cmp(const void *a, const void *b)
272{
273 const LinkData *link_a = (LinkData *)a;
274 const LinkData *link_b = (LinkData *)b;
275 int i = strcmp((const char *)link_a->data, (const char *)link_b->data);
276 return (i > 0) ? 1 : (i < 0) ? -1 : 0;
277}
278
279static int testsort_array_str_cmp_reverse(const void *a, const void *b)
280{
281 return -testsort_array_str_cmp(a, b);
282}
283
284static int testsort_listbase_str_cmp_reverse(const void *a, const void *b)
285{
286 return -testsort_listbase_str_cmp(a, b);
287}
288
289/* check array and listbase compare */
290static bool testsort_listbase_array_str_cmp(ListBase *lb, char **arr, int arr_num)
291{
292 LinkData *link_step;
293 int i;
294
295 link_step = (LinkData *)lb->first;
296 for (i = 0; i < arr_num; i++) {
297 if (!STREQ(arr[i], (char *)link_step->data)) {
298 return false;
299 }
300 link_step = link_step->next;
301 }
302 if (link_step) {
303 return false;
304 }
305
306 return true;
307}
308
309/* assumes nodes are allocated in-order */
310static bool testsort_listbase_sort_is_stable(ListBase *lb, bool forward)
311{
312 LinkData *link_step;
313
314 link_step = (LinkData *)lb->first;
315 while (link_step && link_step->next) {
316 if (STREQ((const char *)link_step->data, (const char *)link_step->next->data)) {
317 if ((link_step < link_step->next) != forward) {
318 return false;
319 }
320 }
321 link_step = link_step->next;
322 }
323 return true;
324}
325
326TEST(listbase, Sort)
327{
328 const int words_len = sizeof(words10k) - 1;
329 char *words = BLI_strdupn(words10k, words_len);
330 int words_num;
331 char **words_arr; /* qsort for comparison */
332 int i;
333 char *w_step;
334 ListBase words_lb;
335 LinkData *words_linkdata_arr;
336
337 /* delimit words */
338 words_num = 1 + char_switch(words, ' ', '\0');
339
340 words_arr = (char **)MEM_mallocN(sizeof(*words_arr) * words_num, __func__);
341
342 words_linkdata_arr = (LinkData *)MEM_mallocN(sizeof(*words_linkdata_arr) * words_num, __func__);
343
344 /* create array */
345 w_step = words;
346 for (i = 0; i < words_num; i++) {
347 words_arr[i] = w_step;
348 w_step += strlen(w_step) + 1;
349 }
350
351 /* sort empty list */
352 {
353 BLI_listbase_clear(&words_lb);
355 EXPECT_TRUE(listbase_is_valid(&words_lb));
356 }
357
358 /* Sort single list. */
359 {
360 LinkData link;
361 link.data = words;
362 BLI_addtail(&words_lb, &link);
364 EXPECT_TRUE(listbase_is_valid(&words_lb));
365 BLI_listbase_clear(&words_lb);
366 }
367
368 /* create listbase */
369 BLI_listbase_clear(&words_lb);
370 w_step = words;
371 for (i = 0; i < words_num; i++) {
372 LinkData *link = &words_linkdata_arr[i];
373 link->data = w_step;
374 BLI_addtail(&words_lb, link);
375 w_step += strlen(w_step) + 1;
376 }
377 EXPECT_TRUE(listbase_is_valid(&words_lb));
378
379 /* sort (forward) */
380 {
381 qsort(words_arr, words_num, sizeof(*words_arr), testsort_array_str_cmp);
382
384 EXPECT_TRUE(listbase_is_valid(&words_lb));
385 EXPECT_TRUE(testsort_listbase_array_str_cmp(&words_lb, words_arr, words_num));
386 EXPECT_TRUE(testsort_listbase_sort_is_stable(&words_lb, true));
387 }
388
389 /* sort (reverse) */
390 {
391 qsort(words_arr, words_num, sizeof(*words_arr), testsort_array_str_cmp_reverse);
392
394 EXPECT_TRUE(listbase_is_valid(&words_lb));
395 EXPECT_TRUE(testsort_listbase_array_str_cmp(&words_lb, words_arr, words_num));
396 EXPECT_TRUE(testsort_listbase_sort_is_stable(&words_lb, true));
397 }
398
399 /* sort (forward but after reversing, test stability in alternate direction) */
400 {
401 BLI_array_reverse(words_arr, words_num);
402 BLI_listbase_reverse(&words_lb);
403
404 EXPECT_TRUE(listbase_is_valid(&words_lb));
405 EXPECT_TRUE(testsort_listbase_array_str_cmp(&words_lb, words_arr, words_num));
406 EXPECT_TRUE(testsort_listbase_sort_is_stable(&words_lb, false));
407
408 /* and again */
409 BLI_array_reverse(words_arr, words_num);
411 EXPECT_TRUE(testsort_listbase_array_str_cmp(&words_lb, words_arr, words_num));
412 EXPECT_TRUE(testsort_listbase_sort_is_stable(&words_lb, false));
413 }
414
415 MEM_freeN(words);
416 MEM_freeN(words_arr);
417 MEM_freeN(words_linkdata_arr);
418}
Generic array manipulation API.
#define BLI_array_reverse(arr, arr_len)
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void * BLI_rfindlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_findstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void * BLI_findlinkfrom(struct Link *start, int step) ATTR_WARN_UNUSED_RESULT
Definition listbase.cc:563
void void void void void BLI_listbase_split_after(struct ListBase *original_listbase, struct ListBase *split_listbase, void *vlink) ATTR_NONNULL(1
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void void BLI_listbase_sort(struct ListBase *listbase, int(*cmp)(const void *, const void *)) ATTR_NONNULL(1
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_rfindptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_rfindstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void void void void void void void BLI_listbase_reverse(struct ListBase *lb) ATTR_NONNULL(1)
Definition listbase.cc:823
ListBase BLI_listbase_from_link(struct Link *some_link)
Definition listbase.cc:787
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void void * BLI_listbase_string_or_index_find(const struct ListBase *listbase, const char *string, size_t string_offset, int index) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
static int testsort_listbase_str_cmp(const void *a, const void *b)
static int testsort_array_str_cmp(const void *a, const void *b)
static bool listbase_is_valid(const ListBase *listbase)
static bool testsort_listbase_array_str_cmp(ListBase *lb, char **arr, int arr_num)
static int testsort_array_str_cmp_reverse(const void *a, const void *b)
TEST(listbase, FindLinkOrIndex)
static int char_switch(char *string, char ch_src, char ch_dst)
static int testsort_listbase_str_cmp_reverse(const void *a, const void *b)
static bool testsort_listbase_sort_is_stable(ListBase *lb, bool forward)
#define TESTFAIL(test)
const char words10k[]
#define STRNCPY(dst, src)
Definition BLI_string.h:593
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.c:29
#define STREQ(a, b)
Read Guarded memory(de)allocation.
local_group_size(16, 16) .push_constant(Type b
#define offsetof(t, d)
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
static ulong * next
void * data
struct LinkData * next
void * last
void * first
PointerRNA * ptr
Definition wm_files.cc:4126