#include <sys/types.h>
#define QUEUE_MACRO_DEBUG_ASSERTIONS
#include <sys/queue.h>
#include <stdio.h>
#include <stdlib.h>
#include <atf-c.h>
#define DIAG(fmt, ...) do { \
fprintf(stderr, "%s(): " fmt "\n", __func__, ##__VA_ARGS__); \
} while (0)
#define QUEUE_TESTS_COMMON(type, TYPE) \
\
\
struct type ## _id_elem { \
TYPE ## _ENTRY(type ## _id_elem) ie_entry; \
u_int ie_id; \
}; \
\
TYPE ## _HEAD(type ## _ids, type ## _id_elem); \
\
static void \
type ## _check(const struct type ## _ids *const type, \
const u_int nb, const u_int id_shift); \
\
\
static struct type ## _ids * \
type ## _create(const u_int nb, const u_int id_shift) \
{ \
struct type ## _ids *const type = \
malloc(sizeof(*type)); \
\
ATF_REQUIRE_MSG(type != NULL, \
"Cannot malloc " #type " head"); \
\
TYPE ## _INIT(type); \
for (u_int i = 0; i < nb; ++i) { \
struct type ## _id_elem *const e = \
malloc(sizeof(*e)); \
\
ATF_REQUIRE_MSG(e != NULL, \
"Cannot malloc " #type " element %u", i); \
e->ie_id = nb - 1 - i + id_shift; \
TYPE ## _INSERT_HEAD(type, e, ie_entry); \
} \
\
DIAG("Created " #type " %p with %u elements", \
type, nb); \
type ## _check(type, nb, id_shift); \
return (type); \
} \
\
\
static void \
type ## _destroy(struct type ## _ids *const type) \
{ \
struct type ## _id_elem *e, *tmp_e; \
\
DIAG("Destroying " #type" %p", type); \
TYPE ## _FOREACH_SAFE(e, type, ie_entry, \
tmp_e) { \
free(e); \
} \
free(type); \
} \
\
\
\
static void \
type ## _check(const struct type ## _ids *const type, \
const u_int nb, const u_int id_shift) \
{ \
struct type ## _id_elem *e; \
u_int i = 0; \
\
TYPE ## _FOREACH(e, type, ie_entry) { \
ATF_REQUIRE_MSG(i + 1 <= nb, \
#type " %p has more than %u elements", \
type, nb); \
ATF_REQUIRE_MSG(e->ie_id == i + id_shift, \
#type " %p element %p: Found ID %u, " \
"expected %u", \
type, e, e->ie_id, i + id_shift); \
++i; \
} \
ATF_REQUIRE_MSG(i == nb, \
#type " %p has only %u elements, expected %u", \
type, i, nb); \
} \
\
\
static struct type ## _id_elem * \
type ## _nth(const struct type ## _ids *const type, \
const u_int idx) \
{ \
struct type ## _id_elem *e; \
u_int i = 0; \
\
TYPE ## _FOREACH(e, type, ie_entry) { \
if (i == idx) { \
DIAG(#type " %p has element %p " \
"(ID %u) at index %u", \
type, e, e->ie_id, idx); \
return (e); \
} \
++i; \
} \
DIAG(#type " %p: Only %u elements, no index %u", \
type, i, idx); \
return (NULL); \
} \
\
\
\
ATF_TC(type ## _split_after_and_concat); \
ATF_TC_HEAD(type ## _split_after_and_concat, tc) \
{ \
atf_tc_set_md_var(tc, "descr", \
"Test " #TYPE "_SPLIT_AFTER() followed by " \
#TYPE "_CONCAT()"); \
} \
ATF_TC_BODY(type ## _split_after_and_concat, tc) \
{ \
struct type ## _ids *const type = \
type ## _create(100, 0); \
struct type ## _ids rest; \
struct type ## _id_elem *e; \
\
e = type ## _nth(type, 49); \
TYPE ## _SPLIT_AFTER(type, e, &rest, ie_entry); \
type ## _check(type, 50, 0); \
type ## _check(&rest, 50, 50); \
QUEUE_TESTS_ ## TYPE ## _CONCAT(type, &rest); \
ATF_REQUIRE_MSG(TYPE ## _EMPTY(&rest), \
"'rest' not empty after concat"); \
type ## _check(type, 100, 0); \
type ## _destroy(type); \
}
#define QUEUE_TESTS_CHECK_REVERSED(type, TYPE) \
\
static void \
type ## _check_reversed(const struct type ## _ids *const type, \
const u_int nb, const u_int id_shift) \
{ \
struct type ## _id_elem *e; \
u_int i = 0; \
\
TYPE ## _FOREACH(e, type, ie_entry) { \
const u_int expected_id = nb - 1 - i + id_shift; \
\
ATF_REQUIRE_MSG(i < nb, \
#type " %p has more than %u elements", \
type, nb); \
ATF_REQUIRE_MSG(e->ie_id == expected_id, \
#type " %p element %p, idx %u: Found ID %u, " \
"expected %u", \
type, e, i, e->ie_id, expected_id); \
++i; \
} \
ATF_REQUIRE_MSG(i == nb, \
#type " %p has only %u elements, expected %u", \
type, i, nb); \
}
#define QUEUE_TESTS_TAILQ_CONCAT(first, second) \
TAILQ_CONCAT(first, second, ie_entry)
#define QUEUE_TESTS_LIST_CONCAT(first, second) \
LIST_CONCAT(first, second, list_id_elem, ie_entry)
#define QUEUE_TESTS_STAILQ_CONCAT(first, second) \
STAILQ_CONCAT(first, second)
#define QUEUE_TESTS_SLIST_CONCAT(first, second) \
SLIST_CONCAT(first, second, slist_id_elem, ie_entry)
#define QUEUE_TESTS_REGISTRATION(tp, type) \
ATF_TP_ADD_TC(tp, type ## _split_after_and_concat)
#define QUEUE_TESTS_TQ_PRINT(type, hfp) \
static void \
type ## _print(const struct type ## _ids *const type) \
{ \
printf(#type " %p: " __STRING(hfp ## _first) \
" = %p, " __STRING(hfp ## _last) " = %p\n", \
type, type->hfp ## _first, type->hfp ## _last); \
}
#define QUEUE_TESTS_L_PRINT(type, hfp) \
static void \
type ## _print(const struct type ## _ids *const type) \
{ \
printf(#type " %p: " __STRING(hfp ## _first) " = %p\n", \
type, type->hfp ## _first); \
}
QUEUE_TESTS_COMMON(tailq, TAILQ);
QUEUE_TESTS_COMMON(list, LIST);
QUEUE_TESTS_COMMON(stailq, STAILQ);
QUEUE_TESTS_COMMON(slist, SLIST);
QUEUE_TESTS_CHECK_REVERSED(stailq, STAILQ);
ATF_TC(stailq_reverse);
ATF_TC_HEAD(stailq_reverse, tc)
{
atf_tc_set_md_var(tc, "descr", "Test STAILQ_REVERSE");
}
ATF_TC_BODY(stailq_reverse, tc)
{
const u_int size = 100;
struct stailq_ids *const stailq = stailq_create(size, 0);
struct stailq_ids *const empty_stailq = stailq_create(0, 0);
const struct stailq_id_elem *last;
stailq_check(stailq, size, 0);
STAILQ_REVERSE(stailq, stailq_id_elem, ie_entry);
stailq_check_reversed(stailq, size, 0);
last = STAILQ_LAST(stailq, stailq_id_elem, ie_entry);
ATF_REQUIRE_MSG(last->ie_id == 0,
"Last element of stailq %p has id %u, expected 0",
stailq, last->ie_id);
stailq_destroy(stailq);
STAILQ_REVERSE(empty_stailq, stailq_id_elem, ie_entry);
stailq_check(empty_stailq, 0, 0);
stailq_destroy(empty_stailq);
}
ATF_TP_ADD_TCS(tp)
{
QUEUE_TESTS_REGISTRATION(tp, tailq);
QUEUE_TESTS_REGISTRATION(tp, list);
QUEUE_TESTS_REGISTRATION(tp, stailq);
QUEUE_TESTS_REGISTRATION(tp, slist);
ATF_TP_ADD_TC(tp, stailq_reverse);
return (atf_no_error());
}