;; lib/relations/tests/tree.sx — extension: common ancestors, LCA, topo order. (define relations-tt-pass 0) (define relations-tt-fail 0) (define relations-tt-failures (list)) (define relations-tt-check! (fn (name got expected) (if (= got expected) (set! relations-tt-pass (+ relations-tt-pass 1)) (do (set! relations-tt-fail (+ relations-tt-fail 1)) (append! relations-tt-failures (str name "\n expected: " expected "\n got: " got)))))) (define relations-tt-subset? (fn (xs ys) (cond ((= (len xs) 0) true) ((relations-member? (first xs) ys) (relations-tt-subset? (rest xs) ys)) (else false)))) (define relations-tt-set=? (fn (xs ys) (and (= (len xs) (len ys)) (relations-tt-subset? xs ys) (relations-tt-subset? ys xs)))) ;; Is xs a valid topo order? every node appears once and no node precedes one of ;; its ancestors. We check the simpler invariant: for each edge u->v (parent), ;; u appears before v in the order. (define relations-tt-index-of (fn (x xs i) (cond ((= (len xs) 0) -1) ((= (first xs) x) i) (else (relations-tt-index-of x (rest xs) (+ i 1)))))) ;; Diamond with an extra branch: ;; parent: a->b, a->c, b->d, c->d, b->e ;; member (different kind): m->n (define relations-tt-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 m) (quote n) (quote member)))))) ;; A cyclic kind, to confirm topo-order refuses it. (define relations-tt-cyc-fixture (fn () (relations-build-db (list (relations-rel (quote x) (quote y) (quote parent)) (relations-rel (quote y) (quote x) (quote parent)))))) (define relations-tt-run-all! (fn () (let ((db (relations-tt-fixture)) (cyc (relations-tt-cyc-fixture))) (do (relations-tt-check! "common ancestors of d and e" (relations-tt-set=? (relations-common-ancestors db (quote d) (quote e) (quote parent)) (list (quote a) (quote b))) true) (relations-tt-check! "common ancestors of b and c" (relations-tt-set=? (relations-common-ancestors db (quote b) (quote c) (quote parent)) (list (quote a))) true) (relations-tt-check! "no common ancestors across kinds" (relations-common-ancestors db (quote d) (quote n) (quote parent)) (list)) (relations-tt-check! "lca of d and e is b" (relations-tt-set=? (relations-lca db (quote d) (quote e) (quote parent)) (list (quote b))) true) (relations-tt-check! "lca of b and c is a" (relations-tt-set=? (relations-lca db (quote b) (quote c) (quote parent)) (list (quote a))) true) (relations-tt-check! "lca of d and d-sibling-path picks deepest" (relations-tt-set=? (relations-lca db (quote d) (quote d) (quote parent)) (list (quote b) (quote c))) true) (relations-tt-check! "no lca when unrelated" (relations-lca db (quote a) (quote n) (quote parent)) (list)) (let ((order (relations-topo-order db (quote parent)))) (do (relations-tt-check! "topo order covers all nodes" (relations-tt-set=? order (list (quote a) (quote b) (quote c) (quote d) (quote e))) true) (relations-tt-check! "topo: a before b" (< (relations-tt-index-of (quote a) order 0) (relations-tt-index-of (quote b) order 0)) true) (relations-tt-check! "topo: b before d" (< (relations-tt-index-of (quote b) order 0) (relations-tt-index-of (quote d) order 0)) true) (relations-tt-check! "topo: c before d" (< (relations-tt-index-of (quote c) order 0) (relations-tt-index-of (quote d) order 0)) true) (relations-tt-check! "topo: b before e" (< (relations-tt-index-of (quote b) order 0) (relations-tt-index-of (quote e) order 0)) true))) (relations-tt-check! "topo order of cyclic kind is nil" (relations-topo-order cyc (quote parent)) nil) (do (relations/load! (list (relations-rel (quote r) (quote s) (quote parent)) (relations-rel (quote r) (quote t) (quote parent)) (relations-rel (quote s) (quote u) (quote parent)) (relations-rel (quote t) (quote u) (quote parent)))) (relations-tt-check! "api common-ancestors" (relations-tt-set=? (relations/common-ancestors (quote u) (quote u) (quote parent)) (list (quote r) (quote s) (quote t))) true) (relations-tt-check! "api lca" (relations-tt-set=? (relations/lca (quote s) (quote t) (quote parent)) (list (quote r))) true) (relations-tt-check! "api topo-order covers nodes" (relations-tt-set=? (relations/topo-order (quote parent)) (list (quote r) (quote s) (quote t) (quote u))) true) (relations/load! (list))))))) (define relations-tree-tests-run! (fn () (do (set! relations-tt-pass 0) (set! relations-tt-fail 0) (set! relations-tt-failures (list)) (relations-tt-run-all!) {:failures relations-tt-failures :total (+ relations-tt-pass relations-tt-fail) :passed relations-tt-pass :failed relations-tt-fail})))