testing.odin 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package testing
  2. import "core:fmt"
  3. import "core:io"
  4. import "core:time"
  5. import "base:intrinsics"
  6. import "core:reflect"
  7. _ :: reflect // alias reflect to nothing to force visibility for -vet
  8. // IMPORTANT NOTE: Compiler requires this layout
  9. Test_Signature :: proc(^T)
  10. // IMPORTANT NOTE: Compiler requires this layout
  11. Internal_Test :: struct {
  12. pkg: string,
  13. name: string,
  14. p: Test_Signature,
  15. }
  16. Internal_Cleanup :: struct {
  17. procedure: proc(rawptr),
  18. user_data: rawptr,
  19. }
  20. T :: struct {
  21. error_count: int,
  22. w: io.Writer,
  23. cleanups: [dynamic]Internal_Cleanup,
  24. _fail_now: proc() -> !,
  25. }
  26. error :: proc(t: ^T, args: ..any, loc := #caller_location) {
  27. fmt.wprintf(t.w, "%v: ", loc)
  28. fmt.wprintln(t.w, ..args)
  29. t.error_count += 1
  30. }
  31. errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
  32. fmt.wprintf(t.w, "%v: ", loc)
  33. fmt.wprintf(t.w, format, ..args)
  34. fmt.wprintln(t.w)
  35. t.error_count += 1
  36. }
  37. fail :: proc(t: ^T, loc := #caller_location) {
  38. error(t, "FAIL", loc=loc)
  39. t.error_count += 1
  40. }
  41. fail_now :: proc(t: ^T, msg := "", loc := #caller_location) {
  42. if msg != "" {
  43. error(t, "FAIL:", msg, loc=loc)
  44. } else {
  45. error(t, "FAIL", loc=loc)
  46. }
  47. t.error_count += 1
  48. if t._fail_now != nil {
  49. t._fail_now()
  50. }
  51. }
  52. failed :: proc(t: ^T) -> bool {
  53. return t.error_count != 0
  54. }
  55. log :: proc(t: ^T, args: ..any, loc := #caller_location) {
  56. fmt.wprintln(t.w, ..args)
  57. }
  58. logf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
  59. fmt.wprintf(t.w, format, ..args)
  60. fmt.wprintln(t.w)
  61. }
  62. // cleanup registers a procedure and user_data, which will be called when the test, and all its subtests, complete
  63. // cleanup procedures will be called in LIFO (last added, first called) order.
  64. cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) {
  65. append(&t.cleanups, Internal_Cleanup{procedure, user_data})
  66. }
  67. expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bool {
  68. if !ok {
  69. error(t, msg, loc=loc)
  70. }
  71. return ok
  72. }
  73. expectf :: proc(t: ^T, ok: bool, format: string, args: ..any, loc := #caller_location) -> bool {
  74. if !ok {
  75. errorf(t, format, ..args, loc=loc)
  76. }
  77. return ok
  78. }
  79. expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) {
  80. ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected)
  81. if !ok {
  82. errorf(t, "expected %v, got %v", expected, value, loc=loc)
  83. }
  84. return ok
  85. }
  86. set_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
  87. _fail_timeout(t, duration, loc)
  88. }