testing.odin 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // The implementation of the `odin test` runner and procedures user tests can use for this purpose.
  2. package testing
  3. /*
  4. (c) Copyright 2024 Feoramund <[email protected]>.
  5. Made available under Odin's BSD-3 license.
  6. List of contributors:
  7. Ginger Bill: Initial implementation.
  8. Feoramund: Total rewrite.
  9. */
  10. import "base:intrinsics"
  11. import "base:runtime"
  12. import "core:log"
  13. import "core:reflect"
  14. import "core:sync"
  15. import "core:sync/chan"
  16. import "core:time"
  17. import "core:mem"
  18. _ :: reflect // alias reflect to nothing to force visibility for -vet
  19. _ :: mem // in case TRACKING_MEMORY is not enabled
  20. MAX_EXPECTED_ASSERTIONS_PER_TEST :: 5
  21. // IMPORTANT NOTE: Compiler requires this layout
  22. Test_Signature :: proc(^T)
  23. // IMPORTANT NOTE: Compiler requires this layout
  24. Internal_Test :: struct {
  25. pkg: string,
  26. name: string,
  27. p: Test_Signature,
  28. }
  29. Internal_Cleanup :: struct {
  30. procedure: proc(rawptr),
  31. user_data: rawptr,
  32. ctx: runtime.Context,
  33. }
  34. T :: struct {
  35. error_count: int,
  36. // If your test needs to perform random operations, it's advised to use
  37. // this value to seed a local random number generator rather than relying
  38. // on the non-thread-safe global one.
  39. //
  40. // This way, your results will be deterministic.
  41. //
  42. // This value is chosen at startup of the test runner, logged, and may be
  43. // specified by the user. It is the same for all tests of a single run.
  44. seed: u64,
  45. channel: Update_Channel_Sender,
  46. cleanups: [dynamic]Internal_Cleanup,
  47. // This allocator is shared between the test runner and its threads for
  48. // cloning log strings, so they can outlive the lifetime of individual
  49. // tests during channel transmission.
  50. _log_allocator: runtime.Allocator,
  51. _fail_now_called: bool,
  52. }
  53. fail :: proc(t: ^T, loc := #caller_location) {
  54. log.error("FAIL", location=loc)
  55. }
  56. // fail_now will cause a test to immediately fail and abort, much in the same
  57. // way a failed assertion or panic call will stop a thread.
  58. //
  59. // It is for when you absolutely need a test to fail without calling any of its
  60. // deferred statements. It will be cleaner than a regular assert or panic,
  61. // as the test runner will know to expect the signal this procedure will raise.
  62. fail_now :: proc(t: ^T, msg := "", loc := #caller_location) -> ! {
  63. t._fail_now_called = true
  64. if msg != "" {
  65. log.error("FAIL:", msg, location=loc)
  66. } else {
  67. log.error("FAIL", location=loc)
  68. }
  69. runtime.trap()
  70. }
  71. failed :: proc(t: ^T) -> bool {
  72. return t.error_count != 0
  73. }
  74. // cleanup registers a procedure and user_data, which will be called when the test, and all its subtests, complete.
  75. // Cleanup procedures will be called in LIFO (last added, first called) order.
  76. //
  77. // Each procedure will use a copy of the context at the time of registering,
  78. // and if the test failed due to a timeout, failed assertion, panic, bounds-checking error,
  79. // memory access violation, or any other signal-based fault, this procedure will
  80. // run with greater privilege in the test runner's main thread.
  81. //
  82. // That means that any cleanup procedure absolutely must not fail in the same way,
  83. // or it will take down the entire test runner with it. This is for when you
  84. // need something to run no matter what, if a test failed.
  85. //
  86. // For almost every usual case, `defer` should be preferable and sufficient.
  87. cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) {
  88. append(&t.cleanups, Internal_Cleanup{procedure, user_data, context})
  89. }
  90. expect :: proc(t: ^T, ok: bool, msg := "", expr := #caller_expression(ok), loc := #caller_location) -> bool {
  91. if !ok {
  92. if msg == "" {
  93. log.errorf("expected %v to be true", expr, location=loc)
  94. } else {
  95. log.error(msg, location=loc)
  96. }
  97. }
  98. return ok
  99. }
  100. expectf :: proc(t: ^T, ok: bool, format: string, args: ..any, loc := #caller_location) -> bool {
  101. if !ok {
  102. log.errorf(format, ..args, location=loc)
  103. }
  104. return ok
  105. }
  106. expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location, value_expr := #caller_expression(value)) -> bool where intrinsics.type_is_comparable(T) {
  107. ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected)
  108. if !ok {
  109. log.errorf("expected %v to be %v, got %v", value_expr, expected, value, location=loc)
  110. }
  111. return ok
  112. }
  113. Memory_Verifier_Proc :: #type proc(t: ^T, ta: ^mem.Tracking_Allocator)
  114. expect_leaks :: proc(t: ^T, client_test: proc(t: ^T), verifier: Memory_Verifier_Proc) {
  115. when TRACKING_MEMORY {
  116. client_test(t)
  117. ta := (^mem.Tracking_Allocator)(context.allocator.data)
  118. sync.mutex_lock(&ta.mutex)
  119. // The verifier can inspect this local tracking allocator.
  120. // And then call `testing.expect_*` as makes sense for the client test.
  121. verifier(t, ta)
  122. sync.mutex_unlock(&ta.mutex)
  123. clear(&ta.bad_free_array)
  124. free_all(context.allocator)
  125. }
  126. }
  127. set_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
  128. chan.send(t.channel, Event_Set_Fail_Timeout {
  129. at_time = time.time_add(time.now(), duration),
  130. location = loc,
  131. })
  132. }
  133. /*
  134. Let the test runner know that it should expect an assertion failure from a
  135. specific location in the source code for this test.
  136. In the event that an assertion fails, a debug message will be logged with its
  137. exact message and location in a copyable format to make it convenient to write
  138. tests which use this API.
  139. This procedure may be called up to 5 times with different locations.
  140. This is a limitation for the sake of simplicity in the implementation, and you
  141. should consider breaking up your tests into smaller procedures if you need to
  142. check for asserts in more than 2 places.
  143. */
  144. expect_assert_from :: proc(t: ^T, expected_place: runtime.Source_Code_Location, caller_loc := #caller_location) {
  145. count := local_test_expected_failures.location_count
  146. if count == MAX_EXPECTED_ASSERTIONS_PER_TEST {
  147. panic("This test cannot handle that many expected assertions based on matching the location.", caller_loc)
  148. }
  149. local_test_expected_failures.locations[count] = expected_place
  150. local_test_expected_failures.location_count += 1
  151. }
  152. /*
  153. Let the test runner know that it should expect an assertion failure with a
  154. specific message for this test.
  155. In the event that an assertion fails, a debug message will be logged with its
  156. exact message and location in a copyable format to make it convenient to write
  157. tests which use this API.
  158. This procedure may be called up to 5 times with different messages.
  159. This is a limitation for the sake of simplicity in the implementation, and you
  160. should consider breaking up your tests into smaller procedures if you need to
  161. check for more than a couple different assertion messages.
  162. */
  163. expect_assert_message :: proc(t: ^T, expected_message: string, caller_loc := #caller_location) {
  164. count := local_test_expected_failures.message_count
  165. if count == MAX_EXPECTED_ASSERTIONS_PER_TEST {
  166. panic("This test cannot handle that many expected assertions based on matching the message.", caller_loc)
  167. }
  168. local_test_expected_failures.messages[count] = expected_message
  169. local_test_expected_failures.message_count += 1
  170. }
  171. expect_assert :: proc {
  172. expect_assert_from,
  173. expect_assert_message,
  174. }
  175. /*
  176. Let the test runner know that it should expect a signal to be raised within
  177. this test.
  178. This API is for advanced users, as arbitrary signals will not be caught; only
  179. the ones already handled by the test runner, such as
  180. - SIGINT, (interrupt)
  181. - SIGTERM, (polite termination)
  182. - SIGILL, (illegal instruction)
  183. - SIGFPE, (arithmetic error)
  184. - SIGSEGV, and (segmentation fault)
  185. - SIGTRAP (only on POSIX systems). (trap / debug trap)
  186. Note that only one signal can be expected per test.
  187. */
  188. expect_signal :: proc(t: ^T, #any_int sig: i32) {
  189. local_test_expected_failures.signal = sig
  190. }