Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.4.0 #8

Merged
merged 3 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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