Welcome! Please see the About page for a little more info on how this works.

0 votes
in test.check by

Including functionality to add support for nested properties would be beneficial. An example of how this may work follows:

`
(defn exists?
[e coll]
(some #{e} coll))

(defn remove-elem
[e coll]
(remove #{e} coll))

;; this test should fail, and illustrates nested properties
(quick-check (for-all [v (gen/vector gen/int)]

           (for-all [e (elements v)]
             #(exists? e (remove-elem e v)))))

`

The requirement for this is support for reifying properties, which is not currently implemented.

(link: https://github.com/reiddraper/simple-check/issues/10 text: Original issue - GitHub #10)

12 Answers

0 votes
by

Comment made by: maciej.jaskowski

What's the status of this issue?
It would help us a lot to have this functionality up and running in the following case: I want to verify my path finding algorithm finds optimal solution in the given graph G, so I generate the graph and a big number of random paths and check that all the random paths are "less optimal" then the one found by the algorithm.

Having nested for-all that would be simple. Is there a way around?

0 votes
by

Comment made by: reiddraper

Sorry for the lack of update here. Nested properties are still in a bit of hammock-time on my part. The good news is they are merely convenient, and you can achieve the exact same effect with {{clojure.test.check.generators/bind}}. You can see an example of {{bind}} being used (link: https://github.com/clojure/test.check/blob/v0.5.8/doc/generator-examples.md#a-vector-and-a-random-element-from-it text: here).

0 votes
by

Comment made by: maciej.jaskowski

Apparently I need some more hints because all I can generate (using gen/bing, btw) are pairs '(graph list-of-random-paths)

That is useful as long as the algorithm works as expected. However, once it fails it's pretty hard to figure out which of random-paths failed it.

0 votes
by

Comment made by: reiddraper

Can you show me how you would write the test with nested properties, and then perhaps I can help you rewrite it to use {{bind}} instead?

0 votes
by

Comment made by: maciej.jaskowski

Great(image: Thank you)

Please, find the most important part below.

`
(defn gen-matrix-and-paths [size]

(gen/bind
  (gen-matrix gen/s-pos-int size)
  (fn [matrix]
    (let [ nodes (into [] (range (count matrix)))
           gen-perms (gen-permutations nodes)
           perms (distinct (gen/sample gen-perms 500)) ]
      (gen/tuple 
        (gen/return matrix) 
        (gen/return perms))))))

`

gen-matrix generates a square matrix of incidence e.g. (link: [0 1) (link: 4 0)]
gen-permutations generates a permutation of provided vector

0 votes
by

Comment made by: reiddraper

I'm confused. What issue are you running into? The code you've pasted uses {{bind}}, and not a nested property.

0 votes
by

Comment made by: maciej.jaskowski

Ok but now I can only write something like this:

`
(prop/for-all [data (gen-matrix-and-paths 6)]
(let [ [matrix random-paths] data ]

 (not-worse-then-others? tsp matrix random-paths))))

`

which, if my 'tsp' algorithm is not working correctly would point me to a matrix and a vector (length 500) of paths one of which is counterexample. It would be better if instead I got the matrix and a single counterexample.

0 votes
by

Comment made by: reiddraper

I'm still not sure what this would look like using a nested-property. Can you please write the code the way you'd like to see it as a nested property? This will involve at least two for-alls.

0 votes
by

Comment made by: maciej.jaskowski

Sure!

I would like to write something like that:

`
(prop/for-all [matrix (gen-matrix 6)]
(prop/for-all [random-path (gen-random-path matrix)]

(<= (cost (tsp matrix)) (cost random-path))))

`

0 votes
by

Comment made by: maciej.jaskowski

ping!

0 votes
by

Comment made by: reiddraper

Ok, let's translate that to using {{gen/bind}}. Just like with nested properties, {{bind}} allows you to create generators that depend on the value of another generator. For example, your {{gen-random-path}} depends on the previously generated {{matrix}} variable. So let's write a generator that returns both a {{matrix}} and a {{random-path}}.

`
(def matrix-and-path
"Return a two-tuple of [matrix random-path]"
(gen/bind (gen-matrix 6)

        (fn [matrix]
           (gen/tuple (gen/return matrix) (gen-random-path matrix)))))

(prop/for-all [[matrix random-path] matrix-and-path]
(<= (cost (tsp matrix)) (cost random-path))
`

0 votes
by
Reference: https://clojure.atlassian.net/browse/TCHECK-7 (reported by hypirion)
...