Fork me on GitHub
#clojure-spec
<
2016-06-17
>
Oliver George07:06:06

Hi Specy Specers. What's the most human friendly way of viewing the output from check-var? I see that reporter-fn can be provided, is there a commonly used one? (I'm using CLJS so perhaps that makes a difference).

jrychter09:06:28

I find myself writing lots of (s/and string? seq) to specify non-empty strings. Also, I'm missing a predicate for strings of length from n to m (analogous to int-in-range?).

jannis09:06:58

Is it possible that s/with-gen alters the spec it defines a generator for? I have a simple (s/cat :base ... :children ...) spec that works fine but as soon as I wrap it in (s/with-gen <spec> #(gen/tuple (s/gen ...) (s/gen ...))), data that would previously conform to the spec now becomes invalid.

jannis09:06:52

Note: I', not generating the data using the generator yet. I'm using hand-written data.

seancorfield15:06:48

@olivergeorge: there's a discussion about that on the main Clojure mailing list. I'm very interested in the answers to that question.

leifp16:06:31

@jannis: It looks like it's introducing a new regex context like spec does:

user=> (s/explain (s/cat :x ::pair) '[a [b c]])
Success!
user=> (s/explain (s/cat :x ::pair-with-gen) '[[a [b c]]])
Success!

sparkofreason17:06:35

I'm working on some code that does simulations over state machines. The transition functions tend to have a lot of detailed conditional logic, and as a result test.check doesn't seem to be a great fit for validating the functions (random inputs tend to be ignored or lead to errors, and writing generators to provide valid input is essentially the same as writing the state machine model). When instrument-all checked the return value it was very useful, since I caught a lot of errors of omission, misspelled keywords, etc. I'd like to suggest we have an option to check :ret and :fn for cases like this.

seancorfield17:06:14

Yeah, whilst I agree in principle with the justification @alexmiller offered as to why :ret and :fn checking was removed in Alpha 6, I also agree that there is potentially a lot of value in having the option to be able to instrument functions in a way that does conform the result at least.

sparkofreason17:06:34

Thanks, I may use that to hammer out my own version of instrument for now.

bfabry17:06:16

I think there's gotta be something coming for spec'ing impure functions that will cover this

bfabry17:06:47

otherwise you could never spec them, really, or the spec would be pointless

Alex Miller (Clojure team)18:06:13

@seancorfield: Rich has some ongoing work, I'm not sure what the endpoint will be on this. you might have noticed that explain-out was made public today in master

Alex Miller (Clojure team)18:06:25

@bfabry: not every function is a great candidate for generative testing via spec (but the spec may still be useful for docs or other purposes)

eggsyntax18:06:58

@alexmiller: where was the explanation @seancorfield mentioned of why :ret and :fn checking was removed? I’m curious to read it.

Alex Miller (Clojure team)18:06:19

@seancorfield: I think Stu is looking at some testing-related mods too btw

seancorfield18:06:24

re: explain-out — I already added a comment on that commit thanking him for that 🙂

seancorfield18:06:57

@alexmiller: I really do appreciate the steady stream of alpha builds so we can all try this stuff out and provide feedback.

Alex Miller (Clojure team)18:06:46

I'm sure there will be more :)

seancorfield18:06:34

Having an option on instrument to use the old version of spec-checking-fn with :ret and :fn conforming would be very nice. I think checking just :args is the right choice for most cases of instrumentation, but I think being able to "fully instrument" certain functions would be very valuable — especially for functions that cannot easily be tested the generative way.

seancorfield18:06:05

For example, working on java.jdbc’s specs, they can’t reasonably be tested generatively because many of them are side-effecting (updating the database) and writing generators that conformed to the database schema would be … a huge amount of work, if it’s even feasible (e.g., unique key constraints etc?).

seancorfield18:06:30

So losing the ability to conform the :ret and :fn specs there is kind of a big deal, IMO.

seancorfield18:06:35

I’ll be interested to see how this all ends up since any given code base is going to have a mix of functions that can reasonably be tested generatively and functions that can’t, so (clojure.spec.test/run-all-tests) needs a way to distinguish those, right?

seancorfield18:06:16

(mind you, right now I can’t run generative testing on java.jdbc because the system doesn’t know how to generate a java.sql.Connection… which might be an interesting exercise 🙂 )

seancorfield18:06:24

(and I’d also need a generator for a java.sql.PreparedStatement)

seancorfield18:06:11

What is the recommendation for stuff like that? How would you even write a generator for some of these Java objects?

tomc19:06:36

Would anyone be willing to offer some guidance on conventions for attributes shared by entities of different types? For instance, I have "question" and "survey" entities, each of which can have names. Right now I'm using (s/def :entity/name string?) and entities of either type can have an :entity/name attribute along with their type-specific attributes. The alternative of course is to define both :question/name and :survey/name. I haven't seen the :entity/attr pattern elsewhere, and I'm wondering whether there's a reason for that.

wilkerlucio20:06:39

@tomc: I believe you can share the attribute as long as the semantic is the same, for example, a car name may have a different semantic from a person name, but that will depend on the requirements on your system

tomc20:06:32

@wilkerlucio: thanks a lot, that's helpful.

gphilipp20:06:29

I’m trying to generate dates within a range with spec, and I’m stucked with this piece of code which generates #inst whose year is above 9999, making them unrecognized when i try to def them manually afterwards : (gen/sample (s/gen inst?) 60)

gphilipp20:06:51

ex of data generated: #inst"26138-06-03T15:09:43.670-00:00

gphilipp20:06:07

(def d1 #inst"26138-06-03T15:09:43.670-00:00")
CompilerException java.lang.RuntimeException: Unrecognized date/time syntax: 26138-06-03T15:09:43.670-00:00, compiling:(/Users/gilles/dev/try-spec/src/try_spec/core.clj:23:46) 

wilkerlucio20:06:07

@gphilipp: you can try something like this:

wilkerlucio20:06:58

the first number, 100000 is a cap to limit the increment, and the 1465391285642 is the ms for a start date, adjust those to met your needs

wilkerlucio20:06:29

ah, and this example is for CLJS, please change the Date initialisation if you are using on Clojure

gphilipp20:06:39

@wilkerlucio: thx, I will try this

leifp20:06:41

@gphilipp: There is also an clojure.spec/inst-in macro.

seancorfield20:06:54

The default 100 tests for test.check can take a really long time on some fairly simple looking specs...

seancorfield20:06:38

…running 50 tests wasn’t too bad but it seems to be taking more than linearly longer to do 5, 10, 25, 50, 100...

leifp20:06:10

@seancorfield: Yeah, as well as being very dependent on ordering. The caveats about "generate-and-test" style vs. constraint satisfaction are in full effect here.

seancorfield20:06:00

On the plus side, I figured out how to write generators for stuff like java.sql.Connection etc 🙂

seancorfield20:06:51

And I also finally figured out how s/keys and the test.check integration hang together… which answered a question I’d asked someone else here before I understood what was going on...

gphilipp20:06:15

Thanks @leifp, (gen/sample (s/gen (s/inst-in #inst "2016-01-01" #inst "2016-12-31")) 100) did the trick

leifp20:06:16

@gphilipp: The end date is exclusive, remember.

gphilipp20:06:23

ah, correct

gphilipp20:06:09

I still wonder why (gen/sample (s/gen inst?) 60) generates invalid instants.

leifp20:06:05

See? Generative testing is going to help us avoid the Year 26k Problem.

seancorfield21:06:41

(and it took me a while to even get so far as to make run-all-tests actually start running… with-gen taking a 0-arity function that returns a generator is very counter-intuitive and I kept getting that wrong 😞 )

leifp21:06:20

@seancorfield: The (s/* (s/or ...)) combo seems to be the culprit. It would probably be very fast if you could limit it to a max size. I don't know how to do that, though.

seancorfield21:06:42

I guess I could always add a custom generator around it just to avoid that?

seancorfield21:06:50

(and, thanks for the pointer on that!)

leifp22:06:28

@seancorfield: This works: (s/def ::column-spec (s/with-gen (s/cat :spec (s/* (s/alt :kw keyword? :str string?))) #(g/vector (g/one-of [g/string g/keyword]) 0 6))) Still not what I'd call lightning-fast, but you can bound the generation time by decreasing the max. And note that g/ refers to clojure.test.check.generators, the equivalent using clojure.spec.gen didn't work.

leifp22:06:39

Kind of clunky, though, and you'd need to do that everywhere you have (s/* (s/alt ...)). Maybe if enough people run into performance problems, rep will be made public, or the regex generators will be optimized.

leongrapenthin23:06:39

Assume I parse with conform. Then I have functions that operate on the value returned by conform. I can't get a spec for the value returned by conform (so that I can spec said functions) - It seems like this could be automated though. Imagine (s/conform-spec ::my-spec) would return the spec of the return value of calling (s/confom ::my-spec 42)