Hi Nico Thanks for your advice.
* 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. Agreed. I should allow 'run' to return all results and rather let test.check reduce the returned set. * 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: I was generating the starting point independently with a, > (gen/not-empty (gen/int)) 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. Worrying about shrinking may be a bit past my current skill level with test.check, maybe as the next stage. :-) I will refactor my solution to take into account some of your points above and see if I have any more success. Thanks again for your help Nico. On Wednesday, 10 December 2014 06:07:26 UTC+2, Nicolás Berger wrote: > > 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...@gmail.com <javascript:> > > 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 clo...@googlegroups.com >> <javascript:> >> Note that posts from new members are moderated - please be patient with >> your first post. >> To unsubscribe from this group, send email to >> clojure+u...@googlegroups.com <javascript:> >> 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+u...@googlegroups.com <javascript:>. >> 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.