;; lib/relations/tests/routes.sx — extension: all simple paths (route enumeration).

(define relations-ro-pass 0)
(define relations-ro-fail 0)
(define relations-ro-failures (list))

(define
  relations-ro-check!
  (fn
    (name got expected)
    (if
      (= got expected)
      (set! relations-ro-pass (+ relations-ro-pass 1))
      (do
        (set! relations-ro-fail (+ relations-ro-fail 1))
        (append!
          relations-ro-failures
          (str name "\n    expected: " expected "\n    got:      " got))))))

(define
  relations-ro-subset?
  (fn
    (xs ys)
    (cond
      ((= (len xs) 0) true)
      ((relations-member? (first xs) ys)
        (relations-ro-subset? (rest xs) ys))
      (else false))))

;; Order-insensitive set equality; elements compared structurally (works for
;; lists-of-paths since `=` is structural).
(define
  relations-ro-set=?
  (fn
    (xs ys)
    (and
      (= (len xs) (len ys))
      (relations-ro-subset? xs ys)
      (relations-ro-subset? ys xs))))

;; Diamond + branch + a cycle with an exit.
;;   parent: a->b, a->c, b->d, c->d, b->e
;;   member: a->z (a different kind, to test isolation)
;;   parent cycle: g->h, h->g, h->out
(define
  relations-ro-fixture
  (fn
    ()
    (relations-build-db
      (list
        (relations-rel (quote a) (quote b) (quote parent))
        (relations-rel (quote a) (quote c) (quote parent))
        (relations-rel (quote b) (quote d) (quote parent))
        (relations-rel (quote c) (quote d) (quote parent))
        (relations-rel (quote b) (quote e) (quote parent))
        (relations-rel (quote a) (quote z) (quote member))
        (relations-rel (quote g) (quote h) (quote parent))
        (relations-rel (quote h) (quote g) (quote parent))
        (relations-rel (quote h) (quote out) (quote parent))))))

(define
  relations-ro-run-all!
  (fn
    ()
    (let
      ((db (relations-ro-fixture)))
      (do
        (relations-ro-check!
          "two routes a->d"
          (relations-ro-set=?
            (relations-all-paths db (quote a) (quote d) (quote parent))
            (list
              (list (quote a) (quote b) (quote d))
              (list (quote a) (quote c) (quote d))))
          true)
        (relations-ro-check!
          "single route a->e"
          (relations-all-paths db (quote a) (quote e) (quote parent))
          (list (list (quote a) (quote b) (quote e))))
        (relations-ro-check!
          "no route -> empty"
          (relations-all-paths db (quote a) (quote zzz) (quote parent))
          (list))
        (relations-ro-check!
          "self route is the singleton path"
          (relations-all-paths db (quote a) (quote a) (quote parent))
          (list (list (quote a))))
        (relations-ro-check!
          "route through a cycle terminates"
          (relations-all-paths db (quote g) (quote out) (quote parent))
          (list (list (quote g) (quote h) (quote out))))
        (relations-ro-check!
          "route count a->d is 2"
          (len (relations-all-paths db (quote a) (quote d) (quote parent)))
          2)
        (relations-ro-check!
          "kind isolation: no parent route to member target"
          (relations-all-paths db (quote a) (quote z) (quote parent))
          (list))
        (relations-ro-check!
          "member route a->z"
          (relations-all-paths db (quote a) (quote z) (quote member))
          (list (list (quote a) (quote z))))
        (do
          (relations/load!
            (list
              (relations-rel (quote p) (quote q) (quote parent))
              (relations-rel (quote p) (quote r) (quote parent))
              (relations-rel (quote q) (quote s) (quote parent))
              (relations-rel (quote r) (quote s) (quote parent))))
          (relations-ro-check!
            "api all-paths two routes p->s"
            (relations-ro-set=?
              (relations/all-paths (quote p) (quote s) (quote parent))
              (list
                (list (quote p) (quote q) (quote s))
                (list (quote p) (quote r) (quote s))))
            true)
          (relations/load! (list)))))))

(define
  relations-routes-tests-run!
  (fn
    ()
    (do
      (set! relations-ro-pass 0)
      (set! relations-ro-fail 0)
      (set! relations-ro-failures (list))
      (relations-ro-run-all!)
      {:failures relations-ro-failures :total (+ relations-ro-pass relations-ro-fail) :passed relations-ro-pass :failed relations-ro-fail})))
