Adding the basics of the servlet testing library, but then discovered a weird error, and took a sidetrack to catch it. I'm making a commit to be able see if there were any real problems or if it is just broken tests

This commit is contained in:
Jay McCarthy 2011-07-24 15:28:23 -04:00
parent 7e933ee8a4
commit df0e35b213
15 changed files with 323 additions and 116 deletions

View File

@ -11,7 +11,8 @@
"stuffers-test.rkt" "stuffers-test.rkt"
"formlets-test.rkt" "formlets-test.rkt"
"dispatch-test.rkt" "dispatch-test.rkt"
"servlet-env-test.rkt") "servlet-env-test.rkt"
"test-tests.rkt")
(provide all-web-server-tests) (provide all-web-server-tests)
(define all-web-server-tests (define all-web-server-tests
@ -28,4 +29,5 @@
all-managers-tests all-managers-tests
all-private-tests all-private-tests
all-servlet-tests all-servlet-tests
servlet-env-tests)) servlet-env-tests
test-tests))

View File

@ -13,7 +13,7 @@
"../util.rkt") "../util.rkt")
(provide dispatch-servlets-tests) (provide dispatch-servlets-tests)
(current-server-custodian (current-custodian)) (current-server-custodian (make-custodian))
(define (mkd p) (define (mkd p)
(define-values (! u->s) (define-values (! u->s)

View File

@ -10,7 +10,7 @@
(define-values (i-port o-port) (make-pipe)) (define-values (i-port o-port) (make-pipe))
(define conn (define conn
(connection 0 (start-timer +inf.0 void) (connection 0 (start-timer +inf.0 void)
i-port o-port (current-custodian) #t)) i-port o-port (make-custodian) #t))
(output-response conn r) (output-response conn r)
(close-output-port o-port) (close-output-port o-port)
(define bs (port->bytes i-port)) (define bs (port->bytes i-port))

View File

@ -15,7 +15,7 @@
(check-true (check-true
(let ([ib (open-input-bytes #"")] (let ([ib (open-input-bytes #"")]
[ob (open-output-bytes)]) [ob (open-output-bytes)])
(new-connection 1 ib ob (current-custodian) #t) (new-connection 1 ib ob (make-custodian) #t)
(sleep 2) (sleep 2)
(with-handlers ([exn? (lambda _ #t)]) (with-handlers ([exn? (lambda _ #t)])
(read ib) #f)))) (read ib) #f))))
@ -25,7 +25,7 @@
(check-true (check-true
(let ([ib (open-input-bytes #"")] (let ([ib (open-input-bytes #"")]
[ob (open-output-bytes)]) [ob (open-output-bytes)])
(new-connection 1 ib ob (current-custodian) #t) (new-connection 1 ib ob (make-custodian) #t)
(sleep 2) (sleep 2)
(with-handlers ([exn? (lambda _ #t)]) (with-handlers ([exn? (lambda _ #t)])
(write 1 ob) #f)))) (write 1 ob) #f))))
@ -35,7 +35,7 @@
(check-true (check-true
(let* ([ib (open-input-bytes #"")] (let* ([ib (open-input-bytes #"")]
[ob (open-output-bytes)] [ob (open-output-bytes)]
[c (new-connection 1 ib ob (current-custodian) #t)]) [c (new-connection 1 ib ob (make-custodian) #t)])
(kill-connection! c) (kill-connection! c)
(and (with-handlers ([exn? (lambda _ #t)]) (and (with-handlers ([exn? (lambda _ #t)])
(read ib) #f) (read ib) #f)
@ -47,7 +47,7 @@
(check-true (check-true
(let* ([ib (open-input-bytes #"")] (let* ([ib (open-input-bytes #"")]
[ob (open-output-bytes)] [ob (open-output-bytes)]
[c (new-connection 1 ib ob (current-custodian) #t)]) [c (new-connection 1 ib ob (make-custodian) #t)])
(adjust-connection-timeout! c 1) (adjust-connection-timeout! c 1)
(sleep 2) (sleep 2)
(and (with-handlers ([exn? (lambda _ #t)]) (and (with-handlers ([exn? (lambda _ #t)])

View File

@ -4,7 +4,5 @@
(require rackunit/text-ui) (require rackunit/text-ui)
(run-tests all-web-server-tests) (run-tests all-web-server-tests)
#;(require rackunit/gui) #;(require rackunit/gui)
#;(test/gui all-web-server-tests) #;(test/gui all-web-server-tests)

View File

@ -0,0 +1,90 @@
#lang racket/base
(require rackunit
racket/list
xml/path
web-server/test
net/url
racket/promise
web-server/http)
(define (test-add-two-numbers -s>)
(define x (random 500))
(define xs (string->bytes/utf-8 (number->string x)))
(define y (random 500))
(define ys (string->bytes/utf-8 (number->string y)))
(define r0 (-s>))
(define k0 (se-path* '(form #:action) r0))
(define i0 (se-path* '(form input #:name) r0))
(define r1
(-s> (format "~a?~a=~a" k0 i0 xs)
(list (make-binding:form (string->bytes/utf-8 i0) xs))))
(define k1 (se-path* '(form #:action) r1))
(define i1 (se-path* '(form input #:name) r1))
(define r2
(-s> (format "~a?~a=~a" k1 i1 ys)
(list (make-binding:form (string->bytes/utf-8 i1) ys))))
(define n (se-path* '(p) r2))
(check-equal? n
(format "The answer is ~a" (+ x y)))
(let ()
(define r2
(-s> (format "~a?~a=~a" k1 i1 ys)
(list (make-binding:form (string->bytes/utf-8 i1) ys))
#:raw? #t))
(check-equal? r2
(string->bytes/utf-8
(format "<html><head><title>Sum</title></head><body bgcolor=\"white\"><p>The answer is ~a</p></body></html>"
(+ x y)))))
(let ()
(define r2
(-s>
(make-request #"GET" (string->url (format "~a?~a=~a" k1 i1 ys)) empty
(delay (list (make-binding:form (string->bytes/utf-8 i1) ys)))
#"" "127.0.0.1" 80 "127.0.0.1")))
(define n (se-path* '(p) r2))
(check-equal? n
(format "The answer is ~a" (+ x y)))))
(require (prefix-in ex:add1: web-server/default-web-root/htdocs/servlets/examples/add)
(prefix-in ex:add2: web-server/default-web-root/htdocs/servlets/examples/add-v2))
(require (prefix-in ex:double: web-server/default-web-root/htdocs/servlets/examples/wc))
(define (test-double-counters -s>)
(define (invoke u)
(define sx (-s> u))
(define ks (se-path*/list '(div div a #:href) sx))
(values (se-path*/list '(div div h3) sx)
(first ks)
(second ks)))
; One add
(define-values (v0.0 0.0+1 0.0+2) (invoke ""))
(check-equal? v0.0 (list "0" "0"))
(define-values (v1.0 1.0+1 1.0+2) (invoke 0.0+1))
(check-equal? v1.0 (list "1" "0")) ; XXX infinite loop after this
(define-values (v0.1 0.1+1 0.1+2) (invoke 0.0+2))
(check-equal? v0.1 (list "0" "1"))
; Two adds
(define-values (v2.0 2.0+1 2.0+2) (invoke 1.0+1))
(check-equal? v2.0 (list "2" "0"))
(define-values (v1.1 1.1+1 1.1+2) (invoke 0.1+1))
(check-equal? v1.1 (list "1" "1"))
(define-values (_v1.1 _1.1+1 _1.1+2) (invoke 1.0+2))
(check-equal? _v1.1 (list "1" "1"))
(define-values (v0.2 0.2+1 0.2+2) (invoke 0.1+2))
(check-equal? v0.2 (list "0" "2")))
(define test-tests
(test-suite "Servlet testing tests"
(test-case "add1"
(test-add-two-numbers
(make-servlet-tester ex:add1:start)))
(test-case "add2"
(test-add-two-numbers
(make-servlet-tester ex:add2:start)))
(test-case "double-counters"
(test-double-counters
(make-servlet-tester ex:double:start)))))
(provide test-tests)

View File

@ -18,76 +18,17 @@
call call
bytes-sort) bytes-sort)
(require xml/path)
(provide (rename-out
[se-path* simple-xpath*]
[se-path*/list simple-xpath*/list]))
(define (bytes-sort bs) (define (bytes-sort bs)
(sort (sort
(with-input-from-bytes bs (with-input-from-bytes bs
(λ () (port->bytes-lines #:line-mode 'return-linefeed))) (λ () (port->bytes-lines #:line-mode 'return-linefeed)))
bytes<?)) bytes<?))
(define keyword->symbol (compose string->symbol keyword->string))
(define (simple-xpath/xexpr p x)
(match p
[(list)
(list x)]
[(list-rest (? symbol? s) r)
(match x
[(list-rest (? (curry equal? s)) rs)
(simple-xpath/tag-body r rs)]
[_
empty])]
[_
empty]))
(define (simple-xpath/tag-body p x)
(match p
[(list)
(match x
[(list) empty]
[(list-rest (list (list (? symbol?) (? string?)) ...) rs)
(simple-xpath/tag-body p rs)]
[(? list?)
x]
[_
empty])]
[(list-rest (? symbol?) _)
(match x
[(list-rest (list (list (? symbol?) (? string?)) ...) rs)
(simple-xpath/tag-body p rs)]
[(? list?)
(append-map (curry simple-xpath/xexpr p) x)]
[_
empty])]
[(list (? keyword? k))
(match x
[(list-rest (and attrs (list (list (? symbol?) (? string?)) ...)) rs)
(simple-xpath/attr (keyword->symbol k) attrs)]
[_
empty])]
[_
empty]))
(define (simple-xpath/attr k attrs)
(dict-ref attrs k empty))
(define (simple-xpath*/list p x)
(append (simple-xpath/xexpr p x)
(match x
[(list-rest (list (cons (? symbol?) (? string?)) ...) rs)
(simple-xpath*/list p rs)]
[(? list?)
(append-map (curry simple-xpath*/list p) x)]
[_
empty])))
(define (simple-xpath* p x)
(match (simple-xpath*/list p x)
[(list) #f]
[(list-rest f rs) f]))
(test
(simple-xpath*/list '(p) '(html (body (p "Hey") (p "Bar")))) => (list "Hey" "Bar")
(simple-xpath* '(p) '(html (body (p "Hey")))) => "Hey"
(simple-xpath* '(p #:bar) '(html (body (p ([bar "Zog"]) "Hey")))) => "Zog")
(provide simple-xpath*
simple-xpath*/list)
(define (call d u bs) (define (call d u bs)
(htxml (collect d (make-request #"GET" (string->url u) empty (delay bs) #"" "127.0.0.1" 80 "127.0.0.1")))) (htxml (collect d (make-request #"GET" (string->url u) empty (delay bs) #"" "127.0.0.1" 80 "127.0.0.1"))))
(define (htxml bs) (define (htxml bs)
@ -99,60 +40,20 @@
[_ [_
(error 'html "Given ~S\n" bs)])) (error 'html "Given ~S\n" bs)]))
; This causes infinite loop. I will try putting it in a thread like on the real server
#;(define (collect d req)
(define-values (c i o) (make-mock-connection #""))
(parameterize ([current-server-custodian (current-custodian)])
(d c req))
(redact (get-output-bytes o)))
; This causes errors because s/s/d tries to jump the barrier, but I have no idea why ; This causes errors because s/s/d tries to jump the barrier, but I have no idea why
(define (collect d req) (define (collect d req)
(define-values (c i o) (make-mock-connection #"")) (define-values (c i o) (make-mock-connection #""))
(parameterize ([current-server-custodian (current-custodian)]) (parameterize ([current-server-custodian (make-custodian)])
(call-with-continuation-barrier (call-with-continuation-barrier
(lambda () (lambda ()
(d c req)))) (d c req))))
(redact (get-output-bytes o))) (redact (get-output-bytes o)))
; This causes a dead lock, even though the log shows that the channel should sync
(define (channel-put* c v)
(printf "+CHAN ~S PUT: ~S\n" c v)
(channel-put c v)
(printf "-CHAN ~S PUT: ~S\n" c v))
(define (channel-get* c)
(printf "+CHAN ~S GET\n" c)
(let ([v (channel-get c)])
(printf "-CHAN ~S GET: ~S\n" c v)
v))
#;(define (collect d req)
(define chan (make-channel))
(define-values (c i o) (make-mock-connection #""))
(parameterize ([current-server-custodian (current-custodian)])
(thread
(lambda ()
(d c req)
(channel-put* chan (get-output-bytes o))
)))
(redact (channel-get* chan)))
; This causes an error, because the output bytes are #""
#;(define (collect d req)
(define-values (c i o) (make-mock-connection #""))
(parameterize ([current-server-custodian (current-custodian)])
(thread-wait
(thread
(lambda ()
(d c req)))))
(redact (get-output-bytes o)))
(define (make-mock-connection ib) (define (make-mock-connection ib)
(define ip (open-input-bytes ib)) (define ip (open-input-bytes ib))
(define op (open-output-bytes)) (define op (open-output-bytes))
(values (make-connection 0 (make-timer never-evt +inf.0 (lambda () (void))) (values (make-connection 0 (make-timer never-evt +inf.0 (lambda () (void)))
ip op (current-custodian) #f) ip op (make-custodian) #f)
ip ip
op)) op))

View File

@ -0,0 +1,8 @@
#lang racket/base
(require xml/path
tests/eli-tester)
(test
(se-path*/list '(p) '(html (body (p "Hey") (p "Bar")))) => (list "Hey" "Bar")
(se-path* '(p) '(html (body (p "Hey")))) => "Hey"
(se-path* '(p #:bar) '(html (body (p ([bar "Zog"]) "Hey")))) => "Zog")

View File

@ -11,6 +11,7 @@ The @web-server implements many HTTP libraries that are provided by this module.
@section[#:tag "request-structs"]{Requests} @section[#:tag "request-structs"]{Requests}
@(require (for-label web-server/http/request-structs @(require (for-label web-server/http/request-structs
xml xml
racket/promise
racket/match)) racket/match))
@defmodule[web-server/http/request-structs]{ @defmodule[web-server/http/request-structs]{

View File

@ -14,6 +14,7 @@
web-server/dispatchers/dispatch-log web-server/dispatchers/dispatch-log
racket/serialize racket/serialize
web-server/stuffers web-server/stuffers
web-server/servlet/servlet-structs
racket/list)) racket/list))
@defmodule[web-server/servlet-env] @defmodule[web-server/servlet-env]

View File

@ -0,0 +1,29 @@
#lang scribble/doc
@(require "web-server.rkt"
(for-label web-server/http/request-structs
xml
racket/promise
racket/match))
@title[#:tag "test"]{Testing Servlets}
@defmodule[web-server/test]
The @web-server provides a simple facility for writing tests for Web servlets.
The core functionality allows a request to be sent to the servlet and the response captured:
@defproc[(make-servlet-tester
[servlet
(-> request?
can-be-response?)])
(->* ()
((or/c string? url? request? false/c)
(listof binding?)
boolean?)
(or/c bytes?
xexpr?))]{
This function accepts
}

View File

@ -34,6 +34,8 @@ This manual closes with a frequently asked questions section: @secref["faq"].
@include-section["templates.scrbl"] @include-section["templates.scrbl"]
@include-section["page.scrbl"] @include-section["page.scrbl"]
@include-section["test.scrbl"]
@include-section["faq.scrbl"] @include-section["faq.scrbl"]
@index-section[] @index-section[]

View File

@ -0,0 +1,86 @@
#lang racket/base
(require racket/contract
web-server/servlet/servlet-structs)
(provide/contract
[make-servlet-tester
(-> (-> request?
can-be-response?)
(->* ()
((or/c string? url? request? false/c)
(listof binding?)
#:raw? boolean?)
(or/c bytes?
xexpr?)))])
; Real Library
(require racket/list
racket/promise
net/url
web-server/http
web-server/servlet-dispatch)
(define (make-servlet-tester servlet)
(define d (dispatch/servlet servlet))
(λ ([s-or-u-or-req #f]
[bs empty]
#:raw? [raw? #f])
(define req
(if (request? s-or-u-or-req)
s-or-u-or-req
(let ()
(define s-or-u
(if s-or-u-or-req
s-or-u-or-req
""))
(define u
(if (string? s-or-u)
(string->url s-or-u)
s-or-u))
(make-request #"GET" u empty (delay bs) #"" "127.0.0.1" 80 "127.0.0.1"))))
(call d req #:raw? raw?)))
; Intermediate Library
(require racket/match
xml
web-server/private/timer
web-server/private/connection-manager
web-server/private/web-server-structs)
(define (call d req #:raw? raw?)
(htxml (collect d req) raw?))
(define (htxml bs raw?)
(match (regexp-match #"^.+\r\n\r\n(.*)$" bs)
[(list _ s)
(if raw?
s
(string->xexpr (bytes->string/utf-8 s)))]
[_
(error 'servlet "Servlet did not return an HTTP response, instead returned ~v"
bs)]))
(define (collect d req)
(parameterize ([current-custodian (make-custodian)])
(define-values (c i o) (make-mock-connection #""))
(parameterize ([current-server-custodian (current-custodian)])
(call-with-continuation-barrier
(lambda ()
(d c req))))
(redact (get-output-bytes o))))
(define (make-mock-connection ib)
(define ip (open-input-bytes ib))
(define op (open-output-bytes))
(values (make-connection 0 (make-timer never-evt +inf.0 (lambda () (void)))
ip op (current-custodian) #f)
ip
op))
(define (redact b)
(regexp-replace
#"Date: [a-zA-Z0-9:, ]+ GMT\r\n"
(regexp-replace
#"Last-Modified: [a-zA-Z0-9:, ]+ GMT\r\n"
b
#"Last-Modified: REDACTED GMT\r\n")
#"Date: REDACTED GMT\r\n"))

81
collects/xml/path.rkt Normal file
View File

@ -0,0 +1,81 @@
#lang racket/base
(require racket/contract
racket/match
racket/dict
racket/function
racket/list
xml)
(define keyword->symbol (compose string->symbol keyword->string))
(define (se-path/xexpr p x)
(match p
[(list)
(list x)]
[(list-rest (? symbol? s) r)
(match x
[(list-rest (? (curry equal? s)) rs)
(se-path/tag-body r rs)]
[_
empty])]
[_
empty]))
(define (se-path/tag-body p x)
(match p
[(list)
(match x
[(list) empty]
[(list-rest (list (list (? symbol?) (? string?)) ...) rs)
(se-path/tag-body p rs)]
[(? list?)
x]
[_
empty])]
[(list-rest (? symbol?) _)
(match x
[(list-rest (list (list (? symbol?) (? string?)) ...) rs)
(se-path/tag-body p rs)]
[(? list?)
(append-map (curry se-path/xexpr p) x)]
[_
empty])]
[(list (? keyword? k))
(match x
[(list-rest (and attrs (list (list (? symbol?) (? string?)) ...)) rs)
(se-path/attr (keyword->symbol k) attrs)]
[_
empty])]
[_
empty]))
(define (se-path/attr k attrs)
(dict-ref attrs k empty))
(define (se-path*/list p x)
(append (se-path/xexpr p x)
(match x
[(list-rest (list (cons (? symbol?) (? string?)) ...) rs)
(se-path*/list p rs)]
[(? list?)
(append-map (curry se-path*/list p) x)]
[_
empty])))
(define (se-path* p x)
(match (se-path*/list p x)
[(list) #f]
[(list-rest f rs) f]))
(define se-path?
(match-lambda
[(list) #t]
[(list (? keyword?)) #t]
[(list-rest (? symbol?) l) (se-path? l)]
[_ #f]))
(provide/contract
[se-path? contract?]
[se-path*
(-> se-path? xexpr?
; XXX maybe this shouldn't be any/c
any/c)]
[se-path*/list
(-> se-path? xexpr?
; XXX see above
(listof any/c))])

View File

@ -470,3 +470,11 @@ looks like the following, if re-formatted by:
</dict> </dict>
</plist> </plist>
}| }|
@; ----------------------------------------------------------------------
@section{Simple X-expression Path Queries}
@defmodule[xml/path]
XXX