123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- ;;;; -*- scheme -*-
- ;;;; control.test --- test suite for delimited continuations
- ;;;;
- ;;;; Copyright (C) 2010, 2011, 2013 Free Software Foundation, Inc.
- ;;;;
- ;;;; 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 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
- ;;;; Lesser General Public License for more details.
- ;;;;
- ;;;; 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-control)
- #:use-module (ice-9 control)
- #:use-module (system vm vm)
- #:use-module (srfi srfi-1)
- #:use-module (srfi srfi-11)
- #:use-module (test-suite lib))
- ;; For these, the compiler should be able to prove that "k" is not referenced,
- ;; so it avoids reifying the continuation. Since that's a slightly different
- ;; codepath, we test them both.
- (with-test-prefix/c&e "escape-only continuations"
- (pass-if "no values, normal exit"
- (equal? '()
- (call-with-values
- (lambda ()
- (% (values)
- (lambda (k . args)
- (error "unexpected exit" args))))
- list)))
- (pass-if "no values, abnormal exit"
- (equal? '()
- (% (begin
- (abort)
- (error "unexpected exit"))
- (lambda (k . args)
- args))))
- (pass-if "single value, normal exit"
- (equal? '(foo)
- (call-with-values
- (lambda ()
- (% 'foo
- (lambda (k . args)
- (error "unexpected exit" args))))
- list)))
- (pass-if "single value, abnormal exit"
- (equal? '(foo)
- (% (begin
- (abort 'foo)
- (error "unexpected exit"))
- (lambda (k . args)
- args))))
- (pass-if "multiple values, normal exit"
- (equal? '(foo bar baz)
- (call-with-values
- (lambda ()
- (% (values 'foo 'bar 'baz)
- (lambda (k . args)
- (error "unexpected exit" args))))
- list)))
- (pass-if "multiple values, abnormal exit"
- (equal? '(foo bar baz)
- (% (begin
- (abort 'foo 'bar 'baz)
- (error "unexpected exit"))
- (lambda (k . args)
- args))))
- (pass-if-equal "call/ec" '(0 1 2) ; example from the manual
- (let ((prefix
- (lambda (x lst)
- (call/ec
- (lambda (return)
- (fold (lambda (element prefix)
- (if (equal? element x)
- (return (reverse prefix))
- (cons element prefix)))
- '()
- lst))))))
- (prefix 'a '(0 1 2 a 3 4 5))))
- (pass-if-equal "let/ec" '(0 1 2)
- (let ((prefix
- (lambda (x lst)
- (let/ec return
- (fold (lambda (element prefix)
- (if (equal? element x)
- (return (reverse prefix))
- (cons element prefix)))
- '()
- lst)))))
- (prefix 'a '(0 1 2 a 3 4 5))))
- (pass-if "loop only in handler"
- (let ((n #f))
- (let lp ()
- (or n
- (call-with-prompt 'foo
- (lambda ()
- (set! n #t)
- (abort-to-prompt 'foo))
- (lambda (k) (lp))))))))
- ;;; And the case in which the compiler has to reify the continuation.
- (with-test-prefix/c&e "reified continuations"
- (pass-if "no values, normal exit"
- (equal? '()
- (call-with-values
- (lambda ()
- (% (values)
- (lambda (k . args)
- (error "unexpected exit" k args))))
- list)))
- (pass-if "no values, abnormal exit"
- (equal? '()
- (cdr
- (% (begin
- (abort)
- (error "unexpected exit"))
- (lambda args
- args)))))
- (pass-if "single value, normal exit"
- (equal? '(foo)
- (call-with-values
- (lambda ()
- (% 'foo
- (lambda (k . args)
- (error "unexpected exit" k args))))
- list)))
- (pass-if "single value, abnormal exit"
- (equal? '(foo)
- (cdr
- (% (begin
- (abort 'foo)
- (error "unexpected exit"))
- (lambda args
- args)))))
- (pass-if "multiple values, normal exit"
- (equal? '(foo bar baz)
- (call-with-values
- (lambda ()
- (% (values 'foo 'bar 'baz)
- (lambda (k . args)
- (error "unexpected exit" k args))))
- list)))
- (pass-if "multiple values, abnormal exit"
- (equal? '(foo bar baz)
- (cdr
- (% (begin
- (abort 'foo 'bar 'baz)
- (error "unexpected exit"))
- (lambda args
- args)))))
- (pass-if "reified pending call frames, instantiated elsewhere on the stack"
- (equal? 'foo
- ((call-with-prompt
- 'p0
- (lambda ()
- (identity ((abort-to-prompt 'p0) 'foo)))
- (lambda (c) c))
- (lambda (x) x)))))
- ;; The variants check different cases in the compiler.
- (with-test-prefix/c&e "restarting partial continuations"
- (pass-if "in side-effect position"
- (let ((k (% (begin (abort) 'foo)
- (lambda (k) k))))
- (eq? (k)
- 'foo)))
- (pass-if "passing values to side-effect abort"
- (let ((k (% (begin (abort) 'foo)
- (lambda (k) k))))
- (eq? (k 'qux 'baz 'hello)
- 'foo)))
- (pass-if "called for one value"
- (let ((k (% (+ (abort) 3)
- (lambda (k) k))))
- (eqv? (k 39)
- 42)))
- (pass-if "called for multiple values"
- (let ((k (% (let-values (((a b . c) (abort)))
- (list a b c))
- (lambda (k) k))))
- (equal? (k 1 2 3 4)
- '(1 2 (3 4)))))
- (pass-if "in tail position"
- (let ((k (% (abort)
- (lambda (k) k))))
- (eq? (k 'xyzzy)
- 'xyzzy))))
- ;; Here we test different cases for the `prompt'.
- (with-test-prefix/c&e "prompt in different contexts"
- (pass-if "push, normal exit"
- (car (call-with-prompt
- 'foo
- (lambda () '(#t))
- (lambda (k) '(#f)))))
- (pass-if "push, nonlocal exit"
- (car (call-with-prompt
- 'foo
- (lambda () (abort-to-prompt 'foo) '(#f))
- (lambda (k) '(#t)))))
- (pass-if "push with RA, normal exit"
- (car (letrec ((test (lambda ()
- (call-with-prompt
- 'foo
- (lambda () '(#t))
- (lambda (k) '(#f))))))
- (test))))
- (pass-if "push with RA, nonlocal exit"
- (car (letrec ((test (lambda ()
- (call-with-prompt
- 'foo
- (lambda () (abort-to-prompt 'foo) '(#f))
- (lambda (k) '(#t))))))
- (test))))
- (pass-if "tail, normal exit"
- (call-with-prompt
- 'foo
- (lambda () #t)
- (lambda (k) #f)))
- (pass-if "tail, nonlocal exit"
- (call-with-prompt
- 'foo
- (lambda () (abort-to-prompt 'foo) #f)
- (lambda (k) #t)))
- (pass-if "tail with RA, normal exit"
- (letrec ((test (lambda ()
- (call-with-prompt
- 'foo
- (lambda () #t)
- (lambda (k) #f)))))
- (test)))
- (pass-if "tail with RA, nonlocal exit"
- (letrec ((test (lambda ()
- (call-with-prompt
- 'foo
- (lambda () (abort-to-prompt 'foo) #f)
- (lambda (k) #t)))))
- (test)))
- (pass-if "drop, normal exit"
- (begin
- (call-with-prompt
- 'foo
- (lambda () #f)
- (lambda (k) #f))
- #t))
- (pass-if "drop, nonlocal exit"
- (begin
- (call-with-prompt
- 'foo
- (lambda () (abort-to-prompt 'foo))
- (lambda (k) #f))
- #t))
- (pass-if "drop with RA, normal exit"
- (begin
- (letrec ((test (lambda ()
- (call-with-prompt
- 'foo
- (lambda () #f)
- (lambda (k) #f)))))
- (test))
- #t))
- (pass-if "drop with RA, nonlocal exit"
- (begin
- (letrec ((test (lambda ()
- (call-with-prompt
- 'foo
- (lambda () (abort-to-prompt 'foo) #f)
- (lambda (k) #f)))))
- (test))
- #t)))
- (define fl (make-fluid))
- (fluid-set! fl 0)
- ;; Not c&e as it assumes this block executes once.
- ;;
- (with-test-prefix "suspend/resume with fluids"
- (pass-if "normal"
- (zero? (% (fluid-ref fl)
- error)))
- (pass-if "with-fluids normal"
- (equal? (% (with-fluids ((fl (1+ (fluid-ref fl))))
- (fluid-ref fl))
- error)
- 1))
- (pass-if "normal (post)"
- (zero? (fluid-ref fl)))
- (pass-if "with-fluids and fluid-set!"
- (equal? (% (with-fluids ((fl (1+ (fluid-ref fl))))
- (fluid-set! fl (1+ (fluid-ref fl)))
- (fluid-ref fl))
- error)
- 2))
- (pass-if "normal (post2)"
- (zero? (fluid-ref fl)))
- (pass-if "normal fluid-set!"
- (equal? (begin
- (fluid-set! fl (1+ (fluid-ref fl)))
- (fluid-ref fl))
- 1))
- (pass-if "reset fluid-set!"
- (equal? (begin
- (fluid-set! fl (1- (fluid-ref fl)))
- (fluid-ref fl))
- 0))
- (let ((k (% (with-fluids ((fl (1+ (fluid-ref fl))))
- (abort)
- (fluid-ref fl))
- (lambda (k) k))))
- (pass-if "pre"
- (equal? (fluid-ref fl) 0))
- (pass-if "res"
- (equal? (k) 1))
- (pass-if "post"
- (equal? (fluid-ref fl) 0))))
- (with-test-prefix/c&e "rewinding prompts"
- (pass-if "nested prompts"
- (let ((k (% 'a
- (% 'b
- (begin
- (abort-to-prompt 'a)
- (abort-to-prompt 'b #t))
- (lambda (k x) x))
- (lambda (k) k))))
- (k))))
- (with-test-prefix/c&e "abort to unknown prompt"
- (pass-if-exception "foo" '(misc-error . "^Abort to unknown prompt")
- (abort-to-prompt 'does-not-exist)))
- (with-test-prefix/c&e "unwind"
- (pass-if "unwind through call-with-vm"
- (let ((proc (lambda (x y)
- (expt x y)))
- (call (lambda (p x y)
- (p x y))))
- (catch 'foo
- (lambda ()
- (call-with-vm (lambda () (throw 'foo))))
- (lambda (key)
- (eq? key 'foo))))))
- ;; These tests from Oleg Kiselyov's delim-control-n.scm, available at
- ;; http://okmij.org/ftp/Scheme/delim-control-n.scm. Public domain.
- ;;
- (with-test-prefix "shift and reset"
- (pass-if (equal?
- 117
- (+ 10 (reset (+ 2 (shift k (+ 100 (k (k 3)))))))))
- (pass-if (equal?
- 60
- (* 10 (reset (* 2 (shift g (* 5 (shift f (+ (f 1) 1)))))))))
- (pass-if (equal?
- 121
- (let ((f (lambda (x) (shift k (k (k x))))))
- (+ 1 (reset (+ 10 (f 100)))))))
- (pass-if (equal?
- 'a
- (car (reset
- (let ((x (shift f
- (shift f1 (f1 (cons 'a (f '())))))))
- (shift g x))))))
-
- ;; Example by Olivier Danvy
- (pass-if (equal?
- '(1 2 3 4 5)
- (let ()
- (define (traverse xs)
- (define (visit xs)
- (if (null? xs)
- '()
- (visit (shift*
- (lambda (k)
- (cons (car xs) (k (cdr xs))))))))
- (reset* (lambda () (visit xs))))
- (traverse '(1 2 3 4 5))))))
- (with-test-prefix "suspendable-continuation?"
- (let ((tag (make-prompt-tag)))
- (pass-if "escape-only"
- (call-with-prompt tag
- (lambda ()
- (suspendable-continuation? tag))
- (lambda _ (error "unreachable"))))
- (pass-if "full"
- (call-with-prompt tag
- (lambda ()
- (suspendable-continuation? tag))
- (lambda (k) (error "unreachable" k))))
- (pass-if "escape-only with barrier"
- (call-with-prompt tag
- (lambda ()
- (with-continuation-barrier
- (lambda ()
- (not (suspendable-continuation? tag)))))
- (lambda _ (error "unreachable"))))
- (pass-if "full with barrier"
- (call-with-prompt tag
- (lambda ()
- (with-continuation-barrier
- (lambda ()
- (not (suspendable-continuation? tag)))))
- (lambda (k) (error "unreachable" k))))))
|