test-check

2017-04-27T00:00:45.763616Z

Yeah, test.check hopes that the public API is sufficient

2017-04-27T00:01:16.768819Z

I assume you see the bug in your code?

2017-04-27T00:01:58.775755Z

Or, why that function doesn't/ can't work

bbloom 2017-04-27T00:02:41.782448Z

other than my typo with the wrong variable? and the lack of the “AST interpreter” that would drive that protocol? no, not really

bbloom 2017-04-27T00:02:48.783598Z

at least i assume you have some insight i’m missing

2017-04-27T00:03:31.790582Z

In the fmap str example, you'll be passing a string to a shrinker that expects integers

2017-04-27T00:04:09.796403Z

value is a string, (shrink x ...) wants an integer

bbloom 2017-04-27T00:05:17.806317Z

i was thinking the value would be whatever value was being wrapped

2017-04-27T00:05:20.807041Z

Somebody somewhere will have to parse the string

bbloom 2017-04-27T00:05:20.807092Z

not the generated value

bbloom 2017-04-27T00:05:38.809617Z

i you’d have a tree of alternating generate/shrink calls

bbloom 2017-04-27T00:06:17.815631Z

the idea was that fmap wouldn’t do any shrinking at all

bbloom 2017-04-27T00:06:23.816557Z

or at least no extra shrinking

bbloom 2017-04-27T00:06:29.817461Z

beyond what the wrapped generator does

2017-04-27T00:07:52.829110Z

That's ideal, I'm just not sure how you wrap this up into the test runner in an arbitrarily composable way

bbloom 2017-04-27T00:08:11.831661Z

i’m not even convinced that it makes sense to combine generating and shrinking one-to-one like this

bbloom 2017-04-27T00:10:23.850621Z

i just assumed that shrinking would be simpler than generating in that the tree would be a subset of the generator tree

bbloom 2017-04-27T00:10:35.852621Z

and only isomorphic to it if most of the nodes were the identity-shrinker

bbloom 2017-04-27T00:11:02.856531Z

for example if you generate an arbitrary number and then filter the even ones, you only want one number shrinker, not two

bbloom 2017-04-27T00:11:31.860590Z

similarly, for the stateful testing case with shrinking, you almost only want to remove actions, not actually alter the contents of any of those actions

bbloom 2017-04-27T00:13:02.873533Z

way beyond just generative testing, i’m not a fan of monadic apis in general…. i’d much rather build a generator out of transparent data and then interpret (or compile) that - especially so that i could add more methods beyond just generate/shrink….

bbloom 2017-04-27T00:14:20.884370Z

but this is way off topic, my apologies

2017-04-27T00:23:58.962997Z

I'm not even sure what sort of tree you're imagining

bbloom 2017-04-27T00:24:44.969426Z

the tree of generators that is returning a pure value (that is, of course, tree shaped)

2017-04-27T00:25:20.974408Z

The shape of generator composition doesn't necessarily mirror the shape of the data

bbloom 2017-04-27T00:26:02.979943Z

sure, it’s not 1-to-1, but it has some similarity in general, no?

2017-04-27T00:26:16.981810Z

I've thought about generators-as-data, but seems hard to do bind

bbloom 2017-04-27T00:26:30.983872Z

bind (well, lexical scope in general) is always the hard part

2017-04-27T00:27:29.991543Z

So test.check has an approach where bind at least works 😛

2017-04-27T00:29:08.005131Z

So you're imagining that the user-facing API for your generators is the protocol of two functions? If so I'm still not seeing how you get around the fmap problem

bbloom 2017-04-27T00:29:35.008868Z

i’m not trying to propose a new solution

bbloom 2017-04-27T00:29:48.010647Z

i’m saying that it didn’t match my intuition - which has now been corrected

bbloom 2017-04-27T00:30:08.013520Z

but the larger problem i have is that, even understanding how it works, i’m not sure how to make effective use of it

