match-test.ss
#lang scheme/base

(require (prefix-in scheme: (only-in scheme/base eq? eqv? equal?))
         srfi/26
         "match.ss"
         "test-base.ss")

; Tests ------------------------------------------

(define match-tests
  (test-suite "match.ss"
      
    (test-case "eq? and equal? expand to their normal forms outside of a match pattern"
      (check-equal? eq? scheme:eq?)
      (check-equal? equal? scheme:equal?))
    
    (test-case "eq? matches (and does not) in the correct situations"
      (let ([x 1]
            [y "1"])
        (check-equal? (match 1 [(eq? 1) "yes"] [_ "no"]) "yes")
        (check-equal? (match 1 [(eq? x) "yes"] [_ "no"]) "yes")
        (check-equal? (match 2 [(eq? 1) "yes"] [_ "no"]) "no")
        (check-equal? (match 2 [(eq? x) "yes"] [_ "no"]) "no")
        (check-equal? (match "1" [(eq? "1") "yes"] [_ "no"]) "no")
        (check-equal? (match "1" [(eq? y) "yes"] [_ "no"]) "no")
        (check-exn exn:fail? (cut match 2 [(eq? x) "yes"]))))
    
    (test-case "equal? matches (and does not) in the correct situations"
      (let ([x 1]
            [y "1"])
        (check-equal? (match 1 [(equal? 1) "yes"] [_ "no"]) "yes")
        (check-equal? (match 1 [(equal? x) "yes"] [_ "no"]) "yes")
        (check-equal? (match 2 [(equal? 1) "yes"] [_ "no"]) "no")
        (check-equal? (match 2 [(equal? x) "yes"] [_ "no"]) "no")
        (check-equal? (match "1" [(equal? "1") "yes"] [_ "no"]) "yes")
        (check-equal? (match "1" [(equal? y) "yes"] [_ "no"]) "yes")
        (check-exn exn:fail? (cut match 2 [(equal? x) "yes"]))))
    
    (test-case "eq? binds correctly"
      (let ([num1 123]
            [num2 123])
        (match num1
          ; Match num1 against num2 and (the confusing bit) rebind num2 inside the match body:
          [(eq? num2 num2)
           ; Mutate the inner version of num2:
           (set! num2 1000)])
        ; We should be back to the outer version now:
        (check-eq? num2 123)))
    
    (test-case "equal? binds correctly"
      (let ([str1 "123"]
            [str2 "123"]
            [str3 #f])
        (check-false (eq? str1 str2))
        (check-true  (equal? str1 str2))
        (match str1
          ; Match str1 against str2 and (the confusing bit) rebind str2 inside the match body:
          [(equal? str2 str2)
           ; The inner str1 should be eq? to str2:
           (check-true (eq? str1 str2))
           (check-true (equal? str1 str2))
           ; Store the inner str1 in str3:
           (set! str3 str2)])
        ; str3 should now be a copy of str1:
        (check-false (eq? str1 str2))
        (check-true  (equal? str1 str2))
        (check-true  (eq? str1 str3))
        (check-true  (equal? str1 str3))
        (check-false (eq? str2 str3))
        (check-true  (equal? str2 str3))))))

; Provide statements -----------------------------

(provide match-tests)