control.test 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. ;;;; -*- scheme -*-
  2. ;;;; control.test --- test suite for delimited continuations
  3. ;;;;
  4. ;;;; Copyright (C) 2010, 2011, 2013 Free Software Foundation, Inc.
  5. ;;;;
  6. ;;;; This library is free software; you can redistribute it and/or
  7. ;;;; modify it under the terms of the GNU Lesser General Public
  8. ;;;; License as published by the Free Software Foundation; either
  9. ;;;; version 3 of the License, or (at your option) any later version.
  10. ;;;;
  11. ;;;; This library is distributed in the hope that it will be useful,
  12. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. ;;;; Lesser General Public License for more details.
  15. ;;;;
  16. ;;;; You should have received a copy of the GNU Lesser General Public
  17. ;;;; License along with this library; if not, write to the Free Software
  18. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19. (define-module (test-suite test-control)
  20. #:use-module (ice-9 control)
  21. #:use-module (system vm vm)
  22. #:use-module (srfi srfi-1)
  23. #:use-module (srfi srfi-11)
  24. #:use-module (test-suite lib))
  25. ;; For these, the compiler should be able to prove that "k" is not referenced,
  26. ;; so it avoids reifying the continuation. Since that's a slightly different
  27. ;; codepath, we test them both.
  28. (with-test-prefix/c&e "escape-only continuations"
  29. (pass-if "no values, normal exit"
  30. (equal? '()
  31. (call-with-values
  32. (lambda ()
  33. (% (values)
  34. (lambda (k . args)
  35. (error "unexpected exit" args))))
  36. list)))
  37. (pass-if "no values, abnormal exit"
  38. (equal? '()
  39. (% (begin
  40. (abort)
  41. (error "unexpected exit"))
  42. (lambda (k . args)
  43. args))))
  44. (pass-if "single value, normal exit"
  45. (equal? '(foo)
  46. (call-with-values
  47. (lambda ()
  48. (% 'foo
  49. (lambda (k . args)
  50. (error "unexpected exit" args))))
  51. list)))
  52. (pass-if "single value, abnormal exit"
  53. (equal? '(foo)
  54. (% (begin
  55. (abort 'foo)
  56. (error "unexpected exit"))
  57. (lambda (k . args)
  58. args))))
  59. (pass-if "multiple values, normal exit"
  60. (equal? '(foo bar baz)
  61. (call-with-values
  62. (lambda ()
  63. (% (values 'foo 'bar 'baz)
  64. (lambda (k . args)
  65. (error "unexpected exit" args))))
  66. list)))
  67. (pass-if "multiple values, abnormal exit"
  68. (equal? '(foo bar baz)
  69. (% (begin
  70. (abort 'foo 'bar 'baz)
  71. (error "unexpected exit"))
  72. (lambda (k . args)
  73. args))))
  74. (pass-if-equal "call/ec" '(0 1 2) ; example from the manual
  75. (let ((prefix
  76. (lambda (x lst)
  77. (call/ec
  78. (lambda (return)
  79. (fold (lambda (element prefix)
  80. (if (equal? element x)
  81. (return (reverse prefix))
  82. (cons element prefix)))
  83. '()
  84. lst))))))
  85. (prefix 'a '(0 1 2 a 3 4 5))))
  86. (pass-if-equal "let/ec" '(0 1 2)
  87. (let ((prefix
  88. (lambda (x lst)
  89. (let/ec return
  90. (fold (lambda (element prefix)
  91. (if (equal? element x)
  92. (return (reverse prefix))
  93. (cons element prefix)))
  94. '()
  95. lst)))))
  96. (prefix 'a '(0 1 2 a 3 4 5))))
  97. (pass-if "loop only in handler"
  98. (let ((n #f))
  99. (let lp ()
  100. (or n
  101. (call-with-prompt 'foo
  102. (lambda ()
  103. (set! n #t)
  104. (abort-to-prompt 'foo))
  105. (lambda (k) (lp))))))))
  106. ;;; And the case in which the compiler has to reify the continuation.
  107. (with-test-prefix/c&e "reified continuations"
  108. (pass-if "no values, normal exit"
  109. (equal? '()
  110. (call-with-values
  111. (lambda ()
  112. (% (values)
  113. (lambda (k . args)
  114. (error "unexpected exit" k args))))
  115. list)))
  116. (pass-if "no values, abnormal exit"
  117. (equal? '()
  118. (cdr
  119. (% (begin
  120. (abort)
  121. (error "unexpected exit"))
  122. (lambda args
  123. args)))))
  124. (pass-if "single value, normal exit"
  125. (equal? '(foo)
  126. (call-with-values
  127. (lambda ()
  128. (% 'foo
  129. (lambda (k . args)
  130. (error "unexpected exit" k args))))
  131. list)))
  132. (pass-if "single value, abnormal exit"
  133. (equal? '(foo)
  134. (cdr
  135. (% (begin
  136. (abort 'foo)
  137. (error "unexpected exit"))
  138. (lambda args
  139. args)))))
  140. (pass-if "multiple values, normal exit"
  141. (equal? '(foo bar baz)
  142. (call-with-values
  143. (lambda ()
  144. (% (values 'foo 'bar 'baz)
  145. (lambda (k . args)
  146. (error "unexpected exit" k args))))
  147. list)))
  148. (pass-if "multiple values, abnormal exit"
  149. (equal? '(foo bar baz)
  150. (cdr
  151. (% (begin
  152. (abort 'foo 'bar 'baz)
  153. (error "unexpected exit"))
  154. (lambda args
  155. args)))))
  156. (pass-if "reified pending call frames, instantiated elsewhere on the stack"
  157. (equal? 'foo
  158. ((call-with-prompt
  159. 'p0
  160. (lambda ()
  161. (identity ((abort-to-prompt 'p0) 'foo)))
  162. (lambda (c) c))
  163. (lambda (x) x)))))
  164. ;; The variants check different cases in the compiler.
  165. (with-test-prefix/c&e "restarting partial continuations"
  166. (pass-if "in side-effect position"
  167. (let ((k (% (begin (abort) 'foo)
  168. (lambda (k) k))))
  169. (eq? (k)
  170. 'foo)))
  171. (pass-if "passing values to side-effect abort"
  172. (let ((k (% (begin (abort) 'foo)
  173. (lambda (k) k))))
  174. (eq? (k 'qux 'baz 'hello)
  175. 'foo)))
  176. (pass-if "called for one value"
  177. (let ((k (% (+ (abort) 3)
  178. (lambda (k) k))))
  179. (eqv? (k 39)
  180. 42)))
  181. (pass-if "called for multiple values"
  182. (let ((k (% (let-values (((a b . c) (abort)))
  183. (list a b c))
  184. (lambda (k) k))))
  185. (equal? (k 1 2 3 4)
  186. '(1 2 (3 4)))))
  187. (pass-if "in tail position"
  188. (let ((k (% (abort)
  189. (lambda (k) k))))
  190. (eq? (k 'xyzzy)
  191. 'xyzzy))))
  192. ;; Here we test different cases for the `prompt'.
  193. (with-test-prefix/c&e "prompt in different contexts"
  194. (pass-if "push, normal exit"
  195. (car (call-with-prompt
  196. 'foo
  197. (lambda () '(#t))
  198. (lambda (k) '(#f)))))
  199. (pass-if "push, nonlocal exit"
  200. (car (call-with-prompt
  201. 'foo
  202. (lambda () (abort-to-prompt 'foo) '(#f))
  203. (lambda (k) '(#t)))))
  204. (pass-if "push with RA, normal exit"
  205. (car (letrec ((test (lambda ()
  206. (call-with-prompt
  207. 'foo
  208. (lambda () '(#t))
  209. (lambda (k) '(#f))))))
  210. (test))))
  211. (pass-if "push with RA, nonlocal exit"
  212. (car (letrec ((test (lambda ()
  213. (call-with-prompt
  214. 'foo
  215. (lambda () (abort-to-prompt 'foo) '(#f))
  216. (lambda (k) '(#t))))))
  217. (test))))
  218. (pass-if "tail, normal exit"
  219. (call-with-prompt
  220. 'foo
  221. (lambda () #t)
  222. (lambda (k) #f)))
  223. (pass-if "tail, nonlocal exit"
  224. (call-with-prompt
  225. 'foo
  226. (lambda () (abort-to-prompt 'foo) #f)
  227. (lambda (k) #t)))
  228. (pass-if "tail with RA, normal exit"
  229. (letrec ((test (lambda ()
  230. (call-with-prompt
  231. 'foo
  232. (lambda () #t)
  233. (lambda (k) #f)))))
  234. (test)))
  235. (pass-if "tail with RA, nonlocal exit"
  236. (letrec ((test (lambda ()
  237. (call-with-prompt
  238. 'foo
  239. (lambda () (abort-to-prompt 'foo) #f)
  240. (lambda (k) #t)))))
  241. (test)))
  242. (pass-if "drop, normal exit"
  243. (begin
  244. (call-with-prompt
  245. 'foo
  246. (lambda () #f)
  247. (lambda (k) #f))
  248. #t))
  249. (pass-if "drop, nonlocal exit"
  250. (begin
  251. (call-with-prompt
  252. 'foo
  253. (lambda () (abort-to-prompt 'foo))
  254. (lambda (k) #f))
  255. #t))
  256. (pass-if "drop with RA, normal exit"
  257. (begin
  258. (letrec ((test (lambda ()
  259. (call-with-prompt
  260. 'foo
  261. (lambda () #f)
  262. (lambda (k) #f)))))
  263. (test))
  264. #t))
  265. (pass-if "drop with RA, nonlocal exit"
  266. (begin
  267. (letrec ((test (lambda ()
  268. (call-with-prompt
  269. 'foo
  270. (lambda () (abort-to-prompt 'foo) #f)
  271. (lambda (k) #f)))))
  272. (test))
  273. #t)))
  274. (define fl (make-fluid))
  275. (fluid-set! fl 0)
  276. ;; Not c&e as it assumes this block executes once.
  277. ;;
  278. (with-test-prefix "suspend/resume with fluids"
  279. (pass-if "normal"
  280. (zero? (% (fluid-ref fl)
  281. error)))
  282. (pass-if "with-fluids normal"
  283. (equal? (% (with-fluids ((fl (1+ (fluid-ref fl))))
  284. (fluid-ref fl))
  285. error)
  286. 1))
  287. (pass-if "normal (post)"
  288. (zero? (fluid-ref fl)))
  289. (pass-if "with-fluids and fluid-set!"
  290. (equal? (% (with-fluids ((fl (1+ (fluid-ref fl))))
  291. (fluid-set! fl (1+ (fluid-ref fl)))
  292. (fluid-ref fl))
  293. error)
  294. 2))
  295. (pass-if "normal (post2)"
  296. (zero? (fluid-ref fl)))
  297. (pass-if "normal fluid-set!"
  298. (equal? (begin
  299. (fluid-set! fl (1+ (fluid-ref fl)))
  300. (fluid-ref fl))
  301. 1))
  302. (pass-if "reset fluid-set!"
  303. (equal? (begin
  304. (fluid-set! fl (1- (fluid-ref fl)))
  305. (fluid-ref fl))
  306. 0))
  307. (let ((k (% (with-fluids ((fl (1+ (fluid-ref fl))))
  308. (abort)
  309. (fluid-ref fl))
  310. (lambda (k) k))))
  311. (pass-if "pre"
  312. (equal? (fluid-ref fl) 0))
  313. (pass-if "res"
  314. (equal? (k) 1))
  315. (pass-if "post"
  316. (equal? (fluid-ref fl) 0))))
  317. (with-test-prefix/c&e "rewinding prompts"
  318. (pass-if "nested prompts"
  319. (let ((k (% 'a
  320. (% 'b
  321. (begin
  322. (abort-to-prompt 'a)
  323. (abort-to-prompt 'b #t))
  324. (lambda (k x) x))
  325. (lambda (k) k))))
  326. (k))))
  327. (with-test-prefix/c&e "abort to unknown prompt"
  328. (pass-if-exception "foo" '(misc-error . "^Abort to unknown prompt")
  329. (abort-to-prompt 'does-not-exist)))
  330. (with-test-prefix/c&e "unwind"
  331. (pass-if "unwind through call-with-vm"
  332. (let ((proc (lambda (x y)
  333. (expt x y)))
  334. (call (lambda (p x y)
  335. (p x y))))
  336. (catch 'foo
  337. (lambda ()
  338. (call-with-vm (lambda () (throw 'foo))))
  339. (lambda (key)
  340. (eq? key 'foo))))))
  341. ;; These tests from Oleg Kiselyov's delim-control-n.scm, available at
  342. ;; http://okmij.org/ftp/Scheme/delim-control-n.scm. Public domain.
  343. ;;
  344. (with-test-prefix "shift and reset"
  345. (pass-if (equal?
  346. 117
  347. (+ 10 (reset (+ 2 (shift k (+ 100 (k (k 3)))))))))
  348. (pass-if (equal?
  349. 60
  350. (* 10 (reset (* 2 (shift g (* 5 (shift f (+ (f 1) 1)))))))))
  351. (pass-if (equal?
  352. 121
  353. (let ((f (lambda (x) (shift k (k (k x))))))
  354. (+ 1 (reset (+ 10 (f 100)))))))
  355. (pass-if (equal?
  356. 'a
  357. (car (reset
  358. (let ((x (shift f
  359. (shift f1 (f1 (cons 'a (f '())))))))
  360. (shift g x))))))
  361. ;; Example by Olivier Danvy
  362. (pass-if (equal?
  363. '(1 2 3 4 5)
  364. (let ()
  365. (define (traverse xs)
  366. (define (visit xs)
  367. (if (null? xs)
  368. '()
  369. (visit (shift*
  370. (lambda (k)
  371. (cons (car xs) (k (cdr xs))))))))
  372. (reset* (lambda () (visit xs))))
  373. (traverse '(1 2 3 4 5))))))
  374. (with-test-prefix "suspendable-continuation?"
  375. (let ((tag (make-prompt-tag)))
  376. (pass-if "escape-only"
  377. (call-with-prompt tag
  378. (lambda ()
  379. (suspendable-continuation? tag))
  380. (lambda _ (error "unreachable"))))
  381. (pass-if "full"
  382. (call-with-prompt tag
  383. (lambda ()
  384. (suspendable-continuation? tag))
  385. (lambda (k) (error "unreachable" k))))
  386. (pass-if "escape-only with barrier"
  387. (call-with-prompt tag
  388. (lambda ()
  389. (with-continuation-barrier
  390. (lambda ()
  391. (not (suspendable-continuation? tag)))))
  392. (lambda _ (error "unreachable"))))
  393. (pass-if "full with barrier"
  394. (call-with-prompt tag
  395. (lambda ()
  396. (with-continuation-barrier
  397. (lambda ()
  398. (not (suspendable-continuation? tag)))))
  399. (lambda (k) (error "unreachable" k))))))