diff tests/automated/test-harness.el @ 5118:e0db3c197671 ben-lisp-object

merge up to latest default branch, doesn't compile yet
author Ben Wing <ben@xemacs.org>
date Sat, 26 Dec 2009 21:18:49 -0600
parents 294a86d29f99
children a3c673c0720b
line wrap: on
line diff
--- a/tests/automated/test-harness.el	Sat Dec 26 00:20:27 2009 -0600
+++ b/tests/automated/test-harness.el	Sat Dec 26 21:18:49 2009 -0600
@@ -38,6 +38,14 @@
 ;;; Implementation-Incomplete-Expect-Failure wrapper macros to mark them.
 ;;; A lot of the tests we run push limits; suppress Ebola message with the
 ;;; Ignore-Ebola wrapper macro.
+;;; Some noisy code will call `message'.  Output from `message' can be
+;;; suppressed with the Silence-Message macro.  Functions that are known to
+;;; issue messages include `write-region', `find-tag', `tag-loop-continue',
+;;; `insert', and `mark-whole-buffer'.  N.B. The Silence-Message macro
+;;; currently does not suppress the newlines printed by `message'.
+;;; Definitely do not use Silence-Message with Check-Message.
+;;; In general it should probably only be used on code that prepares for a
+;;; test, not on tests.
 ;;; 
 ;;; You run the tests using M-x test-emacs-test-file,
 ;;; or $(EMACS) -batch -l .../test-harness.el -f batch-test-emacs file ...
@@ -45,8 +53,35 @@
 
 (require 'bytecomp)
 
+(defvar unexpected-test-suite-failures 0
+  "Cumulative number of unexpected failures since test-harness was loaded.
+
+\"Unexpected failures\" are those caught by a generic handler established
+outside of the test context.  As such they involve an abort of the test
+suite for the file being tested.
+
+They often occur during preparation of a test or recording of the results.
+For example, an executable used to generate test data might not be present
+on the system, or a system error might occur while reading a data file.")
+
+(defvar unexpected-test-suite-failure-files nil
+  "List of test files causing unexpected failures.")
+
+;; Declared for dynamic scope; _do not_ initialize here.
+(defvar unexpected-test-file-failures)
+
 (defvar test-harness-test-compiled nil
-  "Non-nil means the test code was compiled before execution.")
+  "Non-nil means the test code was compiled before execution.
+
+You probably should not make tests depend on compilation.
+However, it can be useful to conditionally change messages based on whether
+the code was compiled or not.  For example, the case that motivated the
+implementation of this variable:
+
+\(when test-harness-test-compiled
+  ;; this ha-a-ack depends on the failing compiled test coming last
+  \(setq test-harness-failure-tag
+	\"KNOWN BUG - fix reverted; after 2003-10-31 notify stephen\n\"))")
 
 (defvar test-harness-verbose
   (and (not noninteractive) (> (device-baud-rate) search-slow-speed))
@@ -134,6 +169,7 @@
 	  (setq body (cons (read buffer) body)))
       (end-of-file nil)
       (error
+       (incf unexpected-test-file-failures)
        (princ (format "Unexpected error %S reading forms from buffer\n"
 		      error-info))))
     `(lambda ()
@@ -144,7 +180,6 @@
        (defvar missing-message-failures)
        (defvar other-failures)
 
-       (defvar unexpected-test-suite-failure)
        (defvar trick-optimizer)
 
        ,@(nreverse body))))
@@ -158,6 +193,7 @@
 	(wrong-error-failures 0)
 	(missing-message-failures 0)
 	(other-failures 0)
+	(unexpected-test-file-failures 0)
 
 	;; #### perhaps this should be a defvar, and output at the very end
 	;; OTOH, this way AC types can use a null EMACSPACKAGEPATH to find
