Mercurial > lasercutter
view src/clojure/test_clojure/multimethods.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line source
1 ; Copyright (c) Rich Hickey. All rights reserved.2 ; The use and distribution terms for this software are covered by the3 ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)4 ; which can be found in the file epl-v10.html at the root of this distribution.5 ; By using this software in any fashion, you are agreeing to be bound by6 ; the terms of this license.7 ; You must not remove this notice, or any other, from this software.9 ; Author: Frantisek Sodomka, Robert Lachlan11 (ns clojure.test-clojure.multimethods12 (:use clojure.test [clojure.test-clojure.helpers :only (with-var-roots)])13 (:require [clojure.set :as set]))15 ; http://clojure.org/multimethods17 ; defmulti18 ; defmethod19 ; remove-method20 ; prefer-method21 ; methods22 ; prefers24 (defmacro for-all25 [& args]26 `(dorun (for ~@args)))28 (defn hierarchy-tags29 "Return all tags in a derivation hierarchy"30 [h]31 (set/select32 #(instance? clojure.lang.Named %)33 (reduce into #{} (map keys (vals h)))))35 (defn transitive-closure36 "Return all objects reachable by calling f starting with o,37 not including o itself. f should return a collection."38 [o f]39 (loop [results #{}40 more #{o}]41 (let [new-objects (set/difference more results)]42 (if (seq new-objects)43 (recur (set/union results more) (reduce into #{} (map f new-objects)))44 (disj results o)))))46 (defn tag-descendants47 "Set of descedants which are tags (i.e. Named)."48 [& args]49 (set/select50 #(instance? clojure.lang.Named %)51 (or (apply descendants args) #{})))53 (defn assert-valid-hierarchy54 [h]55 (let [tags (hierarchy-tags h)]56 (testing "ancestors are the transitive closure of parents"57 (for-all [tag tags]58 (is (= (transitive-closure tag #(parents h %))59 (or (ancestors h tag) #{})))))60 (testing "ancestors are transitive"61 (for-all [tag tags]62 (is (= (transitive-closure tag #(ancestors h %))63 (or (ancestors h tag) #{})))))64 (testing "tag descendants are transitive"65 (for-all [tag tags]66 (is (= (transitive-closure tag #(tag-descendants h %))67 (or (tag-descendants h tag) #{})))))68 (testing "a tag isa? all of its parents"69 (for-all [tag tags70 :let [parents (parents h tag)]71 parent parents]72 (is (isa? h tag parent))))73 (testing "a tag isa? all of its ancestors"74 (for-all [tag tags75 :let [ancestors (ancestors h tag)]76 ancestor ancestors]77 (is (isa? h tag ancestor))))78 (testing "all my descendants have me as an ancestor"79 (for-all [tag tags80 :let [descendants (descendants h tag)]81 descendant descendants]82 (is (isa? h descendant tag))))83 (testing "there are no cycles in parents"84 (for-all [tag tags]85 (is (not (contains? (transitive-closure tag #(parents h %)) tag)))))86 (testing "there are no cycles in descendants"87 (for-all [tag tags]88 (is (not (contains? (descendants h tag) tag)))))))90 (def family91 (reduce #(apply derive (cons %1 %2)) (make-hierarchy)92 [[::parent-1 ::ancestor-1]93 [::parent-1 ::ancestor-2]94 [::parent-2 ::ancestor-2]95 [::child ::parent-2]96 [::child ::parent-1]]))98 (deftest cycles-are-forbidden99 (testing "a tag cannot be its own parent"100 (is (thrown-with-msg? Throwable #"\(not= tag parent\)"101 (derive family ::child ::child))))102 (testing "a tag cannot be its own ancestor"103 (is (thrown-with-msg? Throwable #"Cyclic derivation: :clojure.test-clojure.multimethods/child has :clojure.test-clojure.multimethods/ancestor-1 as ancestor"104 (derive family ::ancestor-1 ::child)))))106 (deftest using-diamond-inheritance107 (let [diamond (reduce #(apply derive (cons %1 %2)) (make-hierarchy)108 [[::mammal ::animal]109 [::bird ::animal]110 [::griffin ::mammal]111 [::griffin ::bird]])112 bird-no-more (underive diamond ::griffin ::bird)]113 (assert-valid-hierarchy diamond)114 (assert-valid-hierarchy bird-no-more)115 (testing "a griffin is a mammal, indirectly through mammal and bird"116 (is (isa? diamond ::griffin ::animal)))117 (testing "a griffin is a bird"118 (is (isa? diamond ::griffin ::bird)))119 (testing "after underive, griffin is no longer a bird"120 (is (not (isa? bird-no-more ::griffin ::bird))))121 (testing "but it is still an animal, via mammal"122 (is (isa? bird-no-more ::griffin ::animal)))))124 (deftest derivation-world-bridges-to-java-inheritance125 (let [h (derive (make-hierarchy) java.util.Map ::map)]126 (testing "a Java class can be isa? a tag"127 (is (isa? h java.util.Map ::map)))128 (testing "if a Java class isa? a tag, so are its subclasses..."129 (is (isa? h java.util.HashMap ::map)))130 (testing "...but not its superclasses!"131 (is (not (isa? h java.util.Collection ::map))))))133 (deftest global-hierarchy-test134 (with-var-roots {#'clojure.core/global-hierarchy (make-hierarchy)}135 (assert-valid-hierarchy @#'clojure.core/global-hierarchy)136 (testing "when you add some derivations..."137 (derive ::lion ::cat)138 (derive ::manx ::cat)139 (assert-valid-hierarchy @#'clojure.core/global-hierarchy))140 (testing "...isa? sees the derivations"141 (is (isa? ::lion ::cat))142 (is (not (isa? ::cat ::lion))))143 (testing "... you can traverse the derivations"144 (is (= #{::manx ::lion} (descendants ::cat)))145 (is (= #{::cat} (parents ::manx)))146 (is (= #{::cat} (ancestors ::manx))))147 (testing "then, remove a derivation..."148 (underive ::manx ::cat))149 (testing "... traversals update accordingly"150 (is (= #{::lion} (descendants ::cat)))151 (is (nil? (parents ::manx)))152 (is (nil? (ancestors ::manx))))))154 #_(defmacro for-all155 "Better than the actual for-all, if only it worked."156 [& args]157 `(reduce158 #(and %1 %2)159 (map true? (for ~@args))))