Add unit tests + fix allocator
This commit is contained in:
parent
6c6f7eb902
commit
d86948b6a9
4 changed files with 495 additions and 16 deletions
254
axl_koan.h
Normal file
254
axl_koan.h
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
/*
|
||||||
|
* koan.h — minimalistic unit-testing library
|
||||||
|
* Author: NukeBird, 2025
|
||||||
|
* License: public domain / MIT — as you wish
|
||||||
|
*/
|
||||||
|
#ifndef KOAN_H
|
||||||
|
#define KOAN_H
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <io.h>
|
||||||
|
#define koan_isatty(_f) _isatty(_fileno(_f))
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#define koan_isatty(_f) isatty(fileno(_f))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct koan_test
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
void (*func)(jmp_buf);
|
||||||
|
struct koan_test* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct koan_test* koan_tests = NULL;
|
||||||
|
static struct koan_test** koan_tail = &koan_tests;
|
||||||
|
|
||||||
|
static void koan_add_test(const char* name, void (*func)(jmp_buf))
|
||||||
|
{
|
||||||
|
struct koan_test* test = malloc(sizeof *test);
|
||||||
|
if (!test) {
|
||||||
|
fprintf(stderr, "koan: out of memory for test registration\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
test->name = name;
|
||||||
|
test->func = func;
|
||||||
|
test->next = NULL;
|
||||||
|
*koan_tail = test;
|
||||||
|
koan_tail = &test->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* koan_fail_msg = NULL;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Assertion macros */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#define KOAN_FAIL(reason) \
|
||||||
|
do { \
|
||||||
|
size_t _sz = strlen(reason) + strlen(__FILE__) + 128; \
|
||||||
|
char* _buf = malloc(_sz); \
|
||||||
|
if (_buf) { \
|
||||||
|
snprintf(_buf, _sz, "%s\n at %s:%d", reason, __FILE__, __LINE__); \
|
||||||
|
} else { \
|
||||||
|
_buf = "koan: malloc failed while formatting error message"; \
|
||||||
|
} \
|
||||||
|
koan_fail_msg = _buf; \
|
||||||
|
longjmp(koan_jmp_buf, 1); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERT_TRUE(cond) \
|
||||||
|
do { if (!(cond)) { char _r[256]; snprintf(_r, sizeof(_r), "ASSERT_TRUE failed: (%s) is false", #cond); KOAN_FAIL(_r); } } while (0)
|
||||||
|
|
||||||
|
#define ASSERT_FALSE(cond) \
|
||||||
|
do { if (cond) { char _r[256]; snprintf(_r, sizeof(_r), "ASSERT_FALSE failed: (%s) is true", #cond); KOAN_FAIL(_r); } } while (0)
|
||||||
|
|
||||||
|
#define ASSERT_INT_EQ(expected, actual) \
|
||||||
|
do { \
|
||||||
|
int64_t _e = (int64_t)(expected); \
|
||||||
|
int64_t _a = (int64_t)(actual); \
|
||||||
|
if (_e != _a) { \
|
||||||
|
char _r[256]; \
|
||||||
|
snprintf(_r, sizeof(_r), "ASSERT_INT_EQ failed: expected %" PRId64 " but got %" PRId64, _e, _a); \
|
||||||
|
KOAN_FAIL(_r); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERT_UINT_EQ(expected, actual) \
|
||||||
|
do { \
|
||||||
|
uint64_t _e = (uint64_t)(expected); \
|
||||||
|
uint64_t _a = (uint64_t)(actual); \
|
||||||
|
if (_e != _a) { \
|
||||||
|
char _r[256]; \
|
||||||
|
snprintf(_r, sizeof(_r), "ASSERT_UINT_EQ failed: expected %" PRIu64 " but got %" PRIu64, _e, _a); \
|
||||||
|
KOAN_FAIL(_r); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERT_DOUBLE_EQ(expected, actual, eps) \
|
||||||
|
do { \
|
||||||
|
double _e = (double)(expected); \
|
||||||
|
double _a = (double)(actual); \
|
||||||
|
double _eps = (double)(eps); \
|
||||||
|
if (fabs(_e - _a) > _eps) { \
|
||||||
|
char _r[512]; \
|
||||||
|
snprintf(_r, sizeof(_r), "ASSERT_DOUBLE_EQ failed: expected %g but got %g (diff %g > eps %g)", \
|
||||||
|
_e, _a, fabs(_e - _a), _eps); \
|
||||||
|
KOAN_FAIL(_r); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERT_STR_EQ(expected, actual) \
|
||||||
|
do { \
|
||||||
|
const char* _e = (expected); \
|
||||||
|
const char* _a = (actual); \
|
||||||
|
if ((_e == NULL || _a == NULL) ? (_e != _a) : strcmp(_e, _a)) { \
|
||||||
|
char _r[512]; \
|
||||||
|
snprintf(_r, sizeof(_r), "ASSERT_STR_EQ failed: expected \"%s\" but got \"%s\"", \
|
||||||
|
_e ? _e : "(null)", _a ? _a : "(null)"); \
|
||||||
|
KOAN_FAIL(_r); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERT_PTR_EQ(expected, actual) \
|
||||||
|
do { \
|
||||||
|
const void* _e = (const void*)(expected); \
|
||||||
|
const void* _a = (const void*)(actual); \
|
||||||
|
if (_e != _a) { \
|
||||||
|
char _r[256]; \
|
||||||
|
snprintf(_r, sizeof(_r), "ASSERT_PTR_EQ failed: expected %p but got %p", _e, _a); \
|
||||||
|
KOAN_FAIL(_r); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERT_NULL(ptr) ASSERT_PTR_EQ(NULL, (ptr))
|
||||||
|
#define ASSERT_NOT_NULL(ptr) ASSERT_TRUE((ptr) != NULL)
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Koan definition */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
#define KOAN(name) \
|
||||||
|
static void koan_test_##name(jmp_buf); \
|
||||||
|
__attribute__((constructor)) static void koan_register_##name(void) \
|
||||||
|
{ koan_add_test(#name, koan_test_##name); } \
|
||||||
|
static void koan_test_##name(jmp_buf koan_jmp_buf)
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Time formatting */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
static void koan_format_time(double secs, char* buf, size_t bufsz)
|
||||||
|
{
|
||||||
|
double ns = secs * 1e9;
|
||||||
|
if (ns < 1e3) snprintf(buf, bufsz, "%.0f ns", ns);
|
||||||
|
else if (ns < 1e6) snprintf(buf, bufsz, "%.2f µs", ns / 1e3);
|
||||||
|
else if (ns < 1e9) snprintf(buf, bufsz, "%.2f ms", ns / 1e6);
|
||||||
|
else snprintf(buf, bufsz, "%.3f s", secs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Progress bar (final only) */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
static void koan_print_bar(int done, int total, int passed, size_t failed, double elapsed)
|
||||||
|
{
|
||||||
|
const int w = 40;
|
||||||
|
int pos = (int)((float)done * w / total);
|
||||||
|
printf("[");
|
||||||
|
for (int i = 0; i < w; ++i) putchar(i < pos ? '=' : (i == pos ? '>' : '-'));
|
||||||
|
printf("] %3d%% %d/%d passed:%d failed:%zu %.3fs\n",
|
||||||
|
(int)((float)done * 100 / total), done, total, passed, failed, elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Run all koans — simple sequential mode only */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
struct koan_failure { const char* name; const char* msg; };
|
||||||
|
|
||||||
|
int koan_run_all(void)
|
||||||
|
{
|
||||||
|
printf("\n");
|
||||||
|
int total = 0;
|
||||||
|
for (struct koan_test* t = koan_tests; t; t = t->next) total++;
|
||||||
|
|
||||||
|
if (total == 0) {
|
||||||
|
printf("koan: no koans — no enlightenment.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int colorful = koan_isatty(stdout);
|
||||||
|
const char* G = colorful ? "\033[32m" : "";
|
||||||
|
const char* R = colorful ? "\033[31m" : "";
|
||||||
|
const char* Y = colorful ? "\033[33m" : "";
|
||||||
|
const char* N = colorful ? "\033[0m" : "";
|
||||||
|
|
||||||
|
const char* OK = colorful ? "PASS " : "[PASS] ";
|
||||||
|
const char* FAIL = colorful ? "FAIL " : "[FAIL] ";
|
||||||
|
|
||||||
|
printf("%sEnlightening %d %s...%s\n\n", Y, total, total == 1 ? "koan" : "koans", N);
|
||||||
|
|
||||||
|
struct koan_failure* failures = NULL;
|
||||||
|
size_t fail_cap = 0, fail_cnt = 0;
|
||||||
|
int passed = 0;
|
||||||
|
clock_t start = clock();
|
||||||
|
|
||||||
|
for (struct koan_test* t = koan_tests; t; t = t->next) {
|
||||||
|
koan_fail_msg = NULL;
|
||||||
|
clock_t ts = clock();
|
||||||
|
jmp_buf env;
|
||||||
|
int result = setjmp(env);
|
||||||
|
if (result == 0) {
|
||||||
|
t->func(env);
|
||||||
|
double sec = (double)(clock() - ts) / CLOCKS_PER_SEC;
|
||||||
|
char tbuf[32];
|
||||||
|
koan_format_time(sec, tbuf, sizeof tbuf);
|
||||||
|
printf(" %s%s%s (%s)%s\n", G, OK, t->name, tbuf, N);
|
||||||
|
passed++;
|
||||||
|
} else {
|
||||||
|
double sec = (double)(clock() - ts) / CLOCKS_PER_SEC;
|
||||||
|
char tbuf[32];
|
||||||
|
koan_format_time(sec, tbuf, sizeof tbuf);
|
||||||
|
printf(" %s%s%s (%s)%s\n", R, FAIL, t->name, tbuf, N);
|
||||||
|
|
||||||
|
if (fail_cnt == fail_cap) {
|
||||||
|
fail_cap = fail_cap ? fail_cap * 2 : 16;
|
||||||
|
failures = realloc(failures, fail_cap * sizeof *failures);
|
||||||
|
if (!failures) exit(1);
|
||||||
|
}
|
||||||
|
failures[fail_cnt].name = t->name;
|
||||||
|
failures[fail_cnt].msg = koan_fail_msg ? koan_fail_msg : "unknown failure";
|
||||||
|
fail_cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double total_time = (double)(clock() - start) / CLOCKS_PER_SEC;
|
||||||
|
koan_print_bar(total, total, passed, fail_cnt, total_time);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if (fail_cnt == 0) {
|
||||||
|
printf("%sAll %d %s achieved enlightenment in %.3f s!%s\n", G, total,
|
||||||
|
total == 1 ? "koan" : "koans", total_time, N);
|
||||||
|
free(failures);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
printf("%s%zu / %d %s did not achieve enlightenment (%.3f s)%s\n\n", R,
|
||||||
|
fail_cnt, total, fail_cnt == 1 ? "koan" : "koans", total_time, N);
|
||||||
|
printf("%s--- Failed Koans ---%s\n\n", R, N);
|
||||||
|
for (size_t i = 0; i < fail_cnt; ++i) {
|
||||||
|
printf(" %sKoan \"%s\" failed%s\n", R, failures[i].name, N);
|
||||||
|
printf(" %s\n\n", failures[i].msg);
|
||||||
|
free((void*)failures[i].msg);
|
||||||
|
}
|
||||||
|
free(failures);
|
||||||
|
printf("%sEnlightenment not achieved.%s\n", R, N);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* KOAN_H */
|
||||||
20
axl_memory.c
20
axl_memory.c
|
|
@ -45,7 +45,7 @@ static mb_header* axl_id_to_mb(mb_handle id)
|
||||||
return (mb_header*)&memory[id];
|
return (mb_header*)&memory[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
static mb_handle axl_ptr_to_id(mb_header* ptr)
|
static mb_handle axl_mb_to_id(mb_header* ptr)
|
||||||
{
|
{
|
||||||
return (mb_handle)((u8*)ptr - memory);
|
return (mb_handle)((u8*)ptr - memory);
|
||||||
}
|
}
|
||||||
|
|
@ -126,15 +126,15 @@ static b8 axl_split_mb(mb_header* block, u32 size)
|
||||||
new_block->size = block->size - size - MB_HEADER_SIZE;
|
new_block->size = block->size - size - MB_HEADER_SIZE;
|
||||||
new_block->is_free = true;
|
new_block->is_free = true;
|
||||||
new_block->next = block->next;
|
new_block->next = block->next;
|
||||||
new_block->prev = axl_ptr_to_id(block);
|
new_block->prev = axl_mb_to_id(block);
|
||||||
|
|
||||||
if(new_block->next)
|
if(new_block->next != AXL_INVALID_MB_HANDLE)
|
||||||
{
|
{
|
||||||
axl_id_to_mb(new_block->next)->prev = axl_ptr_to_id(new_block);
|
axl_id_to_mb(new_block->next)->prev = axl_mb_to_id(new_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
block->size = size;
|
block->size = size;
|
||||||
block->next = axl_ptr_to_id(new_block);
|
block->next = axl_mb_to_id(new_block);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -256,9 +256,9 @@ void axl_free(void* ptr)
|
||||||
{
|
{
|
||||||
block->size += next_block->size + MB_HEADER_SIZE;
|
block->size += next_block->size + MB_HEADER_SIZE;
|
||||||
block->next = next_block->next;
|
block->next = next_block->next;
|
||||||
if(block->next)
|
if(block->next != AXL_INVALID_MB_HANDLE)
|
||||||
{
|
{
|
||||||
axl_id_to_mb(block->next)->prev = axl_ptr_to_id(block);
|
axl_id_to_mb(block->next)->prev = axl_mb_to_id(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -266,12 +266,12 @@ void axl_free(void* ptr)
|
||||||
{
|
{
|
||||||
prev_block->size += block->size + MB_HEADER_SIZE;
|
prev_block->size += block->size + MB_HEADER_SIZE;
|
||||||
prev_block->next = block->next;
|
prev_block->next = block->next;
|
||||||
if(prev_block->next)
|
if(prev_block->next != AXL_INVALID_MB_HANDLE)
|
||||||
{
|
{
|
||||||
axl_id_to_mb(prev_block->next)->prev = axl_ptr_to_id(prev_block);
|
axl_id_to_mb(prev_block->next)->prev = axl_mb_to_id(prev_block);
|
||||||
}
|
}
|
||||||
free_block = prev_block;
|
free_block = prev_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
nomad_handle = axl_ptr_to_id(free_block);
|
nomad_handle = axl_mb_to_id(free_block);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
212
axl_memory_test.c
Normal file
212
axl_memory_test.c
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* axl_memory_tests.c — Comprehensive unit tests for axl_memory.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "axl_memory.h"
|
||||||
|
#include "axl_koan.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Basic allocation & initialization */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
KOAN(init_called)
|
||||||
|
{
|
||||||
|
void* p = axl_malloc(1);
|
||||||
|
ASSERT_NOT_NULL(p);
|
||||||
|
axl_free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(malloc_returns_different_pointers)
|
||||||
|
{
|
||||||
|
void* a = axl_malloc(16);
|
||||||
|
void* b = axl_malloc(32);
|
||||||
|
ASSERT_NOT_NULL(a);
|
||||||
|
ASSERT_NOT_NULL(b);
|
||||||
|
ASSERT_TRUE(a != b);
|
||||||
|
axl_free(a);
|
||||||
|
axl_free(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(malloc_exhaustion_returns_null)
|
||||||
|
{
|
||||||
|
void* p = axl_malloc(AXL_HEAP_SIZE + 4096); /* definitely too big */
|
||||||
|
ASSERT_NULL(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Realloc behavior (strictly per C standard) */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
KOAN(realloc_null_acts_like_malloc)
|
||||||
|
{
|
||||||
|
void* p = axl_realloc(NULL, 64);
|
||||||
|
ASSERT_NOT_NULL(p);
|
||||||
|
axl_memset(p, 0x55, 64);
|
||||||
|
axl_free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(realloc_zero_size_frees_and_returns_null_or_valid)
|
||||||
|
{
|
||||||
|
void* p = axl_malloc(100);
|
||||||
|
ASSERT_NOT_NULL(p);
|
||||||
|
void* q = axl_realloc(p, 0);
|
||||||
|
if (q != NULL) axl_free(q);
|
||||||
|
/* Original p is freed regardless */
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(realloc_enlarge_preserves_content)
|
||||||
|
{
|
||||||
|
void* p = axl_malloc(32);
|
||||||
|
axl_memset(p, 0xCD, 32);
|
||||||
|
void* q = axl_realloc(p, 128);
|
||||||
|
ASSERT_NOT_NULL(q);
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
|
ASSERT_INT_EQ(0xCD, ((u8*)q)[i]);
|
||||||
|
axl_free(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(realloc_shrink_preserves_prefix)
|
||||||
|
{
|
||||||
|
void* p = axl_malloc(256);
|
||||||
|
axl_memset(p, 0xAB, 256);
|
||||||
|
void* q = axl_realloc(p, 64);
|
||||||
|
ASSERT_NOT_NULL(q);
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
ASSERT_INT_EQ(0xAB, ((u8*)q)[i]);
|
||||||
|
axl_free(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(realloc_same_size_returns_same_or_equivalent)
|
||||||
|
{
|
||||||
|
void* p = axl_malloc(100);
|
||||||
|
|
||||||
|
p = axl_realloc(p, 100);
|
||||||
|
ASSERT_NOT_NULL(p);
|
||||||
|
/* May return same or different pointer */
|
||||||
|
axl_free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Memory operations */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
KOAN(memset_fills_correctly)
|
||||||
|
{
|
||||||
|
void* p = axl_malloc(64);
|
||||||
|
axl_memset(p, 0x55, 64);
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
ASSERT_INT_EQ(0x55, ((u8*)p)[i]);
|
||||||
|
axl_free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(memset_zero_bytes_is_nop)
|
||||||
|
{
|
||||||
|
void* p = axl_malloc(16);
|
||||||
|
axl_memset(p, 0xFF, 16);
|
||||||
|
axl_memset(p, 0x00, 0); /* should not change anything */
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
ASSERT_INT_EQ(0xFF, ((u8*)p)[i]);
|
||||||
|
axl_free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(memcpy_copies_exact_bytes)
|
||||||
|
{
|
||||||
|
u8 src[32];
|
||||||
|
for (int i = 0; i < 32; i++) src[i] = (u8)i;
|
||||||
|
void* dst = axl_malloc(32);
|
||||||
|
axl_memcpy(dst, src, 32);
|
||||||
|
ASSERT_INT_EQ(0, memcmp(dst, src, 32));
|
||||||
|
axl_free(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(memcpy_zero_bytes_is_nop)
|
||||||
|
{
|
||||||
|
u8 data[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
|
||||||
|
void* p = axl_malloc(16);
|
||||||
|
axl_memcpy(p, data, 16);
|
||||||
|
axl_memcpy(p, "junk", 0); /* must not modify */
|
||||||
|
ASSERT_INT_EQ(0, memcmp(p, data, 16));
|
||||||
|
axl_free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(memcmp_returns_correct_sign)
|
||||||
|
{
|
||||||
|
u8 a[] = {0, 0, 0, 0};
|
||||||
|
u8 b[] = {0, 0, 0, 1};
|
||||||
|
u8 c[] = {0, 0, 1, 0};
|
||||||
|
|
||||||
|
ASSERT_INT_EQ(0, axl_memcmp(a, a, 4));
|
||||||
|
ASSERT_TRUE(axl_memcmp(a, b, 4) < 0);
|
||||||
|
ASSERT_TRUE(axl_memcmp(b, a, 4) > 0);
|
||||||
|
ASSERT_TRUE(axl_memcmp(a, c, 3) < 0 );
|
||||||
|
ASSERT_TRUE(axl_memcmp(a, b, 2) == 0);
|
||||||
|
ASSERT_TRUE(axl_memcmp(a, c, 2) == 0);
|
||||||
|
ASSERT_TRUE(axl_memcmp(c, b, 2) == 0);
|
||||||
|
ASSERT_TRUE(axl_memcmp(a, c, 4) < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(memcmp_zero_length_returns_zero)
|
||||||
|
{
|
||||||
|
u8 x = 0xFF, y = 0x00;
|
||||||
|
ASSERT_INT_EQ(0, axl_memcmp(&x, &y, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Double-free and use-after-free detection (indirect) */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
KOAN(double_free_does_not_crash)
|
||||||
|
{
|
||||||
|
void* p = axl_malloc(64);
|
||||||
|
ASSERT_NOT_NULL(p);
|
||||||
|
axl_free(p);
|
||||||
|
axl_free(p); /* should be ignored or handled safely */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Large and boundary allocations */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
KOAN(large_allocation_near_heap_limit)
|
||||||
|
{
|
||||||
|
/* Allocate almost entire heap */
|
||||||
|
void* p = axl_malloc(AXL_HEAP_SIZE - 1024);
|
||||||
|
ASSERT_NOT_NULL(p); /* may pass if overhead is small */
|
||||||
|
if (p) axl_free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
KOAN(allocation_of_max_size_fails)
|
||||||
|
{
|
||||||
|
void* p = axl_malloc(AXL_HEAP_SIZE);
|
||||||
|
ASSERT_NULL(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Stress: many small allocations */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
KOAN(many_small_allocations)
|
||||||
|
{
|
||||||
|
#define N 1000
|
||||||
|
void* ptrs[N];
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
ptrs[i] = axl_malloc(16);
|
||||||
|
ASSERT_NOT_NULL(ptrs[i]);
|
||||||
|
*(u32*)ptrs[i] = 0xDEADBEEF;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
ASSERT_INT_EQ(0xDEADBEEF, *(u32*)ptrs[i]);
|
||||||
|
axl_free(ptrs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Main */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
axl_init();
|
||||||
|
return koan_run_all();
|
||||||
|
}
|
||||||
25
makefile
25
makefile
|
|
@ -1,16 +1,29 @@
|
||||||
CC = clang
|
CC = clang
|
||||||
CFLAGS = -Wall -Wextra -Werror -pedantic -std=c11 -nostdlib -static -Oz -ffreestanding
|
AXL_SOURCES = $(filter-out main.c %_test.c, $(wildcard *.c))
|
||||||
|
TEST_EXES = $(patsubst %.c,%.exe,$(wildcard *_test.c))
|
||||||
|
TEST_CFLAGS = -Wall -Wextra -Werror -pedantic -std=c11 -static -Oz
|
||||||
|
CFLAGS = $(TEST_CFLAGS) -nostdlib -ffreestanding
|
||||||
LDFLAGS = -Wl,/SUBSYSTEM:CONSOLE,/ENTRY:_start -fuse-ld=lld
|
LDFLAGS = -Wl,/SUBSYSTEM:CONSOLE,/ENTRY:_start -fuse-ld=lld
|
||||||
LIBS = -lkernel32
|
LIBS = -lkernel32
|
||||||
SOURCES = $(wildcard *.c)
|
SOURCES = $(AXL_SOURCES) main.c
|
||||||
TARGET = prog.exe
|
TARGET = prog.exe
|
||||||
|
|
||||||
all: clean $(TARGET)
|
all: clean test $(TARGET)
|
||||||
|
|
||||||
$(TARGET): $(SOURCES)
|
%_test.exe: %_test.c
|
||||||
|
$(CC) $(TEST_CFLAGS) $(AXL_SOURCES) $< -o $@
|
||||||
|
|
||||||
|
test: $(TEST_EXES)
|
||||||
|
for %%i in ($(TEST_EXES)) do ( \
|
||||||
|
echo Running %%i & \
|
||||||
|
%%i \
|
||||||
|
)
|
||||||
|
|
||||||
|
$(TARGET): $(SOURCES)
|
||||||
$(CC) $(CFLAGS) $(SOURCES) -o $(TARGET) $(LDFLAGS) $(LIBS)
|
$(CC) $(CFLAGS) $(SOURCES) -o $(TARGET) $(LDFLAGS) $(LIBS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGET)
|
rm -f $(TARGET)
|
||||||
|
rm -f $(TEST_EXES)
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean test
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue