Closes what was left of TODO.md sections 1, 2 and 3, and rewrites that
file to hold outstanding items only.
The API break gets a minor bump, because pre-1.0 the soname carries
MAJOR.MINOR and 0.1 and 0.2 are therefore different ABIs. Five
signatures changed and the ato* contract with them; UPGRADING.md is new
and lists every one, with the before/after for the cases the compiler
cannot warn about.
Section 3.1 is finished: reallocarray with the multiplication checked,
aligned_alloc and posix_memalign, asprintf/vasprintf, scanf/vscanf.
Four functions on that list are deliberately absent rather than missing
-- sprintf, strtok, setbuf and perror -- and TODO.md now says which and
why, so nobody adds them thinking they were forgotten.
Section 1.9, the cross-cutting tests:
tests/test_pool.c drives every failure path AKERR_MAX_ARRAY_ERROR
+ 10 times and checks the pool after each round,
because a wrapper that leaks a slot fails a
hundred calls later in unrelated code. It also
asserts that each error names the function and
file it was raised from, which is what catches a
FAIL that migrates into a helper during a
refactor: status right, message right, origin
quietly lying.
tests/negative/ two sources that must FAIL to compile, built with
-Werror and registered WILL_FAIL. AKERR_NOIGNORE
and the format attributes are enforced by the
compiler and by nothing else; drop either and
every ordinary test still passes.
Thread safety is answered rather than tested: the library is not
thread-safe and cannot be made so from here, because libakerror's error
pool is an unlocked process-global array. README.md says so plainly and
TODO.md carries it as the item blocking any future pthread wrappers.
Doxygen is configured and gated. All 147 public functions have @brief,
a @param each, @throws per status and @return; EXTRACT_ALL is off and
WARN_NO_PARAMDOC on, so `cmake --build build --target docs` fails on an
undocumented entity. It ran to 0 warnings. The Doxyfile carries no
version -- cmake/RunDoxygen.cmake feeds PROJECT_NUMBER in from
project(), so that stays the one place a version is written.
CI now builds against the submodule it pins instead of also installing
libakerror@main and never linking it, adds -Werror, and gains a
sanitizer job. The pre-push hook matches, and runs the docs check too.
Coverage: 99.5% of lines (1643/1651), 100% of functions (147/147). The
eight uncovered lines are each uncovered on purpose and TODO.md says
which and why.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
411 lines
15 KiB
C
411 lines
15 KiB
C
/*
|
|
* Cross-cutting properties every wrapper has to have -- TODO.md section 1.9.
|
|
*
|
|
* Two of them, and neither is about what any individual function computes:
|
|
*
|
|
* Error-pool accounting. libakerror hands out error contexts from a fixed
|
|
* process-global array of AKERR_MAX_ARRAY_ERROR slots. A wrapper that raises
|
|
* an error and forgets to release it does not fail visibly -- it fails
|
|
* AKERR_MAX_ARRAY_ERROR calls later, in whatever unrelated code happens to ask
|
|
* for a slot next, by which time the leak is nowhere near the symptom. So
|
|
* every failure path here is driven AKERR_MAX_ARRAY_ERROR + 10 times, which is
|
|
* past exhaustion several times over, with the pool checked after each round.
|
|
*
|
|
* Stack-trace content. An error that reports the wrong origin is worse than
|
|
* useless when the only debugging you get is a log file after the fact, which
|
|
* is the stated reason this library exists at all. Each assertion here names
|
|
* the function the error must say it came from and the source file it must
|
|
* name, so a FAIL that migrates into a helper is caught.
|
|
*
|
|
* AKSL_RUN already checks the pool after every test in every file. This one
|
|
* checks it inside the loop as well, because a leak of one slot per call needs
|
|
* more than one call to show up.
|
|
*/
|
|
|
|
#include "aksl_capture.h"
|
|
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
|
|
/* Rounds enough to exhaust the pool several times over. */
|
|
#define ROUNDS (AKERR_MAX_ARRAY_ERROR + 10)
|
|
|
|
/*
|
|
* Assert the last error came from `func` in a file whose name ends in `file`.
|
|
* The recorded fname is whatever __FILE__ expanded to at the FAIL site, which is
|
|
* an absolute path under CMake, so this matches on the tail rather than the
|
|
* whole thing.
|
|
*/
|
|
static int came_from(const char *func, const char *file)
|
|
{
|
|
size_t flen = strlen(file);
|
|
size_t rlen = strlen(aksl_last_file);
|
|
|
|
if ( strcmp(aksl_last_function, func) != 0 ) {
|
|
fprintf(stderr, " CHECK FAILED: error says it came from \"%s\", expected \"%s\"\n",
|
|
aksl_last_function, func);
|
|
return 1;
|
|
}
|
|
if ( rlen < flen || strcmp(aksl_last_file + (rlen - flen), file) != 0 ) {
|
|
fprintf(stderr, " CHECK FAILED: error names file \"%s\", expected one ending \"%s\"\n",
|
|
aksl_last_file, file);
|
|
return 1;
|
|
}
|
|
if ( aksl_last_line <= 0 ) {
|
|
fprintf(stderr, " CHECK FAILED: error records line %d\n", aksl_last_line);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Pool accounting */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static int test_memory_wrappers_do_not_leak_slots(void)
|
|
{
|
|
void *ptr = NULL;
|
|
int i = 0;
|
|
|
|
for ( i = 0; i < ROUNDS; i++ ) {
|
|
AKSL_CHECK_STATUS(aksl_malloc(32, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_malloc(0, &ptr), AKERR_VALUE);
|
|
AKSL_CHECK_STATUS(aksl_calloc(0, 1, &ptr), AKERR_VALUE);
|
|
AKSL_CHECK_STATUS(aksl_realloc(NULL, 8), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_free(NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_freep(NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_memset(NULL, 0, 1), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_memcpy(NULL, "a", 1), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_memmove(NULL, "a", 1), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_memcmp(NULL, "a", 1, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_memchr(NULL, 'a', 1, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(aksl_slots_in_use() == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int test_stream_wrappers_do_not_leak_slots(void)
|
|
{
|
|
FILE *fp = NULL;
|
|
size_t moved = 0;
|
|
int i = 0;
|
|
|
|
for ( i = 0; i < ROUNDS; i++ ) {
|
|
AKSL_CHECK_STATUS(aksl_fopen(NULL, "r", &fp), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_fopen("/nonexistent/aksl/x", "r", &fp), ENOENT);
|
|
AKSL_CHECK_STATUS(aksl_fread(NULL, 1, 1, stdin, &moved), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_fwrite(NULL, 1, 1, stdout, &moved), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_fclose(NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_fseek(NULL, 0, SEEK_SET), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_ftell(NULL, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_fgetc(NULL, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_fputs(NULL, stdout), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_remove("/nonexistent/aksl/x"), ENOENT);
|
|
AKSL_CHECK(aksl_slots_in_use() == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int test_format_wrappers_do_not_leak_slots(void)
|
|
{
|
|
char buf[16];
|
|
int count = 0;
|
|
int i = 0;
|
|
|
|
for ( i = 0; i < ROUNDS; i++ ) {
|
|
AKSL_CHECK_STATUS(aksl_printf(NULL, "x"), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_fprintf(NULL, stdout, "x"), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_snprintf(NULL, buf, sizeof(buf), "x"), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_snprintf(&count, buf, 4, "%s", "far too long"),
|
|
AKERR_OUTOFBOUNDS);
|
|
AKSL_CHECK(aksl_slots_in_use() == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int test_conversion_wrappers_do_not_leak_slots(void)
|
|
{
|
|
int iout = 0;
|
|
long lout = 0;
|
|
unsigned long ulout = 0;
|
|
double dout = 0.0;
|
|
int i = 0;
|
|
|
|
for ( i = 0; i < ROUNDS; i++ ) {
|
|
AKSL_CHECK_STATUS(aksl_atoi(NULL, &iout), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_atoi("junk", &iout), AKERR_VALUE);
|
|
AKSL_CHECK_STATUS(aksl_atoi("99999999999999999999", &iout), ERANGE);
|
|
AKSL_CHECK_STATUS(aksl_strtol("junk", NULL, 10, &lout), AKERR_VALUE);
|
|
AKSL_CHECK_STATUS(aksl_strtoul("-1", NULL, 10, &ulout), AKERR_VALUE);
|
|
AKSL_CHECK_STATUS(aksl_strtod("junk", NULL, &dout), AKERR_VALUE);
|
|
AKSL_CHECK(aksl_slots_in_use() == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int test_string_wrappers_do_not_leak_slots(void)
|
|
{
|
|
char buf[8];
|
|
char *at = NULL;
|
|
size_t n = 0;
|
|
int r = 0;
|
|
int i = 0;
|
|
|
|
for ( i = 0; i < ROUNDS; i++ ) {
|
|
AKSL_CHECK_STATUS(aksl_strlen(NULL, &n), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_strcpy(buf, sizeof(buf), "far too long for eight"),
|
|
AKERR_OUTOFBOUNDS);
|
|
AKSL_CHECK_STATUS(aksl_strcat(buf, 0, "x"), AKERR_VALUE);
|
|
AKSL_CHECK_STATUS(aksl_strdup(NULL, &at), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_strcmp(NULL, "b", &r), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_strchr(NULL, 'a', &at), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_strerror(0, buf, 0), AKERR_VALUE);
|
|
AKSL_CHECK(aksl_slots_in_use() == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int test_collection_wrappers_do_not_leak_slots(void)
|
|
{
|
|
aksl_ListNode node;
|
|
aksl_ListNode *head = NULL;
|
|
aksl_List list;
|
|
aksl_TreeNode tree;
|
|
aksl_HashMap map;
|
|
aksl_HashEntry slots[4];
|
|
aksl_StrBuf strbuf;
|
|
size_t n = 0;
|
|
int i = 0;
|
|
|
|
AKSL_CHECK_OK(aksl_list_node_init(&node, NULL));
|
|
AKSL_CHECK_OK(aksl_list_init(&list));
|
|
AKSL_CHECK_OK(aksl_tree_node_init(&tree, NULL));
|
|
AKSL_CHECK_OK(aksl_hashmap_init(&map, slots, 4));
|
|
memset((void *)&strbuf, 0x00, sizeof(strbuf));
|
|
|
|
for ( i = 0; i < ROUNDS; i++ ) {
|
|
AKSL_CHECK_STATUS(aksl_list_append(NULL, &node), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_list_pop(&head, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_list_prepend(NULL, &node), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_list_length(&node, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_list_remove(&list, &node), AKERR_VALUE);
|
|
AKSL_CHECK_STATUS(aksl_tree_iterate(&tree, NULL, NULL, NULL,
|
|
AKSL_TREE_SEARCH_DFS_PREORDER, NULL),
|
|
AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_tree_count(&tree, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_hashmap_get(&map, "absent", NULL, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_strbuf_append(&strbuf, "x"), AKERR_NULLPOINTER);
|
|
AKSL_CHECK_STATUS(aksl_strhash_djb2(NULL, 0, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(aksl_slots_in_use() == 0);
|
|
}
|
|
(void)n;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* A traversal that fails part-way through has the most opportunities to leak: an
|
|
* error is raised inside a callback, propagated up through several frames, and
|
|
* handled or not at the top. Drive that repeatedly too.
|
|
*/
|
|
static akerr_ErrorContext AKERR_NOIGNORE *always_fails(aksl_TreeNode *node, void *data)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
(void)node;
|
|
(void)data;
|
|
FAIL_RETURN(e, AKERR_VALUE, "callback always fails");
|
|
}
|
|
|
|
static akerr_ErrorContext AKERR_NOIGNORE *always_breaks(aksl_TreeNode *node, void *data)
|
|
{
|
|
PREPARE_ERROR(e);
|
|
(void)node;
|
|
(void)data;
|
|
FAIL_RETURN(e, AKERR_ITERATOR_BREAK, "callback always breaks");
|
|
}
|
|
|
|
static int test_traversal_failures_do_not_leak_slots(void)
|
|
{
|
|
aksl_TreeNode tree[3];
|
|
int i = 0;
|
|
|
|
for ( i = 0; i < 3; i++ ) {
|
|
AKSL_CHECK_OK(aksl_tree_node_init(&tree[i], NULL));
|
|
}
|
|
tree[0].left = &tree[1];
|
|
tree[0].right = &tree[2];
|
|
|
|
for ( i = 0; i < ROUNDS; i++ ) {
|
|
/* Propagated out of the recursion. */
|
|
AKSL_CHECK_STATUS(aksl_tree_iterate(&tree[0], &always_fails, NULL, NULL,
|
|
AKSL_TREE_SEARCH_DFS_PREORDER, NULL),
|
|
AKERR_VALUE);
|
|
/* Swallowed at the top -- the released-not-returned path. */
|
|
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &always_breaks, NULL, NULL,
|
|
AKSL_TREE_SEARCH_DFS_PREORDER, NULL));
|
|
/* And through the breadth-first walk, which also drains a queue. */
|
|
AKSL_CHECK_STATUS(aksl_tree_iterate(&tree[0], &always_fails, NULL, NULL,
|
|
AKSL_TREE_SEARCH_BFS, NULL),
|
|
AKERR_VALUE);
|
|
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &always_breaks, NULL, NULL,
|
|
AKSL_TREE_SEARCH_BFS, NULL));
|
|
AKSL_CHECK(aksl_slots_in_use() == 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Stack-trace content */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* Each of these asserts that the error names the function that raised it and
|
|
* the file that function lives in. This is what catches a FAIL that gets moved
|
|
* into a shared helper during a refactor: the status stays right, the message
|
|
* stays right, and the origin quietly starts pointing at the wrong place.
|
|
*/
|
|
static int test_errors_name_their_origin_in_stdlib(void)
|
|
{
|
|
void *ptr = NULL;
|
|
int count = 0;
|
|
char resolved[PATH_MAX];
|
|
uint32_t h = 0;
|
|
aksl_ListNode node;
|
|
|
|
AKSL_CHECK_STATUS(aksl_malloc(32, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_malloc", "src/stdlib.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_free(NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_free", "src/stdlib.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_memcpy(NULL, "a", 1), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_memcpy", "src/stdlib.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_fopen(NULL, "r", (FILE **)&ptr), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_fopen", "src/stdlib.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_fclose(NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_fclose", "src/stdlib.c") == 0);
|
|
|
|
/*
|
|
* aksl_printf forwards to aksl_vprintf, so the error legitimately comes
|
|
* from the latter -- the variadic form is a three-line wrapper with no FAIL
|
|
* of its own. Asserting the real origin rather than the one a reader might
|
|
* expect is the point of recording it.
|
|
*/
|
|
AKSL_CHECK_STATUS(aksl_printf(NULL, "x"), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_vprintf", "src/stdlib.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_snprintf(&count, NULL, 8, "x"), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_vsnprintf", "src/stdlib.c") == 0);
|
|
|
|
/* Likewise, the ato* forms are calls into the strto* ones. */
|
|
AKSL_CHECK_STATUS(aksl_atol("junk", (long *)&ptr), AKERR_VALUE);
|
|
AKSL_CHECK(came_from("aksl_strtol", "src/stdlib.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_realpath(NULL, resolved, sizeof(resolved)), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_realpath", "src/stdlib.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_strhash_djb2(NULL, 0, &h), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_strhash_djb2", "src/stdlib.c") == 0);
|
|
|
|
AKSL_CHECK_OK(aksl_list_node_init(&node, NULL));
|
|
AKSL_CHECK_STATUS(aksl_list_append(NULL, &node), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_list_append", "src/stdlib.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_tree_iterate(NULL, NULL, NULL, NULL,
|
|
AKSL_TREE_SEARCH_DFS_PREORDER, NULL),
|
|
AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_tree_iterate", "src/stdlib.c") == 0);
|
|
return 0;
|
|
}
|
|
|
|
static int test_errors_name_their_origin_in_string_and_stream(void)
|
|
{
|
|
char buf[8];
|
|
size_t n = 0;
|
|
long pos = 0;
|
|
|
|
AKSL_CHECK_STATUS(aksl_strlen(NULL, &n), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_strlen", "src/string.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_strcpy(buf, sizeof(buf), "far too long for eight"),
|
|
AKERR_OUTOFBOUNDS);
|
|
AKSL_CHECK(came_from("aksl_strcpy", "src/string.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_strdup(NULL, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_strdup", "src/string.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_strerror(0, buf, 0), AKERR_VALUE);
|
|
AKSL_CHECK(came_from("aksl_strerror", "src/string.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_fseek(NULL, 0, SEEK_SET), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_fseek", "src/stream.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_ftell(NULL, &pos), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_ftell", "src/stream.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_getline(NULL, NULL, NULL, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_getline", "src/stream.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_mkdtemp("no-placeholder"), AKERR_VALUE);
|
|
AKSL_CHECK(came_from("aksl_mkdtemp", "src/stream.c") == 0);
|
|
return 0;
|
|
}
|
|
|
|
static int test_errors_name_their_origin_in_collections(void)
|
|
{
|
|
aksl_ListNode node;
|
|
aksl_List list;
|
|
aksl_HashMap map;
|
|
aksl_HashEntry slots[4];
|
|
aksl_StrBuf strbuf;
|
|
char longkey[AKSL_HASHMAP_MAX_KEY + 4];
|
|
|
|
AKSL_CHECK_OK(aksl_list_node_init(&node, NULL));
|
|
AKSL_CHECK_OK(aksl_list_init(&list));
|
|
AKSL_CHECK_OK(aksl_hashmap_init(&map, slots, 4));
|
|
memset((void *)&strbuf, 0x00, sizeof(strbuf));
|
|
memset(longkey, 'k', sizeof(longkey) - 1);
|
|
longkey[sizeof(longkey) - 1] = '\0';
|
|
|
|
AKSL_CHECK_STATUS(aksl_list_prepend(NULL, &node), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_list_prepend", "src/collections.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_list_remove(&list, &node), AKERR_VALUE);
|
|
AKSL_CHECK(came_from("aksl_list_remove", "src/collections.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_tree_insert(NULL, NULL, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_tree_insert", "src/collections.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_hashmap_put(&map, longkey, NULL), AKERR_OUTOFBOUNDS);
|
|
AKSL_CHECK(came_from("aksl_hashmap_put", "src/collections.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_strhash_fnv1a(NULL, 0, NULL), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_strhash_fnv1a", "src/collections.c") == 0);
|
|
|
|
AKSL_CHECK_STATUS(aksl_strbuf_reset(&strbuf), AKERR_NULLPOINTER);
|
|
AKSL_CHECK(came_from("aksl_strbuf_reset", "src/collections.c") == 0);
|
|
return 0;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
int failures = 0;
|
|
|
|
akerr_init();
|
|
|
|
AKSL_RUN(failures, test_memory_wrappers_do_not_leak_slots);
|
|
AKSL_RUN(failures, test_stream_wrappers_do_not_leak_slots);
|
|
AKSL_RUN(failures, test_format_wrappers_do_not_leak_slots);
|
|
AKSL_RUN(failures, test_conversion_wrappers_do_not_leak_slots);
|
|
AKSL_RUN(failures, test_string_wrappers_do_not_leak_slots);
|
|
AKSL_RUN(failures, test_collection_wrappers_do_not_leak_slots);
|
|
AKSL_RUN(failures, test_traversal_failures_do_not_leak_slots);
|
|
|
|
AKSL_RUN(failures, test_errors_name_their_origin_in_stdlib);
|
|
AKSL_RUN(failures, test_errors_name_their_origin_in_string_and_stream);
|
|
AKSL_RUN(failures, test_errors_name_their_origin_in_collections);
|
|
|
|
AKSL_REPORT(failures);
|
|
}
|