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!
|
- 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
|
- export PATH="${RACKET_DIR}/bin:${PATH}" #install-racket.sh can't set for us
|
||||||
|
|
||||||
install:
|
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
|
|
||||||
# Here supply steps such as raco make, raco test, etc. Note that you
|
# 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
|
# `raco pkg install --deps search-auto repltest` to install any required
|
||||||
# packages without it getting stuck on a confirmation prompt.
|
# packages without it getting stuck on a confirmation prompt.
|
||||||
script:
|
script:
|
||||||
- raco pkg install --deps search-auto cover
|
- raco pkg install --deps search-auto .
|
||||||
- raco test -x -p repltest
|
- raco test -x -p repltest
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
|
|
3
info.rkt
3
info.rkt
|
@ -1,7 +1,8 @@
|
||||||
#lang info
|
#lang info
|
||||||
(define collection "repltest")
|
(define collection "repltest")
|
||||||
(define deps '("base"
|
(define deps '("base"
|
||||||
"rackunit-lib"))
|
"rackunit-lib"
|
||||||
|
"debug"))
|
||||||
(define build-deps '("scribble-lib" "racket-doc"))
|
(define build-deps '("scribble-lib" "racket-doc"))
|
||||||
(define scribblings '(("scribblings/repltest.scrbl" ())))
|
(define scribblings '(("scribblings/repltest.scrbl" ())))
|
||||||
(define pkg-desc "Copy-paste your REPL interactions, and have them run as tests")
|
(define pkg-desc "Copy-paste your REPL interactions, and have them run as tests")
|
||||||
|
|
|
@ -5,65 +5,25 @@
|
||||||
[repltest-get-info get-info]))
|
[repltest-get-info get-info]))
|
||||||
|
|
||||||
(require (for-template repltest/private/run-interactions)
|
(require (for-template repltest/private/run-interactions)
|
||||||
(for-template repltest/private/modbg)
|
|
||||||
racket/syntax
|
racket/syntax
|
||||||
repltest/private/util
|
repltest/private/util
|
||||||
(only-in syntax/module-reader make-meta-reader)
|
(only-in syntax/module-reader make-meta-reader)
|
||||||
syntax/strip-context)
|
syntax/strip-context)
|
||||||
|
|
||||||
(define ((wrap-reader reader) chr in src line col pos)
|
(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))
|
(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))
|
(with-syntax ([(mod nam lang (modbeg . body))
|
||||||
(eval #'(expand #`(mod nm lang . body))
|
(eval #'(expand #'orig-mod)
|
||||||
(variable-reference->namespace (#%variable-reference)))])
|
(variable-reference->namespace (#%variable-reference)))])
|
||||||
#`(m1 n1 l1
|
#`(mod nam lang
|
||||||
(mb1 (module* test racket/base
|
(modbeg
|
||||||
(require repltest/private/run-interactions)
|
(module* test racket/base
|
||||||
(run-interactions ;#'(mod nm lang . body)
|
(require repltest/private/run-interactions)
|
||||||
(open-input-string #,(port->string in))
|
(run-interactions (open-input-string #,(port->string in))
|
||||||
(#%variable-reference)))
|
(#%variable-reference)))
|
||||||
. bd1)))
|
. body))))
|
||||||
|
|
||||||
;#`(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)))
|
|
||||||
|#
|
|
||||||
|
|
||||||
(define-values (repltest-read repltest-read-syntax repltest-get-info)
|
(define-values (repltest-read repltest-read-syntax repltest-get-info)
|
||||||
(make-meta-reader
|
(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)
|
(define (narrow-until-prompt in)
|
||||||
(make-limited-input-port in (peak-until-prompt-length 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
|
(define silent-prompt-read
|
||||||
(λ ()
|
(λ ()
|
||||||
;; Default current-prompt-read, without showing
|
|
||||||
;; the prompt
|
|
||||||
(let ([in ((current-get-interaction-input-port))])
|
(let ([in ((current-get-interaction-input-port))])
|
||||||
((current-read-interaction) (object-name in) in))))
|
((current-read-interaction) (object-name in) in))))
|
|
@ -1,17 +1,131 @@
|
||||||
#lang scribble/manual
|
#lang scribble/manual
|
||||||
@require[@for-label[repltest
|
@require[@for-label[repltest
|
||||||
racket/base]]
|
racket/base]
|
||||||
|
scriblib/footnote]
|
||||||
|
|
||||||
@title{REPL test: copy-paste REPL interactions to define tests}
|
@title{REPL test: copy-paste REPL interactions to define tests}
|
||||||
@author{georges}
|
@author{georges}
|
||||||
|
|
||||||
@defmodule[repltest]
|
Source code: @url{https://github.com/jsmaniac/repltest}
|
||||||
|
|
||||||
This package define a meta-language which parses a REPL
|
@defmodulelang[repltest]{
|
||||||
trace, and re-evaluates it, checking that the outputs
|
The @racketmodname[repltest] language is a meta-language
|
||||||
haven't changed.
|
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
|
@racketblock[
|
||||||
a debugging session. It is obviously not a substitute for
|
@#,hash-lang[] @#,racketmodname[repltest] @#,racketmodname[racket]
|
||||||
writing real tests, and these tests are more prone to the
|
(define x 3)
|
||||||
“copy-pasted bogus output into the tests” problem.
|
@#,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