diff --git a/core/testing/runner.odin b/core/testing/runner.odin index e3286988c..359039243 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -6,68 +6,68 @@ import "core:os" import "core:slice" reset_t :: proc(t: ^T) { - clear(&t.cleanups); - t.error_count = 0; + clear(&t.cleanups) + t.error_count = 0 } end_t :: proc(t: ^T) { for i := len(t.cleanups)-1; i >= 0; i -= 1 { - c := t.cleanups[i]; - c.procedure(c.user_data); + c := t.cleanups[i] + c.procedure(c.user_data) } } runner :: proc(internal_tests: []Internal_Test) -> bool { - stream := os.stream_from_handle(os.stdout); - w, _ := io.to_writer(stream); + stream := os.stream_from_handle(os.stdout) + w, _ := io.to_writer(stream) - t := &T{}; - t.w = w; - reserve(&t.cleanups, 1024); - defer delete(t.cleanups); + t := &T{} + t.w = w + reserve(&t.cleanups, 1024) + defer delete(t.cleanups) - total_success_count := 0; - total_test_count := len(internal_tests); + total_success_count := 0 + total_test_count := len(internal_tests) slice.sort_by(internal_tests, proc(a, b: Internal_Test) -> bool { if a.pkg < b.pkg { - return true; + return true } - return a.name < b.name; - }); + return a.name < b.name + }) - prev_pkg := ""; + prev_pkg := "" for it in internal_tests { if it.p == nil { - total_test_count -= 1; - continue; + total_test_count -= 1 + continue } - free_all(context.temp_allocator); - reset_t(t); - defer end_t(t); + free_all(context.temp_allocator) + reset_t(t) + defer end_t(t) if prev_pkg != it.pkg { - prev_pkg = it.pkg; - logf(t, "[Package: %s]", it.pkg); + prev_pkg = it.pkg + logf(t, "[Package: %s]", it.pkg) } - logf(t, "[Test: %s]", it.name); + logf(t, "[Test: %s]", it.name) - run_internal_test(t, it); + run_internal_test(t, it) if failed(t) { - logf(t, "[%s : FAILURE]", it.name); + logf(t, "[%s : FAILURE]", it.name) } else { - logf(t, "[%s : SUCCESS]", it.name); - total_success_count += 1; + logf(t, "[%s : SUCCESS]", it.name) + total_success_count += 1 } } - logf(t, "----------------------------------------"); + logf(t, "----------------------------------------") if total_test_count == 0 { - log(t, "NO TESTS RAN"); + log(t, "NO TESTS RAN") } else { - logf(t, "%d/%d SUCCESSFUL", total_success_count, total_test_count); + logf(t, "%d/%d SUCCESSFUL", total_success_count, total_test_count) } - return total_success_count == total_test_count; + return total_success_count == total_test_count } diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin index 51315290b..5d2db5250 100644 --- a/core/testing/runner_windows.odin +++ b/core/testing/runner_windows.odin @@ -12,39 +12,39 @@ Sema :: struct { } sema_reset :: proc "contextless" (s: ^Sema) { - intrinsics.atomic_store(&s.count, 0); + intrinsics.atomic_store(&s.count, 0) } sema_wait :: proc "contextless" (s: ^Sema) { for { - original_count := s.count; + original_count := s.count for original_count == 0 { win32.WaitOnAddress( &s.count, &original_count, size_of(original_count), win32.INFINITE, - ); - original_count = s.count; + ) + original_count = s.count } if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) { - return; + return } } } sema_post :: proc "contextless" (s: ^Sema, count := 1) { - intrinsics.atomic_add(&s.count, i32(count)); + intrinsics.atomic_add(&s.count, i32(count)) if count == 1 { - win32.WakeByAddressSingle(&s.count); + win32.WakeByAddressSingle(&s.count) } else { - win32.WakeByAddressAll(&s.count); + win32.WakeByAddressAll(&s.count) } } -Thread_Proc :: #type proc(^Thread); +Thread_Proc :: #type proc(^Thread) -MAX_USER_ARGUMENTS :: 8; +MAX_USER_ARGUMENTS :: 8 Thread :: struct { using specific: Thread_Os_Specific, @@ -67,66 +67,66 @@ Thread_Os_Specific :: struct { thread_create :: proc(procedure: Thread_Proc) -> ^Thread { __windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD { - t := (^Thread)(t_); - context = t.init_context.? or_else runtime.default_context(); + t := (^Thread)(t_) + context = t.init_context.? or_else runtime.default_context() - t.procedure(t); + t.procedure(t) if t.init_context == nil { if context.temp_allocator.data == &runtime.global_default_temp_allocator_data { - runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data); + runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data) } } - intrinsics.atomic_store(&t.done, true); - return 0; + intrinsics.atomic_store(&t.done, true) + return 0 } - thread := new(Thread); + thread := new(Thread) if thread == nil { - return nil; + return nil } - thread.creation_allocator = context.allocator; + thread.creation_allocator = context.allocator - win32_thread_id: win32.DWORD; - win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id); + win32_thread_id: win32.DWORD + win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id) if win32_thread == nil { - free(thread, thread.creation_allocator); - return nil; + free(thread, thread.creation_allocator) + return nil } - thread.procedure = procedure; - thread.win32_thread = win32_thread; - thread.win32_thread_id = win32_thread_id; - thread.init_context = context; + thread.procedure = procedure + thread.win32_thread = win32_thread + thread.win32_thread_id = win32_thread_id + thread.init_context = context - return thread; + return thread } thread_start :: proc "contextless" (thread: ^Thread) { - win32.ResumeThread(thread.win32_thread); + win32.ResumeThread(thread.win32_thread) } thread_join_and_destroy :: proc(thread: ^Thread) { if thread.win32_thread != win32.INVALID_HANDLE { - win32.WaitForSingleObject(thread.win32_thread, win32.INFINITE); - win32.CloseHandle(thread.win32_thread); - thread.win32_thread = win32.INVALID_HANDLE; + win32.WaitForSingleObject(thread.win32_thread, win32.INFINITE) + win32.CloseHandle(thread.win32_thread) + thread.win32_thread = win32.INVALID_HANDLE } - free(thread, thread.creation_allocator); + free(thread, thread.creation_allocator) } thread_terminate :: proc "contextless" (thread: ^Thread, exit_code: int) { - win32.TerminateThread(thread.win32_thread, u32(exit_code)); + win32.TerminateThread(thread.win32_thread, u32(exit_code)) } -global_threaded_runner_semaphore: Sema; -global_exception_handler: rawptr; -global_current_thread: ^Thread; -global_current_t: ^T; +global_threaded_runner_semaphore: Sema +global_exception_handler: rawptr +global_current_thread: ^Thread +global_current_t: ^T run_internal_test :: proc(t: ^T, it: Internal_Test) { thread := thread_create(proc(thread: ^Thread) { @@ -140,47 +140,47 @@ run_internal_test :: proc(t: ^T, it: Internal_Test) { win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED, win32.EXCEPTION_STACK_OVERFLOW: - sema_post(&global_threaded_runner_semaphore); - return win32.EXCEPTION_EXECUTE_HANDLER; + sema_post(&global_threaded_runner_semaphore) + return win32.EXCEPTION_EXECUTE_HANDLER } - return win32.EXCEPTION_CONTINUE_SEARCH; + return win32.EXCEPTION_CONTINUE_SEARCH } - global_exception_handler = win32.AddVectoredExceptionHandler(0, exception_handler_proc); + global_exception_handler = win32.AddVectoredExceptionHandler(0, exception_handler_proc) context.assertion_failure_proc = proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! { - errorf(t=global_current_t, format="%s %s", args={prefix, message}, loc=loc); - intrinsics.trap(); - }; + errorf(t=global_current_t, format="%s %s", args={prefix, message}, loc=loc) + intrinsics.trap() + } - thread.it.p(thread.t); + thread.it.p(thread.t) - thread.success = true; - sema_post(&global_threaded_runner_semaphore); - }); + thread.success = true + sema_post(&global_threaded_runner_semaphore) + }) - sema_reset(&global_threaded_runner_semaphore); - global_current_t = t; + sema_reset(&global_threaded_runner_semaphore) + global_current_t = t t._fail_now = proc() -> ! { - intrinsics.trap(); - }; - - thread.t = t; - thread.it = it; - thread.success = false; - - thread_start(thread); - - sema_wait(&global_threaded_runner_semaphore); - thread_terminate(thread, int(!thread.success)); - thread_join_and_destroy(thread); - - win32.RemoveVectoredExceptionHandler(global_exception_handler); - - if !thread.success && t.error_count == 0 { - t.error_count += 1; + intrinsics.trap() } - return; + thread.t = t + thread.it = it + thread.success = false + + thread_start(thread) + + sema_wait(&global_threaded_runner_semaphore) + thread_terminate(thread, int(!thread.success)) + thread_join_and_destroy(thread) + + win32.RemoveVectoredExceptionHandler(global_exception_handler) + + if !thread.success && t.error_count == 0 { + t.error_count += 1 + } + + return } diff --git a/core/testing/testing.odin b/core/testing/testing.odin index ec47ca4d4..0d3c1dd43 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -4,7 +4,7 @@ import "core:fmt" import "core:io" // IMPORTANT NOTE: Compiler requires this layout -Test_Signature :: proc(^T); +Test_Signature :: proc(^T) // IMPORTANT NOTE: Compiler requires this layout Internal_Test :: struct { @@ -31,53 +31,53 @@ T :: struct { error :: proc(t: ^T, args: ..any, loc := #caller_location) { - fmt.wprintf(t.w, "%v: ", loc); - fmt.wprintln(t.w, ..args); - t.error_count += 1; + fmt.wprintf(t.w, "%v: ", loc) + fmt.wprintln(t.w, ..args) + t.error_count += 1 } errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { - fmt.wprintf(t.w, "%v: ", loc); - fmt.wprintf(t.w, format, ..args); - fmt.wprintln(t.w); - t.error_count += 1; + fmt.wprintf(t.w, "%v: ", loc) + fmt.wprintf(t.w, format, ..args) + fmt.wprintln(t.w) + t.error_count += 1 } fail :: proc(t: ^T) { - error(t, "FAIL"); - t.error_count += 1; + error(t, "FAIL") + t.error_count += 1 } fail_now :: proc(t: ^T) { - fail(t); + fail(t) if t._fail_now != nil { - t._fail_now(); + t._fail_now() } } failed :: proc(t: ^T) -> bool { - return t.error_count != 0; + return t.error_count != 0 } log :: proc(t: ^T, args: ..any, loc := #caller_location) { - fmt.wprintln(t.w, ..args); + fmt.wprintln(t.w, ..args) } logf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { - fmt.wprintf(t.w, format, ..args); - fmt.wprintln(t.w); + fmt.wprintf(t.w, format, ..args) + fmt.wprintln(t.w) } // cleanup registers a procedure and user_data, which will be called when the test, and all its subtests, complete // cleanup proceduers will be called in LIFO (last added, first called) order. cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) { - append(&t.cleanups, Internal_Cleanup{procedure, user_data}); + append(&t.cleanups, Internal_Cleanup{procedure, user_data}) } expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bool { if !ok { - error(t=t, args={msg}, loc=loc); + error(t=t, args={msg}, loc=loc) } - return ok; + return ok }