Browse Source

Add `testing.set_fail_timeout`

gingerBill 3 years ago
parent
commit
e64eb631df
3 changed files with 51 additions and 8 deletions
  1. 6 0
      core/testing/runner_other.odin
  2. 26 4
      core/testing/runner_windows.odin
  3. 19 4
      core/testing/testing.odin

+ 6 - 0
core/testing/runner_other.odin

@@ -2,7 +2,13 @@
 //+build !windows
 //+build !windows
 package testing
 package testing
 
 
+import "core:time"
+
 run_internal_test :: proc(t: ^T, it: Internal_Test) {
 run_internal_test :: proc(t: ^T, it: Internal_Test) {
 	// TODO(bill): Catch panics on other platforms
 	// TODO(bill): Catch panics on other platforms
 	it.p(t);
 	it.p(t);
 }
 }
+
+_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
+
+}

+ 26 - 4
core/testing/runner_windows.odin

@@ -5,7 +5,8 @@ package testing
 import win32 "core:sys/windows"
 import win32 "core:sys/windows"
 import "core:runtime"
 import "core:runtime"
 import "core:intrinsics"
 import "core:intrinsics"
-
+import "core:time"
+import "core:fmt"
 
 
 Sema :: struct {
 Sema :: struct {
 	count: i32,
 	count: i32,
@@ -57,6 +58,9 @@ Thread :: struct {
 	init_context: Maybe(runtime.Context),
 	init_context: Maybe(runtime.Context),
 
 
 	creation_allocator: runtime.Allocator,
 	creation_allocator: runtime.Allocator,
+	
+	internal_fail_timeout: time.Duration,
+	internal_fail_timeout_loc: runtime.Source_Code_Location,
 }
 }
 
 
 Thread_Os_Specific :: struct {
 Thread_Os_Specific :: struct {
@@ -121,7 +125,21 @@ thread_terminate :: proc "contextless" (thread: ^Thread, exit_code: int) {
 }
 }
 
 
 
 
-
+_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
+	thread := thread_create(proc(thread: ^Thread) {
+		t := thread.t
+		time.sleep(thread.internal_fail_timeout)
+		if !intrinsics.atomic_load(&t._is_done) {
+			fail_now(t, "TIMEOUT", thread.internal_fail_timeout_loc)
+		}
+		// NOTE(bill): Complete hack and probably not a good idea
+		thread_join_and_destroy(thread)
+	})
+	thread.internal_fail_timeout = duration
+	thread.internal_fail_timeout_loc = loc
+	thread.t = t
+	thread_start(thread)
+}
 
 
 global_threaded_runner_semaphore: Sema
 global_threaded_runner_semaphore: Sema
 global_exception_handler: rawptr
 global_exception_handler: rawptr
@@ -152,8 +170,13 @@ run_internal_test :: proc(t: ^T, it: Internal_Test) {
 			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()
 		}
 		}
+		
+		t := thread.t
 
 
-		thread.it.p(thread.t)
+		t._fail_timeout_set = false
+		intrinsics.atomic_store(&t._is_done, false)
+		thread.it.p(t)
+		intrinsics.atomic_store(&t._is_done, true)
 
 
 		thread.success = true
 		thread.success = true
 		sema_post(&global_threaded_runner_semaphore)
 		sema_post(&global_threaded_runner_semaphore)
@@ -169,7 +192,6 @@ run_internal_test :: proc(t: ^T, it: Internal_Test) {
 	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)

+ 19 - 4
core/testing/testing.odin

@@ -2,6 +2,7 @@ package testing
 
 
 import "core:fmt"
 import "core:fmt"
 import "core:io"
 import "core:io"
+import "core:time"
 
 
 // IMPORTANT NOTE: Compiler requires this layout
 // IMPORTANT NOTE: Compiler requires this layout
 Test_Signature :: proc(^T)
 Test_Signature :: proc(^T)
@@ -27,6 +28,8 @@ T :: struct {
 	cleanups: [dynamic]Internal_Cleanup,
 	cleanups: [dynamic]Internal_Cleanup,
 
 
 	_fail_now: proc() -> !,
 	_fail_now: proc() -> !,
+	_is_done: bool,
+	_fail_timeout_set: bool,
 }
 }
 
 
 
 
@@ -43,13 +46,18 @@ errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
 	t.error_count += 1
 	t.error_count += 1
 }
 }
 
 
-fail :: proc(t: ^T) {
-	error(t, "FAIL")
+fail :: proc(t: ^T, loc := #caller_location) {
+	error(t=t, args={"FAIL"}, loc=loc)
 	t.error_count += 1
 	t.error_count += 1
 }
 }
 
 
-fail_now :: proc(t: ^T) {
-	fail(t)
+fail_now :: proc(t: ^T, msg := "", loc := #caller_location) {
+	if msg != "" {
+		error(t=t, args={"FAIL:", msg}, loc=loc)
+	} else {
+		error(t=t, args={"FAIL"}, loc=loc)
+	}
+	t.error_count += 1
 	if t._fail_now != nil {
 	if t._fail_now != nil {
 		t._fail_now()
 		t._fail_now()
 	}
 	}
@@ -81,3 +89,10 @@ expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bo
 	}
 	}
 	return ok
 	return ok
 }
 }
+
+
+set_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
+	assert(t._fail_timeout_set == false, "set_fail_timeout previously called", loc)
+	t._fail_timeout_set = true
+	_fail_timeout(t, duration, loc)
+}