diff --git a/lib/std/core/allocators/tracking_allocator.c3 b/lib/std/core/allocators/tracking_allocator.c3 index 79877c57e..a43ea9c7c 100644 --- a/lib/std/core/allocators/tracking_allocator.c3 +++ b/lib/std/core/allocators/tracking_allocator.c3 @@ -113,8 +113,14 @@ fn void TrackingAllocator.clear(&self) self.map.clear(); } +fn bool TrackingAllocator.has_leaks(&self) +{ + return self.map.len() > 0; +} + fn void TrackingAllocator.print_report(&self) => self.fprint_report(io::stdout())!!; + fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool() { usz total = 0; diff --git a/lib/std/core/runtime_test.c3 b/lib/std/core/runtime_test.c3 index d3b00c678..cc0b23bad 100644 --- a/lib/std/core/runtime_test.c3 +++ b/lib/std/core/runtime_test.c3 @@ -260,21 +260,31 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private if (libc::setjmp(&context.buf) == 0) { mute_output(); - $if(!$$OLD_TEST): - unit.func(); - $else - if (catch err = unit.func()) - { - io::printf("[FAIL] Failed due to: %s", err); - continue; - } - $endif + TrackingAllocator mem; + mem.init(allocator::heap()); + bool has_leaks; + mem::@scoped(&mem) + { + $if(!$$OLD_TEST): + unit.func(); + $else + if (catch err = unit.func()) + { + io::printf("[FAIL] Failed due to: %s", err); + continue; + } + $endif + has_leaks = mem.has_leaks(); + }; + mem.free(); unmute_output(false); // all good, discard output - - io::printfn(test_context.has_ansi_codes ? "[\e[0;32mPASS\e[0m]" : "[PASS]"); + io::printf(test_context.has_ansi_codes ? "[\e[0;32mPASS\e[0m]" : "[PASS]"); tests_passed++; + if (has_leaks) io::print(" LEAKS!"); + io::printn(); + if (test_context.teardown_fn) { test_context.teardown_fn(); diff --git a/test/unit/regression/liveness_any.c3 b/test/unit/regression/liveness_any.c3 index 8ae500eb1..bd271ca61 100644 --- a/test/unit/regression/liveness_any.c3 +++ b/test/unit/regression/liveness_any.c3 @@ -17,4 +17,6 @@ fn void reflect_test() @test { TestProto b = mem::alloc(Test); b.tesT(); + defer free(b); + } \ No newline at end of file diff --git a/test/unit/regression/lvalue_handling.c3 b/test/unit/regression/lvalue_handling.c3 index e54482245..a07be6a71 100644 --- a/test/unit/regression/lvalue_handling.c3 +++ b/test/unit/regression/lvalue_handling.c3 @@ -8,6 +8,8 @@ def IntList = List(); fn void subscript_overload() @test { IntList x; + defer x.free(); + x.push({ 3 }); int* a = &x[0].a; assert(*a == 3); diff --git a/test/unit/stdlib/atomic.c3 b/test/unit/stdlib/atomic.c3 index b7ba45bb1..3842ebd33 100644 --- a/test/unit/stdlib/atomic.c3 +++ b/test/unit/stdlib/atomic.c3 @@ -5,7 +5,7 @@ import std::atomic; uint a; float fa; -fn void add() @test +fn void add() @test => mem::@scoped(&allocator::LIBC_ALLOCATOR) { Thread[100] ts; a = 0; @@ -42,7 +42,7 @@ fn void add() @test assert(a == ts.len * 10 * 5, "Threads returned %d, expected %d", a, ts.len * 10 * 5); } -fn void sub() @test +fn void sub() @test => mem::@scoped(&allocator::LIBC_ALLOCATOR) { Thread[100] ts; a = ts.len * 10 * 5; @@ -79,7 +79,7 @@ fn void sub() @test assert(a == 0, "Threads returned %d, expected %d", a, 0); } -fn void div() @test +fn void div() @test => mem::@scoped(&allocator::LIBC_ALLOCATOR) { Thread[8] ts; a = 8 * 8 * 8 * 8 * 8 * 8 * 8 * 8 * 8; @@ -98,7 +98,7 @@ fn void div() @test assert(a == 8, "Threads returned %d, expected %d", a, 8); } -fn void max() @test +fn void max() @test => mem::@scoped(&allocator::LIBC_ALLOCATOR) { Thread[100] ts; a = 0; @@ -134,7 +134,7 @@ fn void max() @test assert(a == 5, "Threads returned %d, expected %d", a, 5); } -fn void min() @test +fn void min() @test => mem::@scoped(&allocator::LIBC_ALLOCATOR) { Thread[100] ts; a = 10; @@ -170,7 +170,7 @@ fn void min() @test assert(a == 0, "Threads returned %d, expected %d", a, 0); } -fn void fadd() @test +fn void fadd() @test => mem::@scoped(&allocator::LIBC_ALLOCATOR) { Thread[100] ts; fa = 0; @@ -207,7 +207,7 @@ fn void fadd() @test assert(fa == ts.len * 10 * 0.5, "Threads returned %f, expected %f", fa, ts.len * 10 * 0.5); } -fn void fsub() @test +fn void fsub() @test => mem::@scoped(&allocator::LIBC_ALLOCATOR) { Thread[100] ts; fa = ts.len * 10 * 0.5; diff --git a/test/unit/stdlib/atomic_types.c3 b/test/unit/stdlib/atomic_types.c3 index 8c9c434c5..0cd721855 100644 --- a/test/unit/stdlib/atomic_types.c3 +++ b/test/unit/stdlib/atomic_types.c3 @@ -7,7 +7,7 @@ def AtomicFloat = Atomic(); AtomicUint a; AtomicFloat fa; -fn void add() @test +fn void add() @test => mem::@scoped(&allocator::LIBC_ALLOCATOR) { Thread[100] ts; a.store(0); @@ -44,7 +44,7 @@ fn void add() @test assert(a.load() == ts.len * 10 * 5, "Threads returned %d, expected %d", a.load(), ts.len * 10 * 5); } -fn void sub() @test +fn void sub() @test => mem::@scoped(&allocator::LIBC_ALLOCATOR) { Thread[100] ts; a.store(ts.len * 10 * 5); @@ -81,7 +81,7 @@ fn void sub() @test assert(a.load() == 0, "Threads returned %d, expected %d", a.load(), 0); } -fn void fadd() @test +fn void fadd() @test => mem::@scoped(&allocator::LIBC_ALLOCATOR) { Thread[100] ts; fa.store(0); @@ -118,7 +118,7 @@ fn void fadd() @test assert(fa.load() == ts.len * 10 * 0.5, "Threads returned %f, expected %f", fa.load(), ts.len * 10 * 0.5); } -fn void fsub() @test +fn void fsub() @test => mem::@scoped(&allocator::LIBC_ALLOCATOR) { Thread[100] ts; fa.store(ts.len * 10 * 0.5); diff --git a/test/unit/stdlib/collections/linkedlist.c3 b/test/unit/stdlib/collections/linkedlist.c3 index 2ce4be4fc..d0c94c195 100644 --- a/test/unit/stdlib/collections/linkedlist.c3 +++ b/test/unit/stdlib/collections/linkedlist.c3 @@ -6,6 +6,7 @@ def IntList = LinkedList(); fn void test_push_front() { IntList list; + defer list.free(); list.push_front(23); assert(list.len() == 1); assert(list.first()!! == 23); @@ -14,11 +15,13 @@ fn void test_push_front() assert(list.len() == 2); assert(list.last()!! == 23); assert(list.first()!! == 55); + } fn void test_push() { IntList list; + defer list.free(); list.push(23); assert(list.len() == 1); assert(list.first()!! == 23); @@ -32,6 +35,7 @@ fn void test_push() fn void test_get() { IntList list; + defer list.free(); list.push(23); list.push(55); list.push(-3); @@ -43,6 +47,7 @@ fn void test_get() fn void test_insert() { IntList list; + defer list.free(); list.push(-3); list.push(55); list.push(23); @@ -62,6 +67,7 @@ fn void test_insert() fn void test_set() { IntList list; + defer list.free(); list.push(-3); list.push(55); list.push(23); @@ -74,6 +80,7 @@ fn void test_set() fn void test_remove_at() { IntList list; + defer list.free(); for (int i = 0; i < 10; i++) list.push(i); list.remove_at(0); list.remove_at(1); @@ -88,6 +95,7 @@ fn void test_remove_at() fn void test_remove() { IntList list; + defer list.free(); list.push(2); for (int i = 0; i < 10; i++) list.push(5); list.push(2); @@ -98,6 +106,7 @@ fn void test_remove() fn void test_remove_first_match() { IntList list; + defer list.free(); list.push(23); list.push(55); list.push(-3); @@ -126,6 +135,7 @@ fn void test_remove_first_match() fn void test_remove_last_match() { IntList list; + defer list.free(); list.push(23); list.push(55); list.push(-3); @@ -154,6 +164,7 @@ fn void test_remove_last_match() fn void test_pop() { IntList list; + defer list.free(); list.push(23); list.push(55); list.push(-3); @@ -178,6 +189,7 @@ fn void test_pop() fn void test_remove_last() { IntList list; + defer list.free(); list.push(23); list.push(55); list.push(-3); @@ -202,6 +214,7 @@ fn void test_remove_last() fn void test_remove_first() { IntList list; + defer list.free(); list.push(23); list.push(55); list.push(-3); diff --git a/test/unit/stdlib/collections/list.c3 b/test/unit/stdlib/collections/list.c3 index 91a2bcd7f..6a31585e1 100644 --- a/test/unit/stdlib/collections/list.c3 +++ b/test/unit/stdlib/collections/list.c3 @@ -13,6 +13,8 @@ def OveralignList = List(); fn void overaligned_type() { OveralignList l; + defer l.free(); + Overalign y; for (int i = 0; i < 1000; i++) l.push(y); assert((usz)l.get_ref(2) - (usz)l.get_ref(1) == Overalign.sizeof); @@ -22,6 +24,8 @@ fn void overaligned_type() fn void delete_contains_index() { IntList test; + defer test.free(); + test.add_array({ 1, 2 }); assert(test.contains(1)); assert(test.contains(2)); @@ -51,6 +55,7 @@ fn void delete_contains_index() fn void compact() { PtrList test; + defer test.free(); test.add_array({ null, &test }); assert(test.compact_count() == 1); test.push(null); @@ -64,6 +69,8 @@ fn void compact() fn void reverse() { IntList test; + defer test.free(); + test.reverse(); test.add_array({ 1, 2 }); test.push(3); @@ -79,6 +86,7 @@ fn void reverse() fn void remove_if() { IntList test; + defer test.free(); usz removed; test.add_array({ 1, 11, 2, 10, 20 }); @@ -96,6 +104,7 @@ fn void remove_if() fn void init_with_array() { IntList foo; + defer foo.free(); foo.new_init_with_array({ 1, 2, 3}); assert(foo.len() == 3); assert(foo[2] == 3); @@ -112,6 +121,7 @@ fn void init_with_temp_array() fn void remove_using_test() { IntList test; + defer test.free(); usz removed; test.add_array({ 1, 11, 2, 10, 20 }); @@ -129,6 +139,7 @@ fn void remove_using_test() fn void retain_if() { IntList test; + defer test.free(); usz removed; test.add_array({ 1, 11, 2, 10, 20 }); @@ -146,6 +157,7 @@ fn void retain_if() fn void retain_using_test() { IntList test; + defer test.free(); usz removed; test.add_array({ 1, 11, 2, 10, 20 }); diff --git a/test/unit/stdlib/collections/object.c3 b/test/unit/stdlib/collections/object.c3 index 5cb4b614e..35053fc36 100644 --- a/test/unit/stdlib/collections/object.c3 +++ b/test/unit/stdlib/collections/object.c3 @@ -4,6 +4,8 @@ import std::collections::object; fn void test_general() { Object* root = object::new_obj(allocator::heap()); + defer root.free(); + root.set("foo", 1); root.set("bar", "baz"); assert(root.get_int("foo")!! == 1); diff --git a/test/unit/stdlib/collections/priorityqueue.c3 b/test/unit/stdlib/collections/priorityqueue.c3 index a95fff150..44bd0c7bc 100644 --- a/test/unit/stdlib/collections/priorityqueue.c3 +++ b/test/unit/stdlib/collections/priorityqueue.c3 @@ -7,6 +7,8 @@ def Queue = PriorityQueue(); fn void priorityqueue() { Queue q; + defer q.free(); + assert(q.is_empty()); q.push(1); @@ -35,6 +37,7 @@ def QueueMax = PriorityQueueMax(); fn void priorityqueue_max() { QueueMax q; + defer q.free(); assert(q.is_empty()); q.push(1); diff --git a/test/unit/stdlib/compression/qoi.c3 b/test/unit/stdlib/compression/qoi.c3 index bf027e5bd..c617edc87 100644 --- a/test/unit/stdlib/compression/qoi.c3 +++ b/test/unit/stdlib/compression/qoi.c3 @@ -30,5 +30,8 @@ fn void test_qoi_all() // cleanup file::delete("unittest.qoi")!!; + free(encoded); + free(decoded); + free(read); }; } diff --git a/test/unit/stdlib/core/array.c3 b/test/unit/stdlib/core/array.c3 index 03d92b252..e356bed5b 100644 --- a/test/unit/stdlib/core/array.c3 +++ b/test/unit/stdlib/core/array.c3 @@ -21,11 +21,12 @@ fn void find_subarray() fn void concat() { int[3] a = { 1, 2, 3 }; - (void)array::concat_new(a, a); - (void)array::concat_new(a[..], a[..]); - (void)array::concat_new(a[:0], a[:0]); - (void)array::concat_new(int[2] { 1, 2 }, a[:0]); - (void)array::concat_new(a[:0], int[2] { 1, 2 }); + free(array::concat_new(a, a)); + free(array::concat_new(a[..], a[..])); + free(array::concat_new(a[:0], a[:0])); + free(array::concat_new(int[2] { 1, 2 }, a[:0])); + free(array::concat_new(a[:0], int[2] { 1, 2 })); int[] c = array::concat_new(a[1..2], a); + defer free(c); assert (c == int[]{ 2, 3, 1, 2, 3 }); } diff --git a/test/unit/stdlib/io/printf.c3 b/test/unit/stdlib/io/printf.c3 index d52373019..9b51a066f 100644 --- a/test/unit/stdlib/io/printf.c3 +++ b/test/unit/stdlib/io/printf.c3 @@ -5,14 +5,19 @@ fn void printf_int() String s; s = string::new_format("[%-10d]", 78); assert(s == "[78 ]"); + free(s); s = string::new_format("[%10d]", 78); assert(s == "[ 78]"); + free(s); s = string::new_format("[%010d]", 78); assert(s == "[0000000078]"); + free(s); s = string::new_format("[%+10d]", 78); assert(s == "[ +78]"); + free(s); s = string::new_format("[%-+10d]", 78); assert(s == "[+78 ]"); + free(s); } fn void printf_a() @@ -20,51 +25,69 @@ fn void printf_a() String s; s = string::new_format("%08.2a", 234.125); assert(s == "0x1.d4p+7", "got '%s'; want '0x1.d4p+7'", s); + free(s); s = string::new_format("%a", 234.125); assert(s == "0x1.d44p+7", "got '%s'; want '0x1.d44p+7'", s); + free(s); s = string::new_format("%A", 234.125); assert(s == "0X1.D44P+7", "got '%s'; want '0X1.D44P+7'", s); + free(s); s = string::new_format("%20a", 234.125); assert(s == " 0x1.d44p+7", "got '%s'; want ' 0x1.d44p+7'", s); + free(s); s = string::new_format("%-20a", 234.125); assert(s == "0x1.d44p+7 ", "got '%s'; want '0x1.d44p+7 '", s); + free(s); s = string::new_format("%-20s", "hello world"); assert(s == "hello world ", "got '%s'; want 'hello world '", s); + free(s); s = string::new_format("%20s", "hello world"); assert(s == " hello world", "got '%s'; want ' hello world'", s); + free(s); String str = "hello!"; s = string::new_format("%-20s", str); assert(s == "hello! ", "got '%s'; want 'hello! '", s); + free(s); s = string::new_format("%20s", str); assert(s == " hello!", "got '%s'; want ' hello!'", s); + free(s); int[2] a = { 12, 23 }; s = string::new_format("%-20s", a); assert(s == "[12, 23] ", "got '%s'; want '[12, 23] '", s); + free(s); s = string::new_format("%20s", a); assert(s == " [12, 23]", "got '%s'; want ' [12, 23]'", s); + free(s); s = string::new_format("%-20s", a[..]); assert(s == "[12, 23] ", "got '%s'; want '[12, 23] '", s); + free(s); s = string::new_format("%20s", a[..]); assert(s == " [12, 23]", "got '%s'; want ' [12, 23]'", s); + free(s); float[2] f = { 12.0, 23.0 }; s = string::new_format("%-24s", f); assert(s == "[12.000000, 23.000000] ", "got '%s'; want '[12.000000, 23.000000] '", s); + free(s); s = string::new_format("%24s", f); assert(s == " [12.000000, 23.000000]", "got '%s'; want ' [12.000000, 23.000000]'", s); + free(s); int[<2>] vec = { 12, 23 }; s = string::new_format("%-20s", vec); assert(s == "[<12, 23>] ", "got '%s'; want '[<12, 23>] '", s); + free(s); s = string::new_format("%20s", vec); assert(s == " [<12, 23>]", "got '%s'; want ' [<12, 23>]'", s); + free(s); String ss = "hello world"; s = string::new_format("%.4s %.5s", ss, ss); assert(s == "hell hello", "got '%s'; want 'hell hello'", s); + free(s); } enum PrintfTest : ushort @@ -79,11 +102,15 @@ fn void printf_enum() s = string::new_format("%s", PrintfTest.ENUMA); assert(s == "ENUMA", "got '%s'; want 'ENUMA'", s); + free(s); s = string::new_format("%s", PrintfTest.ENUMB); assert(s == "ENUMB", "got '%s'; want 'ENUMB'", s); + free(s); s = string::new_format("%d", PrintfTest.ENUMA); assert(s == "0", "got '%s'; want '0'", s); + free(s); s = string::new_format("%d", PrintfTest.ENUMB); assert(s == "1", "got '%s'; want '1'", s); + free(s); } \ No newline at end of file diff --git a/test/unit/stdlib/net/inetaddr.c3 b/test/unit/stdlib/net/inetaddr.c3 index 76f0e1b74..6c8572302 100644 --- a/test/unit/stdlib/net/inetaddr.c3 +++ b/test/unit/stdlib/net/inetaddr.c3 @@ -12,20 +12,21 @@ fn void test_ipv4() fn void test_ipv4_to_string() { InetAddress a = net::ipv4_from_str("127.0.0.1")!!; - assert(a.to_new_string() == "127.0.0.1"); + assert(a.to_new_string(allocator::temp()) == "127.0.0.1"); } fn void test_ipv6_to_string() { InetAddress a = net::ipv6_from_str("2001:db8::2:1")!!; - a.to_new_string(); - assert(a.to_new_string() == "2001:0db8:0000:0000:0000:0000:0002:0001"); - assert(net::ipv6_from_str("2001:db8::1").to_new_string()!! == "2001:0db8:0000:0000:0000:0000:0000:0001"); - assert(net::ipv6_from_str("::1").to_new_string()!! == "0000:0000:0000:0000:0000:0000:0000:0001"); - assert(net::ipv6_from_str("2001::1").to_new_string()!! == "2001:0000:0000:0000:0000:0000:0000:0001"); - assert(net::ipv6_from_str("2001:db8:1234::").to_new_string()!! == "2001:0db8:1234:0000:0000:0000:0000:0000"); - assert(net::ipv6_from_str("2001::").to_new_string()!! == "2001:0000:0000:0000:0000:0000:0000:0000"); - assert(net::ipv6_from_str("::").to_new_string()!! == "0000:0000:0000:0000:0000:0000:0000:0000"); + free(a.to_new_string()); + + assert(a.to_new_string(allocator::temp()) == "2001:0db8:0000:0000:0000:0000:0002:0001"); + assert(net::ipv6_from_str("2001:db8::1").to_new_string(allocator::temp())!! == "2001:0db8:0000:0000:0000:0000:0000:0001"); + assert(net::ipv6_from_str("::1").to_new_string(allocator::temp())!! == "0000:0000:0000:0000:0000:0000:0000:0001"); + assert(net::ipv6_from_str("2001::1").to_new_string(allocator::temp())!! == "2001:0000:0000:0000:0000:0000:0000:0001"); + assert(net::ipv6_from_str("2001:db8:1234::").to_new_string(allocator::temp())!! == "2001:0db8:1234:0000:0000:0000:0000:0000"); + assert(net::ipv6_from_str("2001::").to_new_string(allocator::temp())!! == "2001:0000:0000:0000:0000:0000:0000:0000"); + assert(net::ipv6_from_str("::").to_new_string(allocator::temp())!! == "0000:0000:0000:0000:0000:0000:0000:0000"); } fn void test_ipv4_parse() diff --git a/test/unit/stdlib/os/env.c3 b/test/unit/stdlib/os/env.c3 index 34234b770..ae927b9a3 100644 --- a/test/unit/stdlib/os/env.c3 +++ b/test/unit/stdlib/os/env.c3 @@ -8,7 +8,7 @@ module std::os::env @test; env::set_var(NAME, VALUE); String v = env::get_var(NAME)!!; assert(v == VALUE, "got %s; want %s", v, VALUE); - + free(v); env::clear_var(NAME); if (try env::get_var(NAME)) { diff --git a/test/unit/stdlib/time/format.c3 b/test/unit/stdlib/time/format.c3 index 310078719..0c07fd62c 100644 --- a/test/unit/stdlib/time/format.c3 +++ b/test/unit/stdlib/time/format.c3 @@ -28,6 +28,7 @@ fn void test_with_tz() foreach (test : tests) { String candidate = test.first.new_format(test.second); + defer free(candidate); assert(candidate == test.third, "got: '%s', expected: '%s'", candidate, test.third); } } @@ -44,6 +45,7 @@ fn void test_without_tz() foreach (test : tests) { String candidate = test.first.new_format(test.second); + defer free(candidate); assert(candidate == test.third, "got: '%s', expected: '%s'", candidate, test.third); } }