Files
utun2/tests/test_mem.c
T

131 lines
4.2 KiB
C

/* Проверяем размеры реальных malloc и OOM без выделения гигабайт памяти. */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "debug_config.h"
#include "mem.h"
#define CHECK(expr) do { \
if (!(expr)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #expr); \
exit(EXIT_FAILURE); \
} \
} while (0)
static bool fail_malloc;
static size_t malloc_calls;
static size_t malloc_size;
void* __real_malloc(size_t size);
void* __wrap_malloc(size_t size) {
malloc_calls++;
malloc_size = size;
if (fail_malloc) {
fail_malloc = false;
return NULL;
}
return __real_malloc(size);
}
static void test_small_allocations(void) {
const uint32_t sizes[] = {0, 1, 3, 4, 255, 256, 257, 1024};
size_t count = u_get_allocated_count();
for (size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) {
uint8_t* ptr = u_malloc(sizes[i]);
CHECK(ptr != NULL);
CHECK(u_get_allocated_count() == count + 1);
memset(ptr, 0x5a, sizes[i]);
u_check(ptr, "small allocation", LOCATION);
u_free(ptr);
CHECK(u_get_allocated_count() == count);
}
puts("PASS: zero, odd and ordinary allocation sizes");
}
static void test_oom(void) {
size_t count = u_get_allocated_count();
fail_malloc = true;
CHECK(u_malloc(0) == NULL);
CHECK(!fail_malloc);
fail_malloc = true;
CHECK(u_calloc(0, UINT32_MAX) == NULL);
CHECK(!fail_malloc);
fail_malloc = true;
CHECK(u_calloc(UINT32_MAX, 0) == NULL);
CHECK(!fail_malloc);
fail_malloc = true;
CHECK(u_realloc(NULL, 0) == NULL);
CHECK(!fail_malloc);
fail_malloc = true;
CHECK(u_malloc(17) == NULL);
CHECK(!fail_malloc);
CHECK(u_get_allocated_count() == count);
char* ptr = u_strdup("preserve on OOM");
CHECK(ptr != NULL);
fail_malloc = true;
CHECK(u_realloc(ptr, 128) == NULL);
CHECK(!fail_malloc);
CHECK(strcmp(ptr, "preserve on OOM") == 0);
CHECK(u_get_allocated_count() == count + 1);
u_free(ptr);
CHECK(u_get_allocated_count() == count);
puts("PASS: OOM for zero/nonzero sizes and realloc preserves its old block");
}
static void test_large_sizes(void) {
/* Измеряем служебный размер по запросу malloc для пустого блока. */
void* empty = u_malloc(0);
CHECK(empty != NULL);
size_t overhead = malloc_size;
u_free(empty);
CHECK(overhead > 0 && overhead <= 1024);
const uint32_t sizes[] = {
UINT32_MAX - 1024, UINT32_MAX - (uint32_t)overhead, UINT32_MAX - (uint32_t)overhead + 1, UINT32_MAX - 1, UINT32_MAX
};
size_t count = u_get_allocated_count();
char* old = u_strdup("preserve on large OOM");
CHECK(old != NULL);
for (size_t i = 0; i < sizeof(sizes) / sizeof(sizes[0]); i++) {
for (int api = 0; api < 3; api++) {
size_t calls = malloc_calls;
fail_malloc = true;
void* ptr;
if (api == 0) ptr = u_malloc(sizes[i]);
else if (api == 1) ptr = u_calloc(sizes[i], 1);
else ptr = u_realloc(old, sizes[i]);
CHECK(ptr == NULL);
if ((uint64_t)sizes[i] + overhead > SIZE_MAX) {
CHECK(malloc_calls == calls);
CHECK(fail_malloc);
fail_malloc = false;
} else {
CHECK(malloc_calls == calls + 1);
CHECK(!fail_malloc);
CHECK(malloc_size == (uint64_t)sizes[i] + overhead);
}
CHECK(u_get_allocated_count() == count + 1);
CHECK(strcmp(old, "preserve on large OOM") == 0);
}
}
u_free(old);
CHECK(u_get_allocated_count() == count);
puts("PASS: sizes near UINT32_MAX reach malloc intact or are rejected before overflow");
}
int main(void) {
debug_config_init();
debug_set_level(DEBUG_LEVEL_ERROR);
debug_set_category_level(DEBUG_CATEGORY_SYS, DEBUG_LEVEL_WARN);
test_small_allocations();
test_oom();
test_large_sizes();
CHECK(u_get_allocated_count() == 0);
puts("PASS: mem regression tests");
return 0;
}