569 lines
23 KiB
Scheme
569 lines
23 KiB
Scheme
#lang scheme/base
|
|
(require (planet "test.ss" ("schematics" "schemeunit.plt" 2))
|
|
"util.ss")
|
|
(provide lang-tests)
|
|
|
|
(define (catch-unsafe-context-exn thunk)
|
|
(with-handlers ([void
|
|
(lambda (the-exn)
|
|
(or
|
|
(and
|
|
(regexp-match ".*Attempt to capture a continuation from within an unsafe context"
|
|
(exn-message the-exn))
|
|
#t)
|
|
(raise the-exn)))])
|
|
(and (thunk) #f)))
|
|
|
|
(define the-dispatch
|
|
`(lambda (k*v)
|
|
(lambda (k*v)
|
|
((car k*v) k*v))))
|
|
|
|
(define lang-tests
|
|
(test-suite
|
|
"Web Language Servlets"
|
|
|
|
;; ****************************************
|
|
;; ****************************************
|
|
;; BASIC TESTS
|
|
(test-suite
|
|
"Basic Tests"
|
|
|
|
(test-case
|
|
"Function application with single argument in tail position"
|
|
(let-values ([(test-m00.4)
|
|
(make-module-eval
|
|
(module m00.4 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start initial)
|
|
(let ([f (let ([m 7]) m)])
|
|
(+ f initial)))))])
|
|
(check = 8 (test-m00.4 '(dispatch-start start 1)))))
|
|
|
|
(test-case
|
|
"start-interaction in argument position of a function call"
|
|
(let-values ([(test-m00.3)
|
|
(make-module-eval
|
|
(module m00.3 (lib "lang.ss" "web-server")
|
|
(define (foo x) 'foo)
|
|
(provide start)
|
|
(define (start initial)
|
|
(foo initial))))])
|
|
(check eqv? 'foo (test-m00.3 '(dispatch-start start 7)))))
|
|
|
|
(test-case
|
|
"identity interaction, dispatch-start called multiple times"
|
|
(let-values ([(test-m00)
|
|
(make-module-eval
|
|
(module m00 (lib "lang.ss" "web-server")
|
|
(define (id x) x)
|
|
(provide start)
|
|
(define (start initial)
|
|
(id initial))))])
|
|
(check = 7 (test-m00 '(dispatch-start start 7)))
|
|
(check eqv? 'foo (test-m00 '(dispatch-start start 'foo)))))
|
|
|
|
(test-case
|
|
"start-interaction in argument position of a primitive"
|
|
(let-values ([(test-m00.1)
|
|
(make-module-eval
|
|
(module m00.1 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start initial)
|
|
(+ 1 initial))))])
|
|
(check = 2 (test-m00.1 '(dispatch-start start 1)))))
|
|
|
|
(test-case
|
|
"dispatch-start called multiple times for s-i in non-trivial context"
|
|
(let-values ([(test-m00.2)
|
|
(make-module-eval
|
|
(module m00.2 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start initial)
|
|
(+ (+ 1 1) initial))))])
|
|
(check = 14 (test-m00.2 '(dispatch-start start 12)))
|
|
(check = 20 (test-m00.2 '(dispatch-start start 18)))))
|
|
|
|
(test-case
|
|
"start-interaction in third position"
|
|
(let-values ([(test-m01)
|
|
(make-module-eval
|
|
(module m01 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start initial)
|
|
(+ (* 1 2) (* 3 4) initial))))])
|
|
(check = 14 (test-m01 '(dispatch-start start 0)))
|
|
(check = 20 (test-m01 '(dispatch-start start 6))))))
|
|
|
|
(test-suite
|
|
"Tests involving multiple values"
|
|
(test-case
|
|
"begin with intermediate multiple values"
|
|
(let-values ([(test)
|
|
(make-module-eval
|
|
(module m03 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start x)
|
|
(begin (printf "Before~n")
|
|
(values 1 x)
|
|
(printf "After~n")
|
|
x))))])
|
|
(check = 3 (test `(dispatch-start start 3)))))
|
|
|
|
(test-case
|
|
"begin0 with intermediate multiple values"
|
|
(let-values ([(test)
|
|
(make-module-eval
|
|
(module m03 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start x)
|
|
(begin0 x
|
|
(printf "Before~n")
|
|
(values 1 x)
|
|
(printf "After~n")))))])
|
|
(check = 3 (test `(dispatch-start start 3)))))
|
|
|
|
(test-case
|
|
"begin0 with multiple values"
|
|
(let-values ([(test)
|
|
(make-module-eval
|
|
(module m03 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start x)
|
|
(let-values ([(_ ans)
|
|
(begin0 (values 1 x)
|
|
(printf "Before~n")
|
|
x
|
|
(printf "After~n"))])
|
|
ans))))])
|
|
(check = 3 (test `(dispatch-start start 3))))))
|
|
|
|
(test-suite
|
|
"Tests Involving call/cc"
|
|
|
|
(test-case
|
|
"continuation invoked in non-trivial context from within proc"
|
|
(let-values ([(test-m03)
|
|
(make-module-eval
|
|
(module m03 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start x)
|
|
(let/cc k
|
|
(+ 2 4 (k 3) 6 8)))))])
|
|
(check = 3 (test-m03 '(dispatch-start start 'foo)))
|
|
(check = 3 (test-m03 '(dispatch-start start 7)))))
|
|
|
|
;; in the following test, if you modify
|
|
;; resume to print the "stack" you will
|
|
;; see that this is not tail recursive
|
|
(test-case
|
|
"non-tail-recursive 'escaping' continuation"
|
|
(let-values ([(test-m04)
|
|
(make-module-eval
|
|
(module m04 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start ln)
|
|
(let/cc k
|
|
(cond
|
|
[(null? ln) 1]
|
|
[(zero? (car ln)) (k 0)]
|
|
[else
|
|
(* (car ln)
|
|
(start (cdr ln)))])))))])
|
|
(check = 0 (test-m04 '(dispatch-start start (list 1 2 3 4 5 6 7 0 8 9))))
|
|
(check = 120 (test-m04 '(dispatch-start start (list 1 2 3 4 5))))))
|
|
|
|
;; this version captures the continuation
|
|
;; outside the recursion and should be tail
|
|
;; recursive. A "stack trace" reveals this
|
|
;; as expected.
|
|
(test-case
|
|
"tail-recursive escaping continuation"
|
|
(let-values ([(test-m05)
|
|
(make-module-eval
|
|
(module m05 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
|
|
(define (start ln)
|
|
(let/cc escape
|
|
(mult/escape escape ln)))
|
|
|
|
(define (mult/escape escape ln)
|
|
(cond
|
|
[(null? ln) 1]
|
|
[(zero? (car ln)) (escape 0)]
|
|
[else
|
|
(* (car ln)
|
|
(mult/escape escape (cdr ln)))]))))])
|
|
(check = 0 (test-m05 '(dispatch-start start (list 1 2 3 0 4 5 6))))
|
|
(check = 120 (test-m05 '(dispatch-start start (list 1 2 3 4 5)))))))
|
|
|
|
;; ****************************************
|
|
;; ****************************************
|
|
;; TESTS INVOLVING send/suspend
|
|
(test-suite
|
|
"Tests Involving send/suspend"
|
|
|
|
(test-case
|
|
"curried add with send/suspend"
|
|
(let ([table-01-eval
|
|
(make-module-eval
|
|
(module table01 mzscheme
|
|
(provide store-k
|
|
lookup-k)
|
|
|
|
(define the-table (make-hash-table))
|
|
|
|
(define (store-k k)
|
|
(let ([key (string->symbol (symbol->string (gensym 'key)))])
|
|
(hash-table-put! the-table key k)
|
|
key))
|
|
(define (lookup-k key-pair)
|
|
(hash-table-get the-table (car key-pair) (lambda () #f)))))])
|
|
(table-01-eval
|
|
'(module m06 (lib "lang.ss" "web-server")
|
|
(require 'table01)
|
|
(provide start)
|
|
|
|
(define (gn which)
|
|
(cadr
|
|
(send/suspend
|
|
(lambda (k)
|
|
(let ([ignore (printf "Please send the ~a number.~n" which)])
|
|
(store-k k))))))
|
|
|
|
(define (start ignore)
|
|
(let ([result (+ (gn "first") (gn "second"))])
|
|
(let ([ignore (printf "The answer is: ~s~n" result)])
|
|
result)))))
|
|
(table-01-eval '(require 'm06))
|
|
(let* ([first-key (table-01-eval '(dispatch-start start 'foo))]
|
|
[second-key (table-01-eval `(dispatch lookup-k '(,first-key 1)))]
|
|
[third-key (table-01-eval `(dispatch lookup-k '(,first-key -7)))])
|
|
(printf "~S~n" (list first-key second-key third-key))
|
|
(check = 3 (table-01-eval `(dispatch lookup-k '(,second-key 2))))
|
|
(check = 4 (table-01-eval `(dispatch lookup-k '(,second-key 3))))
|
|
(check-true (zero? (table-01-eval `(dispatch lookup-k '(,second-key -1)))))
|
|
(check = -7 (table-01-eval `(dispatch lookup-k '(,third-key 0))))
|
|
(check-true (zero? (table-01-eval `(dispatch lookup-k '(,third-key 7))))))))
|
|
|
|
(test-case
|
|
"curried with send/suspend and serializaztion"
|
|
|
|
(let-values ([(test-m06.1)
|
|
(make-module-eval
|
|
(module m06.1 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (gn which)
|
|
(cadr
|
|
(send/suspend
|
|
(lambda (k)
|
|
(let ([ignore (printf "Please send the ~a number.~n" which)])
|
|
k)))))
|
|
|
|
(define (start ignore)
|
|
(let ([result (+ (gn "first") (gn "second"))])
|
|
(let ([ignore (printf "The answer is: ~s~n" result)])
|
|
result)))))])
|
|
(let* ([first-key (test-m06.1 '(dispatch-start start 'foo))]
|
|
[second-key (test-m06.1 `(dispatch ,the-dispatch (list (deserialize (serialize ,first-key)) 1)))]
|
|
[third-key (test-m06.1 `(dispatch ,the-dispatch (list (deserialize (serialize ,first-key)) -7)))])
|
|
(check = 3 (test-m06.1 `(abort/cc (lambda () (dispatch ,the-dispatch (list ,second-key 2))))))
|
|
(check = 4 (test-m06.1 `(dispatch ,the-dispatch (list ,second-key 3))))
|
|
(check-true (zero? (test-m06.1 `(dispatch ,the-dispatch (list ,second-key -1)))))
|
|
(check = -7 (test-m06.1 `(dispatch ,the-dispatch (list ,third-key 0))))
|
|
(check-true (zero? (test-m06.1 `(dispatch ,the-dispatch (list ,third-key 7))))))))
|
|
|
|
(test-case
|
|
"curried with send/suspend and serializaztion (keyword args)"
|
|
|
|
(let-values ([(test-m06.2)
|
|
(make-module-eval
|
|
(module m06.2 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (gn #:page which)
|
|
(cadr
|
|
(send/suspend
|
|
(lambda (k)
|
|
(let ([ignore (printf "Please send the ~a number.~n" which)])
|
|
k)))))
|
|
|
|
(define (start ignore)
|
|
(let ([result (+ (gn #:page "first") (gn #:page "second"))])
|
|
(let ([ignore (printf "The answer is: ~s~n" result)])
|
|
result)))))])
|
|
(let* ([first-key (test-m06.2 '(dispatch-start start 'foo))]
|
|
[second-key (test-m06.2 `(dispatch ,the-dispatch (list (deserialize (serialize ,first-key)) 1)))]
|
|
[third-key (test-m06.2 `(dispatch ,the-dispatch (list (deserialize (serialize ,first-key)) -7)))])
|
|
(check = 3 (test-m06.2 `(abort/cc (lambda () (dispatch ,the-dispatch (list ,second-key 2))))))
|
|
(check = 4 (test-m06.2 `(dispatch ,the-dispatch (list ,second-key 3))))
|
|
(check-true (zero? (test-m06.2 `(dispatch ,the-dispatch (list ,second-key -1)))))
|
|
(check = -7 (test-m06.2 `(dispatch ,the-dispatch (list ,third-key 0))))
|
|
(check-true (zero? (test-m06.2 `(dispatch ,the-dispatch (list ,third-key 7)))))))))
|
|
|
|
(test-suite
|
|
"Test the certification process"
|
|
|
|
(test-suite
|
|
"Splicing tests"
|
|
|
|
(test-case
|
|
"quasi-quote with splicing: need to recertify context for qq-append"
|
|
(let-values ([(test-m01.1)
|
|
(make-module-eval
|
|
(module m01.1 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start initial)
|
|
`(,@(list 1 2 initial)))))])
|
|
(check equal? (list 1 2 3) (test-m01.1 '(dispatch-start start 3)))
|
|
(check equal? (list 1 2 'foo) (test-m01.1 '(dispatch-start start 'foo)))))
|
|
|
|
(test-case
|
|
"recertify context test (1)"
|
|
(let-values ([(test-m01.2)
|
|
(make-module-eval
|
|
(module m01.1 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start initial)
|
|
`(foo ,@(list 1 2 3)))))])
|
|
(check-true #t)))
|
|
|
|
(test-case
|
|
"recertify context test (2)"
|
|
(let-values ([(test-m01.3)
|
|
(make-module-eval
|
|
(module m01.3 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start n)
|
|
`(n ,@(list 1 2 3)))))])
|
|
(check-true #t)))
|
|
|
|
(test-case
|
|
"recertify context test (3)"
|
|
(let-values ([(test-m01.4)
|
|
(make-module-eval
|
|
(module m1 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start initial)
|
|
(define (bar n)
|
|
`(n ,@(list 1 2 3)))
|
|
(bar 7))))])
|
|
(check-true #t)))))
|
|
|
|
(test-suite
|
|
"Tests Involving letrec"
|
|
|
|
(test-case
|
|
"mutually recursive even? and odd?"
|
|
(let-values ([(test-m07)
|
|
(make-module-eval
|
|
(module m07 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start initial)
|
|
(letrec ([even? (lambda (n)
|
|
(or (zero? n)
|
|
(odd? (sub1 n))))]
|
|
[odd? (lambda (n)
|
|
(and (not (zero? n))
|
|
(even? (sub1 n))))])
|
|
(even? initial)))))])
|
|
(check-true (test-m07 '(dispatch-start start 0)))
|
|
(check-true (test-m07 '(dispatch-start start 16)))
|
|
(check-false (test-m07 '(dispatch-start start 1)))
|
|
(check-false (test-m07 '(dispatch-start start 7)))))
|
|
|
|
(test-case
|
|
"send/suspend on rhs of letrec binding forms"
|
|
(let-values ([(test-m08)
|
|
(make-module-eval
|
|
(module m08 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (gn which)
|
|
(cadr
|
|
(send/suspend
|
|
(lambda (k)
|
|
(let ([ignore (printf "Please send the ~a number.~n" which)])
|
|
k)))))
|
|
|
|
(define (start ignore)
|
|
(letrec ([f (let ([n (gn "first")])
|
|
(lambda (m) (+ n m)))]
|
|
[g (let ([n (gn "second")])
|
|
(lambda (m) (+ n (f m))))])
|
|
(let ([result (g (gn "third"))])
|
|
(let ([ignore (printf "The answer is: ~s~n" result)])
|
|
result))))))])
|
|
(let* ([k0 (test-m08 '(serialize (dispatch-start start 'foo)))]
|
|
[k1 (test-m08 `(serialize (dispatch ,the-dispatch (list (deserialize ',k0) 1))))]
|
|
[k2 (test-m08 `(serialize (dispatch ,the-dispatch (list (deserialize ',k1) 2))))])
|
|
(check = 6 (test-m08 `(dispatch ,the-dispatch (list (deserialize ',k2) 3))))
|
|
(check = 9 (test-m08 `(dispatch ,the-dispatch (list (deserialize ',k2) 6))))
|
|
(let* ([k1.1 (test-m08 `(serialize (dispatch ,the-dispatch (list (deserialize ',k0) -1))))]
|
|
[k2.1 (test-m08 `(serialize (dispatch ,the-dispatch (list (deserialize ',k1.1) -2))))])
|
|
(check-true (zero? (test-m08 `(dispatch ,the-dispatch (list (deserialize ',k2.1) 3)))))
|
|
(check = 6 (test-m08 `(dispatch ,the-dispatch (list (deserialize ',k2) 3)))))))))
|
|
|
|
(test-suite
|
|
"Unsafe Context Condition Tests"
|
|
|
|
(test-case
|
|
"simple attempt to capture a continuation from an unsafe context"
|
|
|
|
(let-values ([(nta-eval)
|
|
(make-module-eval
|
|
(module nta mzscheme
|
|
(provide non-tail-apply)
|
|
|
|
(define (non-tail-apply f . args)
|
|
(let ([result (apply f args)])
|
|
(printf "result = ~s~n" result)
|
|
result))))])
|
|
(nta-eval '(module m09 (lib "lang.ss" "web-server")
|
|
(require 'nta)
|
|
(provide start)
|
|
(define (start ignore)
|
|
(non-tail-apply (lambda (x) (let/cc k (k x))) 7))))
|
|
|
|
(nta-eval '(require 'm09))
|
|
|
|
(check-true (catch-unsafe-context-exn
|
|
(lambda () (nta-eval '(dispatch-start start 'foo)))))))
|
|
|
|
(test-case
|
|
"sanity-check: capture continuation from safe version of context"
|
|
|
|
(let-values ([(m10-eval)
|
|
(make-module-eval
|
|
(module m10 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (nta f arg)
|
|
(let ([result (f arg)])
|
|
(printf "result = ~s~n" result)
|
|
result))
|
|
(define (start ignore)
|
|
(nta (lambda (x) (let/cc k (k x))) 7))))])
|
|
(check = 7 (m10-eval '(dispatch-start start 'foo)))))
|
|
|
|
(test-case
|
|
"attempt continuation capture from standard call to map"
|
|
|
|
(let-values ([(m11-eval)
|
|
(make-module-eval
|
|
(module m11 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start ignore)
|
|
(map
|
|
(lambda (x) (let/cc k k))
|
|
(list 1 2 3)))))])
|
|
(check-true (catch-unsafe-context-exn
|
|
(lambda () (m11-eval '(dispatch-start start 'foo)))))))
|
|
|
|
;; if the continuation-capture is attempted in tail position then we
|
|
;; should be just fine.
|
|
(test-case
|
|
"continuation capture from tail position of untranslated procedure"
|
|
|
|
(let ([ta-eval
|
|
(make-module-eval
|
|
(module ta mzscheme
|
|
(provide tail-apply)
|
|
|
|
(define (tail-apply f . args)
|
|
(apply f args))))])
|
|
|
|
(ta-eval '(module m12 (lib "lang.ss" "web-server")
|
|
(require 'ta)
|
|
(provide start)
|
|
(define (start initial)
|
|
(+ initial
|
|
(tail-apply (lambda (x) (let/cc k (k x))) 1)))))
|
|
|
|
(ta-eval '(require 'm12))
|
|
|
|
(check = 2 (ta-eval '(dispatch-start start 1)))))
|
|
|
|
(test-case
|
|
"attempt send/suspend from standard call to map"
|
|
|
|
(let-values ([(m13-eval)
|
|
(make-module-eval
|
|
(module m11 (lib "lang.ss" "web-server")
|
|
(provide start)
|
|
(define (start initial)
|
|
(map
|
|
(lambda (n) (send/suspend
|
|
(lambda (k)
|
|
(let ([ignore (printf "n = ~s~n" n)])
|
|
k))))
|
|
(list 1 2 3)))))])
|
|
(check-true (catch-unsafe-context-exn
|
|
(lambda () (m13-eval '(dispatch-start start 'foo)))))))
|
|
|
|
(test-case
|
|
"attempt send/suspend from tail position of untranslated procedure"
|
|
|
|
(let-values ([(ta-eval)
|
|
(make-module-eval
|
|
(module ta mzscheme
|
|
(provide tail-apply)
|
|
|
|
(define (tail-apply f . args)
|
|
(apply f args))))])
|
|
|
|
(ta-eval '(module m14 (lib "lang.ss" "web-server")
|
|
(require 'ta)
|
|
(provide start)
|
|
(define (start ignore)
|
|
(+ 1 (tail-apply
|
|
(lambda (n)
|
|
(cadr
|
|
(send/suspend
|
|
(lambda (k)
|
|
(let ([ignore (printf "n = ~s~n" n)])
|
|
k))))) 7)))))
|
|
(ta-eval '(require 'm14))
|
|
|
|
(let ([k0 (ta-eval '(dispatch-start start 'foo))])
|
|
(check = 3 (ta-eval `(dispatch ,the-dispatch (list ,k0 2))))
|
|
(check = 0 (ta-eval `(dispatch ,the-dispatch (list ,k0 -1))))))))
|
|
|
|
(test-suite
|
|
"Weird Cases"
|
|
|
|
(test-case
|
|
"provide/contract: simple"
|
|
(check-not-exn
|
|
(lambda ()
|
|
(make-module-eval
|
|
(module data (lib "lang.ss" "web-server")
|
|
(require mzlib/contract)
|
|
|
|
(define x 1)
|
|
(provide/contract
|
|
[x integer?]))))))
|
|
|
|
|
|
(test-case
|
|
"provide/contract: struct"
|
|
(check-not-exn
|
|
(lambda ()
|
|
(make-module-eval
|
|
(module data (lib "lang.ss" "web-server")
|
|
(require mzlib/contract)
|
|
|
|
(define-struct posn (x y) #:mutable)
|
|
(provide/contract
|
|
[struct posn ([x integer?] [y integer?])]))))))
|
|
|
|
(test-case
|
|
"define-values error"
|
|
(check-not-exn
|
|
(lambda ()
|
|
(make-module-eval
|
|
(module test (lib "lang.ss" "web-server")
|
|
(define (show-user)
|
|
(define-values (point i) (values #t 1))
|
|
i)))))))
|
|
|
|
))
|