This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenlib/tests/BLI_listbase_test.cc
Sybren A. Stüvel 4b21067aea Fix T94116: Drivers can have multiple variables with same name
The RNA setter now ensures that driver variables are uniquely named
(within the scope of the driver).

Versioning code has been added to ensure this uniqueness. The last
variable with the non-unique name retains the original name; this
ensures that the driver will still evaluate to the same value as before
this fix.

This also introduces a new blenlib function `BLI_listbase_from_link()`,
which can be used to find the entire list from any item within the list.

Manifest Task: T94116

Reviewed By: mont29, JacquesLucke

Maniphest Tasks: T94116

Differential Revision: https://developer.blender.org/D13594
2021-12-17 17:31:15 +01:00

339 lines
10 KiB
C++

/* Apache License, Version 2.0 */
#include "testing/testing.h"
#include "MEM_guardedalloc.h"
#include "BLI_array_utils.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
#include "BLI_ressource_strings.h"
#include "BLI_string.h"
/* local validation function */
static bool listbase_is_valid(const ListBase *listbase)
{
#define TESTFAIL(test) \
if (!(test)) { \
goto fail; \
} \
((void)0)
if (listbase->first) {
const Link *prev, *link;
link = (Link *)listbase->first;
TESTFAIL(link->prev == nullptr);
link = (Link *)listbase->last;
TESTFAIL(link->next == nullptr);
prev = nullptr;
link = (Link *)listbase->first;
do {
TESTFAIL(link->prev == prev);
} while ((void)(prev = link), (link = link->next));
TESTFAIL(prev == listbase->last);
prev = nullptr;
link = (Link *)listbase->last;
do {
TESTFAIL(link->next == prev);
} while ((void)(prev = link), (link = link->prev));
TESTFAIL(prev == listbase->first);
}
else {
TESTFAIL(listbase->last == nullptr);
}
#undef TESTFAIL
return true;
fail:
return false;
}
static int char_switch(char *string, char ch_src, char ch_dst)
{
int tot = 0;
while (*string != 0) {
if (*string == ch_src) {
*string = ch_dst;
tot++;
}
string++;
}
return tot;
}
TEST(listbase, FindLinkOrIndex)
{
ListBase lb;
void *link1 = MEM_callocN(sizeof(Link), "link1");
void *link2 = MEM_callocN(sizeof(Link), "link2");
/* Empty list */
BLI_listbase_clear(&lb);
EXPECT_EQ(BLI_findlink(&lb, -1), (void *)nullptr);
EXPECT_EQ(BLI_findlink(&lb, 0), (void *)nullptr);
EXPECT_EQ(BLI_findlink(&lb, 1), (void *)nullptr);
EXPECT_EQ(BLI_rfindlink(&lb, -1), (void *)nullptr);
EXPECT_EQ(BLI_rfindlink(&lb, 0), (void *)nullptr);
EXPECT_EQ(BLI_rfindlink(&lb, 1), (void *)nullptr);
EXPECT_EQ(BLI_findindex(&lb, link1), -1);
/* One link */
BLI_addtail(&lb, link1);
EXPECT_EQ(BLI_findlink(&lb, 0), link1);
EXPECT_EQ(BLI_rfindlink(&lb, 0), link1);
EXPECT_EQ(BLI_findindex(&lb, link1), 0);
/* Two links */
BLI_addtail(&lb, link2);
EXPECT_EQ(BLI_findlink(&lb, 1), link2);
EXPECT_EQ(BLI_rfindlink(&lb, 0), link2);
EXPECT_EQ(BLI_findindex(&lb, link2), 1);
BLI_freelistN(&lb);
}
TEST(listbase, FindLinkFromStringOrPointer)
{
struct TestLink {
struct TestLink *prev, *next;
char name[64];
const void *ptr;
};
const char *const link1_name = "Link1";
const char *const link2_name = "Link2";
const void *const link1_ptr = nullptr;
const void *const link2_ptr = link2_name;
const size_t name_offset = offsetof(struct TestLink, name);
const size_t ptr_offset = offsetof(struct TestLink, ptr);
ListBase lb;
struct TestLink *link1 = (struct TestLink *)MEM_callocN(sizeof(TestLink), "link1");
BLI_strncpy(link1->name, link1_name, sizeof(link1->name));
link1->ptr = link1_ptr;
struct TestLink *link2 = (struct TestLink *)MEM_callocN(sizeof(TestLink), "link2");
BLI_strncpy(link2->name, link2_name, sizeof(link2->name));
link2->ptr = link2_ptr;
/* Empty list */
BLI_listbase_clear(&lb);
EXPECT_EQ(BLI_findptr(&lb, link1_ptr, ptr_offset), (void *)nullptr);
EXPECT_EQ(BLI_findstring(&lb, link1_name, name_offset), (void *)nullptr);
EXPECT_EQ(BLI_rfindptr(&lb, link1_ptr, ptr_offset), (void *)nullptr);
EXPECT_EQ(BLI_rfindstring(&lb, link1_name, name_offset), (void *)nullptr);
EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, link1_name, name_offset, 0), (void *)nullptr);
/* One link */
BLI_addtail(&lb, link1);
EXPECT_EQ(BLI_findptr(&lb, link1_ptr, ptr_offset), (void *)link1);
EXPECT_EQ(BLI_findstring(&lb, link1_name, name_offset), (void *)link1);
EXPECT_EQ(BLI_rfindptr(&lb, link1_ptr, ptr_offset), (void *)link1);
EXPECT_EQ(BLI_rfindstring(&lb, link1_name, name_offset), (void *)link1);
EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, link1_name, name_offset, 0), (void *)link1);
EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, "", name_offset, 0), (void *)link1);
EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, nullptr, name_offset, 0), (void *)link1);
EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, nullptr, name_offset, 1), (void *)nullptr);
/* Two links */
BLI_addtail(&lb, link2);
EXPECT_EQ(BLI_findptr(&lb, link1_ptr, ptr_offset), (void *)link1);
EXPECT_EQ(BLI_findstring(&lb, link1_name, name_offset), (void *)link1);
EXPECT_EQ(BLI_rfindptr(&lb, link1_ptr, ptr_offset), (void *)link1);
EXPECT_EQ(BLI_rfindstring(&lb, link1_name, name_offset), (void *)link1);
EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, link1_name, name_offset, 0), (void *)link1);
EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, link2_name, name_offset, 0), (void *)link2);
EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, nullptr, name_offset, 0), (void *)link1);
EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, nullptr, name_offset, 1), (void *)link2);
EXPECT_EQ(BLI_listbase_string_or_index_find(&lb, nullptr, name_offset, -1), (void *)nullptr);
BLI_freelistN(&lb);
}
TEST(listbase, FromLink)
{
ListBase lb = {nullptr, nullptr};
Link *link1 = static_cast<Link *>(MEM_callocN(sizeof(Link), "link1"));
Link *link2 = static_cast<Link *>(MEM_callocN(sizeof(Link), "link2"));
Link *link3 = static_cast<Link *>(MEM_callocN(sizeof(Link), "link3"));
/* NULL safety. */
EXPECT_EQ(lb, BLI_listbase_from_link(nullptr));
/* One link. */
BLI_addtail(&lb, link1);
EXPECT_EQ(lb, BLI_listbase_from_link(link1));
/* Two links. */
BLI_addtail(&lb, link2);
EXPECT_EQ(lb, BLI_listbase_from_link(link2));
/* Three links, search from middle. */
BLI_addtail(&lb, link3);
EXPECT_EQ(lb, BLI_listbase_from_link(link2));
BLI_freelistN(&lb);
}
/* -------------------------------------------------------------------- */
/* Sort utilities & test */
static int testsort_array_str_cmp(const void *a, const void *b)
{
int i = strcmp(*(const char **)a, *(const char **)b);
return (i > 0) ? 1 : (i < 0) ? -1 : 0;
}
static int testsort_listbase_str_cmp(const void *a, const void *b)
{
const LinkData *link_a = (LinkData *)a;
const LinkData *link_b = (LinkData *)b;
int i = strcmp((const char *)link_a->data, (const char *)link_b->data);
return (i > 0) ? 1 : (i < 0) ? -1 : 0;
}
static int testsort_array_str_cmp_reverse(const void *a, const void *b)
{
return -testsort_array_str_cmp(a, b);
}
static int testsort_listbase_str_cmp_reverse(const void *a, const void *b)
{
return -testsort_listbase_str_cmp(a, b);
}
/* check array and listbase compare */
static bool testsort_listbase_array_str_cmp(ListBase *lb, char **arr, int arr_tot)
{
LinkData *link_step;
int i;
link_step = (LinkData *)lb->first;
for (i = 0; i < arr_tot; i++) {
if (strcmp(arr[i], (char *)link_step->data) != 0) {
return false;
}
link_step = link_step->next;
}
if (link_step) {
return false;
}
return true;
}
/* assumes nodes are allocated in-order */
static bool testsort_listbase_sort_is_stable(ListBase *lb, bool forward)
{
LinkData *link_step;
link_step = (LinkData *)lb->first;
while (link_step && link_step->next) {
if (strcmp((const char *)link_step->data, (const char *)link_step->next->data) == 0) {
if ((link_step < link_step->next) != forward) {
return false;
}
}
link_step = link_step->next;
}
return true;
}
TEST(listbase, Sort)
{
const int words_len = sizeof(words10k) - 1;
char *words = BLI_strdupn(words10k, words_len);
int words_tot;
char **words_arr; /* qsort for comparison */
int i;
char *w_step;
ListBase words_lb;
LinkData *words_linkdata_arr;
/* delimit words */
words_tot = 1 + char_switch(words, ' ', '\0');
words_arr = (char **)MEM_mallocN(sizeof(*words_arr) * words_tot, __func__);
words_linkdata_arr = (LinkData *)MEM_mallocN(sizeof(*words_linkdata_arr) * words_tot, __func__);
/* create array */
w_step = words;
for (i = 0; i < words_tot; i++) {
words_arr[i] = w_step;
w_step += strlen(w_step) + 1;
}
/* sort empty list */
{
BLI_listbase_clear(&words_lb);
BLI_listbase_sort(&words_lb, testsort_listbase_str_cmp);
EXPECT_TRUE(listbase_is_valid(&words_lb));
}
/* Sort single list. */
{
LinkData link;
link.data = words;
BLI_addtail(&words_lb, &link);
BLI_listbase_sort(&words_lb, testsort_listbase_str_cmp);
EXPECT_TRUE(listbase_is_valid(&words_lb));
BLI_listbase_clear(&words_lb);
}
/* create listbase */
BLI_listbase_clear(&words_lb);
w_step = words;
for (i = 0; i < words_tot; i++) {
LinkData *link = &words_linkdata_arr[i];
link->data = w_step;
BLI_addtail(&words_lb, link);
w_step += strlen(w_step) + 1;
}
EXPECT_TRUE(listbase_is_valid(&words_lb));
/* sort (forward) */
{
qsort(words_arr, words_tot, sizeof(*words_arr), testsort_array_str_cmp);
BLI_listbase_sort(&words_lb, testsort_listbase_str_cmp);
EXPECT_TRUE(listbase_is_valid(&words_lb));
EXPECT_TRUE(testsort_listbase_array_str_cmp(&words_lb, words_arr, words_tot));
EXPECT_TRUE(testsort_listbase_sort_is_stable(&words_lb, true));
}
/* sort (reverse) */
{
qsort(words_arr, words_tot, sizeof(*words_arr), testsort_array_str_cmp_reverse);
BLI_listbase_sort(&words_lb, testsort_listbase_str_cmp_reverse);
EXPECT_TRUE(listbase_is_valid(&words_lb));
EXPECT_TRUE(testsort_listbase_array_str_cmp(&words_lb, words_arr, words_tot));
EXPECT_TRUE(testsort_listbase_sort_is_stable(&words_lb, true));
}
/* sort (forward but after reversing, test stability in alternate direction) */
{
BLI_array_reverse(words_arr, words_tot);
BLI_listbase_reverse(&words_lb);
EXPECT_TRUE(listbase_is_valid(&words_lb));
EXPECT_TRUE(testsort_listbase_array_str_cmp(&words_lb, words_arr, words_tot));
EXPECT_TRUE(testsort_listbase_sort_is_stable(&words_lb, false));
/* and again */
BLI_array_reverse(words_arr, words_tot);
BLI_listbase_sort(&words_lb, testsort_listbase_str_cmp_reverse);
EXPECT_TRUE(testsort_listbase_array_str_cmp(&words_lb, words_arr, words_tot));
EXPECT_TRUE(testsort_listbase_sort_is_stable(&words_lb, false));
}
MEM_freeN(words);
MEM_freeN(words_arr);
MEM_freeN(words_linkdata_arr);
}