#lang scheme (require "test-util.rkt" "../private/reduction-semantics.rkt" "../private/judgment-form.rkt" "../private/matcher.rkt" "../private/term.rkt" "../private/rg.rkt" "../private/keyword-macros.rkt" "../private/error.rkt") (define-namespace-anchor nsa) (define ns (namespace-anchor->namespace nsa)) (reset-count) ;; to-table : hash-table -> assoc ;; extracts the hash-table's mapping in a deterministic way (define (to-table ht) (sort (hash-map ht cons) (λ (x y) (string<=? (format "~a" (car x)) (format "~a" (car y)))))) ;; output : (-> (-> void) string) (define (output thunk) (let ([p (open-output-string)]) (parameterize ([current-output-port p]) (unless (void? (thunk)) (error 'output "expected void result"))) (begin0 (get-output-string p) (close-output-port p)))) (define-syntax (test-contract-violation/client stx) (syntax-case stx () [(form expr) (syntax/loc stx (form "" expr))] [(_ name expr) (syntax/loc stx (test-contract-violation (output (λ () expr)) #:blaming "rg-test" #:message name #:extract (match-lambda [(exn:fail:redex:test _ _ (? exn:fail:contract:blame? e) _) e] [x x])))])) (let () (define-language lc (e x (e e) (λ (x) e)) (x variable)) (let ([bc (find-base-cases lc)]) (test (to-table (base-cases-non-cross bc)) '((e . (1 2 2)) (x . (0)))) (test (to-table (base-cases-cross bc)) '((e-e . (0 2 2 1)) (x-e . (1 2 2 2 2)) (x-x . (0)))))) (let () (define-language lang (e (e e))) (let ([bc (find-base-cases lang)]) (test (to-table (base-cases-non-cross bc)) '((e . (inf)))) (test (to-table (base-cases-cross bc)) '((e-e . (0 inf inf)))))) (let () (define-language lang (a 1 2 3) (b a (a_1 b_!_1))) (let ([bc (find-base-cases lang)]) (test (to-table (base-cases-non-cross bc)) '((a . (0 0 0)) (b . (1 2)))) (test (to-table (base-cases-cross bc)) '((a-a . (0)) (a-b . (1 2 2)) (b-b . (0 1)))))) (let () (define-language lc (e (e e ...) (+ e e) x v) (v (λ (x) e) number) (x variable)) (let ([bc (find-base-cases lc)]) (test (to-table (base-cases-non-cross bc)) '((e . (2 2 1 1)) (v . (2 0)) (x . (0)))) (test (to-table (base-cases-cross bc)) '((e-e . (0 2 2 2 2 2)) (e-v . (1)) (v-e . (2 2 2 2 1)) (v-v . (0 2)) (x-e . (2 2 2 2 1 3)) (x-v . (2 2)) (x-x . (0)))))) (let () (define-language L (x (variable-prefix x) (variable-except y) (name x 1) (name y 1)) (y 12)) (test (hash-ref (base-cases-non-cross (find-base-cases L)) 'x) '(0 0 0 0))) (define (make-random . nums) (let ([nums (box nums)]) (λ ([m +inf.0]) (cond [(null? (unbox nums)) (error 'make-random "out of numbers")] [(>= (car (unbox nums)) m) (error 'make-random "number too large")] [else (begin0 (car (unbox nums)) (set-box! nums (cdr (unbox nums))))])))) (test (pick-from-list '(a b c) (make-random 1)) 'b) (test (pick-number 24 (make-random 1/5)) 3) (test (pick-number 224 (make-random 0 0 1/5)) -5) (test (pick-number 524 (make-random 0 0 1 1/5 1/5)) 3/4) (test (pick-number 1624 (make-random 0 0 0 .5 1 .5)) 3.0) (test (pick-number 2624 (make-random 0 0 0 0 1 1 1/5 1/5 2 .5 0 .5)) (make-rectangular 7/8 -3.0)) (test (pick-natural 224 (make-random 1/5)) 5) (test (pick-integer 900 (make-random 0 0 1/5)) -7) (test (pick-real 9000 (make-random 0 0 0 .5 1 1/8)) 11.0) (let* ([lits '("bcd" "cbd")]) (test (pick-char 0 (make-random 0 0)) #\A) (test (pick-char 0 (make-random 2 1)) #\c) (test (pick-char 1000 (make-random 1 25 0)) #\Z) (test (pick-char 1000 (make-random 0 65)) #\a) (test (pick-char 1500 (make-random 0 1 65)) #\a) (test (pick-char 1500 (make-random 0 0 3)) #\⇒) (test (pick-char 2500 (make-random 0 0 1 3)) #\⇒) (test (pick-char 2500 (make-random 0 0 0 1)) (integer->char #x4E01)) (test (pick-char 1000 (make-random 0 (- (char->integer #\_) #x20))) #\`) (test (random-string lits 3 0 (make-random 0 1)) "cbd") (test (random-string lits 3 0 (make-random 1 0 1 1 1 2 1)) "abc") (test (pick-string lits 0 (make-random .5 1 0 1 1 1 2 1)) "abc") (test (pick-var lits 0 (make-random .01 1 0 1 1 1 2 1)) 'abc)) (define-syntax raised-exn-msg (syntax-rules () [(_ expr) (raised-exn-msg exn:fail? expr)] [(_ exn? expr) (with-handlers ([exn? exn-message]) (begin expr (let () (define-struct exn-not-raised ()) (make-exn-not-raised))))])) (define (patterns . selectors) (map (curry compose list) selectors)) (define (iterator name items) (let ([bi (box items)]) (λ () (if (null? (unbox bi)) (error name "empty") (begin0 (car (unbox bi)) (set-box! bi (cdr (unbox bi)))))))) (let ([iter (iterator 'test-iterator '(a b))]) (test (iter) 'a) (test (iter) 'b) (test (raised-exn-msg (iter)) #rx"empty")) (define (decisions #:var [var pick-var] #:nt [nt pick-nts] #:str [str pick-string] #:num [num pick-number] #:nat [nat pick-natural] #:int [int pick-integer] #:real [real pick-real] #:any [any pick-any] #:seq [seq pick-sequence-length]) (define-syntax decision (syntax-rules () [(_ d) (if (procedure? d) (λ () d) (iterator (quote d) d))])) (unit (import) (export decisions^) (define next-variable-decision (decision var)) (define next-non-terminal-decision (decision nt)) (define next-number-decision (decision num)) (define next-natural-decision (decision nat)) (define next-integer-decision (decision int)) (define next-real-decision (decision real)) (define next-string-decision (decision str)) (define next-any-decision (decision any)) (define next-sequence-decision (decision seq)))) (define-syntax generate-term/decisions (syntax-rules () [(_ lang pat size attempt decisions) (parameterize ([generation-decisions decisions]) (generate-term lang pat size #:attempt-num attempt))])) (let () (define-language lc (e (e e) x (λ (x) e)) (x (variable-except λ))) ;; Generate (λ (x) x) (test (generate-term/decisions lc e 1 0 (decisions #:var (list (λ _ 'x) (λ _'x)) #:nt (patterns third first first first))) '(λ (x) x)) ;; Generate pattern that's not a non-terminal (test (generate-term/decisions lc (x x x_1 x_1) 1 0 (decisions #:var (list (λ _ 'x) (λ _ 'y)))) '(x x y y)) ; After choosing (e e), size decremented forces each e to x. (test (generate-term/decisions lc e 1 0 (decisions #:nt (patterns first) #:var (list (λ _ 'x) (λ _ 'y)))) '(x y))) (let () (define-language L (n 1)) (test ((generate-term L n) 0) 1) (test ((generate-term L n) 0 #:retries 0) 1) (test ((generate-term L n) 0 #:attempt-num 0) 1) (test (with-handlers ([exn:fail:syntax? exn-message]) (parameterize ([current-namespace ns]) (expand #'(generate-term M n)))) #rx"generate-term: expected an identifier defined by define-language([\n ]+in: M)?(\n|$)") (test-contract-violation/client (generate-term L n 1.5)) (test-contract-violation/client (generate-term L n 1 #:retries .5)) (test-contract-violation/client (generate-term L n 1 #:attempt-num .5)) (test-contract-violation/client "#:source" (generate-term #:source 'not-a-reduction-relation))) (let ([set-rand-2 (λ (to-be prg) (parameterize ([current-pseudo-random-generator prg]) (random-seed (case to-be [(0) 5] [(1) 0]))))]) (set-rand-2 0 (current-pseudo-random-generator)) (test (random 2) 0) (set-rand-2 1 (current-pseudo-random-generator)) (test (random 2) 1) (define-language L) (define R (reduction-relation L (--> a 1) (--> b 2))) (define-metafunction L [(F a) 1] [(F b) 2]) (set-rand-2 0 (redex-pseudo-random-generator)) (test (generate-term #:source R 0) 'a) (set-rand-2 1 (redex-pseudo-random-generator)) (test ((generate-term #:source R) 0) 'b) (set-rand-2 0 (redex-pseudo-random-generator)) (test ((generate-term #:source F) 0) '(a)) (set-rand-2 1 (redex-pseudo-random-generator)) (test (generate-term #:source F 0) '(b)) (let ([before (pseudo-random-generator->vector (redex-pseudo-random-generator))]) (generate-term L () 0) (test (pseudo-random-generator->vector (redex-pseudo-random-generator)) before))) ;; variable-except pattern (let () (define-language var (e (variable-except x y))) (test (generate-term/decisions var e 2 0 (decisions #:var (list (λ _ 'x) (λ _ 'y) (λ _ 'x) (λ _ 'z)))) 'z)) (let () (define-language L (n natural) (i integer) (r real)) (test (let ([n (generate-term L n 0 #:attempt-num 10000)]) (and (integer? n) (exact? n) (not (negative? n)))) #t) (test (generate-term/decisions L n 0 1 (decisions #:nat (λ (_) 42))) 42) (test (let ([i (generate-term L i 0 #:attempt-num 10000)]) (and (integer? i) (exact? i))) #t) (test (generate-term/decisions L i 0 1 (decisions #:int (λ (_) -42))) -42) (test (real? (generate-term L r 0 #:attempt-num 10000)) #t) (test (generate-term/decisions L r 0 1 (decisions #:real (λ (_) 4.2))) 4.2)) (let () (define-language lang (a (number number ... "foo" ... "bar" #t ...)) (b (number_1 ..._!_1 number_1 ..._1)) (c (variable_1 ..._1 number_2 ..._1)) (d (z_1 ... z_2 ..._!_1 (z_1 z_2) ...)) (e (n_1 ..._!_1 n_2 ..._!_1 (n_1 n_2) ..._3)) (f (n_1 ..._1 n_2 ..._2 n_2 ..._1)) (g (z_1 ..._!_1 z_2 ... (z_1 z_2) ...)) (n number) (q literal) (z 4)) (test (generate-term/decisions lang a 2 0 (decisions #:num (build-list 3 (λ (n) (λ (_) n))) #:seq (list (λ (_) 2) (λ (_) 3) (λ (_) 1)))) `(0 1 2 "foo" "foo" "foo" "bar" #t)) (test (generate-term/decisions lang b 5 0 (decisions #:seq (list (λ (_) 0)))) null) (test (generate-term/decisions lang c 5 0 (decisions #:seq (list (λ (_) 0)))) null) (test (generate-term/decisions lang d 5 0 (decisions #:seq (list (λ (_) 2)))) '(4 4 4 4 (4 4) (4 4))) (test (raised-exn-msg exn:fail:redex:generation-failure? (generate-term lang e 5 #:retries 42)) #rx"generate-term: unable to generate pattern .* in 42") (test (raised-exn-msg exn:fail:redex:generation-failure? (parameterize ([generation-decisions (decisions #:var (list (λ _ 'x) (λ _ 'x)))]) (generate-term lang (variable-except x) 5 #:retries 1))) #rx"generate-term: unable to generate pattern \\(variable-except x\\) in 1") (test (raised-exn-msg exn:fail:redex:generation-failure? (parameterize ([generation-decisions (decisions #:var (λ _ 'literal))]) (generate-term lang variable-not-otherwise-mentioned 5 #:retries 1))) #rx"generate-term: unable to generate pattern variable-not-otherwise-mentioned in 1") (test (generate-term/decisions lang f 5 0 (decisions #:seq (list (λ (_) 0)))) null) (test (generate-term/decisions lang ((0 ..._!_1) ... (1 ..._!_1) ...) 5 0 (decisions #:seq (list (λ (_) 2) (λ (_) 3) (λ (_) 4) (λ (_) 2) (λ (_) 3) (λ (_) 4) (λ (_) 2) (λ (_) 3) (λ (_) 4) (λ (_) 1) (λ (_) 3)))) '((0 0 0) (0 0 0 0) (1 1 1))) (test (generate-term/decisions lang ((0 ..._!_1) ... (1 ..._!_1) ...) 5 0 (decisions #:seq (list (λ (_) 2) (λ (_) 3) (λ (_) 4) (λ (_) 2) (λ (_) 3) (λ (_) 5)))) '((0 0 0) (0 0 0 0) (1 1 1) (1 1 1 1 1)))) (let () (define-language lang (e (variable-prefix pf))) (test (generate-term/decisions lang e 5 0 (decisions #:var (list (λ _ 'x)))) 'pfx)) (let () (define-language lang (x variable literal)) (test ((is-nt? lang) 'x) #t) (test ((is-nt? lang) 'y) #f)) (let () (define-language lang (e number (e_1 e_2 e e_1 e_2))) (test (generate-term/decisions lang e 5 0 (decisions #:nt (patterns second first first first) #:num (list (λ _ 2) (λ _ 3) (λ _ 4)))) '(2 3 4 2 3))) (let () (define-language lang (a (number_!_1 number_!_2 number_!_1)) (b (c_!_1 c_!_1 c_!_1)) (c 1 2)) (test (generate-term/decisions lang a 5 0 (decisions #:num (list (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 2)))) '(1 1 2)) (test (generate-term/decisions lang (number_!_1 number_!_2 number_!_1) 5 0 (decisions #:num (list (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 1) (λ _ 2)))) '(1 1 2)) (test (raised-exn-msg exn:fail:redex:generation-failure? (generate-term lang b 5000)) #rx"unable")) (let () (define-language lang (e string) (f foo bar)) (test (let/ec k (generate-term/decisions lang e 5 0 (decisions #:str (list (λ (l a) (k (sort l string<=?))))))) '("bar" "foo"))) (let () (define-language lang (a 43) (b (side-condition a_1 (odd? (term a_1)))) (c (side-condition a_1 (even? (term a_1)))) (e (side-condition (x_1 x_!_2 x_!_2) (not (eq? (term x_1) 'x)))) (x variable)) (test (generate-term lang b 5) 43) (test (generate-term lang (side-condition a (odd? (term a))) 5) 43) (test (raised-exn-msg exn:fail:redex:generation-failure? (generate-term lang c 5)) #rx"unable to generate pattern") (test (let/ec k (generate-term lang (number_1 (side-condition 7 (k (term number_1)))) 5)) 'number_1) (test ; mismatch patterns work with side-condition failure/retry (generate-term/decisions lang e 5 0 (decisions #:var (list (λ _ 'x) (λ _ 'x) (λ _ 'y) (λ _ 'y) (λ _ 'x) (λ _ 'y)))) '(y x y)) (test ; generate compiles side-conditions in pattern (generate-term/decisions lang (side-condition x_1 (not (eq? (term x_1) 'x))) 5 0 (decisions #:var (list (λ _ 'x) (λ _ 'y)))) 'y)) (let () (define-language lang (a (name x b)) (b 4) (c (side-condition (name x d) (zero? (term x)))) (d 2 1 0) (e ((side-condition (name d_1 d) (zero? (term d_1))) d_1))) (test (generate-term lang a 5) 4) (test (generate-term lang c 5) 0) (test (generate-term lang e 5) '(0 0))) (let () (define-language lang (a number (+ a a)) (A hole (+ a A) (+ A a)) (C hole) (e ((in-hole (in-hole f (number_1 hole)) number_1) number_1)) (f (in-hole C (number_1 hole))) (g (in-hole (side-condition (hole number_1) (zero? (term number_1))) number_2)) (h ((in-hole i number_1) number_1)) (i (number_1 (in-hole j (number_1 hole)))) (j (in-hole (hole number_1) (number_1 hole))) (x variable) (y variable)) (test (generate-term/decisions lang (in-hole A number ) 5 0 (decisions #:nt (patterns second second first first third first second first first) #:num (build-list 5 (λ (x) (λ (_) x))))) '(+ (+ 1 2) (+ 0 (+ 3 4)))) (test (let/ec k (generate-term lang (side-condition (in-hole C_1 1) (k (term C_1))) 5)) (term hole)) (test (generate-term lang (in-hole (in-hole (1 hole) hole) 5) 5) '(1 5)) (test (generate-term lang (hole 4) 5) (term (hole 4))) (test (generate-term/decisions lang (variable_1 (in-hole C variable_1)) 5 0 (decisions #:var (list (λ _ 'x) (λ _ 'y) (λ _ 'x)))) '(x x)) (test (generate-term/decisions lang (variable_!_1 (in-hole C variable_!_1)) 5 0 (decisions #:var (list (λ _ 'x) (λ _ 'x) (λ _ 'x) (λ _ 'y)))) '(x y)) (test (generate-term/decisions lang e 5 0 (decisions #:num (list (λ _ 1) (λ _ 2)))) '((2 (1 1)) 1)) (test (generate-term/decisions lang g 5 0 (decisions #:num (list (λ _ 1) (λ _ 2) (λ _ 1) (λ _ 0)))) '(1 0)) (test (generate-term/decisions lang h 5 0 (decisions #:num (list (λ _ 1) (λ _ 2) (λ _ 3)))) '((2 ((3 (2 1)) 3)) 1))) (let () (define-language L (C (c hole)) (D (d hole)) (E (e hole)) (F (f hole)) (p (in-hole (hole hole) 4)) (q (in-hole (hole ... hole) 4))) (test (generate-term L (in-hole 3 4) 5) 3) (test (raised-exn-msg exn:fail? (test-match L p (generate-term L p 5))) #rx"two holes") (test (raised-exn-msg exn:fail? (test-match L q (generate-term/decisions L q 5 0 (decisions #:seq (list (λ (_) 1)))))) #rx"two holes") (let ([bindings #f]) (test (generate-term L (side-condition (name CDEF (in-hole (name CDE (in-hole (name CD (in-hole C D)) E)) F)) (set! bindings (term (C D E F CD CDE CDEF)))) 0) (term (c (d (e (f hole)))))) (test bindings (term ((c hole) (d hole) (e hole) (f hole) (c (d hole)) (c (d (e hole))) (c (d (e (f hole)))))))) (test (let/ec return (generate-term L (side-condition (name C (hide-hole hole)) (return (term (in-hole C 1)))) 0)) (term hole)) (test (generate-term L (in-hole ((hide-hole (in-hole hole hole)) hole) 1) 0) (term (hole 1)))) (let () (define-language lc (e (e e) (+ e e) x v) (v (λ (x) e) number) (x variable-not-otherwise-mentioned)) (test (generate-term/decisions lc x 5 0 (decisions #:var (list (λ _ 'λ) (λ _ '+) (λ _ 'x)))) 'x)) (let () (define-language four (e 4) (f 5)) (define-language empty) ;; `any' pattern (let ([four (make-rg-lang '((e . ()) (f . ())) '((e-e . ()) (f-f . ())) 'dont-care)] [sexp (make-rg-lang 'dont-care 'dont-care 'dont-care)]) (test (call-with-values (λ () (pick-any four sexp (make-random 0 1))) list) (list four 'f)) (test (call-with-values (λ () (pick-any four sexp (make-random 1))) list) (list sexp 'sexp))) (test (generate-term/decisions four any 5 0 (decisions #:any (list (λ (lang sexp) (values lang 'e))))) 4) (test (generate-term/decisions four any 5 0 (decisions #:any (list (λ (lang sexp) (values sexp 'sexp))) #:nt (patterns fifth second second second) #:seq (list (λ _ 3)) #:str (list (λ _ "foo") (λ _ "bar") (λ _ "baz")))) '("foo" "bar" "baz")) (test (generate-term/decisions empty any 5 0 (decisions #:nt (patterns first) #:any (λ (langc sexpc) (values sexpc 'sexp)) #:var (list (λ _ 'x)))) 'x) (test (generate-term/decisions empty (in-hole (any hole) 7) 5 0 (decisions #:any (list (λ (_ sexp) (values sexp 'sexp))) #:nt (patterns fourth))) (term (hole 7)))) ;; `hide-hole' pattern (let () (define-language lang (e (hide-hole (in-hole ((hide-hole hole) hole) 1)))) (test (generate-term lang e 5) (term (hole 1))) (test (plug (generate-term lang (hide-hole hole) 0) 3) 3)) (define (output-error-port thunk) (let ([port (open-output-string)]) (parameterize ([current-error-port port]) (thunk)) (get-output-string port))) ;; `cross' pattern (let () (define-language lang (e x (e e) v) (v (λ (x) e)) (x variable-not-otherwise-mentioned)) (define-extended-language name-collision lang (e-e 47)) (test (generate-term/decisions lang (cross e) 3 0 (decisions #:nt (patterns fourth first first second first first first) #:var (list (λ _ 'x) (λ _ 'y)))) (term (λ (x) (hole y)))) (test (generate-term/decisions name-collision (cross e) 3 0 (decisions #:nt (patterns first))) (term hole)) (test (generate-term/decisions name-collision e-e 3 0 (decisions #:nt (patterns first))) 47) (test (hash-ref (base-cases-non-cross (find-base-cases name-collision)) 'e-e) '(0))) (let () (define-language L (a ((a ...) ...))) (test (generate-term/decisions L (cross a) 3 0 (decisions #:nt (patterns second first) #:seq (list (λ _ 0) (λ _ 0) (λ _ 0) (λ _ 0)))) (term ((hole))))) ;; generation failures increase size and attempt (let () (define-language L (a d b) (b d c) (c e) (x variable)) (test (generate-term/decisions L (side-condition a (eq? (term a) 'e)) 0 0 ; It isn't possible for `a' to generate 'y until size is 2. ; When size is 0, the generator has no choice but the 'x production. ; When size is 1, the generator has a choice for `a' but not for `b'. ; Supply enough first-production choices to cover the size 1 attempts ; followed by the choices that produce 'y on the first size 2 attempt. (decisions #:nt (apply patterns (append (build-list (* default-retries proportion-at-size) (λ (_) first)) (list second second first))))) 'e) (test (generate-term/decisions L (side-condition x (number? (term x))) 0 0 (decisions #:var (λ (lang-lits attempt) (if (>= attempt retry-threshold) 0 'x)))) 0) (let ([attempts null] [start (sub1 retry-threshold)] [finish (+ retry-threshold post-threshold-incr)]) (generate-term/decisions L (side-condition x (number? (term x))) 0 start (decisions #:var (λ (lang-lits attempt) (set! attempts (cons attempt attempts)) (if (= attempt finish) 0 'x)))) (test attempts (list finish retry-threshold start)))) ;; At size zero, a sequence length must be zero; otherwise, ;; we risk increasing the problem size. (let () (define-language L (a (a ...))) (test (generate-term/decisions L a 0 1 (decisions #:seq '())) (term ()))) ;; redex-check (let () (define-language lang (d 5) (e 17 4) (n number)) (test (let ([checked 0]) (parameterize ([default-check-attempts 1]) (redex-check lang () (set! checked (add1 checked)) #:print? #f)) checked) 1) (test (redex-check lang d #t #:attempts 1 #:print? (not #t)) #t) (test (redex-check lang d #f #:print? #f) (make-counterexample 5)) (let ([exn (with-handlers ([exn:fail:redex:test? values]) (redex-check lang d (error 'boom ":(") #:print? #f) 'not-an-exn)]) (test (exn-message exn) "checking 5 raises an exception:\nboom: :(") (test (exn-message (exn:fail:redex:test-source exn)) "boom: :(") (test (exn:fail:redex:test-term exn) 5)) (test (output (λ () (redex-check lang d #f))) #rx"redex-check: .*:.*\ncounterexample found after 1 attempt:\n5\n") (test (output (λ () (redex-check lang d #t))) #rx"redex-check: .*:.*\nno counterexamples in 1000 attempts\n") (let-syntax ([noloc (λ (stx) (syntax-case stx () [(_ e) (datum->syntax stx (syntax->datum #'e) #f)]))]) (test (output (λ () (noloc (redex-check lang d #t)))) "redex-check: no counterexamples in 1000 attempts\n")) (test (output (λ () (redex-check lang (d e) (and (eq? (term d) 5) (eq? (term e) 4)) #:attempts 2))) #rx"no counterexamples") (test (output (λ () (redex-check lang (d ...) (zero? (modulo (foldl + 0 (term (d ...))) 5)) #:attempts 2))) #rx"no counterexamples") (test (output (λ () (redex-check lang (d e) #f))) #rx"counterexample found after 1 attempt:\n\\(5 4\\)\n") (let* ([p (open-output-string)] [m (parameterize ([current-output-port p]) (with-handlers ([exn:fail? exn-message]) (redex-check lang d (error 'pred-raised)) 'no-exn-raised))]) (test m "error: pred-raised") (test (get-output-string p) #rx"checking 5 raises.*\n$") (close-output-port p)) (test (output (λ () (redex-check lang n (eq? 42 (term n)) #:attempts 1 #:source (reduction-relation lang (--> 42 dontcare) (--> 0 dontcare z))))) #rx"counterexample found after 1 attempt with z:\n0\n") (let ([generated null] [R (reduction-relation lang (--> 1 dontcare) (--> 2 dontcare))]) (test (output (λ () (redex-check lang n (set! generated (cons (term n) generated)) #:attempts 5 #:source R))) #rx"no counterexamples.*with each clause") (test generated '(2 2 1 1)) (test (redex-check lang any #t #:attempts 1 #:source R #:print? (not #t)) #t) (test (redex-check lang any (= (term any) 1) #:source R #:print? #f) (make-counterexample 2))) (let () (define-metafunction lang [(mf 42) dontcare] [(mf 0) dontcare]) (test (output (λ () (redex-check lang (n) (eq? 42 (term n)) #:attempts 1 #:source mf))) #px"counterexample found after 1 attempt with clause at .*:\\d+:\\d+:\n\\(0\\)\n") (test (redex-check lang any #t #:attempts 1 #:source mf #:print? (not #t)) #t) (test (redex-check lang any (= (car (term any)) 42) #:source mf #:print? #f) (make-counterexample '(0)))) (let () (define-metafunction lang [(f) dontcare (side-condition #f)]) (test (raised-exn-msg exn:fail:redex:generation-failure? (redex-check lang any #t #:attempts 1 #:source f)) #px"unable to generate LHS of clause at .*:\\d+:\\d+")) (let () (define-metafunction lang [(mf d e) dontcare]) (test (output (λ () (redex-check lang (number_1 number_2) (and (= (term number_1) 5) (= (term number_2) 4)) #:attempts 1 #:source mf))) #rx"no counterexamples")) ; Without the #:attempt-size argument, the attempt would use size 0, ; which does not require a non-terminal decision. (test (let/ec k (parameterize ([generation-decisions (decisions #:nt (list (λ _ (k #t))))]) (redex-check lang d #t #:attempts 1 #:print? #f #:attempt-size add1) #f)) #t) (let ([generated '()] [fixed '()] [fix add1]) (redex-check lang number (set! fixed (cons (term number) fixed)) #:prepare (λ (n) (set! generated (cons n generated)) (fix n)) #:attempts 10 #:print? #f) (test fixed (map fix generated))) (test (parameterize ([generation-decisions (decisions #:num (list (λ _ 0)))]) (redex-check lang number (= 0 (term number)) #:prepare add1 #:print? #f)) (counterexample 1)) (test (raised-exn-msg exn:fail? (redex-check lang 0 #t #:prepare (λ (_) (error 'fixer)) #:print? #f)) #rx"fixing 0") (test (raised-exn-msg exn:fail:redex? (redex-check lang natural #t #:prepare (compose - add1))) #rx"does not match natural") (test (raised-exn-msg exn:fail:redex? (redex-check lang natural #t #:prepare - #:source (reduction-relation lang (--> 47 1)))) #rx"-47 does not match natural") (test (redex-check lang number (= 0 (term number)) #:prepare add1 #:source (reduction-relation lang (--> 0 1)) #:print? #f) (counterexample 1)) (test-contract-violation/client "#:attempts argument" (redex-check lang natural #t #:attempts 3.5)) (test-contract-violation/client "#:retries argument" (redex-check lang natural #t #:retries 3.5)) (test-contract-violation/client "#:attempt-size argument" (redex-check lang natural #t #:attempt-size -)) (test-contract-violation/client "#:prepare argument" (redex-check lang natural #t #:prepare (λ (_) (values)))) (test (raised-exn-msg exn:fail:redex? (redex-check lang n #t #:source (reduction-relation lang (--> x 1)))) #rx"x does not match n") (test (raised-exn-msg exn:fail:redex:generation-failure? (redex-check lang (side-condition any #f) #t #:retries 42 #:attempts 1)) #rx"^redex-check: unable .* in 42") (let ([unable-loc #px"^redex-check: unable to generate LHS of clause at .*:\\d+:\\d+ in 42"]) (let-syntax ([test-gen-fail (syntax-rules () [(_ clauses ... expected) (test (raised-exn-msg exn:fail:redex:generation-failure? (redex-check lang any #t #:source (reduction-relation lang clauses ...) #:retries 42 #:attempts 1)) expected)])]) (test-gen-fail (--> (side-condition any #f) any) unable-loc) (test-gen-fail (==> (side-condition any #f) any) with [(--> a b) (==> a b)] unable-loc) (test-gen-fail (--> (side-condition any #f) any impossible) #rx"^redex-check: unable to generate LHS of impossible in 42")))) ;; check-reduction-relation (let () (define-language L (e (+ e ...) number) (E (+ number ... E* e ...)) (E* hole) (n 4)) (let ([R (reduction-relation L (--> 1 2) (--> 2 3))]) (test (check-reduction-relation R (λ (_) #t) #:print? #f) #t) (test (counterexample-term (check-reduction-relation R (curry = 1) #:print? #f)) 2)) (let ([generated null] [R (reduction-relation L (==> (+ number ...) whatever) (--> (side-condition number (even? (term number))) whatever) with [(--> (in-hole E a) whatever) (==> a b)])]) (test (begin (output (λ () (parameterize ([generation-decisions (decisions #:seq (list (λ _ 0) (λ _ 0) (λ _ 0)) #:num (list (λ _ 1) (λ _ 1) (λ _ 0)))]) (check-reduction-relation R (λ (term) (set! generated (cons term generated))) #:attempts 1)))) generated) (reverse '((+ (+)) 0)))) (test (let* ([generated null] [R (reduction-relation L (--> (name t (number_1 number_3)) dontcare (side-condition (set! generated (cons (term t) generated))) (where number_1 4) (where number_2 number_1) (where number_3 number_2)))]) (parameterize ([generation-decisions (decisions #:num (list (λ _ 3) (λ _ 4) (λ _ 4) (λ _ 3) (λ _ 4) (λ _ 4)))]) (check-reduction-relation R (λ (_) #t) #:attempts 1 #:print? #f)) generated) '((4 4) (4 3) (3 4))) ; Extension reinterprets the LHSs of the base relation ; relative to the new language. (let () (define-language L (x 1)) (define-extended-language M L (x 2)) (define R (reduction-relation L (--> x yes))) (define S (extend-reduction-relation R M)) (test (let/ec k (check-reduction-relation S k)) 2)) ; Extension reinterprets the `where' clauses of the base relation ; relative to new language. (let () (define-language L (x 1)) (define-extended-language M L (x 2)) (define R (reduction-relation L (--> () () (where x 2)))) (define S (extend-reduction-relation R M)) (test (with-handlers ([exn:fail:redex:generation-failure? (const #f)]) (check-reduction-relation S (λ (_) #t) #:attempts 1 #:print? #f)) #t)) (let ([generated '()] [fixed '()] [fix add1]) (check-reduction-relation (reduction-relation L (--> number number)) (λ (n) (set! fixed (cons n fixed))) #:prepare (λ (n) (set! generated (cons n generated)) (fix n)) #:attempts 10 #:print? #f) (test fixed (map fix generated))) (test (parameterize ([generation-decisions (decisions #:num (list (λ _ 0)))]) (check-reduction-relation (reduction-relation L (--> number number)) (curry = 0) #:prepare add1 #:print? #f)) (counterexample 1)) (test (raised-exn-msg exn:fail? (check-reduction-relation (reduction-relation L (--> 0 0)) (λ (_) #t) #:prepare (λ (_) (error 'fixer)) #:print? #f)) #rx"fixing 0") (test-contract-violation/client "#:prepare argument" (check-reduction-relation (reduction-relation L (--> 0 0)) void #:prepare (λ () 0))) (let ([S (reduction-relation L (--> 1 2 name) (--> 3 4))]) (test (output (λ () (check-reduction-relation S (λ (x) #t) #:attempts 1))) #rx"check-reduction-relation:.*no counterexamples") (test (output (λ () (check-reduction-relation S (λ (x) #f)))) #rx"counterexample found after 1 attempt with name:\n1\n") (test (output (λ () (check-reduction-relation S (curry eq? 1)))) #px"counterexample found after 1 attempt with clause at .*:\\d+:\\d+:\n3\n")) (test (output (λ () (check-reduction-relation (reduction-relation L (--> 1 2) (--> 3 4 name)) (curry eq? 1)))) #px"counterexample found after 1 attempt with name:\n3\n") (test (let/ec k (parameterize ([generation-decisions (decisions #:nt (list (λ _ (k #t))))]) (check-reduction-relation (reduction-relation L (--> e e)) (λ _ #t) #:attempts 1 #:print? #f #:attempt-size add1) #f)) #t) (let ([T (reduction-relation L (==> number number (where any_num number) (side-condition (eq? (term any_num) 4)) (where any_numb any_num) (side-condition (eq? (term any_numb) 4))) with [(--> (9 a) b) (==> a b)])]) (test (output (λ () (parameterize ([generation-decisions (decisions #:num (build-list 5 (λ (x) (λ _ x))))]) (check-reduction-relation T (curry equal? '(9 4)) #:attempts 1)))) #rx"no counterexamples")) (let ([U (reduction-relation L (--> (side-condition any #f) any))]) (test (raised-exn-msg exn:fail:redex:generation-failure? (check-reduction-relation U (λ (_) #t))) #rx"^check-reduction-relation: unable")) (let ([R (reduction-relation L (--> any any))]) (test-contract-violation/client "#:attempts argument" (check-reduction-relation R values #:attempts -1)) (test-contract-violation/client "#:retries argument" (check-reduction-relation R values #:retries -1)) (test-contract-violation/client "#:attempt-size argument" (check-reduction-relation R values #:attempt-size (λ (_) (values 1 2)))) (test-contract-violation/client "#:prepare argument" (check-reduction-relation R values #:prepare (λ (_) (values 1 2)))) (test-contract-violation/client (check-reduction-relation R #t)))) ; check-metafunction (let () (define-language empty) (define-metafunction empty [(m 1) whatever] [(m 2) whatever]) (define-metafunction empty [(n (side-condition any #f)) any]) (test (check-metafunction m (λ (_) #t) #:print? #f) #t) (test (counterexample-term (check-metafunction m (compose (curry = 1) car) #:print? #f)) '(2)) (let ([generated null]) (test (begin (output (λ () (check-metafunction m (λ (t) (set! generated (cons t generated))) #:attempts 1))) generated) (reverse '((1) (2))))) (test (let/ec k (define-language L (n 2)) (define-metafunction L [(f n) n (where number_2 ,(add1 (term n))) (where number_3 ,(add1 (term number_2))) (side-condition (k (term number_3)))] [(f any) 0]) (check-metafunction f (λ (_) #t))) 4) (let () (define-language L ((m n) number)) (define-metafunction L [(f m_0 m_1 ...) () (where (n_0 ... n_i ...) (m_0 m_1 ...)) (side-condition (null? (term (n_0 ...))))]) (test (with-handlers ([exn:fail:redex:generation-failure? (λ (_) #f)]) (check-metafunction f (λ (_) #t) #:retries 1 #:print? #f #:attempts 1)) #t)) (test (let ([generated null]) (define-language L) (define-metafunction L [(f (name t (number_1 number_3))) dontcare (side-condition (set! generated (cons (term t) generated))) (where number_1 4) (where number_2 number_1) (where number_3 number_2)]) (parameterize ([generation-decisions (decisions #:num (list (λ _ 3) (λ _ 4) (λ _ 4) (λ _ 3) (λ _ 4) (λ _ 4)))]) (check-metafunction f (λ (_) #t) #:attempts 1 #:print? #f)) generated) '((4 4) (4 3) (3 4))) (test (let/ec k (define-language L (n number)) (define-metafunction L [(f n) n]) (parameterize ([generation-decisions (decisions #:nt (list (λ _ (k #t))))]) (check-metafunction f (λ _ #t) #:attempts 1 #:print? #f #:attempt-size add1) #f)) #t) (let ([generated '()] [fixed '()] [fix add1]) (define-metafunction empty [(f number) number]) (check-metafunction f (λ (n) (set! fixed (cons (car n) fixed))) #:prepare (λ (n) (set! generated (cons (car n) generated)) (list (fix (car n)))) #:attempts 10 #:print? #f) (test fixed (map fix generated))) (test (parameterize ([generation-decisions (decisions #:num (list (λ _ 0)))]) (define-metafunction empty [(f number) number]) (check-metafunction f (compose (curry = 0) car) #:prepare (compose list add1 car) #:print? #f)) (counterexample '(1))) (test (let () (define-metafunction empty [(f 0) 0]) (raised-exn-msg exn:fail? (check-metafunction f (λ (_) #t) #:prepare (λ (_) (error 'fixer)) #:print? #f))) #rx"fixing \\(0\\)") (let () (define-metafunction empty [(f 0) 0]) (test-contract-violation/client "#:attempts argument" (check-metafunction f void #:attempts 3.5)) (test-contract-violation/client "#:retries argument" (check-metafunction f void #:retries 3.5)) (test-contract-violation/client "#:attempt-size argument" (check-metafunction f void #:attempt-size 3.5)) (test-contract-violation/client "#:prepare argument" (check-metafunction f void #:prepare car #:print? #f)) (test-contract-violation/client (check-metafunction f (λ () #t)))) ; Extension reinterprets the LHSs of the base metafunction ; relative to the new language. (let () (define-language L (x 1)) (define-extended-language M L (x 2)) (define-metafunction L [(f x) yes]) (define-metafunction/extension f M g : any -> any) (test (let/ec k (check-metafunction g k)) '(2))) ; Extension reinterprets the `where' clauses of the base metafunction ; relative to the new language. (let () (define-language L (x 1)) (define-extended-language M L (x 2)) (define-metafunction L [(f) _ (where x 2)]) (define-metafunction/extension f M g : any -> any) (test (with-handlers ([exn:fail:redex:generation-failure? (const #f)]) (check-metafunction g (λ (_) #t) #:attempts 1 #:print? #f)) #t)) (test (output (λ () (check-metafunction m (λ (_) #t)))) #rx"no counterexamples") (test (output (λ () (check-metafunction m (curry eq? 1)))) #px"check-metafunction:.*counterexample found after 1 attempt with clause at .*:\\d+:\\d+") (test (raised-exn-msg exn:fail:redex:generation-failure? (check-metafunction n (λ (_) #t) #:retries 42)) #rx"check-metafunction: unable .* in 42")) (let () (define-language lang (x variable)) (define-syntax test-class-reassignments (syntax-rules () [(_ pattern expected) (test (to-table (class-reassignments pattern)) expected)])) (test-class-reassignments '(list (repeat (name x_1 (nt x)) ..._1 #f) (repeat (name x_2 (nt x)) ..._2 #f) (repeat (name x_2 (nt x)) ..._1 #f)) '((..._2 . ..._1))) (test-class-reassignments '(list (list (repeat (name x_1 (nt x)) ..._1 #f) (repeat (name x_1 (nt x)) ..._2 #f)) (list (repeat (name x_2 (nt x)) ..._1 #f) (repeat (name x_2 (nt x)) ..._2 #f)) (repeat (name x_3 (nt x)) ..._2 #f)) '((..._1 . ..._2) (..._2 . ..._2))) (test-class-reassignments '(list (repeat (name x_1 (nt x)) ..._1 #f) (repeat (name x (nt x)) ..._2 #f) (repeat (name x_1 (nt x)) ..._2 #f)) '((..._1 . ..._2))) (test-class-reassignments '(list (repeat (name x_1 (nt x)) ..._1 #f) (repeat (name x_2 (nt x)) ..._2 #f) (repeat (list (name x_1 (nt x)) (name x_2 (nt x))) ..._3 #f)) '((..._1 . ..._3) (..._2 . ..._3))) (test-class-reassignments '(list (repeat (list (repeat (name x_1 (nt x)) ..._1 #f)) ..._2 #f) (repeat (name x_2 (nt x)) ..._3 #f) (repeat (list (repeat (name x_1 (nt x)) ..._4 #f) (name x_2 (nt x))) ..._5 #f)) '((..._1 . ..._4) (..._2 . ..._5) (..._3 . ..._5))) (test-class-reassignments '(list (repeat (list (repeat (name x_1 (nt x)) ..._1 #f)) ..._2 #f) (repeat (list (repeat (name x_1 (nt x)) ..._3 #f)) ..._4 #f) (repeat (list (repeat (name x_1 (nt x)) ..._5 #f)) ..._6 #f)) '((..._1 . ..._5) (..._2 . ..._6) (..._3 . ..._5) (..._4 . ..._6))) (test-class-reassignments '(list (repeat (name x_1 (nt x)) ..._1 #f) (repeat (name x_1 (nt x)) ..._2 #f) (repeat (name x_2 (nt x)) ..._1 #f) (repeat (name x_2 (nt x)) ..._4 #f) (repeat (name x_2 (nt x)) ..._3 #f)) '((..._1 . ..._3) (..._2 . ..._3) (..._4 . ..._3))) (test (hash-map (class-reassignments '(list (repeat (name x_1 (nt x)) #f #f) (repeat (name x_1 (nt x)) ..._!_1 #t) (repeat (name x_1 (nt x)) ..._1 #f))) (λ (_ cls) cls)) '(..._1 ..._1)) (test-class-reassignments '(list (repeat (list (repeat 3 ..._1 #f)) ..._2 #f) (repeat (list (repeat 4 ..._1 #f)) ..._3 #f)) '((..._2 . ..._3))) (test-class-reassignments '(list (repeat (name x (nt x)) ..._1 #f) (repeat (name x (nt x)) ..._2 #f) (repeat (name variable variable) ..._2 #f) (repeat (name variable variable) ..._3 #f) (repeat (name variable_1 variable) ..._3 #f) (repeat (name variable_1 variable) ..._4 #f)) '((..._1 . ..._4) (..._2 . ..._4) (..._3 . ..._4)))) ;; redex-test-seed (let ([seed 0]) (define-language L) (define (generate) (generate-term L (number ...) 100 #:attempt-num 10000000)) (test (begin (random-seed seed) (generate)) (begin (random-seed seed) (generate))) (let ([prg (make-pseudo-random-generator)]) (define (seed-effect-generate effect) (begin (parameterize ([current-pseudo-random-generator prg]) (random-seed seed)) (effect) (parameterize ([redex-pseudo-random-generator prg]) (generate)))) (test (seed-effect-generate void) (seed-effect-generate random)))) (print-tests-passed 'rg-test.rkt)