Skip to content

Commit

Permalink
Merge pull request #8 from simendsjo/some-fixes
Browse files Browse the repository at this point in the history
v0.4.0
  • Loading branch information
simendsjo authored Mar 15, 2024
2 parents 9e59163 + cebfdd0 commit 29a0eaa
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 30 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.org
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
The format is based on [[https://keepachangelog.com/en/1.1.0][Keep a Changelog]], and this project *DOES NOT* adhere to [[https://semver.org/spec/v2.0.0.html][Semantic
Versioning]].

** [[https://github.com/simendsjo/sijo-doctest/compare/9e591638cc8619a141ed44b65bb23d318f2bfc67..v0.4.0][0.4.0]] - 2024-03-15
*** Added
- [[https://github.com/simendsjo/sijo-doctest/issues/7][issue#7]] All test functions accepts a ~:test~ keyword to supply another testing
functions than ~cl:equalp~. This allows passing e.g. ~generic-cl:equalp~ to
test classes.
*** Changed
*** Deprecated
*** Removed
*** Fixed
- [[https://github.com/simendsjo/sijo-doctest/issues/6][issue#6]] Extracting docstrings now works for functions without docstrings and with
~(declare)~ form.
*** Security

** [[https://github.com/simendsjo/sijo-doctest/compare/v0.3..v0.3.1][0.3.1]] - 2024-03-11
*** Added
*** Changed
Expand Down
2 changes: 1 addition & 1 deletion sijo-doctest.asd
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(defsystem :sijo-doctest
:in-order-to ((test-op (test-op :sijo-doctest/tests)))
:description "Doctests for Common Lisp"
:version "0.3.1"
:version "0.4.0"
:author "Johan Lindberg (Pulp Software) <[email protected]>, Simen Endsjø <[email protected]>"
:licence "GPL"
:serial t
Expand Down
74 changes: 45 additions & 29 deletions src/doctest.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
(defun string-equal-ignore-ws (string1 string2)
(string-equal (remove-ws string1) (remove-ws string2)))

(defun run-doctest (test-form expected-result expected-output output count)
(defun run-doctest (test-form expected-result expected-output output count &key (test #'equalp))
(let* ((test-form-signaled-condition nil)
(actual-output (make-array '(0) :element-type 'base-char
:fill-pointer 0
Expand Down Expand Up @@ -71,7 +71,7 @@
count test-form
(type-of (car actual-result)) (car actual-result)
(car expected-result))))
(unless (and (equalp actual-result expected-result)
(unless (and (funcall test actual-result expected-result)
expected-output-matches-actual-output)
(setf result nil)
(if expected-output-matches-actual-output
Expand All @@ -85,7 +85,7 @@
expected-output))))
result))

(defun run-doctests (docstring output)
(defun run-doctests (docstring output &key (test #'equalp))
"Run-doctests is used by the test functions to perform the actual work.
It returns the number of tests failed and passed and prints to <output>."
(let ((tests-failed 0)
Expand All @@ -109,12 +109,13 @@
(car expected-result)
expected-output
output
(incf count))
(incf count)
:test test)
(incf tests-passed)
(incf tests-failed))))))
(values tests-failed tests-passed)))

(defun test (thing &key (output t))
(defun test (thing &key (output t) (test #'equalp))
"Test extracts and tests code snippets embedded in the documentation string
of <thing>. It returns the number of tests failed and passed and prints a
description to <output>.
Expand Down Expand Up @@ -224,46 +225,61 @@
(cond ((null thing)
(values 0 0))
((stringp thing)
(test-docstring thing :output output))
(test-docstring thing :output output :test test))
((functionp thing)
(test-function thing :output output))
(test-function thing :output output :test test))
((pathnamep thing)
(test-file thing :output output))
(test-file thing :output output :test test))
((packagep thing)
(test-package thing :output output))
(test-package thing :output output :test test))
((symbolp thing)
(let ((total-failed 0)
(total-passed 0))
(flet ((collect (fn)
(multiple-value-bind (failed passed) (funcall fn)
(incf total-failed failed)
(incf total-passed passed))))
(collect (lambda () (test-variable thing :output output)))
(collect (lambda () (test-variable thing :output output :test test)))
(cond
((macro-function thing)
(collect (lambda () (test-macro thing :output output))))
(collect (lambda () (test-macro thing :output output :test test))))
((fboundp thing)
(collect (lambda () (test-function (symbol-function thing) :output output)))))
(collect (lambda () (test-function (symbol-function thing) :output output :test test)))))
(values total-failed total-passed))))
(t
(error "~&No suitable testing-function available for ~A~%" thing))))

(defun test-variable (thing &key (output t))
(test-docstring (documentation thing 'variable) :output output))
(defun test-variable (thing &key (output t) (test #'equalp))
(test-docstring (documentation thing 'variable) :output output :test test))

(defun test-docstring (documentation &key (output t))
(defun test-docstring (documentation &key (output t) (test #'equalp))
(with-input-from-string (docstring (or documentation ""))
(run-doctests docstring output)))
(run-doctests docstring output :test test)))

(defun extract-function-documentation-and-name (function)
;; ABCL doesn't give documentation for (documentation function 'function) for all expressions.
;; We try function-lambda-expression too
(defun parse-lambda-body (body)
(values (and (stringp (car body)) (cdr body) (pop body))
(and (consp (car body)) (eq 'declare (caar body)) (pop body))
body))

(defun parse-function-lambda-expression (function)
(multiple-value-bind (lambda-expression closure-p name) (function-lambda-expression function)
(declare (ignore closure-p))
(values (or (documentation function 'function) (third lambda-expression))
(symbol-name name))))
(let ((parameters (cadr lambda-expression))
(lambda-body (cddr lambda-expression)))
(multiple-value-bind (docstring declare body) (parse-lambda-body lambda-body)
(values name parameters docstring declare body)))))

(defun test-function (function &key (output t))
(defun extract-function-documentation-and-name (function)
;; ABCL doesn't give documentation for (documentation function 'function) for all expressions.
;; We try function-lambda-expression too
(multiple-value-bind (name parameters docstring declare body) (parse-function-lambda-expression function)
(declare (ignore parameters declare body))
(values (or (documentation function 'function)
docstring
"")
name)))

(defun test-function (function &key (output t) (test #'equalp))
"Test-function extracts and tests code snippets in <function>'s documentation
string. It returns the number of tests failed and passed and prints a
description to <output>.
Expand All @@ -272,11 +288,11 @@
(multiple-value-bind (documentation function-name) (extract-function-documentation-and-name function)
(if documentation
(multiple-value-bind (tests-failed tests-passed)
(test-docstring documentation :output output)
(test-docstring documentation :output output :test test)
(print-results function-name 'function output tests-failed tests-passed))
(values 0 0))))

(defun test-macro (macro &key (output t))
(defun test-macro (macro &key (output t) (test #'equalp))
"Test-macro extracts and tests code snippets in <macro>'s documentation string.
It returns the number of tests failed and passed and prints a description to
<output>.
Expand All @@ -285,29 +301,29 @@
(if (documentation macro 'function)
(let ((macro-name (third (multiple-value-list (function-lambda-expression (macro-function macro))))))
(multiple-value-bind (tests-failed tests-passed)
(test-docstring (documentation macro 'function) :output output)
(test-docstring (documentation macro 'function) :output output :test test)
(print-results macro-name 'macro output tests-failed tests-passed)))
(values 0 0)))


(defun test-file (filename &key (output t))
(defun test-file (filename &key (output t) (test #'equalp))
"Test-file extracts and tests code snippets in the contents of <filename>. It
returns the number of tests failed and passed and prints a description to
<output>.
See also the documentation string for test."
(multiple-value-bind (tests-failed tests-passed)
(with-open-file (docstring filename :direction :input)
(test-docstrting docstring :output output))
(test-docstrting docstring :output output :test test))
(print-results filename 'file output tests-failed tests-passed)))

(defun test-package (package &key (output t))
(defun test-package (package &key (output t) (test #'equalp))
(let ((total-failed 0)
(total-passed 0))
(let ((*package* (find-package package)))
(do-symbols (symbol (find-package package))
(when (eq *package* (symbol-package symbol))
(multiple-value-bind (tests-failed tests-passed) (test symbol :output output)
(multiple-value-bind (tests-failed tests-passed) (test symbol :output output :test test)
(incf total-failed tests-failed)
(incf total-passed tests-passed)))))
(values total-failed total-passed)))
Expand Down
33 changes: 33 additions & 0 deletions tests/doctest.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,39 @@
(assert-equalp expected-output actual-output))
(values (first result) (second result))))

(defun test-parse-function-lambda-expression (function &key
(expected-parameters nil)
(expected-docstring nil)
(expected-declare nil)
(expected-body nil))
(multiple-value-bind (name parameters docstring declare body) (doctest::parse-function-lambda-expression function)
(assert-true name)
(assert-equalp expected-parameters parameters)
(assert-equalp expected-docstring docstring)
(assert-equalp expected-declare declare)
(assert-equalp expected-body body)))

(define-test parse-function-lambda-expression ()
(test-parse-function-lambda-expression (lambda ()))
(test-parse-function-lambda-expression (lambda (a b))
:expected-parameters '(a b))
(test-parse-function-lambda-expression (lambda () "body")
:expected-body '("body"))
(test-parse-function-lambda-expression (lambda () (declare) "body")
:expected-declare '(declare)
:expected-body '("body"))
(test-parse-function-lambda-expression (lambda () "docstring" "body")
:expected-docstring "docstring"
:expected-body '("body"))
(test-parse-function-lambda-expression (lambda () "docstring" (declare) "body")
:expected-docstring "docstring"
:expected-declare '(declare)
:expected-body '("body"))
(test-parse-function-lambda-expression (lambda () (declare))
:expected-declare '(declare))
(test-parse-function-lambda-expression (lambda () "docstring" (declare))
:expected-docstring "docstring"
:expected-declare '(declare)))

(define-test doctest ()
;; Test the documentation for the library itself
Expand Down

0 comments on commit 29a0eaa

Please sign in to comment.