modules.test 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. ;;;; modules.test --- exercise some of guile's module stuff -*- scheme -*-
  2. ;;;; Copyright (C) 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
  3. ;;;;
  4. ;;;; This library is free software; you can redistribute it and/or
  5. ;;;; modify it under the terms of the GNU Lesser General Public
  6. ;;;; License as published by the Free Software Foundation; either
  7. ;;;; version 3 of the License, or (at your option) any later version.
  8. ;;;;
  9. ;;;; This library is distributed in the hope that it will be useful,
  10. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;;;; Lesser General Public License for more details.
  13. ;;;;
  14. ;;;; You should have received a copy of the GNU Lesser General Public
  15. ;;;; License along with this library; if not, write to the Free Software
  16. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. (define-module (test-suite test-modules)
  18. #:use-module (srfi srfi-1)
  19. #:use-module ((ice-9 streams) ;; for test purposes
  20. #:renamer (symbol-prefix-proc 's:))
  21. #:use-module (test-suite lib))
  22. (define (every? . args)
  23. (not (not (apply every args))))
  24. ;;;
  25. ;;; Foundations.
  26. ;;;
  27. (with-test-prefix "foundations"
  28. (pass-if "modules don't remain anonymous"
  29. ;; This is a requirement for `psyntax': it stores module names and relies
  30. ;; on being able to `resolve-module' them.
  31. (let ((m (make-module)))
  32. (and (module-name m)
  33. (eq? m (resolve-module (module-name m))))))
  34. (pass-if "module-add!"
  35. (let ((m (make-module))
  36. (value (cons 'x 'y)))
  37. (module-add! m 'something (make-variable value))
  38. (eq? (module-ref m 'something) value)))
  39. (pass-if "module-define!"
  40. (let ((m (make-module))
  41. (value (cons 'x 'y)))
  42. (module-define! m 'something value)
  43. (eq? (module-ref m 'something) value)))
  44. (pass-if "module-use!"
  45. (let ((m (make-module))
  46. (import (make-module)))
  47. (module-define! m 'something 'something)
  48. (module-define! import 'imported 'imported)
  49. (module-use! m import)
  50. (and (eq? (module-ref m 'something) 'something)
  51. (eq? (module-ref m 'imported) 'imported)
  52. (module-local-variable m 'something)
  53. (not (module-local-variable m 'imported))
  54. #t)))
  55. (pass-if "module-use! (duplicates local binding)"
  56. ;; Imported bindings can't override locale bindings.
  57. (let ((m (make-module))
  58. (import (make-module)))
  59. (module-define! m 'something 'something)
  60. (module-define! import 'something 'imported)
  61. (module-use! m import)
  62. (eq? (module-ref m 'something) 'something)))
  63. (pass-if "module-locally-bound?"
  64. (let ((m (make-module))
  65. (import (make-module)))
  66. (module-define! m 'something #t)
  67. (module-define! import 'imported #t)
  68. (module-use! m import)
  69. (and (module-locally-bound? m 'something)
  70. (not (module-locally-bound? m 'imported)))))
  71. (pass-if "module-{local-,}variable"
  72. (let ((m (make-module))
  73. (import (make-module)))
  74. (module-define! m 'local #t)
  75. (module-define! import 'imported #t)
  76. (module-use! m import)
  77. (and (module-local-variable m 'local)
  78. (not (module-local-variable m 'imported))
  79. (eq? (module-variable m 'local)
  80. (module-local-variable m 'local))
  81. (eq? (module-local-variable import 'imported)
  82. (module-variable m 'imported)))))
  83. (pass-if "module-import-interface"
  84. (and (every? (lambda (sym iface)
  85. (eq? (module-import-interface (current-module) sym)
  86. iface))
  87. '(current-module exception:bad-variable every)
  88. (cons the-scm-module
  89. (map resolve-interface
  90. '((test-suite lib) (srfi srfi-1)))))
  91. ;; For renamed bindings, a custom interface is used so we can't
  92. ;; check for equality with `eq?'.
  93. (every? (lambda (sym iface)
  94. (let ((import
  95. (module-import-interface (current-module) sym)))
  96. (equal? (module-name import)
  97. (module-name iface))))
  98. '(s:make-stream s:stream-car s:stream-cdr)
  99. (make-list 3 (resolve-interface '(ice-9 streams))))))
  100. (pass-if "module-reverse-lookup"
  101. (let ((mods '((srfi srfi-1) (test-suite lib) (ice-9 streams)))
  102. (syms '(every exception:bad-variable make-stream))
  103. (locals '(every exception:bad-variable s:make-stream)))
  104. (every? (lambda (var sym)
  105. (eq? (module-reverse-lookup (current-module) var)
  106. sym))
  107. (map module-variable
  108. (map resolve-interface mods)
  109. syms)
  110. locals)))
  111. (pass-if "module-reverse-lookup [pre-module-obarray]"
  112. (let ((var (module-variable (current-module) 'string?)))
  113. (eq? 'string? (module-reverse-lookup #f var))))
  114. (pass-if-exception "module-reverse-lookup [wrong-type-arg]"
  115. exception:wrong-type-arg
  116. (module-reverse-lookup (current-module) 'foo))
  117. (pass-if "the-root-module"
  118. (eq? (module-public-interface the-root-module) the-scm-module))
  119. (pass-if "the-scm-module"
  120. ;; THE-SCM-MODULE is its own public interface. See
  121. ;; <https://savannah.gnu.org/bugs/index.php?30623>.
  122. (eq? (module-public-interface the-scm-module) the-scm-module)))
  123. ;;;
  124. ;;; module-use! / module-use-interfaces!
  125. ;;;
  126. (with-test-prefix "module-use"
  127. (let ((m (make-module)))
  128. (pass-if "no uses initially"
  129. (null? (module-uses m)))
  130. (pass-if "using ice-9 q"
  131. (begin
  132. (module-use! m (resolve-interface '(ice-9 q)))
  133. (equal? (module-uses m)
  134. (list (resolve-interface '(ice-9 q))))))
  135. (pass-if "using ice-9 q again"
  136. (begin
  137. (module-use! m (resolve-interface '(ice-9 q)))
  138. (equal? (module-uses m)
  139. (list (resolve-interface '(ice-9 q))))))
  140. (pass-if "using ice-9 ftw"
  141. (begin
  142. (module-use-interfaces! m (list (resolve-interface '(ice-9 ftw))))
  143. (equal? (module-uses m)
  144. (list (resolve-interface '(ice-9 q))
  145. (resolve-interface '(ice-9 ftw))))))
  146. (pass-if "using ice-9 ftw again"
  147. (begin
  148. (module-use-interfaces! m (list (resolve-interface '(ice-9 ftw))))
  149. (equal? (module-uses m)
  150. (list (resolve-interface '(ice-9 q))
  151. (resolve-interface '(ice-9 ftw))))))
  152. (pass-if "using ice-9 control twice"
  153. (begin
  154. (module-use-interfaces! m (list (resolve-interface '(ice-9 control))
  155. (resolve-interface '(ice-9 control))))
  156. (equal? (module-uses m)
  157. (list (resolve-interface '(ice-9 q))
  158. (resolve-interface '(ice-9 ftw))
  159. (resolve-interface '(ice-9 control))))))))
  160. ;;;
  161. ;;; Resolve-module.
  162. ;;;
  163. (with-test-prefix "resolve-module"
  164. (pass-if "#:ensure #t by default"
  165. (module? (resolve-module (list (gensym)))))
  166. (pass-if "#:ensure #t explicitly"
  167. (module? (resolve-module (list (gensym)) #:ensure #t)))
  168. (pass-if "#:ensure #f"
  169. (not (resolve-module (list (gensym)) #:ensure #f))))
  170. ;;;
  171. ;;; Observers.
  172. ;;;
  173. (with-test-prefix "observers"
  174. (pass-if "weak observer invoked"
  175. (let* ((m (make-module))
  176. (invoked 0))
  177. (module-observe-weak m (lambda (mod)
  178. (if (eq? mod m)
  179. (set! invoked (+ invoked 1)))))
  180. (module-define! m 'something 2)
  181. (module-define! m 'something-else 1)
  182. (= invoked 2)))
  183. (pass-if "all weak observers invoked"
  184. ;; With the two-argument `module-observe-weak' available in previous
  185. ;; versions, the observer would get unregistered as soon as the observing
  186. ;; closure gets GC'd, making it impossible to use an anonymous lambda as
  187. ;; the observing procedure.
  188. (let* ((m (make-module))
  189. (observer-count 500)
  190. (observer-ids (let loop ((i observer-count)
  191. (ids '()))
  192. (if (= i 0)
  193. ids
  194. (loop (- i 1) (cons (make-module) ids)))))
  195. (observers-invoked (make-hash-table observer-count)))
  196. ;; register weak observers
  197. (for-each (lambda (id)
  198. (module-observe-weak m id
  199. (lambda (m)
  200. (hashq-set! observers-invoked
  201. id #t))))
  202. observer-ids)
  203. (gc)
  204. ;; invoke them
  205. (module-call-observers m)
  206. ;; make sure all of them were invoked
  207. (->bool (every (lambda (id)
  208. (hashq-ref observers-invoked id))
  209. observer-ids))))
  210. (pass-if "imported bindings updated"
  211. (let ((m (make-module))
  212. (imported (make-module)))
  213. ;; Beautify them, notably adding them a public interface.
  214. (beautify-user-module! m)
  215. (beautify-user-module! imported)
  216. (module-use! m (module-public-interface imported))
  217. (module-define! imported 'imported-binding #t)
  218. ;; At this point, `imported-binding' is local to IMPORTED.
  219. (and (not (module-variable m 'imported-binding))
  220. (begin
  221. ;; Export `imported-binding' from IMPORTED.
  222. (module-export! imported '(imported-binding))
  223. ;; Make sure it is now visible from M.
  224. (module-ref m 'imported-binding))))))
  225. ;;;
  226. ;;; Duplicate bindings handling.
  227. ;;;
  228. (with-test-prefix "duplicate bindings"
  229. (pass-if "simple duplicate handler"
  230. ;; Import the same binding twice.
  231. (let* ((m (make-module))
  232. (import1 (make-module))
  233. (import2 (make-module))
  234. (handler-invoked? #f)
  235. (handler (lambda (module name int1 val1 int2 val2 var val)
  236. (set! handler-invoked? #t)
  237. ;; Keep the first binding.
  238. (or var (module-local-variable int1 name)))))
  239. (set-module-duplicates-handlers! m (list handler))
  240. (module-define! m 'something 'something)
  241. (set-module-name! import1 'imported-module-1)
  242. (set-module-name! import2 'imported-module-2)
  243. (module-define! import1 'imported 'imported-1)
  244. (module-define! import2 'imported 'imported-2)
  245. (module-use! m import1)
  246. (module-use! m import2)
  247. (and (eq? (module-ref m 'imported) 'imported-1)
  248. handler-invoked?))))
  249. ;;;
  250. ;;; Lazy binder.
  251. ;;;
  252. (with-test-prefix "lazy binder"
  253. (pass-if "not invoked"
  254. (let ((m (make-module))
  255. (invoked? #f))
  256. (module-define! m 'something 2)
  257. (set-module-binder! m (lambda args (set! invoked? #t) #f))
  258. (and (module-ref m 'something)
  259. (not invoked?))))
  260. (pass-if "not invoked (module-add!)"
  261. (let ((m (make-module))
  262. (invoked? #f))
  263. (set-module-binder! m (lambda args (set! invoked? #t) #f))
  264. (module-add! m 'something (make-variable 2))
  265. (and (module-ref m 'something)
  266. (not invoked?))))
  267. (pass-if "invoked (module-ref)"
  268. (let ((m (make-module))
  269. (invoked? #f))
  270. (set-module-binder! m (lambda args (set! invoked? #t) #f))
  271. (false-if-exception (module-ref m 'something))
  272. invoked?))
  273. (pass-if "invoked (module-define!)"
  274. (let ((m (make-module))
  275. (invoked? #f))
  276. (set-module-binder! m (lambda args (set! invoked? #t) #f))
  277. (module-define! m 'something 2)
  278. (and invoked?
  279. (eq? (module-ref m 'something) 2))))
  280. (pass-if "honored (ref)"
  281. (let ((m (make-module))
  282. (invoked? #f)
  283. (value (cons 'x 'y)))
  284. (set-module-binder! m
  285. (lambda (mod sym define?)
  286. (set! invoked? #t)
  287. (cond ((not (eq? m mod))
  288. (error "invalid module" mod))
  289. (define?
  290. (error "DEFINE? shouldn't be set"))
  291. (else
  292. (make-variable value)))))
  293. (and (eq? (module-ref m 'something) value)
  294. invoked?))))
  295. ;;;
  296. ;;; Higher-level features.
  297. ;;;
  298. (with-test-prefix "autoload"
  299. (pass-if "module-autoload!"
  300. (let ((m (make-module)))
  301. (module-autoload! m '(ice-9 q) '(make-q))
  302. (not (not (module-ref m 'make-q)))))
  303. (pass-if "autoloaded"
  304. (catch #t
  305. (lambda ()
  306. ;; Simple autoloading.
  307. (eval '(begin
  308. (define-module (test-autoload-one)
  309. :autoload (ice-9 q) (make-q))
  310. (not (not make-q)))
  311. (current-module)))
  312. (lambda (key . args)
  313. #f)))
  314. ;; In Guile 1.8.0 this failed because the binder in
  315. ;; `make-autoload-interface' would try to remove the autoload interface
  316. ;; from the module's "uses" without making sure it is still part of these
  317. ;; "uses".
  318. ;;
  319. (pass-if "autoloaded+used"
  320. (catch #t
  321. (lambda ()
  322. (eval '(begin
  323. (define-module (test-autoload-two)
  324. :autoload (ice-9 q) (make-q)
  325. :use-module (ice-9 q))
  326. (not (not make-q)))
  327. (current-module)))
  328. (lambda (key . args)
  329. #f))))
  330. ;;;
  331. ;;; R6RS compatibility
  332. ;;;
  333. (with-test-prefix "module versions"
  334. (pass-if "version-matches? for matching versions"
  335. (version-matches? '(1 2 3) '(1 2 3)))
  336. (pass-if "version-matches? for non-matching versions"
  337. (not (version-matches? '(3 2 1) '(1 2 3))))
  338. (pass-if "version-matches? against more specified version"
  339. (version-matches? '(1 2) '(1 2 3)))
  340. (pass-if "version-matches? against less specified version"
  341. (not (version-matches? '(1 2 3) '(1 2)))))