Sandboxes make up and remember a reasonable default source to filter
uncovered expressions on. (See http://lists.racket-lang.org/users/archive/2010-October/042008.html for a detailed description.)
This commit is contained in:
parent
c4b590d9bf
commit
40ecda1a77
|
@ -439,23 +439,37 @@
|
||||||
[(path? inp) (open-input-file inp)]
|
[(path? inp) (open-input-file inp)]
|
||||||
[else #f]))
|
[else #f]))
|
||||||
|
|
||||||
;; Gets an input spec returns a list of syntaxes. The input can be a list of
|
;; Gets an input spec returns a list of syntaxes and a default source to filter
|
||||||
;; sexprs/syntaxes, or a list with a single input port spec
|
;; uncovered expressions with. The input can be a list of sexprs/syntaxes, or
|
||||||
;; (path/string/bytes) value.
|
;; a list with a single input port spec (path/string/bytes) value. Note that
|
||||||
|
;; the source can be a filtering function.
|
||||||
(define (input->code inps source n)
|
(define (input->code inps source n)
|
||||||
(if (null? inps)
|
(if (null? inps)
|
||||||
'()
|
(values '() source)
|
||||||
(let ([p (input->port (car inps))])
|
(let ([p (input->port (car inps))])
|
||||||
(cond [(and p (null? (cdr inps)))
|
(cond [(and p (null? (cdr inps)))
|
||||||
(port-count-lines! p)
|
(port-count-lines! p)
|
||||||
|
(let ([source (or (object-name p)
|
||||||
|
;; just in case someone uses a function as the
|
||||||
|
;; source...
|
||||||
|
(if (procedure? source)
|
||||||
|
(lambda (x) (eq? x source))
|
||||||
|
source))])
|
||||||
(parameterize ([current-input-port p])
|
(parameterize ([current-input-port p])
|
||||||
(begin0 ((sandbox-reader) (or (object-name p) source))
|
(begin0 (values ((sandbox-reader) source) source)
|
||||||
;; close a port if we opened it
|
;; close a port if we opened it
|
||||||
(unless (eq? p (car inps)) (close-input-port p))))]
|
(unless (eq? p (car inps)) (close-input-port p)))))]
|
||||||
[p (error 'input->code "ambiguous inputs: ~e" inps)]
|
[p (error 'input->code "ambiguous inputs: ~e" inps)]
|
||||||
|
[(andmap syntax? inps)
|
||||||
|
(values inps
|
||||||
|
(let ([srcs (remove-duplicates (map syntax-source inps)
|
||||||
|
equal?)])
|
||||||
|
(if (null? (cdr srcs))
|
||||||
|
(car srcs)
|
||||||
|
(lambda (x) (memq x srcs)))))]
|
||||||
[else (let loop ([inps inps] [n n] [r '()])
|
[else (let loop ([inps inps] [n n] [r '()])
|
||||||
(if (null? inps)
|
(if (null? inps)
|
||||||
(reverse r)
|
(values (reverse r) source)
|
||||||
(loop (cdr inps) (and n (add1 n))
|
(loop (cdr inps) (and n (add1 n))
|
||||||
;; 1st at line#1, pos#1, 2nd at line#2, pos#2 etc
|
;; 1st at line#1, pos#1, 2nd at line#2, pos#2 etc
|
||||||
;; (starting from the `n' argument)
|
;; (starting from the `n' argument)
|
||||||
|
@ -482,8 +496,9 @@
|
||||||
(dynamic-require 'racket/class #f))
|
(dynamic-require 'racket/class #f))
|
||||||
(namespace-attach-module orig-ns 'racket/class))]))
|
(namespace-attach-module orig-ns 'racket/class))]))
|
||||||
|
|
||||||
;; Returns a single (module ...) or (begin ...) expression (a `begin' list
|
;; Returns a single (module ...) or (begin ...) expression (a `begin' list will
|
||||||
;; will be evaluated one by one -- the language might not have a `begin').
|
;; be evaluated one by one -- the language might not have a `begin'), and a
|
||||||
|
;; default source to filter uncovered expressions with.
|
||||||
;;
|
;;
|
||||||
;; FIXME: inserting `#%require's here is bad if the language has a
|
;; FIXME: inserting `#%require's here is bad if the language has a
|
||||||
;; `#%module-begin' that processes top-level forms specially.
|
;; `#%module-begin' that processes top-level forms specially.
|
||||||
|
@ -494,16 +509,19 @@
|
||||||
;; it comes from `#%kernel', so it's always present through
|
;; it comes from `#%kernel', so it's always present through
|
||||||
;; transitive requires.
|
;; transitive requires.
|
||||||
(define (build-program language requires input-program)
|
(define (build-program language requires input-program)
|
||||||
|
(define-values (prog-stxs source) (input->code input-program 'program 1))
|
||||||
(let* ([body (append (if (and (pair? requires) (eq? 'begin (car requires)))
|
(let* ([body (append (if (and (pair? requires) (eq? 'begin (car requires)))
|
||||||
(cdr requires)
|
(cdr requires)
|
||||||
(map (lambda (r) (list #'#%require r)) requires))
|
(map (lambda (r) (list #'#%require r)) requires))
|
||||||
(input->code input-program 'program 1))]
|
prog-stxs)]
|
||||||
[use-lang (lambda (lang) `(module program ,lang . ,body))])
|
[use-lang (lambda (lang) `(module program ,lang . ,body))])
|
||||||
(cond [(decode-language language) => use-lang]
|
(values (cond [(decode-language language) => use-lang]
|
||||||
[(module-path? language) (use-lang language)]
|
[(module-path? language) (use-lang language)]
|
||||||
[(and (list? language) (eq? 'begin (car language)))
|
[(and (list? language) (eq? 'begin (car language)))
|
||||||
(append language body)]
|
(append language body)]
|
||||||
[else (error 'make-evaluator "bad language spec: ~e" language)])))
|
[else (error 'make-evaluator "bad language spec: ~e"
|
||||||
|
language)])
|
||||||
|
source)))
|
||||||
|
|
||||||
(define (decode-language language)
|
(define (decode-language language)
|
||||||
(cond [(and (list? language)
|
(cond [(and (list? language)
|
||||||
|
@ -630,6 +648,7 @@
|
||||||
(define user-cust-box (make-custodian-box user-cust #t))
|
(define user-cust-box (make-custodian-box user-cust #t))
|
||||||
(define coverage? (sandbox-coverage-enabled))
|
(define coverage? (sandbox-coverage-enabled))
|
||||||
(define uncovered #f)
|
(define uncovered #f)
|
||||||
|
(define default-coverage-source-filter #f)
|
||||||
(define input-ch (make-channel))
|
(define input-ch (make-channel))
|
||||||
(define result-ch (make-channel))
|
(define result-ch (make-channel))
|
||||||
(define busy-sema (make-semaphore 1))
|
(define busy-sema (make-semaphore 1))
|
||||||
|
@ -692,9 +711,11 @@
|
||||||
;; first set up the environment
|
;; first set up the environment
|
||||||
(init-hook)
|
(init-hook)
|
||||||
((sandbox-init-hook))
|
((sandbox-init-hook))
|
||||||
;; now read and evaluate the input program
|
;; now read and evaluate the input program (in the user context)
|
||||||
(evaluate-program
|
(evaluate-program
|
||||||
(if (procedure? program-maker) (program-maker) program-maker)
|
(let-values ([(prog src) (program-maker)])
|
||||||
|
(when coverage? (set! default-coverage-source-filter src))
|
||||||
|
prog)
|
||||||
limit-thunk
|
limit-thunk
|
||||||
(and coverage? (lambda (es+get) (set! uncovered es+get))))))
|
(and coverage? (lambda (es+get) (set! uncovered es+get))))))
|
||||||
(channel-put result-ch 'ok))
|
(channel-put result-ch 'ok))
|
||||||
|
@ -710,19 +731,24 @@
|
||||||
(define run
|
(define run
|
||||||
(if (evaluator-message? expr)
|
(if (evaluator-message? expr)
|
||||||
(case (evaluator-message-msg expr)
|
(case (evaluator-message-msg expr)
|
||||||
[(thunk) (limit-thunk (car (evaluator-message-args expr)))]
|
[(thunk) (limit-thunk
|
||||||
|
(car (evaluator-message-args expr)))]
|
||||||
[(thunk*) (car (evaluator-message-args expr))]
|
[(thunk*) (car (evaluator-message-args expr))]
|
||||||
[else (error 'sandbox "internal error (bad message)")])
|
[else (error 'sandbox "internal error (bad message)")])
|
||||||
(limit-thunk
|
(limit-thunk
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(set! n (add1 n))
|
(set! n (add1 n))
|
||||||
(eval* (map (lambda (expr) (cons '#%top-interaction expr))
|
(eval* (map (lambda (expr)
|
||||||
(input->code (list expr) 'eval n)))))))
|
(cons '#%top-interaction expr))
|
||||||
(channel-put result-ch (cons 'vals
|
(let-values ([(code _)
|
||||||
|
(input->code (list expr)
|
||||||
|
'eval n)])
|
||||||
|
code)))))))
|
||||||
|
(channel-put result-ch
|
||||||
|
(cons 'vals
|
||||||
(call-with-break-parameterization
|
(call-with-break-parameterization
|
||||||
break-paramz
|
break-paramz
|
||||||
(lambda ()
|
(lambda () (call-with-values run list))))))
|
||||||
(call-with-values run list))))))
|
|
||||||
(loop)))))))
|
(loop)))))))
|
||||||
(define (get-user-result)
|
(define (get-user-result)
|
||||||
(if (and (sandbox-propagate-breaks)
|
(if (and (sandbox-propagate-breaks)
|
||||||
|
@ -765,12 +791,17 @@
|
||||||
(cond [(eof-object? r) (terminate+kill! #t #t)]
|
(cond [(eof-object? r) (terminate+kill! #t #t)]
|
||||||
[(eq? (car r) 'exn) (raise (cdr r))]
|
[(eq? (car r) 'exn) (raise (cdr r))]
|
||||||
[else (apply values (cdr r))]))]))
|
[else (apply values (cdr r))]))]))
|
||||||
(define (get-uncovered [prog? #t] [src 'program])
|
(define (get-uncovered [prog? #t] [src default-coverage-source-filter])
|
||||||
(unless uncovered
|
(unless uncovered
|
||||||
(error 'get-uncovered-expressions "no coverage information"))
|
(error 'get-uncovered-expressions "no coverage information"))
|
||||||
(let ([uncovered (if prog? (car uncovered) ((cadr uncovered)))])
|
(let ([uncovered (if prog? (car uncovered) ((cadr uncovered)))])
|
||||||
(if src
|
(if src
|
||||||
(filter (lambda (x) (equal? src (syntax-source x))) uncovered)
|
;; when given a list of syntaxes, the src is actually a function that
|
||||||
|
;; checks the input source value (which does a union of the sources)
|
||||||
|
(filter (if (procedure? src)
|
||||||
|
(lambda (x) (src (syntax-source x)))
|
||||||
|
(lambda (x) (equal? src (syntax-source x))))
|
||||||
|
uncovered)
|
||||||
uncovered)))
|
uncovered)))
|
||||||
(define (output-getter p)
|
(define (output-getter p)
|
||||||
(if (procedure? p) (user-eval (make-evaluator-message 'thunk (list p))) p))
|
(if (procedure? p) (user-eval (make-evaluator-message 'thunk (list p))) p))
|
||||||
|
@ -926,7 +957,8 @@
|
||||||
input-program #:allow-read [allow null] #:language [reqlang #f])
|
input-program #:allow-read [allow null] #:language [reqlang #f])
|
||||||
;; this is for a complete module input program
|
;; this is for a complete module input program
|
||||||
(define (make-program)
|
(define (make-program)
|
||||||
(let ([prog (input->code (list input-program) 'program #f)])
|
(let-values ([(prog source)
|
||||||
|
(input->code (list input-program) 'program #f)])
|
||||||
(unless (= 1 (length prog))
|
(unless (= 1 (length prog))
|
||||||
(error 'make-evaluator "expecting a single `module' program; ~a"
|
(error 'make-evaluator "expecting a single `module' program; ~a"
|
||||||
(if (zero? (length prog))
|
(if (zero? (length prog))
|
||||||
|
@ -935,7 +967,7 @@
|
||||||
(syntax-case* (car prog) (module) literal-identifier=?
|
(syntax-case* (car prog) (module) literal-identifier=?
|
||||||
[(module modname lang body ...)
|
[(module modname lang body ...)
|
||||||
(if (or (not reqlang) (equal? reqlang (syntax->datum #'lang)))
|
(if (or (not reqlang) (equal? reqlang (syntax->datum #'lang)))
|
||||||
(car prog)
|
(values (car prog) source)
|
||||||
(error 'make-evaluator
|
(error 'make-evaluator
|
||||||
"module code used `~e' for a language, expecting `~e'"
|
"module code used `~e' for a language, expecting `~e'"
|
||||||
(syntax->datum #'lang) reqlang))]
|
(syntax->datum #'lang) reqlang))]
|
||||||
|
|
|
@ -806,7 +806,7 @@ in a way that depends on the setting of @racket[(sandbox-output)] or
|
||||||
|
|
||||||
@defproc[(get-uncovered-expressions [evaluator (any/c . -> . any)]
|
@defproc[(get-uncovered-expressions [evaluator (any/c . -> . any)]
|
||||||
[prog? any/c #t]
|
[prog? any/c #t]
|
||||||
[src any/c 'program])
|
[src any/c _default-src])
|
||||||
(listof syntax?)]{
|
(listof syntax?)]{
|
||||||
|
|
||||||
Retrieves uncovered expression from an evaluator, as longs as the
|
Retrieves uncovered expression from an evaluator, as longs as the
|
||||||
|
@ -828,11 +828,11 @@ program to ensure that your tests cover the whole code.
|
||||||
|
|
||||||
The second optional argument, @racket[src], specifies that the result
|
The second optional argument, @racket[src], specifies that the result
|
||||||
should be filtered to hold only @tech{syntax objects} whose source
|
should be filtered to hold only @tech{syntax objects} whose source
|
||||||
matches @racket[src]. The default, @racket['program], is the source
|
matches @racket[src]. The default is the source that was used in the
|
||||||
associated with the input program by the default
|
program code, if there was one. Note that @racket['program] is used as
|
||||||
@racket[sandbox-reader]---which provides only @tech{syntax objects}
|
the source value if the input program was given as S-expressions or as a
|
||||||
from the input program (and not from required modules or expressions
|
string (and in these cases it will be the default for filtering). If given
|
||||||
that were passed to the evaluator). A @racket[#f] avoids filtering.
|
@racket[#f], the result is the unfiltered list of expressions.
|
||||||
|
|
||||||
The resulting list of @tech{syntax objects} has at most one expression
|
The resulting list of @tech{syntax objects} has at most one expression
|
||||||
for each position and span. Thus, the contents may be unreliable, but
|
for each position and span. Thus, the contents may be unreliable, but
|
||||||
|
|
Loading…
Reference in New Issue
Block a user