Fork me on GitHub
#test-check
<
2020-12-04
>
andy.fingerhut21:12:05

I am using the collections-check library, which currently depends upon not-the-most-recent version of test.chuck and test.check libraries

andy.fingerhut21:12:59

In trying to verify that it can catch certain kinds of errors, I am intentionally introducing some small errors into a collection implementation. In one case, I make it throw an exception.

andy.fingerhut21:12:21

collections-check makes a call to test.chuck's checking macro, like this:

(chuck/checking "vector-like" num-tests-or-options
    [actions (gen-vector-actions element-generator (transient? empty-coll) ordered?)]
    (let [[a b actions] (build-collections empty-coll base true actions)]
      (assert-equivalent-vectors a b)))

andy.fingerhut21:12:52

The function assert-equivalent-vectors is a series of clojure.test is macro invocations.

andy.fingerhut21:12:26

When I change the implementation so that it throws an exception, the exception comes not from calling assert-equivalent-vectors, but calling build-collections.

andy.fingerhut21:12:22

I put a print statement with a counter inside of assert-equivalent-vectors to see what parameters it was being called with, and how many times, and when there is no exception thrown, I see the checking macro limiting itself to num-tests-or-options iterations (100 or 200 in my short tests).

andy.fingerhut21:12:35

When build-collections throws an exception, something happens where I see assert-equivalent-vectors continue to be called many, many times, perhaps an infinite loop, but a println I added within test.check's quick-check macro (or function? I forget) no longer prints each time through the loop. Either the println gets messed up in some weird way (very odd if so), or somehow that quick-check loop is somehow causing assert-equivalent-vectors to be called in an infinite loop, without calling build-collections again.

andy.fingerhut21:12:59

This may all be rubber ducking if I figure this out, but wanted to ask if:

gfredericks21:12:11

Shrinking is part of this, right?

andy.fingerhut21:12:33

(a) Is test.chuck's checking macro intended to be used with a checking function that calls clojure.test/is one or more times, and its return value isn't important?

andy.fingerhut21:12:55

Is there a way I can tell whether shrinking is involved? I'm happy adding extra println's anywhere here.

andy.fingerhut21:12:07

Oh, perhaps shrinking is causing many calls to assert-equivalent-vectors, and they are all failing, and shrinking is "stuck" somehow?

gfredericks21:12:08

is is the whole point of checking

gfredericks21:12:22

I don't have any theories for why you'd see it stop printing

andy.fingerhut21:12:04

ok. Will keep looking a bit longer.

gfredericks21:12:39

You can money patch that to confirm it's shrinking

andy.fingerhut21:12:48

I was hoping that checking would quickly show some output of a failure if an exception was thrown, rather than going into an infinite loop (if it is infinite -- not sure whether it is infinite or just long)

andy.fingerhut21:12:19

Phone handier than links today? 🙂

gfredericks21:12:36

Yep 🙂

gfredericks21:12:52

That's in the base t.c namespace

andy.fingerhut21:12:59

I can try updating to latest versions of test.chuck and test.check, but initially when I tried that there seemed to be some incorrect args being passed around somewhere, and hadn't figured that out yet to make it work well enough to debug.

andy.fingerhut21:12:19

Thanks for the leads

gfredericks21:12:14

n.p.; happy to help out if you run into something

andy.fingerhut21:12:20

Should latest test.check and test.chuck be compatible with each other, as far as you know?

andy.fingerhut21:12:15

I think collections-check may need some changes to work with latest test.chuck, but would prefer not to guess too many times at which pairs of test.chuck/test.check version combinations should work with each other.

gfredericks21:12:04

Both libraries have generally avoided breaking changes

andy.fingerhut22:12:02

Wow. I think I found it. collections-check is passing a double for the num-tests-or-options, which test.chuck's times function in the latest version returns nil for.

andy.fingerhut22:12:23

