170 lines
4.3 KiB
Odin

package spanpool
import "base:runtime"
import "core:c/libc"
import "core:log"
import "core:slice"
import "core:testing"
expect_assert :: proc(
t: ^testing.T,
data: $T,
client_proc: proc(_: ^testing.T, data: T),
expr := #caller_expression,
loc := #caller_location,
) {
@(thread_local)
jmp: libc.jmp_buf
context.assertion_failure_proc =
proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! {
log.infof("expected assertion %s: %s ", prefix, message, location = loc)
libc.longjmp(&jmp, 1)
}
jmp_res := libc.setjmp(&jmp)
if jmp_res == 0 {
client_proc(t, data)
libc.longjmp(&jmp, 2)
}
if jmp_res != 1 {
log.errorf("%v DID NOT assert", expr, location = loc)
}
}
@(test)
test_basic_alloc :: proc(t: ^testing.T) {
pool := Span_Pool(u32){}
defer destroy_spanpool(&pool)
handle := allocate_elems(&pool, 1, 2, 3, 4)
testing.expect_value(t, handle.first, 0)
testing.expect_value(t, handle.len, 4)
testing.expect_value(t, handle.gen, 1)
}
@(test)
test_basic_free :: proc(t: ^testing.T) {
pool := Span_Pool(u32){}
defer destroy_spanpool(&pool)
handle := allocate_elems(&pool, 1, 2, 3, 4)
free(&pool, handle)
testing.expect_value(t, len(pool.free_spans), 1)
testing.expect_value(t, pool.free_spans[0], Span{first = 0, len = 4})
}
@(test)
test_double_free :: proc(t: ^testing.T) {
pool := Span_Pool(u32){}
defer destroy_spanpool(&pool)
expect_assert(t, &pool, proc(t: ^testing.T, pool: ^Span_Pool(u32)) {
handle := allocate_elems(pool, 1, 2, 3, 4)
free(pool, handle)
free(pool, handle)
})
}
@(test)
test_resolve :: proc(t: ^testing.T) {
pool := Span_Pool(u32){}
defer destroy_spanpool(&pool)
handle := allocate_elems(&pool, 1, 2, 3, 4)
handle2 := allocate_elems(&pool, 4, 3, 2, 1)
testing.expect(t, slice.equal(resolve_slice(&pool, handle), []u32{1, 2, 3, 4}))
testing.expect(t, slice.equal(resolve_slice(&pool, handle2), []u32{4, 3, 2, 1}))
}
@(test)
test_multiple_spans :: proc(t: ^testing.T) {
pool := Span_Pool(u32){}
defer destroy_spanpool(&pool)
handle := allocate_elems(&pool, 1, 2, 3, 4)
handle2 := allocate_elems(&pool, 4, 3, 2, 1)
free(&pool, handle)
free(&pool, handle2)
testing.expect_value(t, len(pool.free_spans), 2)
testing.expect_value(t, pool.free_spans[0], Span{first = 0, len = 4})
testing.expect_value(t, pool.free_spans[1], Span{first = 4, len = 4})
}
@(test)
test_reconcile :: proc(t: ^testing.T) {
pool := Span_Pool(u32){}
defer destroy_spanpool(&pool)
handle := allocate_elems(&pool, 1, 2, 3, 4)
handle2 := allocate_elems(&pool, 4, 3, 2, 1)
free(&pool, handle)
free(&pool, handle2)
reconcile(&pool)
testing.expect_value(t, len(pool.free_spans), 1)
testing.expect_value(t, pool.free_spans[0], Span{first = 0, len = 8})
}
@(test)
test_reconcile_with_gaps :: proc(t: ^testing.T) {
pool := Span_Pool(u32){}
defer destroy_spanpool(&pool)
handle := allocate_elems(&pool, 1, 2, 3, 4)
handle2 := allocate_elems(&pool, 1, 2, 3, 4)
handle3 := allocate_elems(&pool, 1, 2, 3, 4)
handle4 := allocate_elems(&pool, 1, 2, 3, 4)
handle5 := allocate_elems(&pool, 1, 2, 3, 4)
handle6 := allocate_elems(&pool, 1, 2, 3, 4)
handle7 := allocate_elems(&pool, 1, 2, 3, 4)
free(&pool, handle)
free(&pool, handle3)
free(&pool, handle4)
free(&pool, handle6)
free(&pool, handle7)
_, _ = handle2, handle5
reconcile(&pool)
testing.expect_value(t, len(pool.free_spans), 3)
testing.expect_value(t, pool.free_spans[0], Span{first = 0, len = 4})
testing.expect_value(t, pool.free_spans[1], Span{first = 8, len = 8})
testing.expect_value(t, pool.free_spans[2], Span{first = 20, len = 8})
}
@(test)
test_free_span_reuse :: proc(t: ^testing.T) {
pool := Span_Pool(u32){}
defer destroy_spanpool(&pool)
handle := allocate_elems(&pool, 1, 2, 3, 4)
_ = allocate_elems(&pool, 1, 2, 3, 4)
free(&pool, handle)
handle3 := allocate_elems(&pool, 5, 6)
handle4 := allocate_elems(&pool, 7, 8)
handle5 := allocate_elems(&pool, 9, 10)
testing.expect_value(t, handle3, Handle{first = 0, len = 2, gen = 3})
testing.expect_value(t, handle4, Handle{first = 2, len = 2, gen = 3})
testing.expect_value(t, handle5, Handle{first = 8, len = 2, gen = 1})
testing.expect(t, slice.equal(resolve_slice(&pool, handle3), []u32{5, 6}))
testing.expect(t, slice.equal(resolve_slice(&pool, handle4), []u32{7, 8}))
testing.expect(t, slice.equal(resolve_slice(&pool, handle5), []u32{9, 10}))
}