;; Tests for dynamic-wind: after-thunk fires on normal return,
;; non-local exit via raise/guard, and call/cc escape.

(defsuite
  "dynamic-wind-basic"
  (deftest
    "after fires on normal return"
    (let
      ((log (list)))
      (dynamic-wind
        (fn () (append! log "before"))
        (fn () (append! log "body"))
        (fn () (append! log "after")))
      (assert= 3 (len log))
      (assert= "before" (nth log 0))
      (assert= "body" (nth log 1))
      (assert= "after" (nth log 2))))
  (deftest
    "after fires on raise escape"
    (let
      ((log (list)))
      (guard
        (e (true nil))
        (dynamic-wind
          (fn () (append! log "before"))
          (fn () (append! log "body") (error "boom"))
          (fn () (append! log "after"))))
      (assert= 3 (len log))
      (assert= "before" (nth log 0))
      (assert= "body" (nth log 1))
      (assert= "after" (nth log 2))))
  (deftest
    "after fires on call/cc escape"
    (let
      ((log (list)))
      (call/cc
        (fn
          (k)
          (dynamic-wind
            (fn () (append! log "before"))
            (fn () (append! log "body") (k nil))
            (fn () (append! log "after")))))
      (assert= 3 (len log))
      (assert= "before" (nth log 0))
      (assert= "body" (nth log 1))
      (assert= "after" (nth log 2))))
  (deftest
    "nested dynamic-wind after-thunks fire LIFO on normal return"
    (let
      ((log (list)))
      (dynamic-wind
        (fn () (append! log "outer-before"))
        (fn
          ()
          (dynamic-wind
            (fn () (append! log "inner-before"))
            (fn () (append! log "inner-body"))
            (fn () (append! log "inner-after"))))
        (fn () (append! log "outer-after")))
      (assert= 5 (len log))
      (assert= "outer-before" (nth log 0))
      (assert= "inner-before" (nth log 1))
      (assert= "inner-body" (nth log 2))
      (assert= "inner-after" (nth log 3))
      (assert= "outer-after" (nth log 4))))
  (deftest
    "nested dynamic-wind after-thunks fire LIFO on raise"
    (let
      ((log (list)))
      (guard
        (e (true nil))
        (dynamic-wind
          (fn () (append! log "outer-before"))
          (fn
            ()
            (dynamic-wind
              (fn () (append! log "inner-before"))
              (fn () (append! log "inner-body") (error "boom"))
              (fn () (append! log "inner-after"))))
          (fn () (append! log "outer-after"))))
      (assert= 5 (len log))
      (assert= "outer-before" (nth log 0))
      (assert= "inner-before" (nth log 1))
      (assert= "inner-body" (nth log 2))
      (assert= "inner-after" (nth log 3))
      (assert= "outer-after" (nth log 4))))
  (deftest
    "before and after are called"
    (let
      ((count 0))
      (dynamic-wind
        (fn () (set! count (+ count 1)))
        (fn () nil)
        (fn () (set! count (+ count 10))))
      (assert= 11 count)))
  (deftest
    "dynamic-wind return value is body result"
    (let
      ((result (dynamic-wind (fn () nil) (fn () 42) (fn () nil))))
      (assert= 42 result)))
  (deftest
    "after fires before guard handler"
    (let
      ((log (list)))
      (guard
        (e (true (append! log "guard-handler")))
        (dynamic-wind
          (fn () nil)
          (fn () (error "boom"))
          (fn () (append! log "after"))))
      (assert= 2 (len log))
      (assert= "after" (nth log 0))
      (assert= "guard-handler" (nth log 1)))))
