blender/tests/gtests/blenlib/BLI_listbase_test.cc

258 lines
6.3 KiB
C++
Raw Normal View History

/* Apache License, Version 2.0 */
#include "testing/testing.h"
extern "C" {
#include "BLI_array_utils.h"
#include "BLI_listbase.h"
#include "MEM_guardedalloc.h"
#include "BLI_string.h"
#include "BLI_path_util.h"
#include "BLI_ressource_strings.h"
}
/* local validation function */
static bool listbase_is_valid(const ListBase *listbase)
{
#define TESTFAIL(test) \
if (!(test)) goto fail;
if (listbase->first) {
const Link *prev, *link;
link = (Link *)listbase->first;
TESTFAIL(link->prev == NULL);
link = (Link *)listbase->last;
TESTFAIL(link->next == NULL);
prev = NULL;
link = (Link *)listbase->first;
do {
TESTFAIL(link->prev == prev);
} while ((prev = link), (link = link->next));
TESTFAIL(prev == listbase->last);
prev = NULL;
link = (Link *)listbase->last;
do {
TESTFAIL(link->next == prev);
} while ((prev = link), (link = link->prev));
TESTFAIL(prev == listbase->first);
}
else {
TESTFAIL(listbase->last == NULL);
}
#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(NULL, BLI_findlink(&lb, -1));
EXPECT_EQ(NULL, BLI_findlink(&lb, 0));
EXPECT_EQ(NULL, BLI_findlink(&lb, 1));
EXPECT_EQ(NULL, BLI_rfindlink(&lb, -1));
EXPECT_EQ(NULL, BLI_rfindlink(&lb, 0));
EXPECT_EQ(NULL, BLI_rfindlink(&lb, 1));
EXPECT_EQ(-1, BLI_findindex(&lb, link1));
/* One link */
BLI_addtail(&lb, link1);
EXPECT_EQ(link1, BLI_findlink(&lb, 0));
EXPECT_EQ(link1, BLI_rfindlink(&lb, 0));
EXPECT_EQ(0, BLI_findindex(&lb, link1));
/* Two links */
BLI_addtail(&lb, link2);
EXPECT_EQ(link2, BLI_findlink(&lb, 1));
EXPECT_EQ(link2, BLI_rfindlink(&lb, 0));
EXPECT_EQ(1, BLI_findindex(&lb, 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 single */
{
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);
}