Improved documentation.
This commit is contained in:
parent
3049c1867b
commit
f57b1ecd06
|
@ -37,8 +37,6 @@ before_install:
|
|||
- cat travis-racket/install-racket.sh | bash # pipe to bash not sh!
|
||||
- export PATH="${RACKET_DIR}/bin:${PATH}" #install-racket.sh can't set for us
|
||||
|
||||
install:
|
||||
|
||||
before_script:
|
||||
|
||||
# Here supply steps such as raco make, raco test, etc. Note that you
|
||||
|
@ -46,7 +44,7 @@ before_script:
|
|||
# `raco pkg install --deps search-auto repltest` to install any required
|
||||
# packages without it getting stuck on a confirmation prompt.
|
||||
script:
|
||||
- raco pkg install --deps search-auto cover
|
||||
- raco pkg install --deps search-auto .
|
||||
- raco test -x -p repltest
|
||||
|
||||
after_success:
|
||||
|
|
3
info.rkt
3
info.rkt
|
@ -1,7 +1,8 @@
|
|||
#lang info
|
||||
(define collection "repltest")
|
||||
(define deps '("base"
|
||||
"rackunit-lib"))
|
||||
"rackunit-lib"
|
||||
"debug"))
|
||||
(define build-deps '("scribble-lib" "racket-doc"))
|
||||
(define scribblings '(("scribblings/repltest.scrbl" ())))
|
||||
(define pkg-desc "Copy-paste your REPL interactions, and have them run as tests")
|
||||
|
|
|
@ -5,65 +5,25 @@
|
|||
[repltest-get-info get-info]))
|
||||
|
||||
(require (for-template repltest/private/run-interactions)
|
||||
(for-template repltest/private/modbg)
|
||||
racket/syntax
|
||||
repltest/private/util
|
||||
(only-in syntax/module-reader make-meta-reader)
|
||||
syntax/strip-context)
|
||||
|
||||
(define ((wrap-reader reader) chr in src line col pos)
|
||||
(define/with-syntax (mod nm lang . body)
|
||||
(define/with-syntax orig-mod
|
||||
(reader chr (narrow-until-prompt in) src line col pos))
|
||||
;(displayln "WARNING: skipping tests")(port->string in) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;DEBUG
|
||||
|
||||
(with-syntax ([(m1 n1 l1 (mb1 . bd1))
|
||||
(eval #'(expand #`(mod nm lang . body))
|
||||
(with-syntax ([(mod nam lang (modbeg . body))
|
||||
(eval #'(expand #'orig-mod)
|
||||
(variable-reference->namespace (#%variable-reference)))])
|
||||
#`(m1 n1 l1
|
||||
(mb1 (module* test racket/base
|
||||
(require repltest/private/run-interactions)
|
||||
(run-interactions ;#'(mod nm lang . body)
|
||||
(open-input-string #,(port->string in))
|
||||
(#%variable-reference)))
|
||||
. bd1)))
|
||||
|
||||
;#`(mod nm lang . body)
|
||||
#;#`(mod nm repltest/private/modbg
|
||||
require
|
||||
(module nm lang (require lang) . body)
|
||||
#;#,(port->string in)
|
||||
(module* test racket/base
|
||||
(require repltest/private/run-interactions)
|
||||
(run-interactions ;#'(mod nm lang . body)
|
||||
(open-input-string #,(port->string in))
|
||||
(#%variable-reference)))))
|
||||
#|
|
||||
#;(insert-in-module
|
||||
(module code lang . body)
|
||||
(require 'code)
|
||||
(provide (all-from-out 'code))
|
||||
(module test racket/base
|
||||
(require repltest/private/run-interactions)
|
||||
(run-interactions #'(mod nm lang . body)
|
||||
(open-input-string #,(port->string in))
|
||||
(#%variable-reference))))
|
||||
|
||||
#;(define/with-syntax (mod2 nm2 lang2 (modbeg2 . body2))
|
||||
(local-expand #'(module nm lang . body)
|
||||
'module
|
||||
'()))
|
||||
#;((λ (x)
|
||||
(displayln x)
|
||||
x)
|
||||
#`(mod2 nm2 lang2
|
||||
(modbeg2
|
||||
#;(module test racket/base
|
||||
(require repltest/private/run-interactions)
|
||||
(run-interactions #'(mod nm lang . body)
|
||||
(open-input-string #,(port->string in))
|
||||
(#%variable-reference)))
|
||||
. body2)))
|
||||
|#
|
||||
#`(mod nam lang
|
||||
(modbeg
|
||||
(module* test racket/base
|
||||
(require repltest/private/run-interactions)
|
||||
(run-interactions (open-input-string #,(port->string in))
|
||||
(#%variable-reference)))
|
||||
. body))))
|
||||
|
||||
(define-values (repltest-read repltest-read-syntax repltest-get-info)
|
||||
(make-meta-reader
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
#lang racket/base
|
||||
|
||||
(provide (rename-out [insert-in-module #%module-begin]))
|
||||
|
||||
(require (for-syntax racket/base
|
||||
syntax/strip-context))
|
||||
|
||||
(define-syntax (insert-in-module stx)
|
||||
(syntax-case stx ()
|
||||
[(_ rr
|
||||
(mod1 nm1 lang1 (req lng) . bdy1);orig-mod
|
||||
submod
|
||||
;str
|
||||
)
|
||||
(with-syntax ([(mod nm lang (modbg . body)) (expand ;#'orig-mod
|
||||
#'(mod1 nm1 lang1 . bdy1))])
|
||||
;(with-syntax ([req (datum->syntax #'md1 'require)])
|
||||
|
||||
|
||||
((λ (x)
|
||||
(displayln x)
|
||||
x)
|
||||
(syntax-local-introduce
|
||||
#`(modbg ;(require lang)
|
||||
;(req #,(datum->syntax #'req (syntax->datum #'lang)))
|
||||
;(rr lang)
|
||||
. body)))
|
||||
|
||||
#;#`(modbg ;(require lang)
|
||||
;; ok for #%top-interaction:
|
||||
(req #,(datum->syntax #'req (syntax->datum #'lang)))
|
||||
;; not ok for #%top-interaction:
|
||||
;(req lang)
|
||||
(rr lang)
|
||||
(define varref (#,(datum->syntax #'lang '#%variable-reference)))
|
||||
(provide varref)
|
||||
submod
|
||||
#;(module* test racket/base
|
||||
(require repltest/private/run-interactions)
|
||||
(require (submod ".."))
|
||||
#;(define res-mod
|
||||
(module-path-index-resolve
|
||||
(module-path-index-join '(submod "..")
|
||||
(variable-reference->module-path-index
|
||||
varref))))
|
||||
;(define mod-ns (module->namespace res-mod))
|
||||
(define mod-ns (variable-reference->namespace varref))
|
||||
(displayln mod-ns)
|
||||
(run-interactions2 (open-input-string str)
|
||||
mod-ns)
|
||||
#;(run-interactions (open-input-string str)
|
||||
#,(datum->syntax #'modbg '#%variable-reference)
|
||||
#;(#%variable-reference)))
|
||||
. body))]))
|
|
@ -46,9 +46,8 @@
|
|||
(define (narrow-until-prompt in)
|
||||
(make-limited-input-port in (peak-until-prompt-length in)))
|
||||
|
||||
;; Just like the default `current-prompt-read`, but without showing the prompt.
|
||||
(define silent-prompt-read
|
||||
(λ ()
|
||||
;; Default current-prompt-read, without showing
|
||||
;; the prompt
|
||||
(let ([in ((current-get-interaction-input-port))])
|
||||
((current-read-interaction) (object-name in) in))))
|
|
@ -1,17 +1,131 @@
|
|||
#lang scribble/manual
|
||||
@require[@for-label[repltest
|
||||
racket/base]]
|
||||
racket/base]
|
||||
scriblib/footnote]
|
||||
|
||||
@title{REPL test: copy-paste REPL interactions to define tests}
|
||||
@author{georges}
|
||||
|
||||
@defmodule[repltest]
|
||||
Source code: @url{https://github.com/jsmaniac/repltest}
|
||||
|
||||
This package define a meta-language which parses a REPL
|
||||
trace, and re-evaluates it, checking that the outputs
|
||||
haven't changed.
|
||||
@defmodulelang[repltest]{
|
||||
The @racketmodname[repltest] language is a meta-language
|
||||
that replays a copy-pasted transcript of an interactive
|
||||
REPL (@racket[read-eval-print-loop]) session, checking that the
|
||||
outputs have not changed.
|
||||
|
||||
This allows to quickly write preliminary unit tests based
|
||||
on a debugging session. It is however not a substitute for
|
||||
writing real tests, and these tests are more prone to the
|
||||
“copy-pasted bogus output into the tests” problem.}
|
||||
|
||||
This allows to quickly write preliminary unit tests based on
|
||||
a debugging session. It is obviously not a substitute for
|
||||
writing real tests, and these tests are more prone to the
|
||||
“copy-pasted bogus output into the tests” problem.
|
||||
@racketblock[
|
||||
@#,hash-lang[] @#,racketmodname[repltest] @#,racketmodname[racket]
|
||||
(define x 3)
|
||||
@#,racketid[>] (+ x 1)
|
||||
@#,racketresultfont{4}
|
||||
]
|
||||
|
||||
The first part of the file is kept inside the top-level
|
||||
module. This module uses the language indicated just after
|
||||
@racket[@#,hash-lang[] @#,racketmodname[repltest]], for
|
||||
example:
|
||||
|
||||
@racketblock[
|
||||
@#,hash-lang[] @#,racketmodname[repltest] @#,racketmodname[typed/racket]]
|
||||
|
||||
After the first occurrence of the prompt (by default
|
||||
@racket["> "], later versions of this package will allow
|
||||
customizing this) is encountered, all the remaining contents
|
||||
of the file are understood as a REPL transcript. The prompt
|
||||
is only recognized if it is outside of any s-expression,
|
||||
which means that the @racket[>] function can be used
|
||||
normally.
|
||||
|
||||
@racketblock[
|
||||
@#,hash-lang[] @#,racketmodname[repltest] @#,racketmodname[racket]
|
||||
(define x (> 3 4))
|
||||
@#,racketid[>] x
|
||||
@#,racketresultfont{#f}
|
||||
]
|
||||
|
||||
@section{The @racketid[test] submodule}
|
||||
|
||||
This language injects a @racketid[test] submodule
|
||||
using @racket[module*]. When the @racketid[test] module
|
||||
is run, the expression after each prompt is read and
|
||||
evaluated as if it had been typed inside a real REPL, using
|
||||
@racket[read-eval-print-loop]. The result, as printed on the
|
||||
standard output and standard error, is compared with the
|
||||
text read until the next prompt. The next prompt will only
|
||||
be recognized if it is not part of an s-expression, which
|
||||
means that occurrences of @racket[>] inside an expression in
|
||||
the output are correctly handled:
|
||||
|
||||
@racketblock[
|
||||
@#,hash-lang[] @#,racketmodname[repltest] @#,racketmodname[racket]
|
||||
(define x '(> 3 4))
|
||||
@#,racketid[>] x
|
||||
@#,racketresultfont{'(> 3 4)}
|
||||
@#,racketid[>] '(> 5 6)
|
||||
@#,racketresultfont{'(> 5 6)}
|
||||
]
|
||||
|
||||
The fact that a real REPL is used means that any
|
||||
language-specific output will be produced as expected. For
|
||||
example @racketmodname[typed/racket] prints the type of the
|
||||
result before the result itself, so it must be included in
|
||||
the expected output:
|
||||
|
||||
@racketblock[
|
||||
@#,hash-lang[] @#,racketmodname[repltest] @#,racketmodname[typed/racket]
|
||||
(define x 0)
|
||||
@#,racketid[>] x
|
||||
@#,racketresultfont{0}
|
||||
]
|
||||
|
||||
@section{Warning concerning comments}
|
||||
|
||||
Comments are not currently supported inside the REPL
|
||||
transcript. Also, the current version does not the first
|
||||
prompt being preceded by a comment.
|
||||
|
||||
@section{Warning concerning spaces and newlines}
|
||||
|
||||
The tests are space-sensitive, so care should be taken to
|
||||
include a newline at the end of the file, as in most
|
||||
languages, the REPL prints a newline after the result.
|
||||
Furthermore, extra spacing like blank lines should not be
|
||||
added after the first prompt.
|
||||
|
||||
@section{Future improvements}
|
||||
|
||||
Later versions of this package will allow customizing the following aspects:
|
||||
|
||||
@itemlist[
|
||||
@item{Flexibility of whitespace comparisons (strip leading
|
||||
and trailing whitespace, or ignoring all whitespace
|
||||
differences).}
|
||||
@item{Support comments before and inside the REPL
|
||||
transcript.}
|
||||
@item{Specifying a regexp matching the prompt, and a
|
||||
regexp for characters preceding the prompt which are not
|
||||
part of it (and therefore will be part of the preceding
|
||||
result or main module's code).}
|
||||
@item{Disable calling @racket[read] on the output
|
||||
expressions, which can be useful when the output contain
|
||||
unbalanced parenthesis, or do not otherwise match the
|
||||
language's syntax, for example:
|
||||
|
||||
@; TODO: include this in the tests
|
||||
@racketblock[
|
||||
@#,hash-lang[] @#,racketmodname[repltest] @#,racketmodname[racket]
|
||||
@#,racketid[>] (displayln "(unbalanced")
|
||||
@#,racketresultfont{(unbalanced}
|
||||
@#,racketid[>] (displayln "#invalid (syntax . too . many . dots)")
|
||||
@#,racketresultfont{#invalid (syntax . too . many . dots)}]
|
||||
|
||||
This will also have the side-effect of allowing the prompt
|
||||
to be matched inside s-expressions.}
|
||||
@item{Distinguish standard output (purple font in DrRacket),
|
||||
printed result (blue font) and standard error (red font).}]
|
4
test/doc1.rkt
Normal file
4
test/doc1.rkt
Normal file
|
@ -0,0 +1,4 @@
|
|||
#lang repltest racket
|
||||
(define x 3)
|
||||
> (+ x 1)
|
||||
4
|
1
test/doc2-newline-at-end-of-file.rkt
Normal file
1
test/doc2-newline-at-end-of-file.rkt
Normal file
|
@ -0,0 +1 @@
|
|||
#lang repltest typed/racket
|
1
test/doc2-no-newline-at-end-of-file.rkt
Normal file
1
test/doc2-no-newline-at-end-of-file.rkt
Normal file
|
@ -0,0 +1 @@
|
|||
#lang repltest typed/racket
|
4
test/doc3.rkt
Normal file
4
test/doc3.rkt
Normal file
|
@ -0,0 +1,4 @@
|
|||
#lang repltest racket
|
||||
(define x (> 3 4))
|
||||
> x
|
||||
#f
|
6
test/doc4.rkt
Normal file
6
test/doc4.rkt
Normal file
|
@ -0,0 +1,6 @@
|
|||
#lang repltest racket
|
||||
(define x '(> 3 4))
|
||||
> x
|
||||
'(> 3 4)
|
||||
> '(> 5 6)
|
||||
'(> 5 6)
|
5
test/doc5.rkt
Normal file
5
test/doc5.rkt
Normal file
|
@ -0,0 +1,5 @@
|
|||
#lang repltest typed/racket
|
||||
(define x 0)
|
||||
> x
|
||||
- : Integer [more precisely: Zero]
|
||||
0
|
Loading…
Reference in New Issue
Block a user