Re: Transitive spec generation

2018-03-03 Thread James Gatannah
In case anyone was wondering, it looks like this is probably a known issue:

https://dev.clojure.org/jira/browse/CLJ-2079

On Wednesday, February 28, 2018 at 12:05:31 AM UTC-6, James Gatannah wrote:
>
>
>
> On Tuesday, February 27, 2018 at 12:45:26 AM UTC-6, Didier wrote:
>>
>> Don't fully understand what you are doing,
>>
>
> (Slightly) bigger picture, I have a pair of functions that 
> serialize/deserialize maps where the values are mostly fixed-length byte 
> arrays.
>
> I want to verify that they round-trip correctly.
>
> This specific example is about two of those fields which really have the 
> same spec. I'm trying to define this in a way that's similar to a C typedef 
> (or possibly just something like using OOP inheritance for the sake of 
> documentation):
>
> (s/def ::extension ...)
> (s/def ::server-extension ::extension)
> (s/def ::client-extension ::extension)
>
> I'm doing this in an attempt to trying to reduce my nesting levels.
>
> This is what the weird subject is about: I'm trying to take advantage of 
> the transitive property of specs.
>
>  
>
>> but when you run test.check, it seems to not always be generating random 
>> sample, it'll grow the samples. When you run sample, it'll always start 
>> back from the beginning. 
>>
>
>> You can see that by running:
>>
>> (sgen/sample (s/gen ::test) 100)
>>
>> See how the generated samples are getting longer and longer?
>>
>
> Now instead try running this 10 times:
> (sgen/sample (s/gen ::test) 10)
>
> And notice how you always get the same short samples?
>
>  
>
> (I'm sorry about the quote mangling).
>
> Since I have to have a specific length, I'm specifying that as a parameter 
> to sgen/vector.
>
> This part of it works. I can run s/sample on my generator and validate its 
> output against the spec without any trouble.
>
> Actually, if I define the ::extension spec using with-gen, it also looks 
> as though it works (I just ran across this, so haven't tested it 
> thoroughly). But then my library has a runtime dependency on test.check. 
> That's not the end of the world, but it's a definite downside.
>
> I haven't been able to find real documentation covering the arguments to 
> s/gen. But it looks as though I can pass in a map of 0-arity functions to 
> override the different generators. The keys to that map override specs 
> being generated. I can't remember now where I found this, but it can't be 
> totally undocumented. I haven't studied the source code thoroughly enough 
> to have picked out that sort of detail.
>
> So:
>
> (s/gen (s/keys :req [::server-extension
>  ::client-extension])
>{::server-extension #(gen/fmap byte-array (gen/vector (gen/choose 
> -128 127) extension-length))
> ::client-extension #(gen/fmap byte-array (gen/vector (gen/choose 
> -128 127) extension-length))})
>
>
> calls the function in the value associated with each key to override the 
> generator for that spec.
>
> This is the part that seems to only work sometimes.
>
> When I extend this to include all the keys in the full spec, these are the 
> only two that have problems. They're also the only ones that are directly 
> defined as another spec definition. (Which is why I'm pointing my finger at 
> my attempt to use the "transitive" property, though that could totally be a 
> red herring).
>
> When I run this inside deftest, it seems fine. Really, that's all I need. 
> But I'd really like to understand the seemingly non-deterministic failures.
>
> I wrote a function called manual-check that takes that generator and calls 
> sgen/sample on it.
>
> When I call that function directly from the REPL, it very nearly always 
> fails.
>
> When I call it 40 times in a reduce inside deftest, I haven't seen it fail 
> yet.
>
> * While I was writing this, I stumbled across another piece that seems 
> like an interesting clue:
>
> If I override the generator for ::extension rather than the individual 
> specs that I've defined transitively, it seems to start working.
>
> i.e. 
>
> (s/gen (s/keys :req [::server-extension
>  ::client-extension])
>{::extension #(gen/fmap byte-array (gen/vector (gen/choose -128 
> 127) extension-length))})
>  
> seems to work.
>
> It's still less than ideal, in case I ever want to change the underlying 
> spec that's the "base" definition, since I'll have to remember to change 
> the base generator. But I really shouldn't have this sort of thing 
> scattered around everywhere. Especially since it's most interesting inside 
> deftest anyway, where both approaches always seem to work (I haven't tested 
> this out thoroughly yet either).
>
> My current hypothesis is that there's something about the deftest macro 
> that interacts with test.check in a way that makes gen more capable in 
> terms of resolving which spec I actually mean/want to override. I know 
> (based on digging through internals) that it calls something (I think it's 
> named specize) that resolves the spec's 

