#lang scheme
(require "pa3.ss" (planet "test.ss" ("schematics" "schemeunit.plt" 2)))
(require (planet "text-ui.ss" ("schematics" "schemeunit.plt" 2)))
;; When first you run this, it will fetch and install schemeunit automatically.

;; For use in tests. True if the correct error message is raised; false otherwise.
(define read-and-parse-string (compose parse read open-input-string))
(define read-and-eval-string (compose interp read-and-parse-string))
(define (try-to-parse input-string)
  (lambda () (read-and-parse-string input-string)))
(define (try-to-eval input-string)
  (lambda () (read-and-eval-string input-string)))

;; For use in tests. True if the correct error message is raised; false otherwise.
(define (expect-error msg)
  (lambda (exn) (string=? (exn-message exn) msg)))

(define my-value-is-7
  "{with {{x 2} {y 3}}
         {with {{z {+ x y}}}
               {+ x z}}}")

(define factorial-4
  "{with {{fact {fun {f n} {if0 n 1 {* n {f f {- n 1}}}}}}}
     {fact fact 4}}")

(define pa3-tests
  (test-suite
   "pa3"
   ;; Tests that should produce errors.
   (test-exn "parse"
             (expect-error "unexpected keyword")
             (try-to-parse "with"))
   (test-exn "parse"
             (expect-error "unexpected empty list")
             (try-to-parse "{}"))
   (test-exn "parse"
             (expect-error "malformed with expression")
             (try-to-parse "{with}"))
   (test-exn "interp"
             (expect-error "free identifier")
             (try-to-eval "{foo 1 2}"))
   (test-exn "parse"
             (expect-error "wrong number of arguments to binary operator")
             (try-to-parse "{+ 1}"))
   (test-exn "parse"
             (expect-error "invalid datatype in input")
             (try-to-parse "#t"))
   (test-exn "parse"
             (expect-error "expected a list of bindings")
             (try-to-parse "{with x x}"))
   (test-exn "parse"
             (expect-error "expected list in binding of with expression")
             (try-to-parse "{with {x} x}"))
   (test-exn "parse"
             (expect-error "expected (id val) in with expression")
             (try-to-parse "{with {{x}} x}"))
   (test-exn "parse"
             (expect-error "identifiers must be symbols")
             (try-to-parse "{with {{1 2}} x}"))
   (test-exn "parse"
             (expect-error "cannot bind a value to a reserved symbol")
             (try-to-parse "{with {{with 2}} x}"))
   (test-exn "parse"
             (expect-error "duplicate bindings for identifier")
             (try-to-parse "{with {{x 1} {x 2}} x}"))
   (test-exn "interp"
             (expect-error "free identifier")
             (try-to-eval "x"))
   (test-exn "parse"
             (expect-error "malformed if0 expression")
             (try-to-parse "{if0}"))
   (test-exn "interp"
             (expect-error "expected a number")
             (try-to-eval "{if0 {fun {} 0} 1 2}"))
   (test-exn "parse"
             (expect-error "malformed fun expression")
             (try-to-parse "{fun}"))
   (test-exn "parse"
             (expect-error "expected a list of formals")
             (try-to-parse "{fun x x}"))
   (test-exn "parse"
             (expect-error "identifiers must be symbols")
             (try-to-parse "{fun {1} 1}"))
   (test-exn "parse"
             (expect-error "cannot bind a value to a reserved symbol")
             (try-to-parse "{fun {with} 1}"))
   (test-exn "parse"
             (expect-error "duplicate formal parameter name")
             (try-to-parse "{fun {x x} 1}"))
   (test-exn "interp"
             (expect-error "free identifier")
             (try-to-eval "{f}"))
   (test-exn "interp"
             (expect-error "function expects 0 argument(s), given 1")
             (try-to-eval "{{fun {} 1} 1}"))
   (test-exn "interp"
             (expect-error "expected function, given: 1")
             (try-to-eval "{1 2}"))

   ;; Tests that should succeed. You should write a lot more of these!
   (test-equal? "interp" (read-and-eval-string my-value-is-7) 7)
   (test-equal? "interp" (read-and-eval-string factorial-4) 24)
   ))

(test/text-ui pa3-tests)
