test-check

2020-12-04T21:36:05.043Z

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

2020-12-04T21:36:59.044100Z

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.

2020-12-04T21:38:21.044600Z

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)))

2020-12-04T21:38:52.045Z

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

2020-12-04T21:39:26.045800Z

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

2020-12-04T21:40:22.047Z

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).

2020-12-04T21:42:35.049400Z

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.

2020-12-04T21:42:59.050Z

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

2020-12-04T21:43:11.050800Z

Shrinking is part of this, right?

2020-12-04T21:43:33.051300Z

(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?

2020-12-04T21:43:55.051800Z

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

2020-12-04T21:45:07.053200Z

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

2020-12-04T21:45:08.053300Z

is is the whole point of checking

2020-12-04T21:46:22.054300Z

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

2020-12-04T21:47:04.054900Z

ok. Will keep looking a bit longer.

2020-12-04T21:47:39.056Z

You can money patch that to confirm it's shrinking

2020-12-04T21:47:48.056500Z

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)

2020-12-04T21:48:19.057Z

Phone handier than links today? 🙂

2020-12-04T21:48:36.057400Z

Yep 🙂

2020-12-04T21:48:52.057800Z

That's in the base t.c namespace

2020-12-04T21:49:59.058600Z

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.

2020-12-04T21:50:19.058800Z

Thanks for the leads

2020-12-04T21:52:14.059700Z

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

2020-12-04T21:53:20.060400Z

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

2020-12-04T21:54:15.061400Z

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.

2020-12-04T21:54:15.061500Z

Yes

2020-12-04T21:55:04.062600Z

Both libraries have generally avoided breaking changes

2020-12-04T22:15:02.063400Z

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.

2020-12-04T22:15:23.063900Z

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

2020-12-04T22:17:39.064400Z

This thing returns nil???

2020-12-04T22:20:07.064800Z

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))

2020-12-04T22:22:45.065100Z

Oh geez there's two of them

2020-12-04T22:23:45.065700Z

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

2020-12-04T22:24:44.066600Z

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

2020-12-04T23:18:58.066700Z

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

2020-12-04T23:19:30.067Z

What sort of data is it?

2020-12-04T23:20:37.067800Z

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

2020-12-04T23:20:59.068300Z

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?

2020-12-04T23:22:17.069200Z

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.

2020-12-04T23:22:47.069700Z

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

2020-12-04T23:24:41.070Z

The pattern isn't necessarily simple

2020-12-04T23:25:34.071100Z

Especially if the generator uses bind

2020-12-04T23:26:23.072200Z

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.

2020-12-04T23:28:43.072500Z

What problem are you actually trying to solve?

2020-12-04T23:29:39.073400Z

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.

2020-12-04T23:30:25.074900Z

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

2020-12-04T23:30:26.075Z

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.

2020-12-04T23:31:09.076300Z

Wrap the gen in gen/no-shrink 😛 😉

2020-12-04T23:31:21.076700Z

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.

2020-12-04T23:31:30.077200Z

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

2020-12-04T23:31:32.077400Z

will try

2020-12-04T23:31:38.077800Z

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

2020-12-04T23:31:59.078300Z

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

2020-12-04T23:32:33.078700Z

You could. Or you could wrap the whole collection generator

2020-12-04T23:33:13.079200Z

You get different effects for each

2020-12-04T23:35:49.079700Z

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

2020-12-04T23:36:00.079900Z

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

2020-12-04T23:36:36.080200Z

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

2020-12-04T23:41:08.080700Z

Actually no

2020-12-04T23:41:13.081Z

I thought so

2020-12-04T23:41:31.081600Z

But the structure here is more function oriented than data oriented

2020-12-04T23:43:25.082300Z

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

2020-12-04T23:43:36.082600Z

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

2020-12-04T23:43:49.083Z

Use reporter-fn

2020-12-04T23:43:52.083100Z

Pass one in

2020-12-04T23:44:19.083700Z

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.

2020-12-04T23:44:38.084200Z

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