mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Merge pull request #1133 from Kelimion/strip-semicolons
testing: Strip ; from test runner.
This commit is contained in:
+32
-32
@@ -6,68 +6,68 @@ import "core:os"
|
|||||||
import "core:slice"
|
import "core:slice"
|
||||||
|
|
||||||
reset_t :: proc(t: ^T) {
|
reset_t :: proc(t: ^T) {
|
||||||
clear(&t.cleanups);
|
clear(&t.cleanups)
|
||||||
t.error_count = 0;
|
t.error_count = 0
|
||||||
}
|
}
|
||||||
end_t :: proc(t: ^T) {
|
end_t :: proc(t: ^T) {
|
||||||
for i := len(t.cleanups)-1; i >= 0; i -= 1 {
|
for i := len(t.cleanups)-1; i >= 0; i -= 1 {
|
||||||
c := t.cleanups[i];
|
c := t.cleanups[i]
|
||||||
c.procedure(c.user_data);
|
c.procedure(c.user_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runner :: proc(internal_tests: []Internal_Test) -> bool {
|
runner :: proc(internal_tests: []Internal_Test) -> bool {
|
||||||
stream := os.stream_from_handle(os.stdout);
|
stream := os.stream_from_handle(os.stdout)
|
||||||
w, _ := io.to_writer(stream);
|
w, _ := io.to_writer(stream)
|
||||||
|
|
||||||
t := &T{};
|
t := &T{}
|
||||||
t.w = w;
|
t.w = w
|
||||||
reserve(&t.cleanups, 1024);
|
reserve(&t.cleanups, 1024)
|
||||||
defer delete(t.cleanups);
|
defer delete(t.cleanups)
|
||||||
|
|
||||||
total_success_count := 0;
|
total_success_count := 0
|
||||||
total_test_count := len(internal_tests);
|
total_test_count := len(internal_tests)
|
||||||
|
|
||||||
slice.sort_by(internal_tests, proc(a, b: Internal_Test) -> bool {
|
slice.sort_by(internal_tests, proc(a, b: Internal_Test) -> bool {
|
||||||
if a.pkg < b.pkg {
|
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 {
|
for it in internal_tests {
|
||||||
if it.p == nil {
|
if it.p == nil {
|
||||||
total_test_count -= 1;
|
total_test_count -= 1
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
free_all(context.temp_allocator);
|
free_all(context.temp_allocator)
|
||||||
reset_t(t);
|
reset_t(t)
|
||||||
defer end_t(t);
|
defer end_t(t)
|
||||||
|
|
||||||
if prev_pkg != it.pkg {
|
if prev_pkg != it.pkg {
|
||||||
prev_pkg = it.pkg;
|
prev_pkg = it.pkg
|
||||||
logf(t, "[Package: %s]", 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) {
|
if failed(t) {
|
||||||
logf(t, "[%s : FAILURE]", it.name);
|
logf(t, "[%s : FAILURE]", it.name)
|
||||||
} else {
|
} else {
|
||||||
logf(t, "[%s : SUCCESS]", it.name);
|
logf(t, "[%s : SUCCESS]", it.name)
|
||||||
total_success_count += 1;
|
total_success_count += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logf(t, "----------------------------------------");
|
logf(t, "----------------------------------------")
|
||||||
if total_test_count == 0 {
|
if total_test_count == 0 {
|
||||||
log(t, "NO TESTS RAN");
|
log(t, "NO TESTS RAN")
|
||||||
} else {
|
} 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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,39 +12,39 @@ Sema :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sema_reset :: proc "contextless" (s: ^Sema) {
|
sema_reset :: proc "contextless" (s: ^Sema) {
|
||||||
intrinsics.atomic_store(&s.count, 0);
|
intrinsics.atomic_store(&s.count, 0)
|
||||||
}
|
}
|
||||||
sema_wait :: proc "contextless" (s: ^Sema) {
|
sema_wait :: proc "contextless" (s: ^Sema) {
|
||||||
for {
|
for {
|
||||||
original_count := s.count;
|
original_count := s.count
|
||||||
for original_count == 0 {
|
for original_count == 0 {
|
||||||
win32.WaitOnAddress(
|
win32.WaitOnAddress(
|
||||||
&s.count,
|
&s.count,
|
||||||
&original_count,
|
&original_count,
|
||||||
size_of(original_count),
|
size_of(original_count),
|
||||||
win32.INFINITE,
|
win32.INFINITE,
|
||||||
);
|
)
|
||||||
original_count = s.count;
|
original_count = s.count
|
||||||
}
|
}
|
||||||
if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) {
|
if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sema_post :: proc "contextless" (s: ^Sema, count := 1) {
|
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 {
|
if count == 1 {
|
||||||
win32.WakeByAddressSingle(&s.count);
|
win32.WakeByAddressSingle(&s.count)
|
||||||
} else {
|
} 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 {
|
Thread :: struct {
|
||||||
using specific: Thread_Os_Specific,
|
using specific: Thread_Os_Specific,
|
||||||
@@ -67,66 +67,66 @@ Thread_Os_Specific :: struct {
|
|||||||
|
|
||||||
thread_create :: proc(procedure: Thread_Proc) -> ^Thread {
|
thread_create :: proc(procedure: Thread_Proc) -> ^Thread {
|
||||||
__windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
|
__windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
|
||||||
t := (^Thread)(t_);
|
t := (^Thread)(t_)
|
||||||
context = t.init_context.? or_else runtime.default_context();
|
context = t.init_context.? or_else runtime.default_context()
|
||||||
|
|
||||||
t.procedure(t);
|
t.procedure(t)
|
||||||
|
|
||||||
if t.init_context == nil {
|
if t.init_context == nil {
|
||||||
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
|
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);
|
intrinsics.atomic_store(&t.done, true)
|
||||||
return 0;
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
thread := new(Thread);
|
thread := new(Thread)
|
||||||
if thread == nil {
|
if thread == nil {
|
||||||
return nil;
|
return nil
|
||||||
}
|
}
|
||||||
thread.creation_allocator = context.allocator;
|
thread.creation_allocator = context.allocator
|
||||||
|
|
||||||
win32_thread_id: win32.DWORD;
|
win32_thread_id: win32.DWORD
|
||||||
win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id);
|
win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id)
|
||||||
if win32_thread == nil {
|
if win32_thread == nil {
|
||||||
free(thread, thread.creation_allocator);
|
free(thread, thread.creation_allocator)
|
||||||
return nil;
|
return nil
|
||||||
}
|
}
|
||||||
thread.procedure = procedure;
|
thread.procedure = procedure
|
||||||
thread.win32_thread = win32_thread;
|
thread.win32_thread = win32_thread
|
||||||
thread.win32_thread_id = win32_thread_id;
|
thread.win32_thread_id = win32_thread_id
|
||||||
thread.init_context = context;
|
thread.init_context = context
|
||||||
|
|
||||||
return thread;
|
return thread
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_start :: proc "contextless" (thread: ^Thread) {
|
thread_start :: proc "contextless" (thread: ^Thread) {
|
||||||
win32.ResumeThread(thread.win32_thread);
|
win32.ResumeThread(thread.win32_thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_join_and_destroy :: proc(thread: ^Thread) {
|
thread_join_and_destroy :: proc(thread: ^Thread) {
|
||||||
if thread.win32_thread != win32.INVALID_HANDLE {
|
if thread.win32_thread != win32.INVALID_HANDLE {
|
||||||
win32.WaitForSingleObject(thread.win32_thread, win32.INFINITE);
|
win32.WaitForSingleObject(thread.win32_thread, win32.INFINITE)
|
||||||
win32.CloseHandle(thread.win32_thread);
|
win32.CloseHandle(thread.win32_thread)
|
||||||
thread.win32_thread = win32.INVALID_HANDLE;
|
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) {
|
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_threaded_runner_semaphore: Sema
|
||||||
global_exception_handler: rawptr;
|
global_exception_handler: rawptr
|
||||||
global_current_thread: ^Thread;
|
global_current_thread: ^Thread
|
||||||
global_current_t: ^T;
|
global_current_t: ^T
|
||||||
|
|
||||||
run_internal_test :: proc(t: ^T, it: Internal_Test) {
|
run_internal_test :: proc(t: ^T, it: Internal_Test) {
|
||||||
thread := thread_create(proc(thread: ^Thread) {
|
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_ARRAY_BOUNDS_EXCEEDED,
|
||||||
win32.EXCEPTION_STACK_OVERFLOW:
|
win32.EXCEPTION_STACK_OVERFLOW:
|
||||||
|
|
||||||
sema_post(&global_threaded_runner_semaphore);
|
sema_post(&global_threaded_runner_semaphore)
|
||||||
return win32.EXCEPTION_EXECUTE_HANDLER;
|
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) -> ! {
|
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);
|
errorf(t=global_current_t, format="%s %s", args={prefix, message}, loc=loc)
|
||||||
intrinsics.trap();
|
intrinsics.trap()
|
||||||
};
|
}
|
||||||
|
|
||||||
thread.it.p(thread.t);
|
thread.it.p(thread.t)
|
||||||
|
|
||||||
thread.success = true;
|
thread.success = true
|
||||||
sema_post(&global_threaded_runner_semaphore);
|
sema_post(&global_threaded_runner_semaphore)
|
||||||
});
|
})
|
||||||
|
|
||||||
sema_reset(&global_threaded_runner_semaphore);
|
sema_reset(&global_threaded_runner_semaphore)
|
||||||
global_current_t = t;
|
global_current_t = t
|
||||||
|
|
||||||
t._fail_now = proc() -> ! {
|
t._fail_now = proc() -> ! {
|
||||||
intrinsics.trap();
|
intrinsics.trap()
|
||||||
};
|
}
|
||||||
|
|
||||||
thread.t = t;
|
thread.t = t
|
||||||
thread.it = it;
|
thread.it = it
|
||||||
thread.success = false;
|
thread.success = false
|
||||||
|
|
||||||
thread_start(thread);
|
thread_start(thread)
|
||||||
|
|
||||||
sema_wait(&global_threaded_runner_semaphore);
|
sema_wait(&global_threaded_runner_semaphore)
|
||||||
thread_terminate(thread, int(!thread.success));
|
thread_terminate(thread, int(!thread.success))
|
||||||
thread_join_and_destroy(thread);
|
thread_join_and_destroy(thread)
|
||||||
|
|
||||||
win32.RemoveVectoredExceptionHandler(global_exception_handler);
|
win32.RemoveVectoredExceptionHandler(global_exception_handler)
|
||||||
|
|
||||||
if !thread.success && t.error_count == 0 {
|
if !thread.success && t.error_count == 0 {
|
||||||
t.error_count += 1;
|
t.error_count += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-19
@@ -4,7 +4,7 @@ import "core:fmt"
|
|||||||
import "core:io"
|
import "core:io"
|
||||||
|
|
||||||
// IMPORTANT NOTE: Compiler requires this layout
|
// IMPORTANT NOTE: Compiler requires this layout
|
||||||
Test_Signature :: proc(^T);
|
Test_Signature :: proc(^T)
|
||||||
|
|
||||||
// IMPORTANT NOTE: Compiler requires this layout
|
// IMPORTANT NOTE: Compiler requires this layout
|
||||||
Internal_Test :: struct {
|
Internal_Test :: struct {
|
||||||
@@ -31,53 +31,53 @@ T :: struct {
|
|||||||
|
|
||||||
|
|
||||||
error :: proc(t: ^T, args: ..any, loc := #caller_location) {
|
error :: proc(t: ^T, args: ..any, loc := #caller_location) {
|
||||||
fmt.wprintf(t.w, "%v: ", loc);
|
fmt.wprintf(t.w, "%v: ", loc)
|
||||||
fmt.wprintln(t.w, ..args);
|
fmt.wprintln(t.w, ..args)
|
||||||
t.error_count += 1;
|
t.error_count += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
|
errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
|
||||||
fmt.wprintf(t.w, "%v: ", loc);
|
fmt.wprintf(t.w, "%v: ", loc)
|
||||||
fmt.wprintf(t.w, format, ..args);
|
fmt.wprintf(t.w, format, ..args)
|
||||||
fmt.wprintln(t.w);
|
fmt.wprintln(t.w)
|
||||||
t.error_count += 1;
|
t.error_count += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fail :: proc(t: ^T) {
|
fail :: proc(t: ^T) {
|
||||||
error(t, "FAIL");
|
error(t, "FAIL")
|
||||||
t.error_count += 1;
|
t.error_count += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fail_now :: proc(t: ^T) {
|
fail_now :: proc(t: ^T) {
|
||||||
fail(t);
|
fail(t)
|
||||||
if t._fail_now != nil {
|
if t._fail_now != nil {
|
||||||
t._fail_now();
|
t._fail_now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
failed :: proc(t: ^T) -> bool {
|
failed :: proc(t: ^T) -> bool {
|
||||||
return t.error_count != 0;
|
return t.error_count != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
log :: proc(t: ^T, args: ..any, loc := #caller_location) {
|
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) {
|
logf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
|
||||||
fmt.wprintf(t.w, format, ..args);
|
fmt.wprintf(t.w, format, ..args)
|
||||||
fmt.wprintln(t.w);
|
fmt.wprintln(t.w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// cleanup registers a procedure and user_data, which will be called when the test, and all its subtests, complete
|
// 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 proceduers will be called in LIFO (last added, first called) order.
|
||||||
cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) {
|
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 {
|
expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bool {
|
||||||
if !ok {
|
if !ok {
|
||||||
error(t=t, args={msg}, loc=loc);
|
error(t=t, args={msg}, loc=loc)
|
||||||
}
|
}
|
||||||
return ok;
|
return ok
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user