r5449: *** empty log message ***
authorKevin M. Rosenberg <kevin@rosenberg.net>
Mon, 4 Aug 2003 12:01:54 +0000 (12:01 +0000)
committerKevin M. Rosenberg <kevin@rosenberg.net>
Mon, 4 Aug 2003 12:01:54 +0000 (12:01 +0000)
assert.lisp [new file with mode: 0644]
debian/changelog
debian/rules
fixture.lisp [new file with mode: 0644]
package.lisp
printer.lisp [new file with mode: 0644]
result.lisp [new file with mode: 0644]
suite.lisp [new file with mode: 0644]
tests.lisp
xlunit.asd

diff --git a/assert.lisp b/assert.lisp
new file mode 100644 (file)
index 0000000..8307e2f
--- /dev/null
@@ -0,0 +1,43 @@
+;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;; FILE IDENTIFICATION
+;;;;
+;;;; Name:     assert.lisp
+;;;; Purpose:  Assert functions for XLUnit
+;;;; Author:   Kevin Rosenberg
+;;;;
+;;;; $Id: assert.lisp,v 1.1 2003/08/04 12:01:54 kevin Exp $
+;;;; *************************************************************************
+
+(in-package #:xlunit)
+
+
+;;; Assertions
+
+(define-condition test-failure-condition (simple-condition) 
+  ((msg :initform nil :initarg :msg :accessor msg))
+  (:documentation "Base class for all test failures."))
+
+
+(defun failure (format-str &rest args)
+  "Signal a test failure and exit the test."
+  (signal 'test-failure-condition
+         :format-control format-str
+         :format-arguments args))
+
+(defmacro test-assert (test &optional msg)
+  `(unless ,test
+    (failure "Test assertion: ~s" ',test)))
+
+(defun assert-equal (v1 v2 &optional msg)
+  (unless (equal v1 v2)
+    (failure "Test equal: ~s ~s" v1 v2)))
+
+(defun assert-true (v &optional msg)
+  (unless v
+    (failure "Test true: ~s [~A]" v (if msg msg ""))))
+
+(defun assert-false (v &optional msg)
+  (when v
+    (failure "Test false ~A" (if msg msg ""))))
+
index 67f37f88721bb88be711863d220f25f7a3ac2499..b4dd85464f6f4811a4aa90b3af43c1e3ba1b1a88 100644 (file)
@@ -1,3 +1,9 @@
+cl-xlunit (0.2.0-1) unstable; urgency=low
+
+  * New version
+
+ -- Kevin M. Rosenberg <kmr@debian.org>  Mon,  4 Aug 2003 05:35:35 -0600
+
 cl-xlunit (0.1.0-1) unstable; urgency=low
 
   * Initial upload
index 3e2f3404ccc2d08e5c3467bea8d27f83eae86958..426d6e55fef7de918e733f4d684779af7e6b314a 100755 (executable)
@@ -52,7 +52,7 @@ binary-arch: build install
        dh_testroot
 #      dh_installdebconf       
        dh_installdocs 
-       dh_installexamples examples.lisp
+       dh_installexamples example.lisp
 #      dh_installmenu
 #      dh_installlogrotate
 #      dh_installemacsen
diff --git a/fixture.lisp b/fixture.lisp
new file mode 100644 (file)
index 0000000..8408cc8
--- /dev/null
@@ -0,0 +1,124 @@
+;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;; FILE IDENTIFICATION
+;;;;
+;;;; Name:        fixture.lisp
+;;;; Purpose:     eXtreme Lisp Test Suite
+;;;; Authors:     Kevin Rosenberg and Craig Brozefsky
+;;;;
+;;;; $Id: fixture.lisp,v 1.1 2003/08/04 12:01:54 kevin Exp $
+;;;; *************************************************************************
+
+(in-package #:xlunit)
+
+
+(defclass test-fixture ()
+  ((test-thunk
+    :initarg :test-thunk :reader test-thunk
+    :initform 'perform-test
+    :documentation
+    "A thunk or symbol which will be applied to this instance, a
+test-case, to perform that test-case. Defaults to 'perform-test")
+   (test-name
+    :initarg :test-name
+    :reader test-name
+    :documentation
+    "The name of this test-case, used in reports.")
+   (test-description
+    :initarg :description
+    :reader description
+    :documentation
+    "Short description of this test-case, uses in reports"))
+  (:documentation
+   "Base class for test-fixtures.  Test-cases are instances of test-fixtures."))
+
+(defgeneric setup (test)
+  (:documentation
+   "Method called before performing a test, should set up the
+environment the test-case needs to operate in."))
+
+(defmethod setup ((test test-fixture))
+  t)
+
+(defgeneric teardown (test)
+  (:documentation
+   "Method called after performing a test.  Should reverse everything
+that the setup method did for this instance."))
+
+(defmethod teardown ((test test-fixture))
+  t)
+
+
+(defmacro handler-case-if (test form &body cases)
+  `(if ,test
+       (handler-case
+        ,form
+       ,@cases)
+     ,form))
+
+(defmacro unwind-protect-if (test protected cleanup)
+  `(if ,test
+       (unwind-protect
+          ,protected
+        ,cleanup)
+     (progn ,protected ,cleanup)))
+
+
+(defmethod run-test ((test test-fixture)
+                    &key (result (make-instance 'test-result))
+                    (handle-errors t))
+  "Perform the test represented by the given test-case or test-suite.
+Returns one or more test-result objects, one for each test-case
+performed."
+  (incf (test-count result))
+  (with-slots (failures errors) result
+    (unwind-protect-if handle-errors
+       (handler-case-if handle-errors
+        (let ((res (progn (setup test)
+                          (funcall (test-thunk test) test))))
+          (if (typep res 'test-failure-condition)
+              (push (make-instance 'test-failure
+                      :failed-test test
+                      :thrown-condition res)
+                    failures)))
+        (test-failure-condition (failure)
+                                (push (make-instance 'test-failure
+                                        :failed-test test
+                                        :thrown-condition failure)
+                                      failures))
+        (error (err)
+               (push (make-instance 'test-failure 
+                       :failed-test test 
+                       :thrown-condition err)
+                     errors)))
+       (if handle-errors
+           (handler-case
+               (teardown test)
+             (error (err)
+               (push 
+                (make-instance 'test-failure
+                  :failed-test test :thrown-condition err)
+                errors)))
+         (teardown test))))
+  result)
+
+
+(defun make-test (fixture name &key test-thunk test-suite description)
+  "Create a test-case which is an instance of FIXTURE.  TEST-THUNK is
+the method that will be invoked when perfoming this test, and can be a
+symbol or a lambda taking a single argument, the test-fixture
+instance.  DESCRIPTION is obviously what it says it is."
+  (let ((newtest (make-instance fixture
+                  :test-name (etypecase name
+                               (symbol
+                                (string-downcase (symbol-name name)))
+                               (string
+                                name))
+                  :test-thunk 
+                  (if(and (symbolp name) (null test-thunk))
+                      name
+                    test-thunk)
+                  :description description)))
+       (if test-suite (add-test newtest test-suite))
+       newtest))
+
index fc7291a14aba90f1a7598e2a913efa2209c9f8a6..4b51cf509d8d577dd8b202d54911d28435b85925 100644 (file)
@@ -6,7 +6,7 @@
 ;;;; Purpose:     Package definition for XLTEST
 ;;;; Authors:     Kevin Rosenberg and Craig Brozefsky
 ;;;;
-;;;; $Id: package.lisp,v 1.3 2003/08/04 09:50:33 kevin Exp $
+;;;; $Id: package.lisp,v 1.4 2003/08/04 12:01:54 kevin Exp $
 ;;;; *************************************************************************
 
 (in-package #:cl-user)
   (:nicknames #:xlunit #:xptest)
   (:export
      ;;; Framework classes
+   
+   ;; fixture
+   #:test-fixture
    #:setup
    #:teardown
-   #:perform-test
-   #:test-failure
-   #:failure
    #:run-test
    #:make-test
+
+   ;; assert
+   #:assert-equal
+   #:assert-true
+   #:assert-false
+   #:test-assert
+   #:test-failure
+   #:failure
+
+   ;; suite.lisp
+   #:text-testrunner
    #:make-test-suite
    #:setup-testsuite-named
    #:teardown-testsuite-named
    #:test-named
    #:remove-test
    #:tests
-   #:test-result
-   #:report-result
-   #:make-test-suite-for-fixture
-   #:assert-equal
-   #:assert-true
-   #:assert-false
-   #:test-assert
-   #:test-fixture
-   #:text-testrunner
+   
+   ;; printer.lisp
    #:summary
+   
+   ;; result.lisp
+   #:test-result
+   #:make-test-result
+   #:was-successful
    )
-  (:documentation "This is the XP TestSuite Framework."))
+  (:documentation "This is the XLUnit Framework."))
 
diff --git a/printer.lisp b/printer.lisp
new file mode 100644 (file)
index 0000000..16f1bdb
--- /dev/null
@@ -0,0 +1,46 @@
+;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;; FILE IDENTIFICATION
+;;;;
+;;;; Name:        printer.lisp
+;;;; Purpose:     Printer functions for XLUnit
+;;;; Authors:     Kevin Rosenberg
+;;;;
+;;;; $Id: printer.lisp,v 1.1 2003/08/04 12:01:54 kevin Exp $
+;;;; *************************************************************************
+
+(in-package #:xlunit)
+
+
+(defun result-printer (result seconds stream)
+  (format stream "~&Time: ~D~%~%" (coerce seconds 'float))
+  (print-defects (test-errors result) "error" stream)
+  (print-defects (test-failures result) "failure" stream)
+  (if (was-successful result)
+      (format stream "OK (~D tests)~%" (test-count result))
+    (progn
+      (format stream "~%FAILURES!!!~%")
+      (format stream "Tests run: ~D, Failures: ~D, Errors: ~D~%"
+             (test-count result) (length (test-failures result))
+             (length (test-errors result))))))
+
+(defun print-defects (defects type stream)
+  (when defects
+    (let ((count (length defects)))
+      (if (= count 1)
+         (format stream "~&There was ~D ~A:~%" count type)
+       (format stream "~&There were ~D ~As:~%" count type))
+      (dotimes (i count)
+       (let ((defect (nth i defects)))
+         (format stream "~&~D) ~A " i (class-name
+                                       (class-of (failed-test defect))))
+         (apply #'format stream (simple-condition-format-control 
+                                 (thrown-condition defect))
+                (simple-condition-format-arguments 
+                 (thrown-condition defect)))
+         (fresh-line stream))))))
+
+(defmethod summary ((result test-result))
+  (format nil "~D run, ~D erred, ~D failed"
+         (test-count result) (length (test-errors result))
+         (length (test-failures result))))
diff --git a/result.lisp b/result.lisp
new file mode 100644 (file)
index 0000000..ba95e4a
--- /dev/null
@@ -0,0 +1,42 @@
+;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;; FILE IDENTIFICATION
+;;;;
+;;;; Name:        result.lisp
+;;;; Purpose:     Result functions for XLUnit
+;;;; Authors:     Kevin Rosenberg
+;;;;
+;;;; $Id: result.lisp,v 1.1 2003/08/04 12:01:54 kevin Exp $
+;;;; *************************************************************************
+
+(in-package #:xlunit)
+
+
+(defclass test-result ()
+  ((test :initarg :test :reader result-test)
+   (count :initform 0 :accessor test-count)
+   (failures :initarg :failures :reader test-failures :initform nil)
+   (errors :initarg :errors :reader test-errors :initform nil))
+  (:documentation "The result of applying a test"))
+
+
+(defun make-test-result ()
+  (make-instance 'test-result))
+
+(defclass test-failure ()
+  ((failed-test :initarg :failed-test :reader failed-test)
+   (thrown-condition :initarg :thrown-condition :reader thrown-condition)))
+
+(defmethod is-failure ((failure test-failure))
+  (typep (thrown-condition failure) 'test-failure-condition))
+
+(defmethod print-object ((obj test-failure) stream)
+  (print-unreadable-object (obj stream :type t :identity nil)
+    (format stream "~A: " (failed-test obj))
+    (apply #'format stream 
+          (simple-condition-format-control (thrown-condition obj))
+          (simple-condition-format-arguments (thrown-condition obj)))))
+
+(defmethod was-successful ((result test-result))
+  (and (null (test-failures result))
+       (null (test-errors result))))
diff --git a/suite.lisp b/suite.lisp
new file mode 100644 (file)
index 0000000..046b61a
--- /dev/null
@@ -0,0 +1,124 @@
+;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;; FILE IDENTIFICATION
+;;;;
+;;;; Name:        suite.lisp
+;;;; Purpose:     Suite functions for XLUnit
+;;;; Authors:     Kevin Rosenberg and Craig Brozefsky
+;;;;
+;;;; $Id: suite.lisp,v 1.1 2003/08/04 12:01:54 kevin Exp $
+;;;; *************************************************************************
+
+(in-package #:xlunit)
+
+(defclass test-suite ()
+  ((name :initarg :name :reader test-suite-name)
+   (tests :initarg :tests :accessor tests-hash
+         :initform (make-hash-table :test 'equal))
+   (description :initarg :description :reader description
+               :initform "No description.")))
+
+
+(defmethod setup-testsuite-named (name)
+  (declare (ignore name))
+  t)
+
+(defmethod teardown-testsuite-named (name)
+  (declare (ignore name))
+  t)
+
+(defmethod run-test ((suite test-suite)
+                    &key (result (make-instance 'test-result))
+                    (handle-errors t))
+  (setup-testsuite-named (slot-value suite 'name))
+  (dolist (test (tests suite))
+    (run-test test :result result :handle-errors handle-errors))
+  (teardown-testsuite-named (slot-value suite 'name))
+  result)
+
+(defmethod tests ((suite test-suite))
+  (let ((tlist nil))
+    (maphash #'(lambda (k v)
+                (declare (ignore k))
+                (setf tlist (cons v tlist)))
+            (tests-hash suite))
+    (reverse tlist)))
+
+
+(defun make-test-suite (name-or-fixture &optional description testspecs)
+  "Returns a new test-suite based on a name and TESTSPECS or a fixture
+instance"
+  (etypecase name-or-fixture
+    (symbol
+     (make-test-suite-for-fixture (make-instance name-or-fixture)))
+    (string
+     (let ((suite (make-instance 'test-suite :name name-or-fixture
+                                :description description)))
+       (dolist (testspec testspecs)
+        (add-test (apply #'make-test testspec) suite))
+       suite))))
+
+
+(defmethod add-test ((test test-fixture) (suite test-suite))
+  (setf (gethash (test-name test) (tests-hash suite)) test))
+
+(defmethod add-test ((test test-suite) (suite test-suite))
+  (setf (gethash (test-suite-name test) (tests-hash suite)) test))
+
+(defmethod remove-test ((test test-fixture) (suite test-suite))
+  (remhash (test-name test) (tests-hash suite)))
+
+(defmethod remove-test ((test test-suite) (suite test-suite))
+  (remhash (test-suite-name test) (tests-hash suite)))
+
+(defmethod test-named ((name string) (suite test-suite))
+  (gethash name (tests-hash suite)))
+
+
+;; Dynamic test suite
+
+(defun make-test-suite-for-fixture 
+    (fixture &key
+            (name 
+             (format nil "Automatic for ~A"
+                     (if (slot-boundp fixture 'test-name) 
+                         (test-name fixture)
+                       (type-of fixture))))
+            description)
+  (let ((suite  (make-instance 'test-suite
+                 :name name
+                 :description description))
+       (fns (find-test-generic-functions fixture)))
+    (dolist (fn fns)
+      (make-test (class-name (class-of fixture)) fn
+                :test-suite suite))
+    suite))
+
+(defun find-test-generic-functions (instance)
+  "Return a list of symbols for generic functions specialized on the
+class of an instance and whose name begins with the string 'test-'.
+This is used to dynamically generate a list of tests for a fixture."
+  (let ((res)
+       (package (symbol-package (class-name (class-of instance)))))
+    (do-symbols (s package)
+      (when (and (> (length (symbol-name s)) 5)
+                (string-equal "test-" (subseq (symbol-name s) 0 5))
+                (fboundp s)
+                (typep (symbol-function s) 'generic-function)
+                (plusp (length (compute-applicable-methods 
+                                (ensure-generic-function s)
+                                (list instance)))))
+       (push s res)))
+    (nreverse res)))
+
+
+;;; Test Runners
+
+(defmethod text-testrunner ((suite test-suite) &key (stream t)
+                                                   (handle-errors t))
+  (let* ((start-time (get-internal-real-time))
+        (result (run-test suite :handle-errors handle-errors))
+        (seconds (/ (- (get-internal-real-time) start-time)
+                    internal-time-units-per-second)))
+    (result-printer result seconds stream)))
+
index 101125a21730ba790a91f89cf33ba6aa13dd11e7..ef8ed7cf01b09b19da9a99ed82ee962d48ead6dd 100644 (file)
@@ -7,7 +7,7 @@
 ;;;; Author:     Kevin Rosenberg
 ;;;;
 ;;;; Put in public domain by Kevin Rosenberg
-;;;; $Id: tests.lisp,v 1.3 2003/08/04 09:50:33 kevin Exp $
+;;;; $Id: tests.lisp,v 1.4 2003/08/04 12:01:54 kevin Exp $
 ;;;; *************************************************************************
 
 (defpackage #:xlunit-tests
 
 (in-package #:xlunit-tests)
 
+
+;; Helper test fixture
 (defclass was-run (test-fixture)
   ((log :accessor ws-log)))
 
-
 (defmethod setup ((self was-run))
   (setf (ws-log self) "setup "))
 
 (defmethod test-error-method ((self was-run))
   (error "Err"))
 
-(defclass test-case-test (test-fixture)
-  ((result :accessor result)))
+;;; Main test fixture
 
-(defmethod setup ((self test-case-test))
-  (setf (result self) (make-instance 'test-result)))
+(defclass test-case-test (test-fixture)
+  ())
 
 (defmethod test-template-method ((self test-case-test))
   (let ((test (make-test 'was-run 'test-method)))
-    (run-test test (result self))
+    (run-test test)
     (assert-equal (ws-log test) "setup test-method teardown ")))
 
 (defmethod test-result ((self test-case-test))
-  (let ((test (make-test 'was-run 'test-method)))
-    (run-test test (result self))
-    (assert-equal "1 run, 0 errored, 0 failed" (summary (result self)))))
+  (assert-equal "1 run, 0 erred, 0 failed" 
+               (summary (run-test (make-test 'was-run 'test-method)))))
 
 (defmethod test-thunk ((self test-case-test))
   (let ((test (make-test 'was-run '"Test Failure"
                         (lambda (test) 
                           (declare (ignore test))
                           (assert-equal 10 10)))))
-    (run-test test (result self))
-    (assert-equal "1 run, 0 errored, 0 failed"
-                 (summary (result self)))))
+    (assert-equal "1 run, 0 erred, 0 failed"
+                 (summary (run-test test)))))
 
 (defmethod test-failed-result ((self test-case-test))
-  (let ((test (make-test 'was-run 'test-broken-method)))
-    (run-test test (result self))
-    (assert-equal "1 run, 0 errored, 1 failed"
-                 (summary (result self)))))
+  (assert-equal "1 run, 0 erred, 1 failed"
+               (summary (run-test
+                         (make-test 'was-run 'test-broken-method)))))
 
 (defmethod test-error-result ((self test-case-test))
-  (let ((test (make-test 'was-run 'test-error-method)))
-    (run-test test (result self))
-    (assert-equal "1 run, 1 errored, 0 failed"
-                 (summary (result self)))))
-
+  (assert-equal "1 run, 1 erred, 0 failed"
+               (summary (run-test
+                         (make-test 'was-run 'test-error-method)))))
+  
 (defmethod test-suite ((self test-case-test))
-  (let ((suite (make-test-suite "TestSuite")))
+  (let ((suite (make-test-suite "TestSuite"))
+       (result (make-test-result)))
     (add-test (make-test 'was-run 'test-method) suite)
     (add-test (make-test 'was-run 'test-broken-method) suite)
-    (run-test suite (result self)))
-  (assert-equal "2 run, 0 errored, 1 failed" 
-               (summary (result self))))
+    (run-test suite :result result)
+    (assert-equal "2 run, 0 erred, 1 failed" (summary result))))
 
 (defmethod test-dynamic-suite ((self test-case-test))
-  (let ((suite (make-test-suite 'was-run)))
-    (run-test suite (result self)))
-  (assert-equal "3 run, 1 errored, 1 failed" 
-               (summary (result self))))
+  (assert-equal "3 run, 1 erred, 1 failed" 
+               (summary (run-test (make-test-suite 'was-run)))))
 
-(text-testrunner (make-test-suite 'test-case-test))
+(text-testrunner (make-test-suite 'test-case-test) :handle-errors nil)
 
+(defun do-tests ()
+  (or (was-successful 
+       (run-test (make-test-suite 'test-case-test)))
+      (error "Failed tests")))
index 30ca6f74ff389857e64e1436bea3f2750810a598..9cd8bdba2138243763c0cf8ac7efd712c92e301e 100644 (file)
@@ -7,7 +7,7 @@
 ;;;; Programmer:    Kevin M. Rosenberg
 ;;;; Date Started:  Aug 2003
 ;;;;
-;;;; $Id: xlunit.asd,v 1.1 2003/08/04 09:51:36 kevin Exp $
+;;;; $Id: xlunit.asd,v 1.2 2003/08/04 12:01:54 kevin Exp $
 ;;;; *************************************************************************
 
 (defpackage #:xlunit-system (:use #:asdf #:cl))
   :name "xlunit"
   :author "Kevin Rosenberg based on work by Craig Brozensky"
   :maintainer "Kevin M. Rosenberg <kmr@debian.org>"
-  :licence "Public domain"
+  :licence "BSD"
   :description "Extreme Lisp Testing Suite"
-  :long-description "The XLUNIT package is toolkit for building test suites based on the XPTEST package by Craig Brozensky."
+  :long-description "The XLUnit package is toolkit for building test suites. It is based on the XPTest package by Craig Brozensky and the JUnit package by Kent Beck."
   
+  :serial t
   :components
   ((:file "package")
-   (:file "src" :depends-on ("package"))))
+   (:file "assert")
+   (:file "fixture")
+   (:file "suite")
+   (:file "result")
+   (:file "printer")
+   ))
 
 (defmethod perform ((o test-op) (c (eql (find-system 'xlunit))))
-  (oos 'load-op 'xlunit-tests)
-  (oos 'test-op 'xlunit-tests))
+  (oos 'load-op 'xlunit-tests :force t)
+  (oos 'test-op 'xlunit-tests :force t))
 
 (defsystem xlunit-tests
-  :depends-on (xlunit)
-  :components ((:file "tests")))
+    :depends-on (xlunit)
+    :components ((:file "tests")))
 
 (defmethod perform ((o test-op) (c (eql (find-system 'xlunit-tests))))
-  (or (funcall (intern (symbol-name #:do-tests)
-                      (find-package #:xlunit-tests)))
+  (or (funcall (intern (symbol-name '#:do-tests)
+                      (find-package '#:xlunit-tests)))
       (error "test-op failed")))