Add unit tests for qr, ql, and rb.
This commit is contained in:
parent
b954bc5d3a
commit
981bb499d9
@ -110,9 +110,11 @@ C_UTIL_INTEGRATION_SRCS := $(srcroot)src/util.c
|
|||||||
TESTS_UNIT := $(srcroot)test/unit/bitmap.c $(srcroot)test/unit/ckh.c \
|
TESTS_UNIT := $(srcroot)test/unit/bitmap.c $(srcroot)test/unit/ckh.c \
|
||||||
$(srcroot)test/unit/hash.c $(srcroot)test/unit/mallctl.c \
|
$(srcroot)test/unit/hash.c $(srcroot)test/unit/mallctl.c \
|
||||||
$(srcroot)test/unit/math.c $(srcroot)test/unit/mq.c \
|
$(srcroot)test/unit/math.c $(srcroot)test/unit/mq.c \
|
||||||
$(srcroot)test/unit/mtx.c $(srcroot)test/unit/quarantine.c \
|
$(srcroot)test/unit/mtx.c $(srcroot)test/unit/ql.c \
|
||||||
$(srcroot)test/unit/rtree.c $(srcroot)test/unit/SFMT.c \
|
$(srcroot)test/unit/qr.c $(srcroot)test/unit/quarantine.c \
|
||||||
$(srcroot)test/unit/stats.c $(srcroot)test/unit/tsd.c
|
$(srcroot)test/unit/rb.c $(srcroot)test/unit/rtree.c \
|
||||||
|
$(srcroot)test/unit/SFMT.c $(srcroot)test/unit/stats.c \
|
||||||
|
$(srcroot)test/unit/tsd.c
|
||||||
TESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \
|
TESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \
|
||||||
$(srcroot)test/integration/allocated.c \
|
$(srcroot)test/integration/allocated.c \
|
||||||
$(srcroot)test/integration/mallocx.c \
|
$(srcroot)test/integration/mallocx.c \
|
||||||
|
209
test/unit/ql.c
Normal file
209
test/unit/ql.c
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
#include "test/jemalloc_test.h"
|
||||||
|
|
||||||
|
/* Number of ring entries, in [2..26]. */
|
||||||
|
#define NENTRIES 9
|
||||||
|
|
||||||
|
typedef struct list_s list_t;
|
||||||
|
typedef ql_head(list_t) list_head_t;
|
||||||
|
|
||||||
|
struct list_s {
|
||||||
|
ql_elm(list_t) link;
|
||||||
|
char id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_empty_list(list_head_t *head)
|
||||||
|
{
|
||||||
|
list_t *t;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
assert_ptr_null(ql_first(head), "Unexpected element for empty list");
|
||||||
|
assert_ptr_null(ql_last(head, link),
|
||||||
|
"Unexpected element for empty list");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
ql_foreach(t, head, link) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
assert_u_eq(i, 0, "Unexpected element for empty list");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
ql_reverse_foreach(t, head, link) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
assert_u_eq(i, 0, "Unexpected element for empty list");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_BEGIN(test_ql_empty)
|
||||||
|
{
|
||||||
|
list_head_t head;
|
||||||
|
|
||||||
|
ql_new(&head);
|
||||||
|
test_empty_list(&head);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_entries(list_t *entries, unsigned nentries)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < nentries; i++) {
|
||||||
|
entries[i].id = 'a' + i;
|
||||||
|
ql_elm_new(&entries[i], link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_entries_list(list_head_t *head, list_t *entries, unsigned nentries)
|
||||||
|
{
|
||||||
|
list_t *t;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
assert_c_eq(ql_first(head)->id, entries[0].id, "Element id mismatch");
|
||||||
|
assert_c_eq(ql_last(head, link)->id, entries[nentries-1].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
ql_foreach(t, head, link) {
|
||||||
|
assert_c_eq(t->id, entries[i].id, "Element id mismatch");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
ql_reverse_foreach(t, head, link) {
|
||||||
|
assert_c_eq(t->id, entries[nentries-i-1].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nentries-1; i++) {
|
||||||
|
t = ql_next(head, &entries[i], link);
|
||||||
|
assert_c_eq(t->id, entries[i+1].id, "Element id mismatch");
|
||||||
|
}
|
||||||
|
assert_ptr_null(ql_next(head, &entries[nentries-1], link),
|
||||||
|
"Unexpected element");
|
||||||
|
|
||||||
|
assert_ptr_null(ql_prev(head, &entries[0], link), "Unexpected element");
|
||||||
|
for (i = 1; i < nentries; i++) {
|
||||||
|
t = ql_prev(head, &entries[i], link);
|
||||||
|
assert_c_eq(t->id, entries[i-1].id, "Element id mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_BEGIN(test_ql_tail_insert)
|
||||||
|
{
|
||||||
|
list_head_t head;
|
||||||
|
list_t entries[NENTRIES];
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
ql_new(&head);
|
||||||
|
init_entries(entries, sizeof(entries)/sizeof(list_t));
|
||||||
|
for (i = 0; i < NENTRIES; i++)
|
||||||
|
ql_tail_insert(&head, &entries[i], link);
|
||||||
|
|
||||||
|
test_entries_list(&head, entries, NENTRIES);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
TEST_BEGIN(test_ql_tail_remove)
|
||||||
|
{
|
||||||
|
list_head_t head;
|
||||||
|
list_t entries[NENTRIES];
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
ql_new(&head);
|
||||||
|
init_entries(entries, sizeof(entries)/sizeof(list_t));
|
||||||
|
for (i = 0; i < NENTRIES; i++)
|
||||||
|
ql_tail_insert(&head, &entries[i], link);
|
||||||
|
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
test_entries_list(&head, entries, NENTRIES-i);
|
||||||
|
ql_tail_remove(&head, list_t, link);
|
||||||
|
}
|
||||||
|
test_empty_list(&head);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
TEST_BEGIN(test_ql_head_insert)
|
||||||
|
{
|
||||||
|
list_head_t head;
|
||||||
|
list_t entries[NENTRIES];
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
ql_new(&head);
|
||||||
|
init_entries(entries, sizeof(entries)/sizeof(list_t));
|
||||||
|
for (i = 0; i < NENTRIES; i++)
|
||||||
|
ql_head_insert(&head, &entries[NENTRIES-i-1], link);
|
||||||
|
|
||||||
|
test_entries_list(&head, entries, NENTRIES);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
TEST_BEGIN(test_ql_head_remove)
|
||||||
|
{
|
||||||
|
list_head_t head;
|
||||||
|
list_t entries[NENTRIES];
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
ql_new(&head);
|
||||||
|
init_entries(entries, sizeof(entries)/sizeof(list_t));
|
||||||
|
for (i = 0; i < NENTRIES; i++)
|
||||||
|
ql_head_insert(&head, &entries[NENTRIES-i-1], link);
|
||||||
|
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
test_entries_list(&head, &entries[i], NENTRIES-i);
|
||||||
|
ql_head_remove(&head, list_t, link);
|
||||||
|
}
|
||||||
|
test_empty_list(&head);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
TEST_BEGIN(test_ql_insert)
|
||||||
|
{
|
||||||
|
list_head_t head;
|
||||||
|
list_t entries[8];
|
||||||
|
list_t *a, *b, *c, *d, *e, *f, *g, *h;
|
||||||
|
|
||||||
|
ql_new(&head);
|
||||||
|
init_entries(entries, sizeof(entries)/sizeof(list_t));
|
||||||
|
a = &entries[0];
|
||||||
|
b = &entries[1];
|
||||||
|
c = &entries[2];
|
||||||
|
d = &entries[3];
|
||||||
|
e = &entries[4];
|
||||||
|
f = &entries[5];
|
||||||
|
g = &entries[6];
|
||||||
|
h = &entries[7];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ql_remove(), ql_before_insert(), and ql_after_insert() are used
|
||||||
|
* internally by other macros that are already tested, so there's no
|
||||||
|
* need to test them completely. However, insertion/deletion from the
|
||||||
|
* middle of lists is not otherwise tested; do so here.
|
||||||
|
*/
|
||||||
|
ql_tail_insert(&head, f, link);
|
||||||
|
ql_before_insert(&head, f, b, link);
|
||||||
|
ql_before_insert(&head, f, c, link);
|
||||||
|
ql_after_insert(f, h, link);
|
||||||
|
ql_after_insert(f, g, link);
|
||||||
|
ql_before_insert(&head, b, a, link);
|
||||||
|
ql_after_insert(c, d, link);
|
||||||
|
ql_before_insert(&head, f, e, link);
|
||||||
|
|
||||||
|
test_entries_list(&head, entries, sizeof(entries)/sizeof(list_t));
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
return (test(
|
||||||
|
test_ql_empty,
|
||||||
|
test_ql_tail_insert,
|
||||||
|
test_ql_tail_remove,
|
||||||
|
test_ql_head_insert,
|
||||||
|
test_ql_head_remove,
|
||||||
|
test_ql_insert));
|
||||||
|
}
|
248
test/unit/qr.c
Normal file
248
test/unit/qr.c
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
#include "test/jemalloc_test.h"
|
||||||
|
|
||||||
|
/* Number of ring entries, in [2..26]. */
|
||||||
|
#define NENTRIES 9
|
||||||
|
/* Split index, in [1..NENTRIES). */
|
||||||
|
#define SPLIT_INDEX 5
|
||||||
|
|
||||||
|
typedef struct ring_s ring_t;
|
||||||
|
|
||||||
|
struct ring_s {
|
||||||
|
qr(ring_t) link;
|
||||||
|
char id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_entries(ring_t *entries)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
qr_new(&entries[i], link);
|
||||||
|
entries[i].id = 'a' + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_independent_entries(ring_t *entries)
|
||||||
|
{
|
||||||
|
ring_t *t;
|
||||||
|
unsigned i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
j = 0;
|
||||||
|
qr_foreach(t, &entries[i], link) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
assert_u_eq(j, 1,
|
||||||
|
"Iteration over single-element ring should visit precisely "
|
||||||
|
"one element");
|
||||||
|
}
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
j = 0;
|
||||||
|
qr_reverse_foreach(t, &entries[i], link) {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
assert_u_eq(j, 1,
|
||||||
|
"Iteration over single-element ring should visit precisely "
|
||||||
|
"one element");
|
||||||
|
}
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
t = qr_next(&entries[i], link);
|
||||||
|
assert_ptr_eq(t, &entries[i],
|
||||||
|
"Next element in single-element ring should be same as "
|
||||||
|
"current element");
|
||||||
|
}
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
t = qr_prev(&entries[i], link);
|
||||||
|
assert_ptr_eq(t, &entries[i],
|
||||||
|
"Previous element in single-element ring should be same as "
|
||||||
|
"current element");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_BEGIN(test_qr_one)
|
||||||
|
{
|
||||||
|
ring_t entries[NENTRIES];
|
||||||
|
|
||||||
|
init_entries(entries);
|
||||||
|
test_independent_entries(entries);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_entries_ring(ring_t *entries)
|
||||||
|
{
|
||||||
|
ring_t *t;
|
||||||
|
unsigned i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
j = 0;
|
||||||
|
qr_foreach(t, &entries[i], link) {
|
||||||
|
assert_c_eq(t->id, entries[(i+j) % NENTRIES].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
j = 0;
|
||||||
|
qr_reverse_foreach(t, &entries[i], link) {
|
||||||
|
assert_c_eq(t->id, entries[(NENTRIES+i-j-1) %
|
||||||
|
NENTRIES].id, "Element id mismatch");
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
t = qr_next(&entries[i], link);
|
||||||
|
assert_c_eq(t->id, entries[(i+1) % NENTRIES].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
}
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
t = qr_prev(&entries[i], link);
|
||||||
|
assert_c_eq(t->id, entries[(NENTRIES+i-1) % NENTRIES].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_BEGIN(test_qr_after_insert)
|
||||||
|
{
|
||||||
|
ring_t entries[NENTRIES];
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
init_entries(entries);
|
||||||
|
for (i = 1; i < NENTRIES; i++)
|
||||||
|
qr_after_insert(&entries[i - 1], &entries[i], link);
|
||||||
|
test_entries_ring(entries);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
TEST_BEGIN(test_qr_remove)
|
||||||
|
{
|
||||||
|
ring_t entries[NENTRIES];
|
||||||
|
ring_t *t;
|
||||||
|
unsigned i, j;
|
||||||
|
|
||||||
|
init_entries(entries);
|
||||||
|
for (i = 1; i < NENTRIES; i++)
|
||||||
|
qr_after_insert(&entries[i - 1], &entries[i], link);
|
||||||
|
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
j = 0;
|
||||||
|
qr_foreach(t, &entries[i], link) {
|
||||||
|
assert_c_eq(t->id, entries[i+j].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
j = 0;
|
||||||
|
qr_reverse_foreach(t, &entries[i], link) {
|
||||||
|
assert_c_eq(t->id, entries[NENTRIES - 1 - j].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
qr_remove(&entries[i], link);
|
||||||
|
}
|
||||||
|
test_independent_entries(entries);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
TEST_BEGIN(test_qr_before_insert)
|
||||||
|
{
|
||||||
|
ring_t entries[NENTRIES];
|
||||||
|
ring_t *t;
|
||||||
|
unsigned i, j;
|
||||||
|
|
||||||
|
init_entries(entries);
|
||||||
|
for (i = 1; i < NENTRIES; i++)
|
||||||
|
qr_before_insert(&entries[i - 1], &entries[i], link);
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
j = 0;
|
||||||
|
qr_foreach(t, &entries[i], link) {
|
||||||
|
assert_c_eq(t->id, entries[(NENTRIES+i-j) %
|
||||||
|
NENTRIES].id, "Element id mismatch");
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
j = 0;
|
||||||
|
qr_reverse_foreach(t, &entries[i], link) {
|
||||||
|
assert_c_eq(t->id, entries[(i+j+1) % NENTRIES].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
t = qr_next(&entries[i], link);
|
||||||
|
assert_c_eq(t->id, entries[(NENTRIES+i-1) % NENTRIES].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
}
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
t = qr_prev(&entries[i], link);
|
||||||
|
assert_c_eq(t->id, entries[(i+1) % NENTRIES].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_split_entries(ring_t *entries)
|
||||||
|
{
|
||||||
|
ring_t *t;
|
||||||
|
unsigned i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < NENTRIES; i++) {
|
||||||
|
j = 0;
|
||||||
|
qr_foreach(t, &entries[i], link) {
|
||||||
|
if (i < SPLIT_INDEX) {
|
||||||
|
assert_c_eq(t->id,
|
||||||
|
entries[(i+j) % SPLIT_INDEX].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
} else {
|
||||||
|
assert_c_eq(t->id, entries[(i+j-SPLIT_INDEX) %
|
||||||
|
(NENTRIES-SPLIT_INDEX) + SPLIT_INDEX].id,
|
||||||
|
"Element id mismatch");
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_BEGIN(test_qr_meld_split)
|
||||||
|
{
|
||||||
|
ring_t entries[NENTRIES];
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
init_entries(entries);
|
||||||
|
for (i = 1; i < NENTRIES; i++)
|
||||||
|
qr_after_insert(&entries[i - 1], &entries[i], link);
|
||||||
|
|
||||||
|
qr_split(&entries[0], &entries[SPLIT_INDEX], link);
|
||||||
|
test_split_entries(entries);
|
||||||
|
|
||||||
|
qr_meld(&entries[0], &entries[SPLIT_INDEX], link);
|
||||||
|
test_entries_ring(entries);
|
||||||
|
|
||||||
|
qr_meld(&entries[0], &entries[SPLIT_INDEX], link);
|
||||||
|
test_split_entries(entries);
|
||||||
|
|
||||||
|
qr_split(&entries[0], &entries[SPLIT_INDEX], link);
|
||||||
|
test_entries_ring(entries);
|
||||||
|
|
||||||
|
qr_split(&entries[0], &entries[0], link);
|
||||||
|
test_entries_ring(entries);
|
||||||
|
|
||||||
|
qr_meld(&entries[0], &entries[0], link);
|
||||||
|
test_entries_ring(entries);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
return (test(
|
||||||
|
test_qr_one,
|
||||||
|
test_qr_after_insert,
|
||||||
|
test_qr_remove,
|
||||||
|
test_qr_before_insert,
|
||||||
|
test_qr_meld_split));
|
||||||
|
}
|
327
test/unit/rb.c
Normal file
327
test/unit/rb.c
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
#include "test/jemalloc_test.h"
|
||||||
|
|
||||||
|
#define rbtn_black_height(a_type, a_field, a_rbt, r_height) do { \
|
||||||
|
a_type *rbp_bh_t; \
|
||||||
|
for (rbp_bh_t = (a_rbt)->rbt_root, (r_height) = 0; \
|
||||||
|
rbp_bh_t != &(a_rbt)->rbt_nil; \
|
||||||
|
rbp_bh_t = rbtn_left_get(a_type, a_field, rbp_bh_t)) { \
|
||||||
|
if (rbtn_red_get(a_type, a_field, rbp_bh_t) == false) { \
|
||||||
|
(r_height)++; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
typedef struct node_s node_t;
|
||||||
|
|
||||||
|
struct node_s {
|
||||||
|
#define NODE_MAGIC 0x9823af7e
|
||||||
|
uint32_t magic;
|
||||||
|
rb_node(node_t) link;
|
||||||
|
uint64_t key;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
node_cmp(node_t *a, node_t *b) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
assert_u32_eq(a->magic, NODE_MAGIC, "Bad magic");
|
||||||
|
assert_u32_eq(b->magic, NODE_MAGIC, "Bad magic");
|
||||||
|
|
||||||
|
ret = (a->key > b->key) - (a->key < b->key);
|
||||||
|
if (ret == 0) {
|
||||||
|
/*
|
||||||
|
* Duplicates are not allowed in the tree, so force an
|
||||||
|
* arbitrary ordering for non-identical items with equal keys.
|
||||||
|
*/
|
||||||
|
ret = (((uintptr_t)a) > ((uintptr_t)b))
|
||||||
|
- (((uintptr_t)a) < ((uintptr_t)b));
|
||||||
|
}
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef rb_tree(node_t) tree_t;
|
||||||
|
rb_gen(static, tree_, tree_t, node_t, link, node_cmp);
|
||||||
|
|
||||||
|
TEST_BEGIN(test_rb_empty)
|
||||||
|
{
|
||||||
|
tree_t tree;
|
||||||
|
node_t key;
|
||||||
|
|
||||||
|
tree_new(&tree);
|
||||||
|
|
||||||
|
assert_ptr_null(tree_first(&tree), "Unexpected node");
|
||||||
|
assert_ptr_null(tree_last(&tree), "Unexpected node");
|
||||||
|
|
||||||
|
key.key = 0;
|
||||||
|
key.magic = NODE_MAGIC;
|
||||||
|
assert_ptr_null(tree_search(&tree, &key), "Unexpected node");
|
||||||
|
|
||||||
|
key.key = 0;
|
||||||
|
key.magic = NODE_MAGIC;
|
||||||
|
assert_ptr_null(tree_nsearch(&tree, &key), "Unexpected node");
|
||||||
|
|
||||||
|
key.key = 0;
|
||||||
|
key.magic = NODE_MAGIC;
|
||||||
|
assert_ptr_null(tree_psearch(&tree, &key), "Unexpected node");
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
tree_recurse(node_t *node, unsigned black_height, unsigned black_depth,
|
||||||
|
node_t *nil)
|
||||||
|
{
|
||||||
|
unsigned ret = 0;
|
||||||
|
node_t *left_node = rbtn_left_get(node_t, link, node);
|
||||||
|
node_t *right_node = rbtn_right_get(node_t, link, node);
|
||||||
|
|
||||||
|
if (rbtn_red_get(node_t, link, node) == false)
|
||||||
|
black_depth++;
|
||||||
|
|
||||||
|
/* Red nodes must be interleaved with black nodes. */
|
||||||
|
if (rbtn_red_get(node_t, link, node)) {
|
||||||
|
node_t *t_node = rbtn_left_get(node_t, link, left_node);
|
||||||
|
assert_false(rbtn_red_get(node_t, link, left_node),
|
||||||
|
"Node should be black");
|
||||||
|
t_node = rbtn_right_get(node_t, link, left_node);
|
||||||
|
assert_false(rbtn_red_get(node_t, link, left_node),
|
||||||
|
"Node should be black");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node == nil)
|
||||||
|
return (ret);
|
||||||
|
/* Self. */
|
||||||
|
assert_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
|
||||||
|
|
||||||
|
/* Left subtree. */
|
||||||
|
if (left_node != nil)
|
||||||
|
ret += tree_recurse(left_node, black_height, black_depth, nil);
|
||||||
|
else
|
||||||
|
ret += (black_depth != black_height);
|
||||||
|
|
||||||
|
/* Right subtree. */
|
||||||
|
if (right_node != nil)
|
||||||
|
ret += tree_recurse(right_node, black_height, black_depth, nil);
|
||||||
|
else
|
||||||
|
ret += (black_depth != black_height);
|
||||||
|
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static node_t *
|
||||||
|
tree_iterate_cb(tree_t *tree, node_t *node, void *data)
|
||||||
|
{
|
||||||
|
unsigned *i = (unsigned *)data;
|
||||||
|
node_t *search_node;
|
||||||
|
|
||||||
|
assert_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
|
||||||
|
|
||||||
|
/* Test rb_search(). */
|
||||||
|
search_node = tree_search(tree, node);
|
||||||
|
assert_ptr_eq(search_node, node,
|
||||||
|
"tree_search() returned unexpected node");
|
||||||
|
|
||||||
|
/* Test rb_nsearch(). */
|
||||||
|
search_node = tree_nsearch(tree, node);
|
||||||
|
assert_ptr_eq(search_node, node,
|
||||||
|
"tree_nsearch() returned unexpected node");
|
||||||
|
|
||||||
|
/* Test rb_psearch(). */
|
||||||
|
search_node = tree_psearch(tree, node);
|
||||||
|
assert_ptr_eq(search_node, node,
|
||||||
|
"tree_psearch() returned unexpected node");
|
||||||
|
|
||||||
|
(*i)++;
|
||||||
|
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
tree_iterate(tree_t *tree)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
tree_iter(tree, NULL, tree_iterate_cb, (void *)&i);
|
||||||
|
|
||||||
|
return (i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
tree_iterate_reverse(tree_t *tree)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
tree_reverse_iter(tree, NULL, tree_iterate_cb, (void *)&i);
|
||||||
|
|
||||||
|
return (i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
node_remove(tree_t *tree, node_t *node, unsigned nnodes)
|
||||||
|
{
|
||||||
|
node_t *search_node;
|
||||||
|
unsigned black_height, imbalances;
|
||||||
|
|
||||||
|
tree_remove(tree, node);
|
||||||
|
|
||||||
|
/* Test rb_nsearch(). */
|
||||||
|
search_node = tree_nsearch(tree, node);
|
||||||
|
assert(search_node == NULL || search_node->key >= node->key);
|
||||||
|
|
||||||
|
/* Test rb_psearch(). */
|
||||||
|
search_node = tree_psearch(tree, node);
|
||||||
|
assert(search_node == NULL || search_node->key <= node->key);
|
||||||
|
|
||||||
|
node->magic = 0;
|
||||||
|
|
||||||
|
rbtn_black_height(node_t, link, tree, black_height);
|
||||||
|
imbalances = tree_recurse(tree->rbt_root, black_height, 0,
|
||||||
|
&(tree->rbt_nil));
|
||||||
|
assert_u_eq(imbalances, 0, "Tree is unbalanced");
|
||||||
|
assert(nnodes - 1 == tree_iterate(tree));
|
||||||
|
assert(nnodes - 1 == tree_iterate_reverse(tree));
|
||||||
|
}
|
||||||
|
|
||||||
|
static node_t *
|
||||||
|
remove_iterate_cb(tree_t *tree, node_t *node, void *data)
|
||||||
|
{
|
||||||
|
unsigned *nnodes = (unsigned *)data;
|
||||||
|
node_t *ret = tree_next(tree, node);
|
||||||
|
|
||||||
|
node_remove(tree, node, *nnodes);
|
||||||
|
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static node_t *
|
||||||
|
remove_reverse_iterate_cb(tree_t *tree, node_t *node, void *data)
|
||||||
|
{
|
||||||
|
unsigned *nnodes = (unsigned *)data;
|
||||||
|
node_t *ret = tree_prev(tree, node);
|
||||||
|
|
||||||
|
node_remove(tree, node, *nnodes);
|
||||||
|
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_BEGIN(test_rb_random)
|
||||||
|
{
|
||||||
|
#define NNODES 25
|
||||||
|
#define NBAGS 250
|
||||||
|
#define SEED 42
|
||||||
|
sfmt_t *sfmt;
|
||||||
|
uint64_t bag[NNODES];
|
||||||
|
tree_t tree;
|
||||||
|
node_t nodes[NNODES];
|
||||||
|
unsigned i, j, k, black_height, imbalances;
|
||||||
|
|
||||||
|
sfmt = init_gen_rand(SEED);
|
||||||
|
for (i = 0; i < NBAGS; i++) {
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
/* Insert in order. */
|
||||||
|
for (j = 0; j < NNODES; j++)
|
||||||
|
bag[j] = j;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* Insert in reverse order. */
|
||||||
|
for (j = 0; j < NNODES; j++)
|
||||||
|
bag[j] = NNODES - j - 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
for (j = 0; j < NNODES; j++)
|
||||||
|
bag[j] = gen_rand64_range(sfmt, NNODES);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 1; j <= NNODES; j++) {
|
||||||
|
/* Initialize tree and nodes. */
|
||||||
|
tree_new(&tree);
|
||||||
|
tree.rbt_nil.magic = 0;
|
||||||
|
for (k = 0; k < j; k++) {
|
||||||
|
nodes[k].magic = NODE_MAGIC;
|
||||||
|
nodes[k].key = bag[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert nodes. */
|
||||||
|
for (k = 0; k < j; k++) {
|
||||||
|
tree_insert(&tree, &nodes[k]);
|
||||||
|
|
||||||
|
rbtn_black_height(node_t, link, &tree,
|
||||||
|
black_height);
|
||||||
|
imbalances = tree_recurse(tree.rbt_root,
|
||||||
|
black_height, 0, &(tree.rbt_nil));
|
||||||
|
assert_u_eq(imbalances, 0,
|
||||||
|
"Tree is unbalanced");
|
||||||
|
|
||||||
|
assert_u_eq(tree_iterate(&tree), k+1,
|
||||||
|
"Unexpected node iteration count");
|
||||||
|
assert_u_eq(tree_iterate_reverse(&tree), k+1,
|
||||||
|
"Unexpected node iteration count");
|
||||||
|
|
||||||
|
assert_ptr_not_null(tree_first(&tree),
|
||||||
|
"Tree should not be empty");
|
||||||
|
assert_ptr_not_null(tree_last(&tree),
|
||||||
|
"Tree should not be empty");
|
||||||
|
|
||||||
|
tree_next(&tree, &nodes[k]);
|
||||||
|
tree_prev(&tree, &nodes[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove nodes. */
|
||||||
|
switch (i % 4) {
|
||||||
|
case 0:
|
||||||
|
for (k = 0; k < j; k++)
|
||||||
|
node_remove(&tree, &nodes[k], j - k);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
for (k = j; k > 0; k--)
|
||||||
|
node_remove(&tree, &nodes[k-1], k);
|
||||||
|
break;
|
||||||
|
case 2: {
|
||||||
|
node_t *start;
|
||||||
|
unsigned nnodes = j;
|
||||||
|
|
||||||
|
start = NULL;
|
||||||
|
do {
|
||||||
|
start = tree_iter(&tree, start,
|
||||||
|
remove_iterate_cb, (void *)&nnodes);
|
||||||
|
nnodes--;
|
||||||
|
} while (start != NULL);
|
||||||
|
assert_u_eq(nnodes, 0,
|
||||||
|
"Removal terminated early");
|
||||||
|
break;
|
||||||
|
} case 3: {
|
||||||
|
node_t *start;
|
||||||
|
unsigned nnodes = j;
|
||||||
|
|
||||||
|
start = NULL;
|
||||||
|
do {
|
||||||
|
start = tree_reverse_iter(&tree, start,
|
||||||
|
remove_reverse_iterate_cb,
|
||||||
|
(void *)&nnodes);
|
||||||
|
nnodes--;
|
||||||
|
} while (start != NULL);
|
||||||
|
assert_u_eq(nnodes, 0,
|
||||||
|
"Removal terminated early");
|
||||||
|
break;
|
||||||
|
} default:
|
||||||
|
not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fini_gen_rand(sfmt);
|
||||||
|
#undef NNODES
|
||||||
|
#undef NBAGS
|
||||||
|
#undef SEED
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
return (test(
|
||||||
|
test_rb_empty,
|
||||||
|
test_rb_random));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user