Added support for collecting test coverage of metafunctions
svn: r16193
This commit is contained in:
parent
b50c10efa2
commit
f58882b4d1
|
@ -727,8 +727,14 @@
|
||||||
body-code)])
|
body-code)])
|
||||||
(cond
|
(cond
|
||||||
[really-matched
|
[really-matched
|
||||||
(when (relation-coverage)
|
(for-each
|
||||||
(cover-case case-id name (relation-coverage)))
|
(λ (c)
|
||||||
|
(let ([r (coverage-relation c)])
|
||||||
|
(when (and (reduction-relation? r)
|
||||||
|
(memf (λ (r) (eq? case-id (rewrite-proc-id r)))
|
||||||
|
(reduction-relation-make-procs r)))
|
||||||
|
(cover-case case-id c))))
|
||||||
|
(relation-coverage))
|
||||||
(loop (cdr mtchs)
|
(loop (cdr mtchs)
|
||||||
(map/mt (λ (x) (list name (f x))) really-matched acc))]
|
(map/mt (λ (x) (list name (f x))) really-matched acc))]
|
||||||
[else
|
[else
|
||||||
|
@ -933,27 +939,41 @@
|
||||||
(rewrite-proc-lhs child-make-proc)
|
(rewrite-proc-lhs child-make-proc)
|
||||||
(rewrite-proc-id child-make-proc)))
|
(rewrite-proc-id child-make-proc)))
|
||||||
|
|
||||||
(define relation-coverage (make-parameter #f))
|
(define relation-coverage (make-parameter null))
|
||||||
|
|
||||||
(define (cover-case id name cov)
|
(define (cover-case id cov)
|
||||||
(hash-update! (coverage-unwrap cov) id
|
(hash-update! (coverage-counts cov) id
|
||||||
(λ (c) (cons (car c) (add1 (cdr c))))
|
(λ (c) (cons (car c) (add1 (cdr c))))))
|
||||||
(λ () (raise-user-error
|
|
||||||
'relation-coverage
|
|
||||||
"coverage structure not initilized for this relation"))))
|
|
||||||
|
|
||||||
(define (covered-cases cov)
|
(define (covered-cases cov)
|
||||||
(hash-map (coverage-unwrap cov) (λ (k v) v)))
|
(hash-map (coverage-counts cov) (λ (k v) v)))
|
||||||
|
|
||||||
(define-struct coverage (unwrap))
|
(define-struct coverage (relation counts))
|
||||||
|
|
||||||
(define (fresh-coverage relation)
|
(define-syntax (fresh-coverage stx)
|
||||||
(let ([h (make-hasheq)])
|
(syntax-case stx ()
|
||||||
(for-each
|
[(name subj-stx)
|
||||||
(λ (rwp)
|
(with-syntax ([subj
|
||||||
(hash-set! h (rewrite-proc-id rwp) (cons (or (rewrite-proc-name rwp) "unnamed") 0)))
|
(cond [(and (identifier? (syntax subj-stx))
|
||||||
(reduction-relation-make-procs relation))
|
(let ([tf (syntax-local-value (syntax subj-stx) (λ () #f))])
|
||||||
(make-coverage h)))
|
(and (term-fn? tf) (term-fn-get-id tf))))
|
||||||
|
=> values]
|
||||||
|
[else (syntax (let ([r subj-stx])
|
||||||
|
(if (reduction-relation? r)
|
||||||
|
r
|
||||||
|
(raise-type-error 'name "reduction-relation" r))))])])
|
||||||
|
(syntax
|
||||||
|
(let ([h (make-hasheq)])
|
||||||
|
(cond [(metafunc-proc? subj)
|
||||||
|
(for-each
|
||||||
|
(λ (c) (hash-set! h (metafunc-case-id c) (cons (metafunc-case-src-loc c) 0)))
|
||||||
|
(metafunc-proc-cases subj))]
|
||||||
|
[(reduction-relation? subj)
|
||||||
|
(for-each
|
||||||
|
(λ (rwp)
|
||||||
|
(hash-set! h (rewrite-proc-id rwp) (cons (or (rewrite-proc-name rwp) (rewrite-proc-lhs-src rwp)) 0)))
|
||||||
|
(reduction-relation-make-procs subj))])
|
||||||
|
(make-coverage subj h))))]))
|
||||||
|
|
||||||
(define-syntax (test-match stx)
|
(define-syntax (test-match stx)
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
|
@ -1005,7 +1025,7 @@
|
||||||
|
|
||||||
(define-struct metafunction (proc))
|
(define-struct metafunction (proc))
|
||||||
|
|
||||||
(define-struct metafunc-case (cp rhs lhs-pat src-loc))
|
(define-struct metafunc-case (cp rhs lhs-pat src-loc id))
|
||||||
|
|
||||||
(define-syntax (in-domain? stx)
|
(define-syntax (in-domain? stx)
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
|
@ -1196,7 +1216,7 @@
|
||||||
rg-cp-let-bindings ... ...)
|
rg-cp-let-bindings ... ...)
|
||||||
(let ([cases (map (λ (pat rhs-fn rg-lhs src)
|
(let ([cases (map (λ (pat rhs-fn rg-lhs src)
|
||||||
(make-metafunc-case
|
(make-metafunc-case
|
||||||
(compile-pattern lang pat #t) rhs-fn rg-lhs src))
|
(compile-pattern lang pat #t) rhs-fn rg-lhs src (gensym)))
|
||||||
sc
|
sc
|
||||||
(list rhs-fns ...)
|
(list rhs-fns ...)
|
||||||
`(rg-side-conditions-rewritten ...)
|
`(rg-side-conditions-rewritten ...)
|
||||||
|
@ -1398,6 +1418,16 @@
|
||||||
(wrap
|
(wrap
|
||||||
(letrec ([cache (make-hash)]
|
(letrec ([cache (make-hash)]
|
||||||
[not-in-cache (gensym)]
|
[not-in-cache (gensym)]
|
||||||
|
[log-coverage (λ (id)
|
||||||
|
(when id
|
||||||
|
(for-each
|
||||||
|
(λ (c)
|
||||||
|
(let ([r (coverage-relation c)])
|
||||||
|
(when (and (metafunc-proc? r)
|
||||||
|
(findf (λ (c) (eq? id (metafunc-case-id c)))
|
||||||
|
(metafunc-proc-cases r)))
|
||||||
|
(cover-case id c))))
|
||||||
|
(relation-coverage))))]
|
||||||
[metafunc
|
[metafunc
|
||||||
(λ (exp)
|
(λ (exp)
|
||||||
(let ([cache-ref (hash-ref cache exp not-in-cache)])
|
(let ([cache-ref (hash-ref cache exp not-in-cache)])
|
||||||
|
@ -1414,12 +1444,13 @@
|
||||||
[(null? cases)
|
[(null? cases)
|
||||||
(if relation?
|
(if relation?
|
||||||
(begin
|
(begin
|
||||||
(hash-set! cache exp #f)
|
(hash-set! cache exp (cons #f #f))
|
||||||
#f)
|
#f)
|
||||||
(redex-error name "no clauses matched for ~s" `(,name . ,exp)))]
|
(redex-error name "no clauses matched for ~s" `(,name . ,exp)))]
|
||||||
[else
|
[else
|
||||||
(let ([pattern (metafunc-case-cp (car cases))]
|
(let ([pattern (metafunc-case-cp (car cases))]
|
||||||
[rhs (metafunc-case-rhs (car cases))])
|
[rhs (metafunc-case-rhs (car cases))]
|
||||||
|
[id (metafunc-case-id (car cases))])
|
||||||
(let ([mtchs (match-pattern pattern exp)])
|
(let ([mtchs (match-pattern pattern exp)])
|
||||||
(cond
|
(cond
|
||||||
[(not mtchs) (loop (cdr cases) (+ num 1))]
|
[(not mtchs) (loop (cdr cases) (+ num 1))]
|
||||||
|
@ -1431,7 +1462,8 @@
|
||||||
(redex-error name "codomain test failed for ~s, call was ~s" ans `(,name ,@exp)))
|
(redex-error name "codomain test failed for ~s, call was ~s" ans `(,name ,@exp)))
|
||||||
(cond
|
(cond
|
||||||
[ans
|
[ans
|
||||||
(hash-set! cache exp #t)
|
(hash-set! cache exp (cons #t id))
|
||||||
|
(log-coverage id)
|
||||||
#t]
|
#t]
|
||||||
[else
|
[else
|
||||||
(loop (cdr cases) (+ num 1))]))]
|
(loop (cdr cases) (+ num 1))]))]
|
||||||
|
@ -1459,10 +1491,12 @@
|
||||||
"codomain test failed for ~s, call was ~s"
|
"codomain test failed for ~s, call was ~s"
|
||||||
ans
|
ans
|
||||||
`(,name ,@exp)))
|
`(,name ,@exp)))
|
||||||
(hash-set! cache exp ans)
|
(hash-set! cache exp (cons ans id))
|
||||||
|
(log-coverage id)
|
||||||
ans)]))])))]))]
|
ans)]))])))]))]
|
||||||
[else
|
[else
|
||||||
cache-ref])))]
|
(log-coverage (cdr cache-ref))
|
||||||
|
(car cache-ref)])))]
|
||||||
[ot (current-trace-print-args)]
|
[ot (current-trace-print-args)]
|
||||||
[traced-metafunc (lambda (exp)
|
[traced-metafunc (lambda (exp)
|
||||||
(if (or (eq? (current-traced-metafunctions) 'all)
|
(if (or (eq? (current-traced-metafunctions) 'all)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
(module tl-test mzscheme
|
(module tl-test scheme
|
||||||
(require "../reduction-semantics.ss"
|
(require "../reduction-semantics.ss"
|
||||||
"test-util.ss"
|
"test-util.ss"
|
||||||
(only "matcher.ss" make-bindings make-bind)
|
(only-in "matcher.ss" make-bindings make-bind)
|
||||||
scheme/match
|
scheme/match
|
||||||
"struct.ss")
|
"struct.ss")
|
||||||
|
|
||||||
|
@ -1616,33 +1616,121 @@
|
||||||
[else #f])
|
[else #f])
|
||||||
#t))
|
#t))
|
||||||
|
|
||||||
|
(let ([< (λ (c d) (string<? (car c) (car d)))])
|
||||||
|
|
||||||
|
(let* ([R (reduction-relation
|
||||||
|
empty-language
|
||||||
|
(--> number (q ,(add1 (term number)))
|
||||||
|
(side-condition (odd? (term number)))
|
||||||
|
side-condition)
|
||||||
|
(--> 1 4 plain)
|
||||||
|
(==> 2 t
|
||||||
|
shortcut)
|
||||||
|
with
|
||||||
|
[(--> (q a) b)
|
||||||
|
(==> a b)])]
|
||||||
|
[c (make-coverage R)])
|
||||||
|
(parameterize ([relation-coverage (list c)])
|
||||||
|
(apply-reduction-relation R 4)
|
||||||
|
(test (sort (covered-cases c) <)
|
||||||
|
'(("plain" . 0) ("shortcut" . 0) ("side-condition" . 0)))
|
||||||
|
|
||||||
|
(apply-reduction-relation R 3)
|
||||||
|
(test (sort (covered-cases c) <)
|
||||||
|
'(("plain" . 0) ("shortcut" . 0) ("side-condition" . 1)))
|
||||||
|
|
||||||
|
(apply-reduction-relation* R 1)
|
||||||
|
(test (sort (covered-cases c) <)
|
||||||
|
'(("plain" . 1) ("shortcut" . 1) ("side-condition" . 2)))))
|
||||||
|
|
||||||
|
(let* ([S (reduction-relation
|
||||||
|
empty-language
|
||||||
|
(--> 1 1 uno))]
|
||||||
|
[S+ (extend-reduction-relation
|
||||||
|
S empty-language
|
||||||
|
(--> 2 2 dos))])
|
||||||
|
(let ([c (make-coverage S+)])
|
||||||
|
(parameterize ([relation-coverage (list c)])
|
||||||
|
(apply-reduction-relation S (term 1))
|
||||||
|
(test (sort (covered-cases c) <)
|
||||||
|
'(("dos" . 0) ("uno" . 1)))))
|
||||||
|
(let ([c (make-coverage S)])
|
||||||
|
(parameterize ([relation-coverage (list c)])
|
||||||
|
(apply-reduction-relation S+ (term 1))
|
||||||
|
(test (sort (covered-cases c) <)
|
||||||
|
'(("uno" . 1))))))
|
||||||
|
|
||||||
|
(let* ([T (reduction-relation empty-language (--> any any))]
|
||||||
|
[c (make-coverage T)])
|
||||||
|
(parameterize ([relation-coverage (list c)])
|
||||||
|
(apply-reduction-relation T (term q))
|
||||||
|
(test (and (regexp-match #px"tl-test.ss:\\d+:\\d+" (caar (covered-cases c))) #t)
|
||||||
|
#t))))
|
||||||
|
|
||||||
(let* ([R (reduction-relation
|
(let* ([R (reduction-relation
|
||||||
empty-language
|
empty-language
|
||||||
(--> number (q ,(add1 (term number)))
|
(--> any any id))]
|
||||||
(side-condition (odd? (term number)))
|
|
||||||
side-condition)
|
|
||||||
(--> 1 4)
|
|
||||||
(==> 2 t
|
|
||||||
shortcut)
|
|
||||||
with
|
|
||||||
[(--> (q a) b)
|
|
||||||
(==> a b)])]
|
|
||||||
[c (make-coverage R)]
|
[c (make-coverage R)]
|
||||||
[< (λ (c d) (string<? (car c) (car d)))])
|
[c* (make-coverage R)])
|
||||||
(parameterize ([relation-coverage c])
|
(parameterize ([relation-coverage (list c c*)])
|
||||||
(apply-reduction-relation R 4)
|
(apply-reduction-relation R 4)
|
||||||
(test (sort (covered-cases c) <)
|
(test (covered-cases c) '(("id" . 1)))
|
||||||
'(("shortcut" . 0) ("side-condition" . 0) ("unnamed" . 0)))
|
(test (covered-cases c*) '(("id" . 1)))))
|
||||||
|
|
||||||
(apply-reduction-relation R 3)
|
|
||||||
(test (sort (covered-cases c) <)
|
|
||||||
'(("shortcut" . 0) ("side-condition" . 1) ("unnamed" . 0)))
|
|
||||||
|
|
||||||
(apply-reduction-relation* R 1)
|
|
||||||
(test (sort (covered-cases c) <)
|
|
||||||
'(("shortcut" . 1) ("side-condition" . 2) ("unnamed" . 1)))))
|
|
||||||
|
|
||||||
|
|
||||||
|
(let* ([< (λ (c d)
|
||||||
|
(let ([line-no (compose
|
||||||
|
string->number
|
||||||
|
second
|
||||||
|
(curry regexp-match #px".*:(\\d+):\\d+"))])
|
||||||
|
(< (line-no (car c)) (line-no (car d)))))]
|
||||||
|
[src-ok? (curry regexp-match? #px"tl-test.ss:\\d+:\\d+")]
|
||||||
|
[sorted-counts (λ (cc) (map cdr (sort (covered-cases cc) <)))])
|
||||||
|
(define-metafunction empty-language
|
||||||
|
[(f 1) 1]
|
||||||
|
[(f 2) 2])
|
||||||
|
(define-metafunction/extension f empty-language
|
||||||
|
[(g 3) 3])
|
||||||
|
(define-relation empty-language
|
||||||
|
[(R number)
|
||||||
|
,(even? (term number))]
|
||||||
|
[(R number)
|
||||||
|
,(= 3 (term number))])
|
||||||
|
|
||||||
|
(let ([fc (make-coverage f)]
|
||||||
|
[rc (make-coverage (reduction-relation empty-language (--> any any)))])
|
||||||
|
(parameterize ([relation-coverage (list rc fc)])
|
||||||
|
(term (f 2))
|
||||||
|
(test (andmap (compose src-ok? car) (covered-cases fc))
|
||||||
|
#t)
|
||||||
|
(test (sorted-counts fc) '(0 1))
|
||||||
|
|
||||||
|
(term (f 1))
|
||||||
|
(term (f 1))
|
||||||
|
(test (sorted-counts fc) '(2 1))))
|
||||||
|
|
||||||
|
(let ([c (make-coverage f)])
|
||||||
|
(parameterize ([relation-coverage (list c)])
|
||||||
|
(term (g 1))
|
||||||
|
(test (sorted-counts c) '(1 0))))
|
||||||
|
(let ([c (make-coverage g)])
|
||||||
|
(parameterize ([relation-coverage (list c)])
|
||||||
|
(term (f 1))
|
||||||
|
(test (sorted-counts c) '(1 0 0))))
|
||||||
|
|
||||||
|
(let ([c (make-coverage R)])
|
||||||
|
(parameterize ([relation-coverage (list c)])
|
||||||
|
(term (R 2))
|
||||||
|
(term (R 3))
|
||||||
|
(term (R 5))
|
||||||
|
(test (sorted-counts c) '(1 1))))
|
||||||
|
|
||||||
|
(let ([c (make-coverage f)]
|
||||||
|
[c* (make-coverage f)])
|
||||||
|
(parameterize ([relation-coverage (list c* c)])
|
||||||
|
(term (f 1))
|
||||||
|
(test (sorted-counts c) '(1 0))
|
||||||
|
(test (sorted-counts c*) '(1 0)))))
|
||||||
|
|
||||||
;
|
;
|
||||||
;
|
;
|
||||||
;
|
;
|
||||||
|
|
|
@ -1062,47 +1062,55 @@ counters so that next time this function is called, it
|
||||||
prints the test results for the next round of tests.
|
prints the test results for the next round of tests.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(make-coverage [r reduction-relation?]) coverage?]{
|
@defform/subs[(make-coverage subject)
|
||||||
Constructs a structure to contain the per-case test coverage of
|
([subject (code:line metafunction)
|
||||||
the relation @scheme[r]. Use with @scheme[relation-coverage]
|
(code:line relation-expr)])]{
|
||||||
and @scheme[covered-cases].
|
Constructs a structure (recognized by @scheme[coverage?])
|
||||||
|
to contain per-case test coverage of the supplied metafunction
|
||||||
|
or reduction relation. Use with @scheme[relation-coverage] and
|
||||||
|
@scheme[covered-cases].
|
||||||
}
|
}
|
||||||
|
|
||||||
@defproc[(coverage? [v any/c]) boolean?]{
|
@defproc[(coverage? [v any/c]) boolean?]{
|
||||||
Returns @scheme[#t] for a value produced by @scheme[make-coverage]
|
Returns @scheme[#t] for a value produced by @scheme[make-coverage]
|
||||||
and @scheme[#f] for any other.}
|
and @scheme[#f] for any other.}
|
||||||
|
|
||||||
@defparam[relation-coverage c (or/c false/c coverage?)]{
|
@defparam[relation-coverage tracked (listof coverage?)]{
|
||||||
When @scheme[c] is a @scheme[coverage] structure, rather than
|
Redex populates the coverage records in @scheme[tracked] (default @scheme[null]),
|
||||||
@scheme[#f] (the default), procedures such as
|
counting the times that tests exercise each case of the associated metafunction
|
||||||
@scheme[apply-reduction-relation], @scheme[traces], etc. count
|
and relations.}
|
||||||
the number applications of each case of the
|
|
||||||
@scheme[reduction-relation], storing the results in @scheme[c].
|
|
||||||
}
|
|
||||||
|
|
||||||
@defproc[(covered-cases
|
@defproc[(covered-cases
|
||||||
[c coverage?])
|
[c coverage?])
|
||||||
(listof (cons/c string? natural-number/c))]{
|
(listof (cons/c string? natural-number/c))]{
|
||||||
Extracts the coverage information recorded in @scheme[c], producing
|
Extracts the coverage information recorded in @scheme[c], producing
|
||||||
an association list mapping names to application counts.}
|
an association list mapping names (or source locations, in the case of
|
||||||
|
metafunctions or unnamed reduction-relation cases) to application counts.}
|
||||||
|
|
||||||
@examples[
|
@examples[
|
||||||
#:eval redex-eval
|
#:eval redex-eval
|
||||||
(define-language empty-lang)
|
(define-language empty-lang)
|
||||||
|
|
||||||
|
(define-metafunction empty-lang
|
||||||
|
[(plus number_1 number_2)
|
||||||
|
,(+ (term number_1) (term number_2))])
|
||||||
|
|
||||||
(define equals
|
(define equals
|
||||||
(reduction-relation
|
(reduction-relation
|
||||||
empty-lang
|
empty-lang
|
||||||
(--> (+) 0 "zero")
|
(--> (+) 0 "zero")
|
||||||
(--> (+ number) number)
|
(--> (+ number) number)
|
||||||
(--> (+ number_1 number_2 number ...)
|
(--> (+ number_1 number_2 number ...)
|
||||||
(+ ,(+ (term number_1) (term number_2))
|
(+ (plus number_1 number_2)
|
||||||
number ...)
|
number ...)
|
||||||
"add")))
|
"add")))
|
||||||
(let ([coverage (make-coverage equals)])
|
(let ([equals-coverage (make-coverage equals)]
|
||||||
(parameterize ([relation-coverage coverage])
|
[plus-coverage (make-coverage plus)])
|
||||||
|
(parameterize ([relation-coverage (list equals-coverage
|
||||||
|
plus-coverage)])
|
||||||
(apply-reduction-relation* equals (term (+ 1 2 3)))
|
(apply-reduction-relation* equals (term (+ 1 2 3)))
|
||||||
(covered-cases coverage)))]
|
(values (covered-cases equals-coverage)
|
||||||
|
(covered-cases plus-coverage))))]
|
||||||
|
|
||||||
@defform/subs[(generate-term language @#,ttpattern size-exp kw-args ...)
|
@defform/subs[(generate-term language @#,ttpattern size-exp kw-args ...)
|
||||||
([kw-args (code:line #:attempts attempts-expr)
|
([kw-args (code:line #:attempts attempts-expr)
|
||||||
|
@ -1131,7 +1139,7 @@ argument @scheme[retries-expr] (default @scheme[100]) bounds the number of times
|
||||||
@scheme[generate-term] is unable to produce a satisfying term after
|
@scheme[generate-term] is unable to produce a satisfying term after
|
||||||
@scheme[retries-expr] attempts, it raises an exception recognized by
|
@scheme[retries-expr] attempts, it raises an exception recognized by
|
||||||
@scheme[exn:fail:redex:generation-failure?].}
|
@scheme[exn:fail:redex:generation-failure?].}
|
||||||
|
|
||||||
@defform/subs[(redex-check language @#,ttpattern property-expr kw-arg ...)
|
@defform/subs[(redex-check language @#,ttpattern property-expr kw-arg ...)
|
||||||
([kw-arg (code:line #:attempts attempts-expr)
|
([kw-arg (code:line #:attempts attempts-expr)
|
||||||
(code:line #:source metafunction)
|
(code:line #:source metafunction)
|
||||||
|
|
|
@ -29,7 +29,8 @@
|
||||||
define-relation
|
define-relation
|
||||||
metafunction
|
metafunction
|
||||||
in-domain?
|
in-domain?
|
||||||
caching-enabled?)
|
caching-enabled?
|
||||||
|
make-coverage)
|
||||||
|
|
||||||
(provide (rename-out [test-match redex-match])
|
(provide (rename-out [test-match redex-match])
|
||||||
term-match
|
term-match
|
||||||
|
@ -69,6 +70,5 @@
|
||||||
(-> bindings? symbol? (-> any) any))]
|
(-> bindings? symbol? (-> any) any))]
|
||||||
[variable-not-in (any/c symbol? . -> . symbol?)]
|
[variable-not-in (any/c symbol? . -> . symbol?)]
|
||||||
[variables-not-in (any/c (listof symbol?) . -> . (listof symbol?))]
|
[variables-not-in (any/c (listof symbol?) . -> . (listof symbol?))]
|
||||||
[relation-coverage (parameter/c (or/c false/c coverage?))]
|
[relation-coverage (parameter/c (listof coverage?))]
|
||||||
[make-coverage (-> reduction-relation? coverage?)]
|
|
||||||
[covered-cases (-> coverage? (listof (cons/c string? natural-number/c)))])
|
[covered-cases (-> coverage? (listof (cons/c string? natural-number/c)))])
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
v4.2.3
|
||||||
|
|
||||||
|
* added support for collecting metafunction coverage, using the
|
||||||
|
'relation-coverage' parameter. This includes a backwards
|
||||||
|
incompatible change: the parameter's value is now a list of
|
||||||
|
coverage structures, to allow coverage collection for multiple
|
||||||
|
metafunctions and reduction relations at once.
|
||||||
|
|
||||||
|
* minor bug fixes
|
||||||
|
|
||||||
v4.2.2
|
v4.2.2
|
||||||
|
|
||||||
* minor bug fixes
|
* minor bug fixes
|
||||||
|
|
Loading…
Reference in New Issue
Block a user