test-framework.lisp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. ;;; test-framework.lisp
  2. ;;; written by Peter Lane
  3. ;;; (c) School of Computer Science, University of Hertfordshire, 2007
  4. ;;; ABOUT THIS SOFTWARE
  5. ;;;
  6. ;;; The functions in this file support use of a testing framework for
  7. ;;; scientific theories. Tests are separated into three classes, as
  8. ;;; described in [1]:
  9. ;;; 1. Unit tests - for implementational details
  10. ;;; 2. Process tests - for the main processes within the theory
  11. ;;; 3. Canonical results - for the experimental results achieved by the
  12. ;;; theory
  13. ;;; [1] P.C.R. Lane and F. Gobet, Developing reproducible and comprehensible
  14. ;;; computational models, Artificial Intelligence, 144:251--63, 2003.
  15. ;;; INSTRUCTIONS FOR USE
  16. ;;;
  17. ;;; Individual tests are written in one of the following forms:
  18. ;;; (test THING-TO-CHECK [MESSAGE])
  19. ;;; where: THING-TO-CHECK is a test, returning a boolean value
  20. ;;; test should return T if the test passed
  21. ;;; MESSAGE is optional, and is given if the test is NIL
  22. ;;; (assert-= EXPECTED ACTUAL [MESSAGE])
  23. ;;; (assert-eq EXPECTED ACTUAL [MESSAGE])
  24. ;;; (assert-equalp EXPECTED ACTUAL [MESSAGE])
  25. ;;; (assert-string= EXPECTED ACTUAL [MESSAGE])
  26. ;;; where: EXPECTED is the predicted value
  27. ;;; ACTUAL is the computed value
  28. ;;; EXPECTED and ACTUAL are compared using respective function
  29. ;;; MESSAGE is optional, and given if the comparison fails
  30. ;;; (assert-true THING-TO-CHECK [MESSAGE]) is the same as (test THING-TO-CHECK MESSAGE)
  31. ;;; (assert-false THING-TO-CHECK [MESSAGE]) is the same as (test (not THING-TO-CHECK) MESSAGE)
  32. ;;; (assert-null THING-TO-CHECK [MESSAGE]) is the same as (test (null THING-TO-CHECK) MESSAGE)
  33. ;;;
  34. ;;; To create a group of tests, use either of:
  35. ;;; def-unit-tests
  36. ;;; def-process-tests
  37. ;;; def-canonical-result-tests
  38. ;;; All three do the same job, which is to define a function containing one or
  39. ;;; more calls to (test .. ..)
  40. ;;; The framework stores the functions in one of the three categories of tests.
  41. ;;;
  42. ;;; Tests can be run using either of:
  43. ;;; (run-unit-tests) evaluates every function defined using def-unit-tests
  44. ;;; (run-process-tests) evaluates every function defined using def-process-tests
  45. ;;; (run-canonical-result-tests) evaluates every function defined using def-canonical-result-tests
  46. ;;; (run-all-tests) evaluates all three of the above, running all tests
  47. (let ((error-count 0)
  48. (total-tests 0)
  49. (unit-tests ())
  50. (process-tests ())
  51. (canonical-results-tests ()))
  52. (defun reset-error-count ()
  53. (setf error-count 0
  54. total-tests 0))
  55. (defun error-feedback ()
  56. (format t "~%=== DONE: There ~a ~a error~a in ~a test~a~%"
  57. (if (= 1 error-count) "was" "were")
  58. error-count
  59. (if (= 1 error-count) "" "s")
  60. total-tests
  61. (if (= 1 total-tests) "" "s")))
  62. ;; the basic test function
  63. (defun test (bool &optional (msg ""))
  64. "If bool is true, display a dot, else the message"
  65. (incf total-tests)
  66. (if bool
  67. (format t ".")
  68. (format t "~&Error ~a: ~a~&" (incf error-count) msg)))
  69. ;; some customised test functions, to provide a more direct test syntax
  70. (flet ((test-compare (comp-fn expected actual &optional (msg ""))
  71. (test (funcall comp-fn expected actual)
  72. (format nil "Expected ~a got ~a. ~a" expected actual msg))))
  73. (defun assert-eq (expected actual &optional (msg ""))
  74. (test-compare #'eq expected actual msg))
  75. (defun assert-equalp (expected actual &optional (msg ""))
  76. (test-compare #'equalp expected actual msg))
  77. (defun assert= (expected actual &optional (msg ""))
  78. (test-compare #'= expected actual msg))
  79. (defun assert-string= (expected actual &optional (msg ""))
  80. (test-compare #'string= expected actual msg)))
  81. (defun assert-true (bool &optional (msg ""))
  82. (test bool msg))
  83. (defun assert-false (bool &optional (msg ""))
  84. (test (not bool) msg))
  85. (defun assert-null (item &optional (msg ""))
  86. (test (null item) msg))
  87. ;; macros for creating functions containing tests, placing the tests into groups
  88. (defmacro def-unit-tests (name &rest body)
  89. (push name unit-tests)
  90. `(defun ,name ,@body))
  91. (defmacro def-process-tests (name &rest body)
  92. (push name process-tests)
  93. `(defun ,name ,@body))
  94. (defmacro def-canonical-result-tests (name &rest body)
  95. (push name canonical-results-tests)
  96. `(defun ,name ,@body))
  97. ;; functions for running the tests
  98. (flet ((run-tests (name test-list)
  99. (format t "Running ~a: " name)
  100. (reset-error-count)
  101. (dolist (test test-list) (funcall test))
  102. (error-feedback)))
  103. (defun run-unit-tests () (run-tests "Unit tests" unit-tests))
  104. (defun run-process-tests () (run-tests "Process tests" process-tests))
  105. (defun run-canonical-result-tests ()
  106. (run-tests "Canonical results" canonical-results-tests)))
  107. (defun run-all-tests ()
  108. (run-unit-tests)
  109. (run-process-tests)
  110. (run-canonical-result-tests)))
  111. #|
  112. ;;; example of use
  113. (defun adder (x y) (+ x y))
  114. (defun multiplier (x y) (* x y))
  115. (def-unit-tests x ()
  116. (test (= 4 (adder 2 2)) "test 1")
  117. (test (= 5 (adder 2 3)) "test 2")
  118. (assert-equalp 4 (adder 2 2)))
  119. (def-unit-tests x2 ()
  120. (assert= 7 (adder 2 1))
  121. (assert= 7 (adder 2 1) "adding error"))
  122. (def-unit-tests x3 ()
  123. (assert-true (< 2 (adder 2 1)))
  124. (assert-false (> 2 (adder 2 1))))
  125. (def-process-tests y ()
  126. (test (= 4 (multiplier 2 2))))
  127. (def-canonical-result-tests z ()
  128. (test (= 20 (multiplier (adder 2 3) 4))))
  129. (run-all-tests) ;; runs all the defined tests, separated into groups
  130. ;; you can run an individual test function by calling it:
  131. ;; (x)
  132. ;; or the functions from one group by calling the relevant 'run' function
  133. ;; (run-unit-tests)
  134. |#