#lang scheme
(require "pa5.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 (try-to-parse input-string)
  (lambda () (parse input-string)))
(define (try-to-type input-string)
  (lambda () (type-of (parse 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 zomg-double
  '{with {f {fun {n : number} : number {+ n n}}}
     {with {g {fun {h : (number -> number)} : (number -> number) f}}
       {g f}}})

(define pa5-tests
  (test-suite
   "pa5"
   ;; Tests that should produce errors.
   (test-exn "parse"
             (expect-error "wrong number of operands")
             (try-to-parse '{+ 1}))
   (test-exn "parse"
             (expect-error "malformed fun expression")
             (try-to-parse '{fun {x} x}))
   (test-exn "parse"
             (expect-error "expected a typed formal parameter")
             (try-to-parse '{fun x : number x}))
   (test-exn "parse"
             (expect-error "malformed typed formal parameter")
             (try-to-parse '{fun {x} : number x}))
   (test-exn "parse"
             (expect-error "expected an identifier")
             (try-to-parse '{fun {1 : number} : number x}))
   (test-exn "parse-type"
             (expect-error "expected a type expression")
             (try-to-type '{fun {x : 1} : 1 x}))
   (test-exn "parse-type"
             (expect-error "malformed type expression")
             (try-to-type '{fun {x : {number}} : number {x 1}}))
   (test-exn "parse-type"
             (expect-error "unknown type: foo")
             (try-to-type '{fun {x : foo} : foo x}))
   (test-exn "type-of"
             (expect-error "free identifier: x")
             (try-to-type 'x))
   (test-exn "type-of"
             (expect-error "type mismatch: operands must be numbers")
             (try-to-type '{+ 1 {fun {x : number} : number x}}))
   (test-exn "type-of"
             (expect-error "type mismatch: function body has wrong type")
             (try-to-type '{fun {x : number} : (number -> number) x}))
   (test-exn "type-of"
             (expect-error "type mismatch: actual parameter has wrong type")
             (try-to-type '{{fun {f : (number -> number)} : number 0} 0}))
   (test-exn "type-of"
             (expect-error "expected a function")
             (try-to-type '{1 2}))

   ;; Tests that should succeed. You should write a lot more of these!
   (test-equal? "type=?" (type=? (t-fun (t-num) (t-num)) (t-fun (t-num) (t-num))) #t)

   (test-equal? "parse-type" (parse-type '(number -> number)) (t-fun (t-num) (t-num)))

   (test-equal? "type-of" (type-of (parse 0)) (t-num))
   (test-equal? "type-of" (type-of (parse zomg-double)) (t-fun (t-num) (t-num)))
   ))

(test/text-ui pa5-tests)
