modules.test 15 KB

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