;; The first three lines of this file were inserted by DrRacket. They record metadata ;; about the language level of this file in a form that our tools can easily process. #reader(lib "htdp-advanced-reader.ss" "lang")((modname T0-test) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #t #t none #f () #f))) ; Note: this file is written in *advanced*-student ; (because the test-functions do sequences of I/O, rather than return values). ; Your program should be written in "intermediate student with lambda". (require "T0.rkt") (require "scanner.rkt") (require rackunit) ;;;;;;;;;;;;;;;;;;; TEST CASES: T0 ;;;;;;;;;;;;;;;; ; Some expressions to test in a non-automated way: (check-equal? (parse! (create-scanner "34")) 34) (check-equal? (parse! (create-scanner "-34")) -34) (check-equal? (parse! "34") 34) (check-equal? (parse! "<34>") (make-paren 34)) (check-equal? (parse! "#3 boii 4#") (make-binop 3 "boii" 4)) (check-equal? (parse! "#<34> boii #3 boiii 4##") (make-binop (make-paren 34) "boii" (make-binop 3 "boiii" 4))) (check-equal? (parse! "even? 3 dope 7 nope 9 dawg") (make-parity 3 7 9)) (check-equal? (parse! "even? <34> dope #<34> boii #3 boiii 4## nope even? 3 dope 7 nope 9 dawg dawg") (make-parity (make-paren 34) (make-binop (make-paren 34) "boii" (make-binop 3 "boiii" 4)) (make-parity 3 7 9))) (check-equal? (eval 34) 34) (check-equal? (eval (parse! "<34>")) 34) (check-equal? (eval (parse! "#3 boii 4#")) 7) (check-equal? (eval (parse! "#3 boi 4#")) -1) (check-equal? (eval (parse! "#3 boiii 4#")) 12) (check-equal? (eval (parse! "even? 3 dope 4 nope 5 dawg")) 5) (check-equal? (eval (parse! "even? 0 dope 4 nope 5 dawg")) 4) (check-equal? (eval (parse! "even? #2 boi 4# dope #1 boii 2# nope #3 boii 4# dawg")) 3) (check-equal? (eval (parse! "even? 7 dope #3 boii 4# nope 5 dawg")) 5) (check-equal? (expr->string (parse! "34")) "34") (check-equal? (expr->string (parse! "<34>")) "<34>") (check-equal? (expr->string (parse! "#3 boii 4#")) "#3 boii 4#") (check-equal? (expr->string (parse! "#3 boi 4#")) "#3 boi 4#") (check-equal? (expr->string (parse! " # 3 boiii 4 # ")) "#3 boiii 4#") (check-equal? (expr->string (parse! "even? 3 dope 4 nope 5 dawg")) "even? 3 dope 4 nope 5 dawg") (check-equal? (expr->string (parse! "even? 0 dope #3 boii 4# nope 5 dawg")) "even? 0 dope #3 boii 4# nope 5 dawg") ;; Add more specific tests here, ;; if you want things more specific that provided via adding to `all-tests` below. (define e0 "43") (define e1 "<43>") (define e2 "#4 boii 3#") (define e3 "<<#4 boii <3>#>>") (define e4 "#<43> boii #42 boiii 3##") ;;; we can automate checking that parse! is the (right)inverses of expr->string: (for-each (λ(e) (check-equal? (expr->string (parse! e)) e)) (list e0 e1 e2 e3 e4)) ; `for-each` is like map except that it discards the result from each function-call; ; it is suitable for functions which are called solely for a side-effect. ; (`test-all` is such a function.) ;;; Though we also want to check that e0..e4 eval to 43,43,7,7,169 respectively. (for-each (λ(e v) (check-equal? (eval (parse! e)) v)) (list e0 e1 e2 e3 e4) (list 43 43 7 7 169)) ;;; The above is a promising start, to automating tests. ;;; Okay, we'll generalize the above to a more complete test-harness. ;;; One thing, is that we don't want to have two parallel-lists; ;;; instead keep a list of pairs. ;;; Three sorts of tests we want to make, for many different exprs: (check-equal? (parse! "#4 boii 3#") (make-binop 4 "boii" 3)) (check-equal? (eval (parse! "#4 boii 3#")) 7) (check-equal? (expr->string (parse! "#4 boii 3#")) "#4 boii 3#") ; Data Def'n: a `S-example` is a list of length two or length three: ; '[str val] (where val is the expected result `(eval (parse! str))`, or ; '[str val expr] (as above, but `expr` is the internal (struct) representation of `(parse! str)`). ; A list of S-examples; ; The last line of this file runs two-to-three tests on each S-example. ; ; BE AWARE of the comma preceding the constructors; it's necessary to actually call it. ; See explanation at http://www.radford.edu/~itec380/2017fall-ibarland/Lectures/backquote.html ; (define all-tests `{["7" 7 7] ["<3>" 3 ,(make-paren 3)] ["#3 boii 4#" 7 ,(make-binop 3 "boii" 4)] ["#3 boiii 4#" 12 ,(make-binop 3 "boiii" 4 )] ["##3 boii 4# boii #3 boiii 4##" 19] ["#<3> boiii <#2 boii 3#>#" 15] ["even? 0 dope 1 nope 2 dawg" 1 ,(make-parity 0 1 2)] ["even? 1 dope 1 nope 2 dawg" 2 ,(make-parity 1 1 2)] ["even? #3 boii -3# dope 1 nope 2 dawg" 1 ,(make-parity (make-binop 3 "boii" -3) 1 2)] ["even? #even? even? 0 dope 1 nope 2 dawg dope 3 nope 4 dawg boii -3# dope 1 nope 2 dawg" 2 ,(make-parity (make-binop (make-parity (make-parity 0 1 2) 3 4) "boii" -3) 1 2)] #| ;>>>T1 tests ; Uncomment these tests, once `mod` is implemented: ["#3 bae 4#" 3] ["##5 boii 6# bae 3#" 2] ["#8.1 bae 3#" 2.1] ["#8 bae 3.1#" 1.8] ["#-8.1 bae 3#" 0.9] ["#-8 bae 3.1#" 1.3] ["#8.1 bae -3#" -0.9] ["#8 bae -3.1#" -1.3] ["#-8.1 bae -3#" -2.1] ["#-8 bae -3.1#" -1.8] ["#8 bae 2#" 0] ["#-8 bae 2#" 0] ["#8 bae -2#" 0] ["#-8 bae -2#" 0] ["#8 bae 3#" 2] ["#-8 bae 3#" 1] ["#8 bae -3#" -1] ["#-8 bae -3#" -2] YOU-MUST-CREATE-SOME-TESTS-FOR-IfGT |# }) ; ; For info on backquote, see documentation and/or: ; http://www.radford.edu/itec380/2017fall-ibarland/Lectures/backquote.html ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; helper functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The following functions should really be in a separate file, and exported from there. ;; However, putting this in to 'Ti-test-harness.rkt' becomes mildly problematic: ;; since it calls 'eval', 'parse!' as provided in T0, it needs to require S0.rkt. ;; So it would need to be updated for every Ti-test.rkt, changing *nothing* but the 'require'. ;; The actual solution would be using "units": ;; http://docs.racket-lang.org/reference/creatingunits.html?q=unit#%28form._%28%28lib._racket%2Funit..rkt%29._unit%29%29 ;; ;; But rather than add that level of indirection for a student-assignment, we'll just repeat ;; this code inside each Ti-test.rkt. ; my-check-equal? : any, any, string -> boolean ; If the two values aren't equal, print an error message. ; If they are equal (and `print-on-success`), a "." gets printed, just to show progress. ; (define my-check-equal? (local {(define test# 0) (define print-on-success #true)} (λ (actual expected err-msg) (begin (set! test# (add1 test#)) (if (equal? actual expected) (when print-on-success (printf ".~a" (if (zero? (modulo test# 5)) " " ""))) (printf "\ntest #~a failed:\n~a\n" test# err-msg)) ;(check-equal? actual expected) ; Use `check-equal?` to additionally-but-redundantly manage test cases. (equal? actual expected) ; return this boolean )))) ; test-internal-representation : S-example -> void? ; Test that t parses to the correct internal tree-representation (if provided) ; (define (test-internal-representation t) (when (>= (length t) 3) (my-check-equal? (parse! (first t)) (third t) (format "Parsing ~v\nresulted in ~v\ninstead of ~v\nas expected." (first t) (parse! (first t)) (third t))))) ; test-eval : S-example -> void? ; Test that the S-example `eval`s to what it should. ; (define (test-eval t) (my-check-equal? (eval (parse! (first t))) (second t) (format "Program ~v\neval'd to ~v\ninstead of ~v\nas expected." (first t) (eval (parse! (first t))) (second t)))) ; test-parse-inverse-of-to-string : S-example -> void? ; Test that `parse` and `expr->string` are inverses of each other: ; `parse` is a right-inverse: for a string `s`, (expr->string (parse s)) = s, and ; `parse` is a left- inverse: for a tree `expr`, (parse (expr->string expr)) = expr. ; Note that spaces between tokens in a string is ignored, so they're not *quite* exact inverses. ; ; Also, other tests are redundant with checking the left-inverse, ; but we still check it to be independent of other code. ; (define (test-parse-inverse-of-to-string t) (begin (my-check-equal? (string->tokens (expr->string (parse! (first t)))) (string->tokens (first t)) (format "Parsing ~v then converting back to string gave ~v." (first t) (expr->string (parse! (first t))))) (when (>= (length t) 3) (my-check-equal? (parse! (expr->string (third t))) (third t) (format "Converting ~v to string and re-parsing it gave ~v." (third t) (expr->string (third t))))))) ; test-all : S-example -> void? ; Make sure that t meets the following properties: ; i. Parsing the string results in the expected internal representation (*) ; ii. Check that parsing the string and then to-string'ing the result ; gives back the initial string ; iii. Check that to-string'ing the internal representation and then parsing ; that resulting string gives back the initial internal representation (*) ; iv. check that eval'ing the (parsed) string gives the expected value. ; ; (*) steps i,iii can only be performed if the S-example contained all three values. ; If it only contained a string and a value, then only *two* tests get performed. ; This affects the test-number reported, should a later test fail. ; (define (test-all t) (begin (test-internal-representation t) (test-parse-inverse-of-to-string t) ; N.B. counts as two tests (test-eval t))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; run the tests ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (printf "Running T-test-harness:\n") ; Just parse & re-print out each of our tests above: #;(for-each (lambda (t) (printf "~a~n" (expr->string (parse! t)))) (append (list e0 e1 e2 e3 e4) (map first all-tests))) (for-each test-all all-tests) ; This line actually invokes the checker (printf "\nS-test-harness complete; ~v expressions each tested two-or-four ways.\n" (length all-tests)) ; `for-each` is like map except that it discards the result from each function-call; ; it is suitable for functions which are called solely for a side-effect. ; (`test-all` is such a function.)