Re: Transitive spec generation

2018-02-27 Thread James Gatannah


On Tuesday, February 27, 2018 at 12:45:26 AM UTC-6, Didier wrote:
>
> Don't fully understand what you are doing,
>

(Slightly) bigger picture, I have a pair of functions that 
serialize/deserialize maps where the values are mostly fixed-length byte 
arrays.

I want to verify that they round-trip correctly.

This specific example is about two of those fields which really have the 
same spec. I'm trying to define this in a way that's similar to a C typedef 
(or possibly just something like using OOP inheritance for the sake of 
documentation):

(s/def ::extension ...)
(s/def ::server-extension ::extension)
(s/def ::client-extension ::extension)

I'm doing this in an attempt to trying to reduce my nesting levels.

This is what the weird subject is about: I'm trying to take advantage of 
the transitive property of specs.

 

> but when you run test.check, it seems to not always be generating random 
> sample, it'll grow the samples. When you run sample, it'll always start 
> back from the beginning. 
>

> You can see that by running:
>
> (sgen/sample (s/gen ::test) 100)
>
> See how the generated samples are getting longer and longer?
>

Now instead try running this 10 times:
(sgen/sample (s/gen ::test) 10)

And notice how you always get the same short samples?

 

(I'm sorry about the quote mangling).

Since I have to have a specific length, I'm specifying that as a parameter 
to sgen/vector.

This part of it works. I can run s/sample on my generator and validate its 
output against the spec without any trouble.

Actually, if I define the ::extension spec using with-gen, it also looks as 
though it works (I just ran across this, so haven't tested it thoroughly). 
But then my library has a runtime dependency on test.check. That's not the 
end of the world, but it's a definite downside.

I haven't been able to find real documentation covering the arguments to 
s/gen. But it looks as though I can pass in a map of 0-arity functions to 
override the different generators. The keys to that map override specs 
being generated. I can't remember now where I found this, but it can't be 
totally undocumented. I haven't studied the source code thoroughly enough 
to have picked out that sort of detail.

So:

(s/gen (s/keys :req [::server-extension
 ::client-extension])
   {::server-extension #(gen/fmap byte-array (gen/vector (gen/choose 
-128 127) extension-length))
::client-extension #(gen/fmap byte-array (gen/vector (gen/choose 
-128 127) extension-length))})


calls the function in the value associated with each key to override the 
generator for that spec.

This is the part that seems to only work sometimes.

When I extend this to include all the keys in the full spec, these are the 
only two that have problems. They're also the only ones that are directly 
defined as another spec definition. (Which is why I'm pointing my finger at 
my attempt to use the "transitive" property, though that could totally be a 
red herring).

When I run this inside deftest, it seems fine. Really, that's all I need. 
But I'd really like to understand the seemingly non-deterministic failures.

I wrote a function called manual-check that takes that generator and calls 
sgen/sample on it.

When I call that function directly from the REPL, it very nearly always 
fails.

When I call it 40 times in a reduce inside deftest, I haven't seen it fail 
yet.

* While I was writing this, I stumbled across another piece that seems like 
an interesting clue:

If I override the generator for ::extension rather than the individual 
specs that I've defined transitively, it seems to start working.

i.e. 

(s/gen (s/keys :req [::server-extension
 ::client-extension])
   {::extension #(gen/fmap byte-array (gen/vector (gen/choose -128 127) 
extension-length))})
 
seems to work.

It's still less than ideal, in case I ever want to change the underlying 
spec that's the "base" definition, since I'll have to remember to change 
the base generator. But I really shouldn't have this sort of thing 
scattered around everywhere. Especially since it's most interesting inside 
deftest anyway, where both approaches always seem to work (I haven't tested 
this out thoroughly yet either).

My current hypothesis is that there's something about the deftest macro 
that interacts with test.check in a way that makes gen more capable in 
terms of resolving which spec I actually mean/want to override. I know 
(based on digging through internals) that it calls something (I think it's 
named specize) that resolves the spec's name. Then it has to use that 
name/those names in the overriding map parameter to pick out the 
appropriate function to call to get the generator.

I'm very skeptical, but could that possibly hold water?

Thank you to anyone who actually took the time to read this!

 

>
>
> On Monday, 26 February 2018 21:39:09 UTC-8, James Gatannah wrote:
>>
>> Fairly minimalist example available at 
>> 

Re: Transitive spec generation

2018-02-26 Thread Didier
Don't fully understand what you are doing, but when you run test.check, it 
seems to not always be generating random sample, it'll grow the samples. 
When you run sample, it'll always start back from the beginning.

