goops.test 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. ;;;; goops.test --- test suite for GOOPS -*- scheme -*-
  2. ;;;;
  3. ;;;; Copyright (C) 2001,2003,2004, 2006, 2008, 2009 Free Software Foundation, Inc.
  4. ;;;;
  5. ;;;; This program is free software; you can redistribute it and/or modify
  6. ;;;; it under the terms of the GNU General Public License as published by
  7. ;;;; the Free Software Foundation; either version 2, or (at your option)
  8. ;;;; any later version.
  9. ;;;;
  10. ;;;; This program is distributed in the hope that it will be useful,
  11. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;;;; GNU General Public License for more details.
  14. ;;;;
  15. ;;;; You should have received a copy of the GNU General Public License
  16. ;;;; along with this software; see the file COPYING. If not, write to
  17. ;;;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  18. ;;;; Boston, MA 02110-1301 USA
  19. (define-module (test-suite test-goops)
  20. #:use-module (test-suite lib)
  21. #:autoload (srfi srfi-1) (unfold))
  22. (pass-if "GOOPS loads"
  23. (false-if-exception
  24. (begin (resolve-module '(oop goops))
  25. #t)))
  26. (use-modules (oop goops))
  27. ;;; more tests here...
  28. (with-test-prefix "basic classes"
  29. (with-test-prefix "<top>"
  30. (pass-if "instance?"
  31. (instance? <top>))
  32. (pass-if "class-of"
  33. (eq? (class-of <top>) <class>))
  34. (pass-if "is a class?"
  35. (is-a? <top> <class>))
  36. (pass-if "class-name"
  37. (eq? (class-name <top>) '<top>))
  38. (pass-if "direct superclasses"
  39. (equal? (class-direct-supers <top>) '()))
  40. (pass-if "superclasses"
  41. (equal? (class-precedence-list <top>) (list <top>)))
  42. (pass-if "direct slots"
  43. (equal? (class-direct-slots <top>) '()))
  44. (pass-if "slots"
  45. (equal? (class-slots <top>) '())))
  46. (with-test-prefix "<object>"
  47. (pass-if "instance?"
  48. (instance? <object>))
  49. (pass-if "class-of"
  50. (eq? (class-of <object>) <class>))
  51. (pass-if "is a class?"
  52. (is-a? <object> <class>))
  53. (pass-if "class-name"
  54. (eq? (class-name <object>) '<object>))
  55. (pass-if "direct superclasses"
  56. (equal? (class-direct-supers <object>) (list <top>)))
  57. (pass-if "superclasses"
  58. (equal? (class-precedence-list <object>) (list <object> <top>)))
  59. (pass-if "direct slots"
  60. (equal? (class-direct-slots <object>) '()))
  61. (pass-if "slots"
  62. (equal? (class-slots <object>) '())))
  63. (with-test-prefix "<class>"
  64. (pass-if "instance?"
  65. (instance? <class>))
  66. (pass-if "class-of"
  67. (eq? (class-of <class>) <class>))
  68. (pass-if "is a class?"
  69. (is-a? <class> <class>))
  70. (pass-if "class-name"
  71. (eq? (class-name <class>) '<class>))
  72. (pass-if "direct superclass"
  73. (equal? (class-direct-supers <class>) (list <object>))))
  74. (with-test-prefix "class-precedence-list"
  75. (for-each (lambda (class)
  76. (run-test (if (slot-bound? class 'name)
  77. (class-name class)
  78. (with-output-to-string
  79. (lambda ()
  80. (display class))))
  81. #t
  82. (lambda ()
  83. (catch #t
  84. (lambda ()
  85. (equal? (class-precedence-list class)
  86. (compute-cpl class)))
  87. (lambda args #t)))))
  88. (let ((table (make-hash-table)))
  89. (let rec ((class <top>))
  90. (hash-create-handle! table class #f)
  91. (for-each rec (class-direct-subclasses class)))
  92. (hash-fold (lambda (class ignore classes)
  93. (cons class classes))
  94. '()
  95. table))))
  96. )
  97. (with-test-prefix "classes for built-in types"
  98. (pass-if "subr"
  99. (eq? (class-of fluid-ref) <procedure>))
  100. (pass-if "gsubr"
  101. (eq? (class-of hashq-ref) <procedure>))
  102. (pass-if "car"
  103. (eq? (class-of car) <procedure>))
  104. (pass-if "string"
  105. (eq? (class-of "foo") <string>))
  106. (pass-if "port"
  107. (is-a? (%make-void-port "w") <port>)))
  108. (with-test-prefix "defining classes"
  109. (with-test-prefix "define-class"
  110. (pass-if "creating a new binding"
  111. (if (eval '(defined? '<foo-0>) (current-module))
  112. (throw 'unresolved))
  113. (eval '(define-class <foo-0> ()) (current-module))
  114. (eval '(is-a? <foo-0> <class>) (current-module)))
  115. (pass-if "overwriting a binding to a non-class"
  116. (eval '(define <foo> #f) (current-module))
  117. (eval '(define-class <foo> ()) (current-module))
  118. (eval '(is-a? <foo> <class>) (current-module)))
  119. (expect-fail "bad init-thunk"
  120. (catch #t
  121. (lambda ()
  122. (eval '(define-class <foo> ()
  123. (x #:init-thunk (lambda (x) 1)))
  124. (current-module))
  125. #t)
  126. (lambda args
  127. #f)))
  128. (pass-if "interaction with `struct-ref'"
  129. (eval '(define-class <class-struct> ()
  130. (foo #:init-keyword #:foo)
  131. (bar #:init-keyword #:bar))
  132. (current-module))
  133. (eval '(let ((x (make <class-struct>
  134. #:foo 'hello
  135. #:bar 'world)))
  136. (and (struct? x)
  137. (eq? (struct-ref x 0) 'hello)
  138. (eq? (struct-ref x 1) 'world)))
  139. (current-module)))
  140. (pass-if "interaction with `struct-set!'"
  141. (eval '(define-class <class-struct-2> ()
  142. (foo) (bar))
  143. (current-module))
  144. (eval '(let ((x (make <class-struct-2>)))
  145. (struct-set! x 0 'hello)
  146. (struct-set! x 1 'world)
  147. (and (struct? x)
  148. (eq? (struct-ref x 0) 'hello)
  149. (eq? (struct-ref x 1) 'world)))
  150. (current-module)))))
  151. (with-test-prefix "defining generics"
  152. (with-test-prefix "define-generic"
  153. (pass-if "creating a new top-level binding"
  154. (if (eval '(defined? 'foo-0) (current-module))
  155. (throw 'unresolved))
  156. (eval '(define-generic foo-0) (current-module))
  157. (eval '(and (is-a? foo-0 <generic>)
  158. (null? (generic-function-methods foo-0)))
  159. (current-module)))
  160. (pass-if "overwriting a top-level binding to a non-generic"
  161. (eval '(define (foo) #f) (current-module))
  162. (eval '(define-generic foo) (current-module))
  163. (eval '(and (is-a? foo <generic>)
  164. (= 1 (length (generic-function-methods foo))))
  165. (current-module)))
  166. (pass-if "overwriting a top-level binding to a generic"
  167. (eval '(define (foo) #f) (current-module))
  168. (eval '(define-generic foo) (current-module))
  169. (eval '(define-generic foo) (current-module))
  170. (eval '(and (is-a? foo <generic>)
  171. (null? (generic-function-methods foo)))
  172. (current-module)))))
  173. (with-test-prefix "defining methods"
  174. (pass-if "define-method"
  175. (let ((m (current-module)))
  176. (eval '(define-method (my-plus (s1 <string>) (s2 <string>))
  177. (string-append s1 s2))
  178. m)
  179. (eval '(define-method (my-plus (i1 <integer>) (i2 <integer>))
  180. (+ i1 i2))
  181. m)
  182. (eval '(and (is-a? my-plus <generic>)
  183. (= (length (generic-function-methods my-plus))
  184. 2))
  185. m)))
  186. (pass-if "method-more-specific?"
  187. (eval '(let* ((m+ (generic-function-methods my-plus))
  188. (m1 (car m+))
  189. (m2 (cadr m+))
  190. (arg-types (list <string> <string>)))
  191. (if (memq <string> (method-specializers m1))
  192. (method-more-specific? m1 m2 arg-types)
  193. (method-more-specific? m2 m1 arg-types)))
  194. (current-module)))
  195. (pass-if-exception "method-more-specific? (failure)"
  196. exception:wrong-type-arg
  197. (eval '(let* ((m+ (generic-function-methods my-plus))
  198. (m1 (car m+))
  199. (m2 (cadr m+)))
  200. (method-more-specific? m1 m2 '()))
  201. (current-module))))
  202. (with-test-prefix "defining accessors"
  203. (with-test-prefix "define-accessor"
  204. (pass-if "creating a new top-level binding"
  205. (if (eval '(defined? 'foo-1) (current-module))
  206. (throw 'unresolved))
  207. (eval '(define-accessor foo-1) (current-module))
  208. (eval '(and (is-a? foo-1 <generic-with-setter>)
  209. (null? (generic-function-methods foo-1)))
  210. (current-module)))
  211. (pass-if "overwriting a top-level binding to a non-accessor"
  212. (eval '(define (foo) #f) (current-module))
  213. (eval '(define-accessor foo) (current-module))
  214. (eval '(and (is-a? foo <generic-with-setter>)
  215. (= 1 (length (generic-function-methods foo))))
  216. (current-module)))
  217. (pass-if "overwriting a top-level binding to an accessor"
  218. (eval '(define (foo) #f) (current-module))
  219. (eval '(define-accessor foo) (current-module))
  220. (eval '(define-accessor foo) (current-module))
  221. (eval '(and (is-a? foo <generic-with-setter>)
  222. (null? (generic-function-methods foo)))
  223. (current-module)))))
  224. (with-test-prefix "object update"
  225. (pass-if "defining class"
  226. (eval '(define-class <foo> ()
  227. (x #:accessor x #:init-value 123)
  228. (z #:accessor z #:init-value 789))
  229. (current-module))
  230. (eval '(is-a? <foo> <class>) (current-module)))
  231. (pass-if "making instance"
  232. (eval '(define foo (make <foo>)) (current-module))
  233. (eval '(and (is-a? foo <foo>) (= (x foo) 123)) (current-module)))
  234. (pass-if "redefining class"
  235. (eval '(define-class <foo> ()
  236. (x #:accessor x #:init-value 123)
  237. (y #:accessor y #:init-value 456)
  238. (z #:accessor z #:init-value 789))
  239. (current-module))
  240. (eval '(and (= (y foo) 456) (= (z foo) 789)) (current-module)))
  241. (pass-if "changing class"
  242. (let* ((c1 (class () (the-slot #:init-keyword #:value)))
  243. (c2 (class () (the-slot #:init-keyword #:value)
  244. (the-other-slot #:init-value 888)))
  245. (o1 (make c1 #:value 777)))
  246. (and (is-a? o1 c1)
  247. (not (is-a? o1 c2))
  248. (equal? (slot-ref o1 'the-slot) 777)
  249. (let ((o2 (change-class o1 c2)))
  250. (and (eq? o1 o2)
  251. (is-a? o2 c2)
  252. (not (is-a? o2 c1))
  253. (equal? (slot-ref o2 'the-slot) 777))))))
  254. (pass-if "`hell' in `goops.c' grows as expected"
  255. ;; This snippet yielded a segfault prior to the 2008-08-19 `goops.c'
  256. ;; fix (i.e., Guile 1.8.5 and earlier). The root of the problem was
  257. ;; that `go_to_hell ()' would not reallocate enough room for the `hell'
  258. ;; array, leading to out-of-bounds accesses.
  259. (let* ((parent-class (class ()
  260. #:name '<class-that-will-be-redefined>))
  261. (classes
  262. (unfold (lambda (i) (>= i 20))
  263. (lambda (i)
  264. (make-class (list parent-class)
  265. '((the-slot #:init-value #:value)
  266. (the-other-slot))
  267. #:name (string->symbol
  268. (string-append "<foo-to-redefine-"
  269. (number->string i)
  270. ">"))))
  271. (lambda (i)
  272. (+ 1 i))
  273. 0))
  274. (objects
  275. (map (lambda (class)
  276. (make class #:value 777))
  277. classes)))
  278. (define-method (change-class (foo parent-class)
  279. (new <class>))
  280. ;; Called by `scm_change_object_class ()', via `purgatory ()'.
  281. (if (null? classes)
  282. (next-method)
  283. (let ((class (car classes))
  284. (object (car objects)))
  285. (set! classes (cdr classes))
  286. (set! objects (cdr objects))
  287. ;; Redefine the class so that its instances are eventually
  288. ;; passed to `scm_change_object_class ()'. This leads to
  289. ;; nested `scm_change_object_class ()' calls, which increases
  290. ;; the size of HELL and increments N_HELL.
  291. (class-redefinition class
  292. (make-class '() (class-slots class)
  293. #:name (class-name class)))
  294. ;; Use `slot-ref' to trigger the `scm_change_object_class ()'
  295. ;; and `go_to_hell ()' calls.
  296. (slot-ref object 'the-slot)
  297. (next-method))))
  298. ;; Initiate the whole `change-class' chain.
  299. (let* ((class (car classes))
  300. (object (change-class (car objects) class)))
  301. (is-a? object class)))))
  302. (with-test-prefix "object comparison"
  303. (pass-if "default method"
  304. (eval '(begin
  305. (define-class <c> ()
  306. (x #:accessor x #:init-keyword #:x)
  307. (y #:accessor y #:init-keyword #:y))
  308. (define o1 (make <c> #:x '(1) #:y '(2)))
  309. (define o2 (make <c> #:x '(1) #:y '(3)))
  310. (define o3 (make <c> #:x '(4) #:y '(3)))
  311. (define o4 (make <c> #:x '(4) #:y '(3)))
  312. (not (eqv? o1 o2)))
  313. (current-module)))
  314. (pass-if "eqv?"
  315. (eval '(begin
  316. (define-method (eqv? (a <c>) (b <c>))
  317. (equal? (x a) (x b)))
  318. (eqv? o1 o2))
  319. (current-module)))
  320. (pass-if "not eqv?"
  321. (eval '(not (eqv? o2 o3))
  322. (current-module)))
  323. (pass-if "transfer eqv? => equal?"
  324. (eval '(equal? o1 o2)
  325. (current-module)))
  326. (pass-if "equal?"
  327. (eval '(begin
  328. (define-method (equal? (a <c>) (b <c>))
  329. (equal? (y a) (y b)))
  330. (equal? o2 o3))
  331. (current-module)))
  332. (pass-if "not equal?"
  333. (eval '(not (equal? o1 o2))
  334. (current-module)))
  335. (pass-if "="
  336. (eval '(begin
  337. (define-method (= (a <c>) (b <c>))
  338. (and (equal? (x a) (x b))
  339. (equal? (y a) (y b))))
  340. (= o3 o4))
  341. (current-module)))
  342. (pass-if "not ="
  343. (eval '(not (= o1 o2))
  344. (current-module)))
  345. )
  346. (use-modules (oop goops active-slot))
  347. (with-test-prefix "active-slot"
  348. (pass-if "defining class with active slot"
  349. (eval '(begin
  350. (define z '())
  351. (define-class <bar> ()
  352. (x #:accessor x
  353. #:init-value 1
  354. #:allocation #:active
  355. #:before-slot-ref
  356. (lambda (o)
  357. (set! z (cons 'before-ref z))
  358. #t)
  359. #:after-slot-ref
  360. (lambda (o)
  361. (set! z (cons 'after-ref z)))
  362. #:before-slot-set!
  363. (lambda (o v)
  364. (set! z (cons* v 'before-set! z)))
  365. #:after-slot-set!
  366. (lambda (o v)
  367. (set! z (cons* v (x o) 'after-set! z))))
  368. #:metaclass <active-class>)
  369. (define bar (make <bar>))
  370. (x bar)
  371. (set! (x bar) 2)
  372. (equal? (reverse z)
  373. '(before-ref before-set! 1 before-ref after-ref
  374. after-set! 1 1 before-ref after-ref
  375. before-set! 2 before-ref after-ref after-set! 2 2)))
  376. (current-module))))
  377. (use-modules (oop goops composite-slot))
  378. (with-test-prefix "composite-slot"
  379. (pass-if "creating instance with propagated slot"
  380. (eval '(begin
  381. (define-class <a> ()
  382. (x #:accessor x #:init-keyword #:x)
  383. (y #:accessor y #:init-keyword #:y))
  384. (define-class <c> ()
  385. (o1 #:accessor o1 #:init-form (make <a> #:x 1 #:y 2))
  386. (o2 #:accessor o2 #:init-form (make <a> #:x 3 #:y 4))
  387. (x #:accessor x
  388. #:allocation #:propagated
  389. #:propagate-to '(o1 (o2 y)))
  390. #:metaclass <composite-class>)
  391. (define o (make <c>))
  392. (is-a? o <c>))
  393. (current-module)))
  394. (pass-if "reading propagated slot"
  395. (eval '(= (x o) 1) (current-module)))
  396. (pass-if "writing propagated slot"
  397. (eval '(begin
  398. (set! (x o) 5)
  399. (and (= (x (o1 o)) 5)
  400. (= (y (o1 o)) 2)
  401. (= (x (o2 o)) 3)
  402. (= (y (o2 o)) 5)))
  403. (current-module))))