(module mp6-test (lib "eopl.ss" "eopl") (require "lang.scm") ; for scan&parse (require "drscheme-init.scm") (require "mp6.scm") ;; parse-exp : String -> Expression ;; usage: (parse-exp s) returns main expression of program encoded in s (define parse-exp (lambda (s) (let ((pgm (scan&parse s))) (cases program pgm (a-program (e) e))))) ;; A TestFamily is ;; (a-test-family (Expression -> Outcome) ;; Listof[ (list TestName Inputs Outcome)] ) ;; A TestName is a SchemeVal ;; An Inputs is a Listof[SchemeVal] ;; An Outcome is a SchemeVal ;; ;; note: Strings in tests are parsed as Expressions by the ;; infrastructure, for ease of writing tests. Thus the ;; 2 is just 2, while "2" is treated as (const-exp 2) (define-datatype test-family test-family? (a-test-family (func procedure?) (tests (list-of (lambda (x) (and (pair? x) (pair? (cdr x)) (list? (cadr x)) (pair? (cddr x)) (null? (cdddr x)))))))) ;; test-families! : ;; Listof[ (list (Expression -> Y) Listof[(list Name String Y)]) ] ;; -> unspecified ;; usage: (test-family func testlist) tests func on every entry in testlist. (define test-families! (let* (;; convert-fam : TestFamily -> Listof[(Name Xform*Inputs Outcome)] (convert-fam (lambda (family) (cases test-family family (a-test-family (func tests) (map (lambda (entry) (let ((name (list-ref entry 0)) (inputs (list-ref entry 1)) (outcome (list-ref entry 2))) (list name (list func inputs) outcome))) tests))))) ;; append-map : (X -> Listof[Y]) Listof[X] -> Listof[Y] (append-map (lambda (f l) (let loop ((l l)) (cond ((null? l) '()) (else (append (f (car l)) (loop (cdr l)))))))) ;; maybe-parse : SchemeVal -> SchemeVal ;; if input is a String, parses it as an Expression (maybe-parse (lambda (x) (cond ((string? x) (parse-exp x)) (else x)))) ;; run-xform : (list Xform Inputs) -> Outcome (run-xform (lambda (xform*inputs) (let ((func (car xform*inputs)) (inputs (cadr xform*inputs))) (apply func (map maybe-parse inputs))))) ;; equal-answer? : Outcome Outcome -> Boolean (equal-answer? (lambda (actual expected) (equal? actual (maybe-parse expected)))) ) (lambda (l) (run-tests! run-xform equal-answer? (append-map convert-fam l))))) ;; signal-error-on-zero : Num -> Num ;; Signals error on nonzero; returns zero otherwise. (define signal-error-on-nonzero (lambda (n) (if (zero? n) 0 (eopl:error 'signal-error-on-zero "got a nonzero input: ~a" n)))) (test-families! (list ;; This test is not testing anything in MP6; it merely illustrates ;; how you can write your own tests to check that a function ;; signals an error on certain inputs. (a-test-family signal-error-on-nonzero '((error-illustration1 (0) 0) (error-illustration2 (100) error) (error-illustration3 (-10) error))) (a-test-family exp-to-mnum '((exp-to-num-1 ("1") 1) (exp-to-num-2 ("2") 2) (exp-to-nonum-1 ("-(2,1)") #f) (exp-to-nonum-2 ("x") #f) (exp-to-nonum-3 ("proc (x) 1") #f) )) (a-test-family transform-diff-zero-simple '((xform-diffz-1 ("0") #f) (xform-diffz-2 ("-(1,0)") "1") (xform-diffz-3 ("let x = 3 in -(x,0)") #f) (xform-diffz-4 ("-(let x = 3 in x,0)") "let x = 3 in x") (xform-diffz-5 ("let x = -(3,0) in x") #f) )) (a-test-family transform-diff-zero* '((xform-diffz*-const-unchanged ("0") " 0") (xform-diffz*-var-unchanged ("x") " x") (xform-diffz*-diff-changed-1 ("-(1,0)") " 1") (xform-diffz*-diff-changed-2 ("-(let x = 3 in x,0)") " let x = 3 in x") (xform-diffz*-descend-diff-lhs ("-(-(x,0),y)") " -(x,y)") (xform-diffz*-descend-diff-rhs ("-(y,-(x,0))") " -(y,x)") (xform-diffz*-descend-diff-both-subexps ("-(-(x,0),-(y,0))") " -(x,y)") (xform-diffz*-descend-zero? ("zero?(-(x,0))") " zero?(x)") (xform-diffz*-descend-if-test ("if zero?(-(x,0)) then y else z") " if zero?(x) then y else z") (xform-diffz*-descend-if-then ("if w then -(x,0) else y") " if w then x else y") (xform-diffz*-descend-if-else ("if v then w else -(x,0)") " if v then w else x") (xform-diffz*-descend-if-all-subexps ("if zero?(-(x,0)) then -(y,0) else -(z,0)") " if zero?(x) then y else z") (xform-diffz*-descend-let-body ("let x = 3 in -(x,0)") " let x = 3 in x") (xform-diffz*-descend-let-binding ("let x = -(3,0) in x") " let x = 3 in x") (xform-diffz*-descend-let-both-subexps ("let x = -(3,0) in -(x,0)") " let x = 3 in x") (xform-diffz*-descend-proc ("proc (x) -(x,0)") " proc (x) x") (xform-diffz*-descend-call-rator ("(proc (x) -(x,0) y)") " (proc (x) x y)") (xform-diffz*-descend-call-rand ("(f -(x,0))") " (f x)") (xform-diffz*-descend-call-both-subexps ("(proc (x) -(x,0) -(y,0))") " (proc (x) x y)") (xform-diffz*-descend-letrec-bindings ("letrec f(x) = -(x,0) g(y) = -(y,0) h(z) = -(z,0) in w") " letrec f(x) = x g(y) = y h(z) = z in w") (xform-diffz*-descend-letrec-body ("letrec f(y) = (f y) in (f -(x,0))") " letrec f(y) = (f y) in (f x)") (xform-diffz*-descend-letrec-all-subexps ("letrec f(x) = -(x,0) g(y) = -(y,0) h(z) = -(z,0) in (f -(x,0))") " letrec f(x) = x g(y) = y h(z) = z in (f x)") (xform-diffz*-repeated-diffs-1 ("-(-(x,0),0)") " x") (xform-diffz*-repeated-diffs-2 ("-(-(-(w,-(x,0)),0),-(-(y,0),z))") " -( -(w,x), -( y, z))") (xform-diffz*-result-of-xform-in-xform ("-(x,-(0,0))") "x") ;; Q: what is particularly interesting about this case? (xform-diffz*-interesting ("let f = proc (y) -(y,1) in (-(f,0) 5)") " let f = proc (y) -(y,1) in (f 5)") )) )) )