init.lisp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. (import io/term (coloured))
  2. (import lua/debug debug)
  3. (import lua/os (clock))
  4. (import test/assert () :export)
  5. (import test/check () :export)
  6. (define tests-passed :hidden (gensym))
  7. (define tests-failed :hidden (gensym))
  8. (define tests-pending :hidden (gensym))
  9. (define tests-total :hidden (gensym))
  10. (define prefix :hidden (gensym))
  11. (define quiet :hidden (gensym))
  12. (define time :hidden (gensym))
  13. (define start-time :hidden (gensym))
  14. (defun traceback (obj)
  15. :hidden
  16. (debug/traceback (if (string? obj) obj (pretty obj)) 2))
  17. (defmacro marker (colour)
  18. "Add a dot with the given COLOUR to mark a single test's result"
  19. :hidden
  20. `(when ,quiet
  21. (write (coloured ,colour "\226\128\162"))))
  22. (defun format-err (x)
  23. "Format an error message"
  24. :hidden
  25. (if (string? x) (const-val x) (pretty x)))
  26. (defmacro it (name &body)
  27. "Create a test NAME which executes all expressions and assertions in
  28. BODY"
  29. `(with (,start-time (clock))
  30. (inc! ,tests-total)
  31. (xpcall
  32. (lambda ()
  33. ,@body
  34. (push! ,tests-passed (.. ,prefix " " ,name (if ,time (string/format " (took %.2f seconds)" (- (clock) ,start-time)) "")))
  35. (marker 32))
  36. (lambda (,'msg)
  37. (marker 31)
  38. (push! ,tests-failed (list (.. ,prefix " " ,name) (if ,quiet (format-err ,'msg) (traceback (format-err ,'msg)))))))))
  39. (defmacro pending (name &body)
  40. "Create a test NAME whose BODY will not be run.
  41. This is primarily designed for assertions you know will fail and need
  42. to be fixed, or features which have not been implemented yet"
  43. `(progn
  44. (marker 33)
  45. (push! ,tests-pending (.. ,prefix " " ,name))))
  46. (defmacro section (name &body)
  47. "Create a group of tests defined in BODY whose names take the form
  48. `<prefix> NAME <test_name>`"
  49. `(with (,prefix (.. ,prefix " " ,name)) ,@body))
  50. (defmacro may (name &body)
  51. "Create a group of tests defined in BODY whose names take the form
  52. `<prefix> may NAME, and <test_name>`"
  53. `(with (,prefix (.. ,prefix " may " ,name ", and")) ,@body))
  54. (defmacro will (name &body)
  55. "Create a test whose BODY asserts NAME will happen"
  56. `(with (,prefix (.. ,prefix " will")) (it ,name ,@body)))
  57. (defmacro will-not (name &body)
  58. "Create a test whose BODY asserts NAME will not happen"
  59. `(with (,prefix (.. ,prefix " won't")) (it ,name ,@body)))
  60. (defmacro is (name &body)
  61. "Create a test whose BODY asserts NAME is true"
  62. `(with (,prefix (.. ,prefix " is")) (it ,name ,@body)))
  63. (defmacro can (name &body)
  64. "Create a test whose BODY asserts NAME can happen"
  65. `(with (,prefix (.. ,prefix " can")) (it ,name ,@body)))
  66. (defmacro cannot (name &body)
  67. "Create a test whose BODY asserts NAME cannot happen"
  68. `(with (,prefix (.. ,prefix " cannot")) (it ,name ,@body)))
  69. (defmacro describe (name &body)
  70. "Create a group of tests, defined in BODY, which test NAME"
  71. `(let [(,tests-passed '())
  72. (,tests-failed '())
  73. (,tests-pending '())
  74. (,tests-total 0)
  75. (,prefix ,name)
  76. (,quiet (any (lambda (,'x) (or (= ,'x "--quiet") (= ,'x "-q"))) *arguments*))
  77. (,time (any (lambda (,'x) (or (= ,'x "--time") (= ,'x "-t"))) *arguments*))]
  78. ,@body
  79. (when (and ,quiet (or (> ,tests-total 0) (> (n ,tests-pending) 0)))
  80. ;; If we've been outputting dots then add a new line
  81. (print!))
  82. ;; Display a summary of all tests
  83. (print! (string/format "%d (%.2f%%) out of %d passed, %d (%.2f%%) out of %d failed"
  84. (n ,tests-passed) (if (= ,tests-total 0) 100 (* 100 (/ (n ,tests-passed) ,tests-total))) ,tests-total
  85. (n ,tests-failed) (if (= ,tests-total 0) 0 (* 100 (/ (n ,tests-failed) ,tests-total))) ,tests-total))
  86. ;; We don't care about successful tests when quiet
  87. (unless (or ,quiet (empty? ,tests-passed))
  88. (print! (string/format "%s (%d)" (coloured 32 "- Passed tests:") (n ,tests-passed)))
  89. (for-each ,'passed ,tests-passed
  90. (print! (.. (coloured 32 "+ ") ,'passed))))
  91. (unless (empty? ,tests-pending)
  92. (print! (string/format "%s (%d)" (coloured 33 "- Pending tests:") (n ,tests-pending)))
  93. (for-each ,'pending ,tests-pending
  94. (print! (.. (coloured 33 "* ") ,'pending))))
  95. (unless (empty? ,tests-failed)
  96. (print! (string/format "%s (%d)" (coloured 31 "- Failed tests:") (n ,tests-failed)))
  97. (for-each ,'failed ,tests-failed
  98. (print! (.. (coloured 31 "* ") (car ,'failed)))
  99. (with (,'lines (string/split (cadr ,'failed) "\n"))
  100. (for-each ,'line ,'lines (print! (string/format " %s" ,'line)))))
  101. (exit! 1))))