You can see that by running:

(sgen/sample (s/gen ::test) 100)

See how the generated samples are getting longer and longer?

Now instead try running this 10 times:

(sgen/sample (s/gen ::test) 10)

And notice how you always get the same short samples?


On Monday, 26 February 2018 21:39:09 UTC-8, James Gatannah wrote:
>
> Fairly minimalist example available at 
> https://gist.github.com/jimrthy/21851c52a8cd6b04a31ed08b1d0a7f04
>
> When I call gen/sample from inside a unit test, it seems to pass with 
> flying colors.
>
> When I directly eval the gen/sample form or call (manual-check) from the 
> REPL (I checked both CIDER and the boot CLI, just in case), I usually get 
> the "Couldn't satisfy such-that predicate after 100 tries."
>
> To be a little more specific about this:
>
> Calling (manual-check) failed 49/50 times.
>
> Calling (transitive-indirect) passed 50 times in a row. If you haven't 
> bothered looking at the gist, each of those calls (manual-check) 40 times.
>
> I was quite surprised by this. Does anyone have any suggestions about why 
> wrapping the call in a unit test might help it succeed more often?
>
> If I just call gen/sample on the generator I'm trying to use, followed by 
> s/valid? for one of the specs I'm trying to generate, it seems fine. I 
> haven't dug into the source here (and I'm not positive what all's going on 
> inside the spec.gen namespace), but I thought that's what gen/generate does 
> when you define a custom generator for your spec.
>
> Except that isn't really what I'm doing.
>
> I'm trying to avoid adding extra runtime dependencies on a library like 
> tools.check, so I'm trying to do this with overrides in the test namespaces 
> to try to limit the extra dependencies to test time.
>
> Could that be where I'm breaking core assumptions that don't seem to cause 
> trouble for anyone else?
>
> Thanks,
> James
>
>
>
> On Sunday, February 25, 2018 at 6:45:39 PM UTC-6, James Gatannah wrote:
>>
>> I have a spec for an array of 16 bytes:
>>
>> (s/def ::extension (s/and bytes?
>>#(= (count %) 16))
>>
>> Then I have a couple of other specs that are really just renaming it:
>>
>> (s/def ::client-extension ::extension)
>> (s/def ::server-extension ::extension)
>>
>> I started doing some refactoring today, and the definitions wound up 
>> needing to move to a different namespace.
>>
>> So now the original definitions have changed to
>>
>> (s/def ::client-extension ::refactored/client-extension)
>>
>> I also started dabbling with generators, and came up with this:
>>
>> (gen/generate (s/gen ::client-extension
>>   {::client-extension #(gen/fmap 
>> byte-array (gen/vector (gen/choose -128 127) 16)}))
>>
>> When I define things this way, I get a "Couldn't satisfy such-that 
>> predicate after 100 tries." exception a little more than half the time.
>>
>> If I rearrange things so that either
>> a) The refactored namespace defines the spec directly
>> or
>> b) I change my generator override to specify the top-level spec that the 
>> others are copying
>>
>> i.e.
>> a) would mean changing the refactored ns such that I have
>> (s/def ::client-extension (s/and bytes?
>>  #(= (count %) 16))
>>
>> b) changing the generator to
>> (gen/generate (s/gen ::client-extension
>>   {::refactored/extension #(gen/fmap 
>> byte-array (gen/vector (gen/choose -128 127) 16)}))
>>
>> it seems to fail (with the same problem) about 1 time in 5.
>>
>> I haven't seen it fail yet if I undo my refactoring and move the spec 
>> back to the original location.
>>
>> I haven't collected any sorts of real numbers on this, much less tried to 
>> make enough test runs to collect a statistically significant sample. I know 
>> the next real steps are to put together a minimalist example.
>>
>> But before I do that, I figured it might be asking whether anyone sees 
>> anything obviously wrong in what I'm trying to do, or whether there's a 
>> better way to do it.
>>
>> Thanks in advance,
>> James
>>
>>

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

Re: Transitive spec generation

2018-02-26 Thread James Gatannah
Fairly minimalist example available 
at https://gist.github.com/jimrthy/21851c52a8cd6b04a31ed08b1d0a7f04

When I call gen/sample from inside a unit test, it seems to pass with 
flying colors.

When I directly eval the gen/sample form or call (manual-check) from the 
REPL (I checked both CIDER and the boot CLI, just in case), I usually get 
the "Couldn't satisfy such-that predicate after 100 tries."

To be a little more specific about this:

Calling (manual-check) failed 49/50 times.

Calling (transitive-indirect) passed 50 times in a row. If you haven't 
bothered looking at the gist, each of those calls (manual-check) 40 times.

I was quite surprised by this. Does anyone have any suggestions about why 
wrapping the call in a unit test might help it succeed more often?

If I just call gen/sample on the generator I'm trying to use, followed by 
s/valid? for one of the specs I'm trying to generate, it seems fine. I 
haven't dug into the source here (and I'm not positive what all's going on 
inside the spec.gen namespace), but I thought that's what gen/generate does 
when you define a custom generator for your spec.

