(defsuite
  "promises"
  (deftest
    "delay creates a promise"
    (do (assert (promise? (delay 42)))))
  (deftest
    "delay does not evaluate immediately"
    (do
      (let
        ((count 0))
        (let
          ((p (delay (do (set! count (+ count 1)) count))))
          (assert= 0 count)))))
  (deftest
    "force evaluates the expression"
    (do (assert= 42 (force (delay 42)))))
  (deftest
    "force with arithmetic"
    (do (assert= 10 (force (delay (+ 3 7))))))
  (deftest
    "force memoises result"
    (do
      (let
        ((count 0))
        (let
          ((p (delay (do (set! count (+ count 1)) count))))
          (force p)
          (force p)
          (assert= 1 count)))))
  (deftest
    "force returns same value on repeated calls"
    (do
      (let
        ((p (delay (+ 1 2))))
        (assert= 3 (force p))
        (assert= 3 (force p)))))
  (deftest
    "make-promise creates an already-forced promise"
    (do
      (let
        ((p (make-promise 99)))
        (assert (promise? p))
        (assert= 99 (force p)))))
  (deftest
    "make-promise memoises without evaluating"
    (do
      (let
        ((count 0))
        (let
          ((p (make-promise 42)))
          (force p)
          (force p)
          (assert= 0 count)))))
  (deftest
    "promise? returns true for delay"
    (do (assert (promise? (delay 1)))))
  (deftest
    "promise? returns true for make-promise"
    (do (assert (promise? (make-promise 1)))))
  (deftest
    "promise? returns false for non-promise"
    (do
      (assert= false (promise? 42))
      (assert= false (promise? "hello"))
      (assert= false (promise? nil))
      (assert= false (promise? (list 1 2)))))
  (deftest
    "force non-promise returns value unchanged"
    (do
      (assert= 42 (force 42))
      (assert= "hi" (force "hi"))
      (assert= nil (force nil))))
  (deftest
    "delay captures environment"
    (do
      (let
        ((x 10))
        (let
          ((p (delay (+ x 5))))
          (assert= 15 (force p))))))
  (deftest
    "delay-force basic"
    (do (assert= 42 (force (delay-force (delay 42))))))
  (deftest
    "delay-force chains"
    (do
      (assert=
        5
        (force (delay-force (delay-force (delay 5)))))))
  (deftest
    "delay with string"
    (do (assert= "hello" (force (delay "hello")))))
  (deftest
    "delay with list"
    (do
      (assert-equal
        (list 1 2 3)
        (force (delay (list 1 2 3))))))
  (deftest
    "delay with function call"
    (do (assert= 6 (force (delay (* 2 3))))))
  (deftest
    "nested delay"
    (do
      (let
        ((p (delay (delay 99))))
        (assert (promise? (force p))))))
  (deftest
    "force already forced promise"
    (do
      (let
        ((p (make-promise 7)))
        (assert= 7 (force p))
        (assert= 7 (force p)))))
  (deftest
    "lazy stream first element"
    (do
      (define (stream-cons x s) (delay (list x s)))
      (define (stream-car s) (first (force s)))
      (define (stream-cdr s) (nth (force s) 1))
      (let
        ((s (stream-cons 1 (stream-cons 2 (stream-cons 3 nil)))))
        (assert= 1 (stream-car s))
        (assert= 2 (stream-car (stream-cdr s))))))
  (deftest
    "delay-force is a promise"
    (do (assert (promise? (delay-force (delay 1))))))
  (deftest
    "force with side effects runs once"
    (do
      (let
        ((log (list)))
        (let
          ((p (delay (do (set! log (cons 42 log)) 42))))
          (force p)
          (force p)
          (assert= 1 (len log))))))
  (deftest
    "make-promise with nil"
    (do
      (let
        ((p (make-promise nil)))
        (assert (promise? p))
        (assert= nil (force p)))))
  (deftest
    "delay in let binding"
    (do
      (let
        ((p (delay (+ 10 20))))
        (assert= 30 (force p))))))