bbloom 2017-04-27T00:30:20.015381Z

let’s take the stack example again

bbloom 2017-04-27T00:31:01.020981Z

if i have random push/pop operations and it’s going to do shrinking, it’s going to waste a bunch of time pushing or popping smaller integers, when really i want to try fewer pops or fewer pushes

bbloom 2017-04-27T00:31:02.021221Z

right?

2017-04-27T00:31:32.025210Z

Somewhat

2017-04-27T00:31:44.026724Z

It will shrink each integer straight to 0

2017-04-27T00:31:49.027283Z

In one step

2017-04-27T00:32:05.029416Z

Only if that doesn't work does it try other things

2017-04-27T00:32:14.030434Z

So N steps total to accomplish that

2017-04-27T00:32:28.032601Z

But it could also try removing things first, I'm not sure

2017-04-27T00:33:06.037531Z

That depends on which step bind tries first

2017-04-27T00:34:24.048059Z

One downside to this structure is you can't give shrink hints

2017-04-27T00:35:08.053525Z

I think shrinking efficiency is only an issue when generating really large compositions

2017-04-27T00:36:45.066665Z

I suppose I shouldn't say there's no way to give hints; for your example, if it was a problem, you could wrap the integer generator in gen/no-shrink

2017-04-27T00:37:35.073386Z

test.check does this itself for the builtin UUID generator, on the assumption that there's not much to be gained from shrinking a UUID

2017-04-27T00:39:01.084550Z

w.r.t. the earlier question on large-integer boundary testing, that's a more general issue I'm aware of but haven't been sure how clever it should try to be

2017-04-27T00:40:16.094049Z

I've considered rebooting the generators namespace to clean up the API; maybe there could be a partition into lower-level generators and higher-level clever ones

2017-04-27T00:40:49.098537Z

the low level ones do simple easy to understand things, the high level ones do lots of ad-hoc stuff to try to break your code

2017-04-27T00:40:56.099397Z

¯\(ツ)

bbloom 2017-04-27T00:45:52.137272Z

interesting

bbloom 2017-04-27T00:45:59.138104Z

i’m gonna have a think on all this

bbloom 2017-04-27T00:46:19.140823Z

thanks again

👍 1
nberger 2017-04-27T02:04:25.749269Z

Hey guys, great discussion here. I think the announcement of hedgehog, a new quickcheck-like library for haskell is very relevant: https://www.reddit.com/r/haskell/comments/646k3d/ann_hedgehog_property_testing/. One of its selling points is that it "uses integrated shrinking, so shrinks obey the invariants of generated values by construction [...] an approach to property testing which is also used by QuiviQ's Erlang QuickCheck, Hypothesis, and test.check"

2017-04-27T02:07:18.771404Z

Oh nice - I forgot about that

nberger 2017-04-27T02:10:27.795733Z

I see references to a shrink tree in the code. But can't say much more 🙂

nberger 2017-04-27T02:23:00.885682Z

@gfredericks: btw I made some progress on the async quick-check. I pushed a draft to my repo today: https://github.com/nberger/test.check/compare/test.check.refactor...refactor-allow-async. I'm working on a defspec-async macro now that would use that stuff and would make the tests much easier to write (for example it "knows" when to call the done from cljs.test/async, which is on :succeeded or :shrunk) so the :step-fn that I'm using in the tests would be gone

2017-04-27T02:31:55.949511Z

@nberger someone made a ticket asking for that

nberger 2017-04-27T02:34:09.965258Z

yep, saw it. I've been thinking about this for a long time, the ticket made me go to the keyboard and try to make it work, and I'm quite happy with the result so far

nberger 2017-04-27T02:34:47.969515Z

I'm hoping to have a more rounded version soon

2017-04-27T02:34:57.970659Z

cool, let me know and I'll take a look

nberger 2017-04-27T02:35:03.971391Z

will do