Except that isn't really what I'm doing.

I'm trying to avoid adding extra runtime dependencies on a library like 
tools.check, so I'm trying to do this with overrides in the test namespaces 
to try to limit the extra dependencies to test time.

Could that be where I'm breaking core assumptions that don't seem to cause 
trouble for anyone else?

Thanks,
James



On Sunday, February 25, 2018 at 6:45:39 PM UTC-6, James Gatannah wrote:
>
> I have a spec for an array of 16 bytes:
>
> (s/def ::extension (s/and bytes?
>#(= (count %) 16))
>
> Then I have a couple of other specs that are really just renaming it:
>
> (s/def ::client-extension ::extension)
> (s/def ::server-extension ::extension)
>
> I started doing some refactoring today, and the definitions wound up 
> needing to move to a different namespace.
>
> So now the original definitions have changed to
>
> (s/def ::client-extension ::refactored/client-extension)
>
> I also started dabbling with generators, and came up with this:
>
> (gen/generate (s/gen ::client-extension
>   {::client-extension #(gen/fmap 
> byte-array (gen/vector (gen/choose -128 127) 16)}))
>
> When I define things this way, I get a "Couldn't satisfy such-that 
> predicate after 100 tries." exception a little more than half the time.
>
> If I rearrange things so that either
> a) The refactored namespace defines the spec directly
> or
> b) I change my generator override to specify the top-level spec that the 
> others are copying
>
> i.e.
> a) would mean changing the refactored ns such that I have
> (s/def ::client-extension (s/and bytes?
>  #(= (count %) 16))
>
> b) changing the generator to
> (gen/generate (s/gen ::client-extension
>   {::refactored/extension #(gen/fmap 
> byte-array (gen/vector (gen/choose -128 127) 16)}))
>
> it seems to fail (with the same problem) about 1 time in 5.
>
> I haven't seen it fail yet if I undo my refactoring and move the spec back 
> to the original location.
>
> I haven't collected any sorts of real numbers on this, much less tried to 
> make enough test runs to collect a statistically significant sample. I know 
> the next real steps are to put together a minimalist example.
>
> But before I do that, I figured it might be asking whether anyone sees 
> anything obviously wrong in what I'm trying to do, or whether there's a 
> better way to do it.
>
> Thanks in advance,
> James
>
>

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


Transitive spec generation

2018-02-25 Thread James Gatannah
I have a spec for an array of 16 bytes:

(s/def ::extension (s/and bytes?
   #(= (count %) 16))

Then I have a couple of other specs that are really just renaming it:

(s/def ::client-extension ::extension)
(s/def ::server-extension ::extension)

I started doing some refactoring today, and the definitions wound up 
needing to move to a different namespace.

So now the original definitions have changed to

(s/def ::client-extension ::refactored/client-extension)

I also started dabbling with generators, and came up with this:

(gen/generate (s/gen ::client-extension
  {::client-extension #(gen/fmap byte-array 
(gen/vector (gen/choose -128 127) 16)}))

When I define things this way, I get a "Couldn't satisfy such-that 
predicate after 100 tries." exception a little more than half the time.

If I rearrange things so that either
a) The refactored namespace defines the spec directly
or
b) I change my generator override to specify the top-level spec that the 
others are copying

i.e.
a) would mean changing the refactored ns such that I have
(s/def ::client-extension (s/and bytes?
 #(= (count %) 16))

b) changing the generator to
(gen/generate (s/gen ::client-extension
  {::refactored/extension #(gen/fmap 
byte-array (gen/vector (gen/choose -128 127) 16)}))

it seems to fail (with the same problem) about 1 time in 5.

I haven't seen it fail yet if I undo my refactoring and move the spec back 
to the original location.

I haven't collected any sorts of real numbers on this, much less tried to 
make enough test runs to collect a statistically significant sample. I know 
the next real steps are to put together a minimalist example.

But before I do that, I figured it might be asking whether anyone sees 
anything obviously wrong in what I'm trying to do, or whether there's a 
better way to do it.

Thanks in advance,
James

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