X-Git-Url: https://git.hcoop.net/bpt/guile.git/blobdiff_plain/e9b8556ec92039396e740620238d56a3748f2a99..7b0a8dfb752d9d63179be944869db8447fdb7c5e:/test-suite/tests/goops.test diff --git a/test-suite/tests/goops.test b/test-suite/tests/goops.test index 8ed697c59..5b26cb83f 100644 --- a/test-suite/tests/goops.test +++ b/test-suite/tests/goops.test @@ -1,24 +1,27 @@ ;;;; goops.test --- test suite for GOOPS -*- scheme -*- ;;;; -;;;; Copyright (C) 2001,2003,2004, 2006 Free Software Foundation, Inc. +;;;; Copyright (C) 2001,2003,2004, 2006, 2008, 2009, 2011, 2012, 2014, 2015 Free Software Foundation, Inc. ;;;; -;;;; This program is free software; you can redistribute it and/or modify -;;;; it under the terms of the GNU General Public License as published by -;;;; the Free Software Foundation; either version 2, or (at your option) -;;;; any later version. +;;;; 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 program is distributed in the hope that it will be useful, +;;;; 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 General Public License for more details. +;;;; 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 General Public License -;;;; along with this software; see the file COPYING. If not, write to -;;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;;;; Boston, MA 02110-1301 USA +;;;; 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 (define-module (test-suite test-goops) - #:use-module (test-suite lib)) + #:use-module (test-suite lib) + #:autoload (srfi srfi-1) (unfold)) + +(define exception:no-applicable-method + '(goops-error . "^No applicable method")) (pass-if "GOOPS loads" (false-if-exception @@ -124,6 +127,31 @@ table)))) ) +(with-test-prefix "classes for built-in types" + + (pass-if "subr" + (eq? (class-of fluid-ref) )) + + (pass-if "gsubr" + (eq? (class-of hashq-ref) )) + + (pass-if "car" + (eq? (class-of car) )) + + (pass-if "string" + (eq? (class-of "foo") )) + + (pass-if "port" + (is-a? (%make-void-port "w") )) + + (pass-if "struct vtable" + ;; Previously, `class-of' would fail for nameless structs, i.e., structs + ;; for which `struct-vtable-name' is #f. + (is-a? (class-of (make-vtable + (string-append standard-vtable-fields "prprpr"))) + ))) + + (with-test-prefix "defining classes" (with-test-prefix "define-class" @@ -139,16 +167,55 @@ (eval '(define-class ()) (current-module)) (eval '(is-a? ) (current-module))) - (expect-fail "bad init-thunk" - (catch #t - (lambda () - (eval '(define-class () - (x #:init-thunk (lambda (x) 1))) - (current-module)) - #t) - (lambda args - #f))) - )) + (pass-if "bad init-thunk" + (catch #t + (lambda () + (eval '(define-class () + (x #:init-thunk (lambda (x) 1))) + (current-module)) + #f) + (lambda args + #t))) + + (pass-if "interaction with `struct-ref'" + (eval '(define-class () + (foo #:init-keyword #:foo) + (bar #:init-keyword #:bar)) + (current-module)) + (eval '(let ((x (make + #:foo 'hello + #:bar 'world))) + (and (struct? x) + (eq? (struct-ref x 0) 'hello) + (eq? (struct-ref x 1) 'world))) + (current-module))) + + (pass-if "interaction with `struct-set!'" + (eval '(define-class () + (foo) (bar)) + (current-module)) + (eval '(let ((x (make ))) + (struct-set! x 0 'hello) + (struct-set! x 1 'world) + (and (struct? x) + (eq? (struct-ref x 0) 'hello) + (eq? (struct-ref x 1) 'world))) + (current-module))) + + (pass-if "with accessors" + (eval '(define-class () + (x #:accessor x #:init-value 123) + (z #:accessor z #:init-value 789)) + (current-module)) + (eval '(equal? (x (make )) 123) (current-module))) + + (pass-if-exception "cannot redefine fields of " + '(misc-error . "cannot be redefined") + (eval '(begin + (define-class () + name) + (make )) + (current-module))))) (with-test-prefix "defining generics" @@ -175,7 +242,11 @@ (eval '(define-generic foo) (current-module)) (eval '(and (is-a? foo ) (null? (generic-function-methods foo))) - (current-module))))) + (current-module))) + + (pass-if-exception "getters do not have setters" + exception:wrong-type-arg + (eval '(setter foo) (current-module))))) (with-test-prefix "defining methods" @@ -210,6 +281,19 @@ (method-more-specific? m1 m2 '())) (current-module)))) +(with-test-prefix "the method cache" + (pass-if "defining a method with a rest arg" + (let ((m (current-module))) + (eval '(define-method (foo bar . baz) + (cons bar baz)) + m) + (eval '(foo 1) + m) + (eval '(foo 1 2) + m) + (eval '(equal? (foo 1 2) '(1 2)) + m)))) + (with-test-prefix "defining accessors" (with-test-prefix "define-accessor" @@ -222,6 +306,9 @@ (null? (generic-function-methods foo-1))) (current-module))) + (pass-if "accessors have setters" + (procedure? (eval '(setter foo-1) (current-module)))) + (pass-if "overwriting a top-level binding to a non-accessor" (eval '(define (foo) #f) (current-module)) (eval '(define-accessor foo) (current-module)) @@ -253,7 +340,77 @@ (y #:accessor y #:init-value 456) (z #:accessor z #:init-value 789)) (current-module)) - (eval '(and (= (y foo) 456) (= (z foo) 789)) (current-module)))) + (eval '(and (= (y foo) 456) (= (z foo) 789)) (current-module))) + + (pass-if "changing class" + (let* ((c1 (class () (the-slot #:init-keyword #:value))) + (c2 (class () (the-slot #:init-keyword #:value) + (the-other-slot #:init-value 888))) + (o1 (make c1 #:value 777))) + (and (is-a? o1 c1) + (not (is-a? o1 c2)) + (equal? (slot-ref o1 'the-slot) 777) + (let ((o2 (change-class o1 c2))) + (and (eq? o1 o2) + (is-a? o2 c2) + (not (is-a? o2 c1)) + (equal? (slot-ref o2 'the-slot) 777)))))) + + (pass-if "`hell' in `goops.c' grows as expected" + ;; This snippet yielded a segfault prior to the 2008-08-19 `goops.c' + ;; fix (i.e., Guile 1.8.5 and earlier). The root of the problem was + ;; that `go_to_hell ()' would not reallocate enough room for the `hell' + ;; array, leading to out-of-bounds accesses. + + (let* ((parent-class (class () + #:name ')) + (classes + (unfold (lambda (i) (>= i 20)) + (lambda (i) + (make-class (list parent-class) + '((the-slot #:init-value #:value) + (the-other-slot)) + #:name (string->symbol + (string-append "string i) + ">")))) + (lambda (i) + (+ 1 i)) + 0)) + (objects + (map (lambda (class) + (make class #:value 777)) + classes))) + + (define-method (change-class (foo parent-class) + (new )) + ;; Called by `scm_change_object_class ()', via `purgatory ()'. + (if (null? classes) + (next-method) + (let ((class (car classes)) + (object (car objects))) + (set! classes (cdr classes)) + (set! objects (cdr objects)) + + ;; Redefine the class so that its instances are eventually + ;; passed to `scm_change_object_class ()'. This leads to + ;; nested `scm_change_object_class ()' calls, which increases + ;; the size of HELL and increments N_HELL. + (class-redefinition class + (make-class '() (class-slots class) + #:name (class-name class))) + + ;; Use `slot-ref' to trigger the `scm_change_object_class ()' + ;; and `go_to_hell ()' calls. + (slot-ref object 'the-slot) + + (next-method)))) + + + ;; Initiate the whole `change-class' chain. + (let* ((class (car classes)) + (object (change-class (car objects) class))) + (is-a? object class))))) (with-test-prefix "object comparison" (pass-if "default method" @@ -267,18 +424,6 @@ (define o4 (make #:x '(4) #:y '(3))) (not (eqv? o1 o2))) (current-module))) - (pass-if "eqv?" - (eval '(begin - (define-method (eqv? (a ) (b )) - (equal? (x a) (x b))) - (eqv? o1 o2)) - (current-module))) - (pass-if "not eqv?" - (eval '(not (eqv? o2 o3)) - (current-module))) - (pass-if "transfer eqv? => equal?" - (eval '(equal? o1 o2) - (current-module))) (pass-if "equal?" (eval '(begin (define-method (equal? (a ) (b )) @@ -328,9 +473,9 @@ (x bar) (set! (x bar) 2) (equal? (reverse z) - '(before-ref before-set! 1 before-ref after-ref - after-set! 1 1 before-ref after-ref - before-set! 2 before-ref after-ref after-set! 2 2))) + '(before-set! 1 before-ref after-ref + after-set! 1 1 before-ref after-ref + before-set! 2 before-ref after-ref after-set! 2 2))) (current-module)))) (use-modules (oop goops composite-slot)) @@ -361,3 +506,154 @@ (= (x (o2 o)) 3) (= (y (o2 o)) 5))) (current-module)))) + +(with-test-prefix "no-applicable-method" + (pass-if-exception "calling generic, no methods" + exception:no-applicable-method + (eval '(begin + (define-class ()) + (define-generic quxy) + (quxy 1)) + (current-module))) + (pass-if "calling generic, one method, applicable" + (eval '(begin + (define-method (quxy (q )) + #t) + (define q (make )) + (quxy q)) + (current-module))) + (pass-if-exception "calling generic, one method, not applicable" + exception:no-applicable-method + (eval '(quxy 1) + (current-module)))) + +(with-test-prefix "foreign slots" + (define-class () + (a #:init-keyword #:a #:class + #:accessor test-a) + (b #:init-keyword #:b #:init-form 3 #:class + #:accessor test-b)) + + (pass-if-equal "constructing, no initargs" + '(0 3) + (let ((x (make ))) + (list (slot-ref x 'a) + (slot-ref x 'b)))) + + (pass-if-equal "constructing, initargs" + '(1 2) + (let ((x (make #:a 1 #:b 2))) + (list (slot-ref x 'a) + (slot-ref x 'b)))) + + (pass-if-equal "getters" + '(0 3) + (let ((x (make ))) + (list (test-a x) (test-b x)))) + + (pass-if-equal "setters" + '(10 20) + (let ((x (make ))) + (set! (test-a x) 10) + (set! (test-b x) 20) + (list (test-a x) (test-b x)))) + + (pass-if-exception "out of range" + exception:out-of-range + (make #:a (ash 1 64)))) + +(with-test-prefix "#:each-subclass" + (let* (( + (class () + (test #:init-value '() #:allocation #:each-subclass) + #:name ')) + (a (make ))) + (pass-if-equal '() (slot-ref a 'test)) + (let ((b (make ))) + (pass-if-equal '() (slot-ref b 'test)) + (slot-set! a 'test 100) + (pass-if-equal 100 (slot-ref a 'test)) + (pass-if-equal 100 (slot-ref b 'test)) + + ;; #:init-value of the class shouldn't reinitialize slot when + ;; instances are allocated. + (make ) + + (pass-if-equal 100 (slot-ref a 'test)) + (pass-if-equal 100 (slot-ref b 'test)) + + (let (( + (class ()))) + (pass-if-equal 100 (slot-ref a 'test)) + (pass-if-equal 100 (slot-ref b 'test)) + (let ((c (make ))) + (pass-if-equal 100 (slot-ref a 'test)) + (pass-if-equal 100 (slot-ref b 'test)) + (pass-if-equal '() (slot-ref c 'test)) + (slot-set! c 'test 200) + (pass-if-equal 200 (slot-ref c 'test)) + + (make ) + + (pass-if-equal 100 (slot-ref a 'test)) + (pass-if-equal 100 (slot-ref b 'test)) + (pass-if-equal 200 (slot-ref c 'test))))))) + +(define-class ()) +(define-class ()) +(define-class ()) +(define-class ()) +(define-class ()) +(define-class ( )) + +(define-class ()) +(define-class ()) +(define-class ()) +(define-class ( )) +(define-class ( )) +(define-class ( )) + +(with-test-prefix "compute-cpl" + (pass-if-equal "" + (list ) + (compute-cpl )) + + (pass-if-equal "" + (list ) + (compute-cpl ))) + +(with-test-prefix "accessor slots" + (let* ((a-accessor (make-accessor 'a)) + (b-accessor (make-accessor 'b)) + ( (class () + (a #:init-keyword #:a #:accessor a-accessor) + #:name ')) + ( (class () + (b #:init-keyword #:b #:accessor b-accessor) + #:name ')) + ( (class ( ) #:name ')) + ( (class ( ) #:name ')) + ( (class () + (a #:init-keyword #:a) + #:name ')) + ( (class () + (a #:init-keyword #:a) + #:name ')) + (a (make #:a 'a)) + (b (make #:b 'b)) + (ab (make #:a 'a #:b 'b)) + (ba (make #:a 'a #:b 'b)) + (cab (make #:a 'a #:b 'b)) + (cba (make #:a 'a #:b 'b))) + (pass-if-equal "a accessor on a" 'a (a-accessor a)) + (pass-if-equal "a accessor on ab" 'a (a-accessor ab)) + (pass-if-equal "a accessor on ba" 'a (a-accessor ba)) + (pass-if-exception "a accessor on cab" exception:no-applicable-method + (a-accessor cab)) + (pass-if-exception "a accessor on cba" exception:no-applicable-method + (a-accessor cba)) + (pass-if-equal "b accessor on a" 'b (b-accessor b)) + (pass-if-equal "b accessor on ab" 'b (b-accessor ab)) + (pass-if-equal "b accessor on ba" 'b (b-accessor ba)) + (pass-if-equal "b accessor on cab" 'b (b-accessor cab)) + (pass-if-equal "b accessor on cba" 'b (b-accessor cba))))