Hey Cliff, A couple comments:
* The `size` param in `gen-hierarchy` is being passed as the first param to `run`, which limits the count of results returned by core.logic. What's the intention there? I think it's most common to grab one result from run, or all of them (to check if it always generates correct solutions, for example), but not something in the middle, nevertheless based on the size of the test.check iteration. * I think that your generator should generate a graph and a starting point. The traversal could be generated in the body of the `for-all`, and some relation between the graph, the starting point and the traversal should be asserted there. Here's a draft example: ``` (defn gen-graph "Should return a real graph. Node count could be given by size" [size] {1 [2 3 4] 2 [5 6 7] 6 [8 9] 10 [11 12 13]}) (defn traverse-graph [graph start] #{3 4 5 7 8 9}) (defn gen-graph-and-starting-point [size] (gen/bind gen-graph #(gen/tuple (gen/return %) (gen/elements (keys %))))) (defspec graph-traversal (prop/for-all [[graph start] gen-graph-and-starting-point] (let [traversal (traverse-graph graph start)] ; every element in traversal is a node in the graph (is (every? #(in-graph? graph %) traversal)) ; start is not part of traversal (is (not (traversal start))) ; some other property? (is (some-other-property? graph start traversal))))) ``` I'm not sure what the size means with regard to the graph and the starting point. This is important to think about so as to get a better shrinking when there's a failure. The graph generator should implement the shrinking strategy, I think... But all of that should not be strictly necessary to have something working. Hope it helps. Nico On Tue, Dec 9, 2014 at 3:50 AM, cliff <clifford.goldb...@gmail.com> wrote: > Hi Gary > > I have tried your suggestion but I fear there is a deeper problem. > > Thanks > Cliff > > On Monday, 8 December 2014 12:03:47 UTC+2, Gary Verhaegen wrote: >> >> I haven't touched test.check yet, si this might be completely off the >> mark, but based on my limited understanding, here's what I think happens. >> >> for-all probably removes one level of nesting from your generator, which >> means that bindings is bound to a seq with one element, which is a map. >> Then, keys in the (let [ks (into #{} (keys bindings))]... Form turns its >> argument into a seq, which is easy as it is already one, and then tries to >> map the key function on the result. The key function expects a Map.Entry, >> but here the elements of the seq are (or actually is, as there is only one) >> maps, so it blows up. Easiest way to test (I'm on my phone, emse I'd have >> done it) this hypothesis would be to turn the into for into (into #{} (keys >> (first bindings))). >> >> Once confirmed, I would suggest getting ris of that extra level of >> nesting earlier, for example inside your ->maps fn (mapcat identity should >> do the trick). >> >> On Monday, 8 December 2014, cig <clifford...@gmail.com> wrote: >> >>> Hi >>> >>> I would like to test a function which recursively traverses the nodes in >>> a graph and collects them. For example, >>> >>> (def graph {1 [2 3 4] >>> 2 [5 6 7] >>> 6 [8 9] >>> 10 [11 12 13]} >>> >>> my function is given a starting point say, 1 and should then traverse >>> each node which is reachable and return the set. In this case the result >>> should be: >>> #{3, 4, 5, 7, 8, 9} >>> >>> note: it does not return any elements reachable by 10. >>> >>> I would like to test this using test.check, but I would like to generate >>> test data which will exercise the traversal of the graph. >>> >>> I found a similar thread here: https://groups.google.com/ >>> forum/#!topic/clojure/YWeT8BFc8k4 >>> >>> But, I don't think the proposed solution would suit this use case. So, I >>> tried generating a graph with relations using core.logic >>> >>> (defn ->maps >>>> >>>> "take the output of run* and convert it into sequence of maps" >>>> [q] >>>> (let [r (->> q >>>> (partition 2) >>>> (map (fn [[k v]] {k (apply vector v)})) >>>> (apply merge))] >>>> r )) >>>> (defn gen-hierarchy >>>> "generate a related hierarchy" >>>> [size] >>>> (let [vars1 (->> (repeatedly 7 lvar) (into [])) >>>> vars2 (->> (repeatedly 7 lvar) (into [])) >>>> vars3 (->> (repeatedly 7 lvar) (into []))] >>>> (->> >>>> (run size [q] >>>> (fresh [?k1 ?k2 ?k3 ?v1 ?v2 ?v3 ?a] >>>> (fd/distinct vars1) >>>> (everyg #(fd/in % (fd/interval 1 9)) vars1) >>>> (fd/in ?k1 (fd/interval 1 9)) >>>> (rembero ?k1 vars1 ?v1) >>>> (membero ?k2 ?v1) >>>> (fd/distinct vars2) >>>> (everyg #(fd/in % (fd/interval 1 9)) vars2) >>>> (rembero ?k2 vars2 ?v2) >>>> (membero ?k3 ?v2) >>>> (fd/distinct vars3) >>>> (everyg #(fd/in % (fd/interval 1 9)) vars3) >>>> (rembero ?k3 vars3 ?v3) >>>> (appendo [?k1 ?v1] [?k2 ?v2] ?a) >>>> (appendo ?a [?k3 ?v3] q))) >>>> (map ->maps)))) >>>> >>>> >>> Hooking this into test.check. I tried the following: >>> >>> (defn gen-port-hierarchy [] >>>> (gen/sized (fn [size] >>>> (gen/fmap #(gen-hierarchy %) (gen/return size))))) >>>> (gen/sample (gen/not-empty (gen-port-hierarchy)) 1) >>> >>> >>> Which does produce more or less what I'm after: >>> >>> (({6 [2 3 4 5 7 1], 3 [6 7 1 2 4 5], 1 [3 2 4 5 6 7]}) >>> ({5 [1 2 3 4 6 7], 7 [5 3 4 6 1 2], 1 [7 2 3 4 5 6]})) >>> >>> However, when I try use this in a spec: >>> >>> (prop/for-all [bindings (gen/not-empty (gen-port-hierarchy))] >>> >>> (let [ ks (into #{} (keys bindings))] ...) >>> >>> >>> I seem to be getting back a LazySeq which then leads to a >>> ClassCastException: >>> >>> java.lang.ClassCastException: clojure.lang.PersistentArrayMap cannot be >>> cast to java.util.Map$Entry >>> >>> Am I on the completely wrong path here? >>> Or have I incorrectly hooked this generator up with test.check? >>> >>> Any help would be very appreciated. >>> >>> -- >>> You received this message because you are subscribed to the Google >>> Groups "Clojure" group. >>> To post to this group, send email to clojure@googlegroups.com >>> Note that posts from new members are moderated - please be patient with >>> your first post. >>> To unsubscribe from this group, send email to >>> clojure+unsubscr...@googlegroups.com >>> For more options, visit this group at >>> http://groups.google.com/group/clojure?hl=en >>> --- >>> You received this message because you are subscribed to the Google >>> Groups "Clojure" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to clojure+unsubscr...@googlegroups.com. >>> For more options, visit https://groups.google.com/d/optout. >>> >> -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.