summaryrefslogtreecommitdiff
path: root/tests/lib/malloc-test.c
blob: 452d74f88879dc4d86de5d4acf6d62ea8f1a49e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/* SPDX-License-Identifier: GPL-2.0-only */

/* Include malloc() and memalign() source code and alter its name to indicate the functions
   source origin. */
#define calloc cb_calloc
#define malloc cb_malloc
#define free cb_free
#define memalign cb_memalign
#undef __noreturn
#define __noreturn

#include "../lib/malloc.c"

#undef calloc
#undef malloc
#undef free
#undef memalign
#undef __noreturn
#define __noreturn __attribute__((noreturn))

#include <stdlib.h>
#include <tests/test.h>
#include <commonlib/helpers.h>
#include <types.h>
#include <symbols.h>

/* 4 MiB */
#define TEST_HEAP_SZ 0x400000

/* Heap region setup */
__weak extern uint8_t _test_heap[];
__weak extern uint8_t _etest_heap[];
TEST_REGION(test_heap, TEST_HEAP_SZ);
TEST_SYMBOL(_heap, _test_heap);
TEST_SYMBOL(_eheap, _etest_heap);

void die(const char *msg, ...)
{
	function_called();
}

static int setup_test(void **state)
{
	free_mem_ptr = &_heap;
	free_mem_end_ptr = &_eheap;
	free_last_alloc_ptr = &_heap;

	return 0;
}

static int setup_calloc_test(void **state)
{
	memset(_test_heap, 0xFF, TEST_HEAP_SZ);
	return setup_test(state);
}

static void test_malloc_out_of_memory(void **state)
{
	/* Expect die() call if out of memory */
	expect_function_call(die);
	cb_malloc(TEST_HEAP_SZ);
}

static void test_malloc_zero(void **state)
{
	void *ptr1 = cb_malloc(0);
	void *ptr2 = cb_malloc(0);
	void *ptr3 = cb_malloc(0);

	/* Expect malloc(0) to return the same pointer as there are no bytes
	   to be added to the heap */
	assert_ptr_equal(ptr1, ptr2);
	assert_ptr_equal(ptr2, ptr3);
}

static void test_malloc_multiple_small_allocations(void **state)
{
	/* Make multiple small allocations (smaller than alignment)
	   Expect no call to die(), as this allocations should be small
	   enough to fit in provided memory */
	void *prev;
	void *curr = cb_malloc(3);
	assert_non_null(curr);
	for (int i = 0; i < 1000; ++i) {
		prev = curr;
		curr = cb_malloc(3);
		assert_non_null(curr);
		assert_true(prev < curr);
	}
}

static void test_memalign_different_alignments(void **state)
{
	void *ptr1 = cb_memalign(4, 30);
	void *ptr2 = cb_memalign(16, 22);
	void *ptr3 = cb_memalign(8, 64);

	assert_true((uintptr_t)ptr1 % 4 == 0);
	assert_true((uintptr_t)ptr2 % 16 == 0);
	assert_true((uintptr_t)ptr3 % 8 == 0);
}

static void test_memalign_out_of_memory(void **state)
{
	expect_function_call(die);
	cb_memalign(16, TEST_HEAP_SZ);
}

static void test_memalign_zero(void **state)
{
	void *ptr1 = cb_memalign(16, 0);
	void *ptr2 = cb_memalign(7, 0);
	void *ptr3 = cb_memalign(11, 0);

	/* Expect memalign(x, 0) to return the same pointer as there are no bytes
	   to be added to the heap */
	assert_ptr_equal(ptr1, ptr2);
	assert_ptr_equal(ptr2, ptr3);
}

static void test_memalign_multiple_small_allocations(void **state)
{
	/* Make multiple small allocations (smaller than alignment)
	   Expect no call to die(), as this allocations should be small
	   enough to fit in provided memory. There should also be no error
	   when allocating memory with different align values. */
	void *prev;
	void *curr = cb_memalign(3, 3);
	assert_non_null(curr);
	for (int i = 0; i < 1000; ++i) {
		/* Expect new pointer larger than previously allocated and aligned to provided
		   value. Alignment has to be power of 2 to be applied correctly. */
		prev = curr;
		curr = cb_memalign(2u << (i % 6), 3);
		assert_non_null(curr);
		assert_true(prev < curr);
		assert_true((uintptr_t)curr % (2u << (i % 6)) == 0);
	}
}

static void test_calloc_memory_is_zeroed(void **state)
{
	const size_t nitems = 42;
	const size_t size = sizeof(uint32_t);
	void *ptr = cb_calloc(nitems, size);
	assert_non_null(ptr);

	for (size_t i = 0; i < nitems; i++) {
		const uint32_t *p = (const uint32_t *)ptr + i;
		assert_int_equal(*p, 0);
	}
}

int main(void)
{
	const struct CMUnitTest tests[] = {
		cmocka_unit_test_setup(test_malloc_out_of_memory, setup_test),
		cmocka_unit_test_setup(test_malloc_zero, setup_test),
		cmocka_unit_test_setup(test_malloc_multiple_small_allocations, setup_test),
		cmocka_unit_test_setup(test_memalign_different_alignments, setup_test),
		cmocka_unit_test_setup(test_memalign_out_of_memory, setup_test),
		cmocka_unit_test_setup(test_memalign_zero, setup_test),
		cmocka_unit_test_setup(test_memalign_multiple_small_allocations, setup_test),
		cmocka_unit_test_setup(test_calloc_memory_is_zeroed, setup_calloc_test),
	};

	return cb_run_group_tests(tests, NULL, NULL);
}