123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- ;;;; guardians.test --- test suite for Guile Guardians -*- scheme -*-
- ;;;; Jim Blandy <jimb@red-bean.com> --- July 1999
- ;;;;
- ;;;; Copyright (C) 1999, 2001, 2006, 2014 Free Software Foundation, Inc.
- ;;;;
- ;;;; This library is free software; you can redistribute it and/or
- ;;;; modify it under the terms of the GNU Lesser General Public
- ;;;; License as published by the Free Software Foundation; either
- ;;;; version 3 of the License, or (at your option) any later version.
- ;;;;
- ;;;; This library is distributed in the hope that it will be useful,
- ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- ;;;; Lesser General Public License for more details.
- ;;;;
- ;;;; You should have received a copy of the GNU Lesser General Public
- ;;;; License along with this library; if not, write to the Free Software
- ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- ;;; These tests make some questionable assumptions.
- ;;;
- ;;; - They assume that a GC will find all dead objects, so they
- ;;; will become flaky if we have a generational GC.
- ;;;
- ;;; - More generally, when a weakly referenced object doesn't disappear as
- ;;; expected, it's hard to tell whether that's because of a guardian bug of
- ;;; because a reference to it is being held somewhere, e.g., one some part
- ;;; of the stack that hasn't been overwritten. Thus, most tests cannot
- ;;; fail, they can just throw `unresolved'. We try hard to clear
- ;;; references that may have been left on the stacks (see "clear refs left
- ;;; on the stack" lines).
- ;;;
- ;;; - They assume that objects won't be saved by the guardian until
- ;;; they explicitly invoke GC --- in other words, they assume that GC
- ;;; won't happen too often.
- (define-module (test-guardians)
- :use-module (test-suite lib)
- :use-module (ice-9 documentation)
- :use-module (ice-9 weak-vector))
- ;;;
- ;;; miscellaneous
- ;;;
- (define (documented? object)
- (not (not (object-documentation object))))
- (gc)
- ;;; Who guards the guardian?
- ;;; Note: We use strings rather than symbols because symbols are usually
- ;;; ``interned'', i.e., kept in a weakly-keyed hash table, thus making them
- ;;; inappropriate for the tests below. Furthermore, we use `string-copy' in
- ;;; order to make sure that no string is kept around in the interpreter
- ;;; unwillingly (e.g., in the source-property weak hash table).
- (gc)
- (define g2 (make-guardian))
- (g2 (list (string-copy "g2-garbage")))
- (define g3 (make-guardian))
- (g3 (list (string-copy "g3-garbage")))
- (g3 g2)
- (pass-if "g2-garbage not collected yet" (equal? (g2) #f))
- (pass-if "g3-garbage not collected yet" (equal? (g3) #f))
- (set! g2 #f)
- (gc)
- (let ((seen-g3-garbage #f)
- (seen-g2 #f)
- (seen-something-else #f))
- (let loop ()
- (let ((saved (g3)))
- (if saved
- (begin
- (cond
- ((equal? saved (list (string-copy "g3-garbage")))
- (set! seen-g3-garbage #t))
- ((procedure? saved) (set! seen-g2 saved))
- (else (pk 'junk saved) (set! seen-something-else #t)))
- (loop)))))
- (pass-if "g3-garbage saved" (or seen-g3-garbage (throw 'unresolved)))
- (pass-if "g2-saved" (or (procedure? seen-g2) (throw 'unresolved)))
- (pass-if "nothing else saved" (not seen-something-else))
- ;; FIXME: The following test fails because the guardian for `g2-garbage'
- ;; disappared from the weak-car guardian list of `g2-garbage' right before
- ;; `g2-garbage' was finalized (in `finalize_guarded ()'). Sample session
- ;; (compiled with `-DDEBUG_GUARDIANS'):
- ;;
- ;; guile> (define g (make-guardian))
- ;; guile> (let ((g2 (make-guardian)))
- ;; (format #t "g2 = ~x~%" (object-address g2))
- ;; (g2 (string-copy "foo"))
- ;; (g g2))
- ;; g2 = 81fde18
- ;; guile> (gc)
- ;; finalizing guarded 0x827f6a0 (1 guardians)
- ;; guardian for 0x827f6a0 vanished
- ;; end of finalize (0x827f6a0)
- ;; finalizing guarded 0x81fde18 (1 guardians)
- ;; end of finalize (0x81fde18)
- (pass-if "g2-garbage saved" (or (and (procedure? seen-g2)
- (equal? (seen-g2)
- (list (string-copy
- "g2-garbage"))))
- (throw 'unresolved))))
- (with-test-prefix "standard guardian functionality"
- (with-test-prefix "make-guardian"
- (pass-if "documented?"
- (documented? make-guardian))
- (pass-if "returns procedure"
- (procedure? (make-guardian)))
- (pass-if "returns new procedure each time"
- (not (equal? (make-guardian) (make-guardian)))))
- (with-test-prefix "empty guardian"
- (pass-if "returns #f"
- (eq? ((make-guardian)) #f))
- (pass-if "returns always #f"
- (let ((g (make-guardian)))
- (and (eq? (g) #f)
- (begin (gc) (eq? (g) #f))
- (begin (gc) (eq? (g) #f))))))
- (with-test-prefix "guarding independent objects"
- (pass-if "guarding immediate"
- (let ((g (make-guardian)))
- (g #f)
- (and (eq? (g) #f)
- (begin (gc) (eq? (g) #f))
- (begin (gc) (eq? (g) #f)))))
- (pass-if "guarding non-immediate"
- (let ((g (make-guardian)))
- (gc)
- (g (cons #f #f))
- (cons 'clear 'stack) ;; clear refs left on the stack
- (if (not (eq? (g) #f))
- (throw 'unresolved)
- (begin
- (gc)
- (if (not (equal? (g) (cons #f #f)))
- (throw 'unresolved)
- (eq? (g) #f))))))
- (pass-if "guarding two non-immediates"
- (let ((g (make-guardian)))
- (gc)
- (g (cons #f #f))
- (g (cons #t #t))
- (cons 'clear 'stack) ;; clear refs left on the stack
- (if (not (eq? (g) #f))
- (throw 'unresolved)
- (begin
- (gc)
- (let ((l (list (g) (g))))
- (if (not (or (equal? l (list (cons #f #f) (cons #t #t)))
- (equal? l (list (cons #t #t) (cons #f #f)))))
- (throw 'unresolved)
- (eq? (g) #f)))))))
- (pass-if "re-guarding non-immediates"
- (let ((g (make-guardian)))
- (gc)
- (g (cons #f #f))
- (cons 'clear 'stack) ;; clear refs left on the stack
- (if (not (eq? (g) #f))
- (throw 'unresolved)
- (begin
- (gc)
- (let ((p (g)))
- (if (not (equal? p (cons #f #f)))
- (throw 'unresolved)
- (begin
- (g p)
- (set! p #f)
- (gc)
- (if (not (equal? (g) (cons #f #f)))
- (throw 'unresolved)
- (eq? (g) #f)))))))))
- (pass-if "guarding living non-immediate"
- (let ((g (make-guardian))
- (p (cons #f #f)))
- (g p)
- (if (not (eq? (g) #f))
- (throw 'fail)
- (begin
- (gc)
- (not (eq? (g) p)))))))
- (with-test-prefix "guarding weakly referenced objects"
- (pass-if "guarded weak vector element gets returned from guardian"
- (let ((g (make-guardian))
- (v (weak-vector #f)))
- (gc)
- (let ((p (cons #f #f)))
- (g p)
- (weak-vector-set! v 0 p)
- (set! p #f)) ;; clear refs left on the stack
- (if (not (eq? (g) #f))
- (throw 'unresolved)
- (begin
- (gc)
- (if (not (equal? (g) (cons #f #f)))
- (throw 'unresolved)
- (eq? (g) #f))))))
- (pass-if "guarded element of weak vector gets eventually removed from weak vector"
- (let ((g (make-guardian))
- (v (weak-vector #f)))
- (gc)
- (let ((p (cons #f #f)))
- (g p)
- (weak-vector-set! v 0 p)
- (set! p #f)) ;; clear refs left on the stack
- (begin
- (gc)
- (if (not (equal? (g) (cons #f #f)))
- (throw 'unresolved)
- (begin
- (gc)
- (or (not (weak-vector-ref v 0))
- (throw 'unresolved))))))))
- (with-test-prefix "guarding weak containers"
- (pass-if "element of guarded weak vector gets collected"
- (let ((g (make-guardian))
- (v (weak-vector #f)))
- ;; Note: We don't pass `(cons #f #f)' as an argument to `weak-vector'
- ;; otherwise references to it are likely to be left on the stack.
- (weak-vector-set! v 0 (cons #f #f))
- (g v)
- (gc)
- (if (equal? (weak-vector-ref v 0) (cons #f #f))
- (throw 'unresolved)
- #t))))
- (with-test-prefix "guarding guardians"
- #t)
- (with-test-prefix "guarding dependent objects"
- ;; We don't make any guarantees about the order objects are
- ;; returned from guardians and therefore we skip the following
- ;; test.
- (if #f
- (pass-if "guarding vector and element"
- (let ((g (make-guardian)))
- (gc)
- (let ((p (cons #f #f)))
- (g p)
- (g (vector p)))
- (if (not (eq? (g) #f))
- (throw 'unresolved)
- (begin
- (gc)
- (if (not (equal? (g) (vector (cons #f #f))))
- (throw 'unresolved)
- (if (not (eq? (g) #f))
- (throw 'unresolved)
- (begin
- (gc)
- (if (not (equal? (g) (cons #f #f)))
- (throw 'unresolved)
- (eq? (g) #f)))))))))))
- (with-test-prefix "guarding objects more than once"
- (pass-if "guarding twice in one guardian"
- (let ((g (make-guardian)))
- (gc)
- (let ((p (cons #f #f)))
- (g p)
- (g p)
- (set! p #f)) ;; clear refs left on the stack
- (if (not (eq? (g) #f))
- (throw 'unresolved)
- (begin
- (gc)
- (or (and (and=> (g) (lambda (o) (equal? o (cons #f #f))))
- (and=> (g) (lambda (o) (equal? o (cons #f #f)))))
- (throw 'unresolved))))))
- (pass-if "guarding twice in two guardians"
- (let ((g (make-guardian))
- (h (make-guardian)))
- (gc)
- (let ((p (cons #f #f)))
- (g p)
- (h p)
- (set! p #f)) ;; clear refs left on the stack
- (if (not (eq? (g) #f))
- (throw 'unresolved)
- (begin
- (gc)
- (or (and (and=> (g) (lambda (o) (equal? o (cons #f #f))))
- (and=> (h) (lambda (o) (equal? o (cons #f #f)))))
- (throw 'unresolved)))))))
- (with-test-prefix "guarding cyclic dependencies"
- #t)
- )
|