From 0f8c2291470c0da960c7d0a02e0f1557403a79a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simen=20Endsj=C3=B8?= Date: Fri, 15 Mar 2024 22:09:49 +0100 Subject: [PATCH 1/3] Fix docstring extraction Correctly extract docstrings (and other parts) from `function-lambda-expression`. Closes #6 --- src/doctest.lisp | 23 +++++++++++++++++++---- tests/doctest.lisp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/doctest.lisp b/src/doctest.lisp index f592d73..a1a17bb 100644 --- a/src/doctest.lisp +++ b/src/doctest.lisp @@ -255,13 +255,28 @@ (with-input-from-string (docstring (or documentation "")) (run-doctests docstring output))) +(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)) + (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 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 (lambda-expression closure-p name) (function-lambda-expression function) - (declare (ignore closure-p)) - (values (or (documentation function 'function) (third lambda-expression)) - (symbol-name name)))) + (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-function extracts and tests code snippets in 's documentation diff --git a/tests/doctest.lisp b/tests/doctest.lisp index c41cd8e..608b929 100644 --- a/tests/doctest.lisp +++ b/tests/doctest.lisp @@ -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 From 0b1083547c0bf34a9cfab35926c441c7d3228384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simen=20Endsj=C3=B8?= Date: Fri, 15 Mar 2024 22:21:29 +0100 Subject: [PATCH 2/3] Make the equality function configurable `cl:equalp` doesn't work for classes, so we make it configurable so we can pass in e.g. `generic-cl:equalp`. Closes #7 --- src/doctest.lisp | 49 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/doctest.lisp b/src/doctest.lisp index a1a17bb..2a85a99 100644 --- a/src/doctest.lisp +++ b/src/doctest.lisp @@ -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 @@ -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 @@ -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 ." (let ((tests-failed 0) @@ -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 . It returns the number of tests failed and passed and prints a description to . @@ -224,13 +225,13 @@ (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)) @@ -238,22 +239,22 @@ (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 parse-lambda-body (body) (values (and (stringp (car body)) (cdr body) (pop body)) @@ -278,7 +279,7 @@ "") name))) -(defun test-function (function &key (output t)) +(defun test-function (function &key (output t) (test #'equalp)) "Test-function extracts and tests code snippets in 's documentation string. It returns the number of tests failed and passed and prints a description to . @@ -287,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 's documentation string. It returns the number of tests failed and passed and prints a description to . @@ -300,12 +301,12 @@ (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 . It returns the number of tests failed and passed and prints a description to . @@ -313,16 +314,16 @@ 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))) From cebfdd035f8cdeece937253542ac77f70812829d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simen=20Endsj=C3=B8?= Date: Fri, 15 Mar 2024 22:32:52 +0100 Subject: [PATCH 3/3] Add changelog and bump version --- CHANGELOG.org | 13 +++++++++++++ sijo-doctest.asd | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.org b/CHANGELOG.org index 7872363..2dc67c3 100644 --- a/CHANGELOG.org +++ b/CHANGELOG.org @@ -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 diff --git a/sijo-doctest.asd b/sijo-doctest.asd index 5c32ee6..d8a1598 100644 --- a/sijo-doctest.asd +++ b/sijo-doctest.asd @@ -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) , Simen Endsjø " :licence "GPL" :serial t