I mean, I found the thing that I thought would take 30 seconds in about 20 mins 🙂. Not the final answer.

gfredericks22:12:39

This thing returns nil???

andy.fingerhut22:12:07

This thing:

(defn times [num-tests-or-options]
  (cond (map? num-tests-or-options)     (:num-tests num-tests-or-options tc.clojure-test/*default-test-count*)
        (integer? num-tests-or-options) num-tests-or-options))

gfredericks22:12:45

Oh geez there's two of them

andy.fingerhut22:12:45

It was weird that collection-check was using doubles anyway -- probably Zach T liked 1e2, 1e3 for brevity on test counts.

gfredericks22:12:44

This is why we can't have nice static type systems

andy.fingerhut23:12:58

The shrink-loop is definitely going for far more iterations than I would have expected.

gfredericks23:12:30

What sort of data is it?

andy.fingerhut23:12:37

collections-check generates sequences of operations on data structures, in this case on vectors, like assoc, conj, transient, persistent!, seq, etc.

andy.fingerhut23:12:59

So I think it must be attempting to shrink that sequence of operations down to a case where it passes, and then increase it a bit?

andy.fingerhut23:12:17

I am printing out the length of the vectors produced in the function that does multiple is calls to compare the results of two different vector implementations, each produced with two different vector implementations, and they do fluctuate a bit, but they are not getting consistently smaller.

andy.fingerhut23:12:47

I guess I could figure out how to look at the sequence of operations being tried in the shrink-loop to see if it is getting smaller/simpler

gfredericks23:12:41

The pattern isn't necessarily simple

gfredericks23:12:34

Especially if the generator uses bind

andy.fingerhut23:12:23

This is a wild guess, but it also generates the values to be added to the vector via gen/int, and there are about 500 of them in the failing vector, so if it is spending any work trying to simplify those numeric values, most of those are going to give the same result, since the error I put into the bad vector implementation fails over about 520 vector elements, unless pretty much most of the vector elements are equal to each other.

gfredericks23:12:43

What problem are you actually trying to solve?

andy.fingerhut23:12:39

Good question. In this case, I wanted to verify that collections-check could detect a simple bug in an implementation, because I wanted to better trust the results when it returns no bugs found.

gfredericks23:12:25

Ah okay, and the fact that it's spinning instead of returning failure is concerning

andy.fingerhut23:12:26

So I'm really done verifying that it can find this example bug I've introduced, but only because I've added extra debug prints. If I don't have those, I just get what appears to be an infinite loop, with no failing test case given.

gfredericks23:12:09

Wrap the gen in gen/no-shrink 😛 😉

andy.fingerhut23:12:21

I suppose I could eliminate the possibility of gen/int shrinking slowing things down, by forcing vector elements in the sequence of operations to be increasing numbers.

andy.fingerhut23:12:30

OK, didn't know about that, so thanks.

gfredericks23:12:38

If there's not a feature for limiting shrink time then there oughta be

andy.fingerhut23:12:59

Just (gen/no-shrink gen/int) where I currently have gen/int I guess?

gfredericks23:12:33

You could. Or you could wrap the whole collection generator

gfredericks23:12:13

You get different effects for each

andy.fingerhut23:12:49

Is there anything in shrink-loop that represents the value passed to the function being tested?

andy.fingerhut23:12:00

i.e. that I could print and look at?

andy.fingerhut23:12:36

I know it isn't head and it is not result because those have the wrong class

gfredericks23:12:31

But the structure here is more function oriented than data oriented

gfredericks23:12:25

You know what I think there's a callback feature you'd like

andy.fingerhut23:12:36

The value of (:args (rose/root head)) might be what I'm seeking

gfredericks23:12:49

Use reporter-fn

andy.fingerhut23:12:19

I'm comfortable hacking on my own modified copy of test.check and test.chuck if it helps, which is how I got to that result.

andy.fingerhut23:12:38

Thanks for the help. Will be away from keyboard for a bit here.