@@ -165,7 +201,6 @@
 	(skipped-test-reasons (make-hash-table :test 'equal))
 
 	(trick-optimizer nil)
-	(unexpected-test-suite-failure nil)
 	(debug-on-error t)
 	(pass-stream nil))
     (with-output-to-temp-buffer "*Test-Log*"
@@ -178,7 +213,29 @@
 	`(let ((test-harness-failure-tag "KNOWN BUG")
 	       (test-harness-success-tag "PASS (FAILURE EXPECTED)"))
 	  ,@body))
-    
+
+      (defmacro Known-Bug-Expect-Error (expected-error &rest body)
+	(let ((quoted-body (if (= 1 (length body))
+			       `(quote ,(car body)) `(quote (progn ,@body)))))
+          `(let ((test-harness-failure-tag "KNOWN BUG")
+                 (test-harness-success-tag "PASS (FAILURE EXPECTED)"))
+            (condition-case error-info
+                (progn
+                  (setq trick-optimizer (progn ,@body))
+                  (Print-Pass 
+                   "%S executed successfully, but expected error %S"
+                   ,quoted-body
+                   ',expected-error)
+                  (incf passes))
+              (,expected-error
+               (Print-Failure "%S ==> error %S, as expected"
+                              ,quoted-body ',expected-error)
+               (incf no-error-failures))
+              (error
+               (Print-Failure "%S ==> expected error %S, got error %S instead"
+                              ,quoted-body ',expected-error error-info)
+               (incf wrong-error-failures))))))
+
       (defmacro Implementation-Incomplete-Expect-Failure (&rest body)
 	`(let ((test-harness-failure-tag "IMPLEMENTATION INCOMPLETE")
 	       (test-harness-success-tag "PASS (FAILURE EXPECTED)"))
@@ -210,22 +267,27 @@
 	       (Print-Skip ,description ,reason))
 	   ,@body))
 
-      (defmacro Assert (assertion &optional failing-case)
+      (defmacro Assert (assertion &optional failing-case description)
+	"Test passes if ASSERTION is true.
+Optional FAILING-CASE describes the particular failure.
+Optional DESCRIPTION describes the assertion.
+FAILING-CASE and DESCRIPTION are useful when Assert is used in a loop."
 	`(condition-case error-info
 	  (progn
 	    (assert ,assertion)
-	    (Print-Pass "%S" (quote ,assertion))
+	    (Print-Pass "%S" (quote ,(or description assertion)))
 	    (incf passes))
 	  (cl-assertion-failed
 	   (Print-Failure (if ,failing-case
 			      "Assertion failed: %S; failing case = %S"
 			    "Assertion failed: %S")
-			  (quote ,assertion) ,failing-case)
+			  (quote ,(or description assertion)) ,failing-case)
 	   (incf assertion-failures))
 	  (t (Print-Failure (if ,failing-case
 				"%S ==> error: %S; failing case =  %S"
 			      "%S ==> error: %S")
-			    (quote ,assertion) error-info ,failing-case)
+			    (quote ,(or description assertion))
+			    error-info ,failing-case)
 	     (incf other-failures)
 	     )))
 
@@ -260,6 +322,10 @@
 				,quoted-body ',expected-error)
 		 (incf no-error-failures))
 	     (,expected-error
+	      ;; #### Damn, this binding doesn't capture frobs, eg, for
+	      ;; invalid_argument() ... you only get the REASON.  And for
+	      ;; wrong_type_argument(), there's no reason only FROBs.
+	      ;; If this gets fixed, fix tests in regexp-tests.el.
 	      (let ((error-message (second error-info)))
 		(if (string-match ,expected-error-regexp error-message)
 		    (progn
@@ -274,7 +340,7 @@
 			     ,quoted-body ',expected-error error-info)
 	      (incf wrong-error-failures)))))
 
-
+      ;; Do not use this with Silence-Message.
       (defmacro Check-Message (expected-message-regexp &rest body)
 	(Skip-Test-Unless (fboundp 'defadvice)
 			  "can't defadvice"
@@ -306,6 +372,12 @@
 		  (incf other-failures)))
 	       (ad-unadvise 'message)))))
 
+      ;; #### Perhaps this should override `message' itself, too?
+      (defmacro Silence-Message (&rest body)
+	`(flet ((append-message (&rest args) ())
+                (clear-message (&rest args) ()))
+          ,@body))
+
       (defmacro Ignore-Ebola (&rest body)
 	`(let ((debug-issue-ebola-notices -42)) ,@body))
 
@@ -320,7 +392,7 @@
       (condition-case error-info
 	  (funcall (test-harness-read-from-buffer inbuffer))
 	(error
-	 (setq unexpected-test-suite-failure t)
+	 (incf unexpected-test-file-failures)
 	 (princ (format "Unexpected error %S while executing interpreted code\n"
 		error-info))
 	 (message "Unexpected error %S while executing interpreted code." error-info)
@@ -341,6 +413,7 @@
 	(condition-case error-info
 	    (if code (funcall code))
 	  (error
+	   (incf unexpected-test-file-failures)
 	   (princ (format "Unexpected error %S while executing byte-compiled code\n"
 			  error-info))
 	   (message "Unexpected error %S while executing byte-compiled code." error-info)
@@ -376,14 +449,18 @@
 		 skipped-test-reasons)
 	(when (> (length reasons) 1)
 	  (setq summary-msg (concat summary-msg reasons "
-    Probably XEmacs cannot find your installed packages.  Set EMACSPACKAGEPATH
-    to the package hierarchy root or configure with --package-path to enable
-    the skipped tests.")))
+    It may be that XEmacs cannot find your installed packages.  Set
+    EMACSPACKAGEPATH to the package hierarchy root or configure with
+    --package-path to enable the skipped tests.")))
 	(setq test-harness-file-results-alist
 	      (cons (list filename passes total)
 		    test-harness-file-results-alist))
 	(message "%s" summary-msg))
-      (when unexpected-test-suite-failure
+      (when (> unexpected-test-file-failures 0)
+	(setq unexpected-test-suite-failure-files
+	      (cons filename unexpected-test-suite-failure-files))
+	(setq unexpected-test-suite-failures
+	      (+ unexpected-test-suite-failures unexpected-test-file-failures))
 	(message "Test suite execution failed unexpectedly."))
       (fmakunbound 'Assert)
       (fmakunbound 'Check-Error)
@@ -510,7 +587,23 @@
 			 (/ (* 100 nsucc) ntest))
 	      (message test-harness-null-summary-template
 		       (concat basename ":")))
-	    (setq results (cdr results))))))
+	    (setq results (cdr results)))))
+      (when (> unexpected-test-suite-failures 0)
+	(message "\n***** There %s %d unexpected test suite %s in %s:"
+		 (if (= unexpected-test-suite-failures 1) "was" "were")
+		 unexpected-test-suite-failures
+		 (if (= unexpected-test-suite-failures 1) "failure" "failures")
+		 (if (= (length unexpected-test-suite-failure-files) 1)
+		     "file"
+		   "files"))
+	(while unexpected-test-suite-failure-files
+	  (let ((line (pop unexpected-test-suite-failure-files)))
+	    (while (and (< (length line) 61)
+			unexpected-test-suite-failure-files)
+	      (setq line
+		    (concat line " "
+			    (pop unexpected-test-suite-failure-files))))
+	    (message line)))))
     (message "\nDone")
     (kill-emacs (if error 1 0))))