diff options
| author | Gemini Lasswell | 2017-09-26 08:14:23 -0700 |
|---|---|---|
| committer | Gemini Lasswell | 2017-10-08 16:13:39 -0700 |
| commit | 3c2e8eff8cc9a4a535f473b3e150cb056d8f891d (patch) | |
| tree | 730be0a589aa785ebcbb48886d7d2c62afa77843 | |
| parent | d79cf638f278e50c22feb53d6ba556f5ce9d7853 (diff) | |
| download | emacs-3c2e8eff8cc9a4a535f473b3e150cb056d8f891d.tar.gz emacs-3c2e8eff8cc9a4a535f473b3e150cb056d8f891d.zip | |
Stop Testcover from producing spurious 1value errors
Fix bug#25351 by copying results of form evaluations for later
comparison.
* lisp/emacs-lisp/testcover.el (testcover-after): Copy the result
of a form's first evaluation and compare subsequent evaluations to
the copy. Improve the error message used when a form's value
changes.
(testcover--copy-object, testcover--copy-object1): New functions.
* test/lisp/emacs-lisp/testcover-resources/testcases.el
(by-value-vs-by-reference-bug-25351): Remove expected failure tag.
(circular-lists-bug-24402): Add another circular list case.
| -rw-r--r-- | lisp/emacs-lisp/testcover.el | 95 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/testcover-resources/testcases.el | 15 |
2 files changed, 81 insertions, 29 deletions
diff --git a/lisp/emacs-lisp/testcover.el b/lisp/emacs-lisp/testcover.el index 320c43b59fa..3628968974c 100644 --- a/lisp/emacs-lisp/testcover.el +++ b/lisp/emacs-lisp/testcover.el | |||
| @@ -49,11 +49,10 @@ | |||
| 49 | ;; function being called is capable of returning in other cases. | 49 | ;; function being called is capable of returning in other cases. |
| 50 | 50 | ||
| 51 | ;; Problems: | 51 | ;; Problems: |
| 52 | ;; * To detect different values, we store the form's result in a vector and | 52 | ;; * `equal', which is used to compare the results of repeatedly executing |
| 53 | ;; compare the next result using `equal'. We don't copy the form's | 53 | ;; a form, has a couple of shortcomings. It considers strings to be the same |
| 54 | ;; result, so if caller alters it (`setcar', etc.) we'll think the next | 54 | ;; if they only differ in properties, and it raises an error when asked to |
| 55 | ;; call has the same value! Also, equal thinks two strings are the same | 55 | ;; compare circular lists. |
| 56 | ;; if they differ only in properties. | ||
| 57 | ;; * Because we have only a "1value" class and no "always nil" class, we have | 56 | ;; * Because we have only a "1value" class and no "always nil" class, we have |
| 58 | ;; to treat as potentially 1-valued any `and' whose last term is 1-valued, | 57 | ;; to treat as potentially 1-valued any `and' whose last term is 1-valued, |
| 59 | ;; in case the last term is always nil. Example: | 58 | ;; in case the last term is always nil. Example: |
| @@ -259,26 +258,25 @@ BEFORE-INDEX is the form's index into the code-coverage vector." | |||
| 259 | AFTER-INDEX is the form's index into the code-coverage | 258 | AFTER-INDEX is the form's index into the code-coverage |
| 260 | vector. Return VALUE." | 259 | vector. Return VALUE." |
| 261 | (let ((old-result (aref testcover-vector after-index))) | 260 | (let ((old-result (aref testcover-vector after-index))) |
| 262 | (cond | 261 | (cond |
| 263 | ((eq 'unknown old-result) | 262 | ((eq 'unknown old-result) |
| 264 | (aset testcover-vector after-index value)) | 263 | (aset testcover-vector after-index (testcover--copy-object value))) |
| 265 | ((eq 'maybe old-result) | 264 | ((eq 'maybe old-result) |
| 266 | (aset testcover-vector after-index 'ok-coverage)) | 265 | (aset testcover-vector after-index 'ok-coverage)) |
| 267 | ((eq '1value old-result) | 266 | ((eq '1value old-result) |
| 268 | (aset testcover-vector after-index | 267 | (aset testcover-vector after-index |
| 269 | (cons old-result value))) | 268 | (cons old-result (testcover--copy-object value)))) |
| 270 | ((and (eq (car-safe old-result) '1value) | 269 | ((and (eq (car-safe old-result) '1value) |
| 271 | (not (condition-case () | 270 | (not (condition-case () |
| 272 | (equal (cdr old-result) value) | 271 | (equal (cdr old-result) value) |
| 273 | ;; TODO: Actually check circular lists for equality. | 272 | (circular-list t)))) |
| 274 | (circular-list t)))) | 273 | (error "Value of form expected to be constant does vary, from %s to %s" |
| 275 | (error "Value of form marked with `1value' does vary: %s" value)) | 274 | old-result value)) |
| 276 | ;; Test if a different result. | 275 | ;; Test if a different result. |
| 277 | ((not (condition-case () | 276 | ((not (condition-case () |
| 278 | (equal value old-result) | 277 | (equal value old-result) |
| 279 | ;; TODO: Actually check circular lists for equality. | 278 | (circular-list nil))) |
| 280 | (circular-list nil))) | 279 | (aset testcover-vector after-index 'ok-coverage)))) |
| 281 | (aset testcover-vector after-index 'ok-coverage)))) | ||
| 282 | value) | 280 | value) |
| 283 | 281 | ||
| 284 | ;; Add these behaviors to Edebug. | 282 | ;; Add these behaviors to Edebug. |
| @@ -286,6 +284,53 @@ vector. Return VALUE." | |||
| 286 | (push '(testcover testcover-enter testcover-before testcover-after) | 284 | (push '(testcover testcover-enter testcover-before testcover-after) |
| 287 | edebug-behavior-alist)) | 285 | edebug-behavior-alist)) |
| 288 | 286 | ||
| 287 | (defun testcover--copy-object (obj) | ||
| 288 | "Make a copy of OBJ. | ||
| 289 | If OBJ is a cons cell, copy both its car and its cdr. | ||
| 290 | Contrast to `copy-tree' which does the same but fails on circular | ||
| 291 | structures, and `copy-sequence', which copies only along the | ||
| 292 | cdrs. Copy vectors as well as conses." | ||
| 293 | (let ((ht (make-hash-table :test 'eq))) | ||
| 294 | (testcover--copy-object1 obj t ht))) | ||
| 295 | |||
| 296 | (defun testcover--copy-object1 (obj vecp hash-table) | ||
| 297 | "Make a copy of OBJ, using a HASH-TABLE of objects already copied. | ||
| 298 | If OBJ is a cons cell, this recursively copies its car and | ||
| 299 | iteratively copies its cdr. When VECP is non-nil, copy | ||
| 300 | vectors as well as conses." | ||
| 301 | (if (and (atom obj) (or (not vecp) (not (vectorp obj)))) | ||
| 302 | obj | ||
| 303 | (let ((copy (gethash obj hash-table nil))) | ||
| 304 | (unless copy | ||
| 305 | (cond | ||
| 306 | ((consp obj) | ||
| 307 | (let* ((rest obj) current) | ||
| 308 | (setq copy (cons nil nil) | ||
| 309 | current copy) | ||
| 310 | (while | ||
| 311 | (progn | ||
| 312 | (puthash rest current hash-table) | ||
| 313 | (setf (car current) | ||
| 314 | (testcover--copy-object1 (car rest) vecp hash-table)) | ||
| 315 | (setq rest (cdr rest)) | ||
| 316 | (cond | ||
| 317 | ((atom rest) | ||
| 318 | (setf (cdr current) | ||
| 319 | (testcover--copy-object1 rest vecp hash-table)) | ||
| 320 | nil) | ||
| 321 | ((gethash rest hash-table nil) | ||
| 322 | (setf (cdr current) (gethash rest hash-table nil)) | ||
| 323 | nil) | ||
| 324 | (t (setq current | ||
| 325 | (setf (cdr current) (cons nil nil))))))))) | ||
| 326 | (t ; (and vecp (vectorp obj)) is true due to test in if above. | ||
| 327 | (setq copy (copy-sequence obj)) | ||
| 328 | (puthash obj copy hash-table) | ||
| 329 | (dotimes (i (length copy)) | ||
| 330 | (aset copy i | ||
| 331 | (testcover--copy-object1 (aref copy i) vecp hash-table)))))) | ||
| 332 | copy))) | ||
| 333 | |||
| 289 | ;;;========================================================================= | 334 | ;;;========================================================================= |
| 290 | ;;; Display the coverage data as color splotches on your code. | 335 | ;;; Display the coverage data as color splotches on your code. |
| 291 | ;;;========================================================================= | 336 | ;;;========================================================================= |
diff --git a/test/lisp/emacs-lisp/testcover-resources/testcases.el b/test/lisp/emacs-lisp/testcover-resources/testcases.el index d8b8192748d..6a9612db05a 100644 --- a/test/lisp/emacs-lisp/testcover-resources/testcases.el +++ b/test/lisp/emacs-lisp/testcover-resources/testcases.el | |||
| @@ -357,7 +357,6 @@ | |||
| 357 | 357 | ||
| 358 | ;; ==== by-value-vs-by-reference-bug-25351 ==== | 358 | ;; ==== by-value-vs-by-reference-bug-25351 ==== |
| 359 | "An object created by a 1value expression may be modified by other code." | 359 | "An object created by a 1value expression may be modified by other code." |
| 360 | :expected-result :failed | ||
| 361 | ;; ==== | 360 | ;; ==== |
| 362 | (defun testcover-testcase-ab () | 361 | (defun testcover-testcase-ab () |
| 363 | (list 'a 'b)) | 362 | (list 'a 'b)) |
| @@ -491,10 +490,18 @@ regarding the odd-looking coverage result for the quoted form." | |||
| 491 | "Testcover captures and ignores circular list errors." | 490 | "Testcover captures and ignores circular list errors." |
| 492 | ;; ==== | 491 | ;; ==== |
| 493 | (defun testcover-testcase-cyc1 (a) | 492 | (defun testcover-testcase-cyc1 (a) |
| 494 | (let ((ls (make-list 10 a%%%))) | 493 | (let ((ls (make-list 10 a%%%)%%%)) |
| 495 | (nconc ls ls) | 494 | (nconc ls%%% ls%%%) |
| 496 | ls)) | 495 | ls)) ; The lack of a mark here is due to an ignored circular list error. |
| 497 | (testcover-testcase-cyc1 1) | 496 | (testcover-testcase-cyc1 1) |
| 498 | (testcover-testcase-cyc1 1) | 497 | (testcover-testcase-cyc1 1) |
| 498 | (defun testcover-testcase-cyc2 (a b) | ||
| 499 | (let ((ls1 (make-list 10 a%%%)%%%) | ||
| 500 | (ls2 (make-list 10 b))) | ||
| 501 | (nconc ls2 ls2) | ||
| 502 | (nconc ls1%%% ls2) | ||
| 503 | ls1)) | ||
| 504 | (testcover-testcase-cyc2 1 2) | ||
| 505 | (testcover-testcase-cyc2 1 4) | ||
| 499 | 506 | ||
| 500 | ;; testcases.el ends here. | 507 | ;; testcases.el ends here. |