This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-07-22
Channels
- # admin-announcements (1)
- # aws-lambda (1)
- # beginners (38)
- # boot (48)
- # cider (11)
- # clara (4)
- # cljs-dev (61)
- # cljsrn (9)
- # clojure (68)
- # clojure-austin (3)
- # clojure-greece (9)
- # clojure-mexico (6)
- # clojure-russia (40)
- # clojure-spec (165)
- # clojure-uk (134)
- # clojurescript (37)
- # cursive (5)
- # datomic (25)
- # defnpodcast (2)
- # hoplon (1)
- # jobs (1)
- # juxt (1)
- # lein-figwheel (3)
- # leiningen (4)
- # mount (14)
- # off-topic (8)
- # om (29)
- # onyx (9)
- # protorepl (4)
- # quil (1)
- # re-frame (56)
- # reagent (3)
- # rethinkdb (1)
- # spacemacs (4)
- # specter (12)
- # test-check (2)
- # testing (1)
- # vim (12)
- # yada (4)
@lvh: I think coll-of
and every
use clojure.test.check.generators/vector-distinct
under the hood
rather, they use vector-distinct if they get :distinct true
this is the entire body of the coll-of
macro: backtick before ( ---> (every ~pred ::conform-all true ~@opts)
damn, how do you get a backtick in a code block -_-
I ran some test.check specs, but even with only 100 samples I’m getting actual: java.lang.OutOfMemoryError: GC overhead limit exceeded. I guess recursive data structures can grow big.
Is there something extra weird that goes on when you test against (for-all […] true)? I was expecting that to trivially pass.
@lvh is a generator in control of the size (depth or breadth) of the generated structure? The current integer generators in spec grow very quickly. By the 20th test, you're probably in 8-digit range.
Is there a way to say that if there are any numbers, the string also needs to be there, but if there aren't, it's optional?
(s/def ::xs (s/cat :str (s/? string?) :nums (s/* number?) :key keyword?))
;; so this should be valid
(s/valid? ::xs ["s" 2 3 4 :k])
;; And this
(s/valid? ::xs ["s" :k])
;; but not this
(s/valid? ::xs [2 3 4 :k])
(s/def ::xs
(s/cat :pre (s/alt :opt1 (s/cat :str string? :nums (s/* number?))
:opt2 (s/? string?))
:key keyword?))
What do you think makes more sense for a lib with optional specs: ship with specs in a separate namespace or have specs in a separate repo even. former requires macro hackery to allow to run/use with clj1.9- latter is just a dependency.
any idea why this fails when trying to validate with it:
(s/def :foo (s/fspec :args (s/cat :err #(instance? ExceptionInfo %))
:ret any?))
(s/valid? ::foo (fn [x] 1)) -> ExceptionInfo Unable to construct gen at: [:err] for: (instance? clojure.lang.ExceptionInfo %) clojure.core/ex-info (core.clj:4724)
@mpenet: regarding your earlier question, can you not use the port someone made of spec to clojure 1.8?
oops, I missed your "specs in a separate namespace" option
I am mostly interested in instrumentation in that case, yet I have to specify a generator apparently
I would guess that valid?
checks validity by generating some arguments using the :args
generator and then checking that the return values match your :ret
and :fn
predicates
that's good to know
it worked for me here:
=> (defn foo [& exs] (map str exs))
=> (s/fdef foo :args (s/cat :err #(instance? clojure.lang.ExceptionInfo %)) :ret any?)
=> (stest/instrument `foo)
=> (foo 1 2 3)
ExceptionInfo Call to #'user/foo did not conform to spec:
In: [0] val: 1 fails at: [:args :err] predicate: (instance? clojure.lang.ExceptionInfo %)
:clojure.spec/args (1 2 3)
:clojure.spec/failure :instrument
:clojure.spec.test/caller {:file "form-init8739029786060363099.clj", :line 990, :var-scope user/eval90084}
clojure.core/ex-info (core.clj:4724)
=> (foo (ex-info "test" {}))
("clojure.lang.ExceptionInfo: test {}")
how did it fail for you?
not the same in my case, it's a function that takes another fn as argument, the fn passed fails to gen
oh, I see
(s/def ::alia.execute-async/error
(s/fspec :args (s/cat :err (instance-pred clojure.lang.ExceptionInfo))
:ret any?))
it tries to call valid?
on the function returned rather than doing something like propogate the instrumentation to the returned function
I understand it's necessary when running valid? on the single spec, but via instrumentation not really
I tend to agree.
@alexmiller: any thoughts on this?
In my mind instrumentation should "only" be pre/post assertions based on specs. no gen involved (same as plumatic/schema actually)
Here’s what I have now that blows up at 50: https://gist.github.com/lvh/243576d4e825d3792d4cd1bb8d8c39d7
Oh … no, it doesn’t look like a generated value is in controlling the depth … in fact, nothing is controlling it except chance.
So on line 36, there’s a 1-in-7 chance that the generator will recur and add another level to the structure … but at line 32, there’s a 50% chance. It’s easy to imagine the process rolling the dice just right so that the structure grows until it fills the JVM’s memory allocation, and that seems to be happening.
The problem for writing the specs is this: you’ve accurately captured the semantics for the purposes of verification, but when generating for tests, you want to add some additional constraints. I ran into this when testing a 2D grid … in production code I don’t want to arbitrarily restrict the allowable sizes, but in my specs, I was ending up with grids that had many millions of cells, which was slow, failure-prone (because I also got out-of-memory errors) and also pointless (because a bunch of tests with 25x25 grids will catch any problems — the size of the grid isn’t really the important factor).
So I ended up with this spec:
;; restrict grid sizes for testing, but allow larger grids in production.
(s/def ::grid-dimen (s/with-gen (s/and integer? #(>= % 2))
#(s/gen (s/int-in 2 25))))
You might need something similar, but rather than limiting a size (because you don’t have one) you can alter the probabilities, so that the generator for ::additional-properties
only chooses to recur 10% of the time, say, instead of 50%.
when running check
etc you can supply generator overrides
same for exercise
, gen
, and instrument
this allows you to override the generator with a more specific one at test time
also see s/*recursion-limit*
I saw this. But shouldn't it be possible to avoid generators usage for instrumentation?
you can do so with the replace/stub functionality in instrument
or maybe I’m not understanding your question
the fspec thing?
I imagined that instrumentation would work without having the need to stub anything since it's all specified, I didn't expect it to run "gen" on arguments (hundreds of calls)
there is still some unfinished work in this area
oki, glad to hear that, so in the final design "gen" wouldn't be required by instrumentation code
@lvh: just to test my hypothesis, try changing ::additional-properties
to this and see if that helps:
(s/def ::additional-properties
(s/with-gen
(s/or
:implicit-additional-properties boolean?
:explicit-additional-properties ::schema)
#(sg/fmap (fn [d10]
(sg/generate (s/gen (if (= d10 1)
::schema
boolean?))))
(sg/choose 1 10))))
alexmiller: ugh that "open intervals" email makes me wish I'd made the bounds opts on those numeric generators named #{:< :<= :> :>=}
:/
alexmiller: I'd consider deprecating the old opts and adding those four if you think that'd be useful for spec
I don’t think anything needs to change with it, it’s fine
independently I also halfway regret how :NaN? and :infinite? interact with :min and :max
or don't interact rather
but that's harder to change without technically breaking
Hmmm... any tips for debugging stackoverflow exceptions in specs?
also is it possible to reset the spec registry?
pretty sure I've heard people ask about this before
I've probably got a mistake in my specs...
I had a typo along the lines of by mistake (s/def foo ::foo)
Is it possible to call an s/fdef'd
function and validate its :ret
spec?
I saw the guide said it wasn't supported because that's for testing
the only place that’s checked by spec is during check
you can of course obtain and check it yourself
something like (s/valid? (:ret (s/get-spec 'user/foo)) ret)
ahh thanks
or s/assert
nice - I'd missed that one
@alexmiller: As I opined on reddit, I don't see any deep reason for why you can't just have in clojure.spec
a schema transpiler:
(spec/def ::json-api
(spec/schema
{:id uuid?
:text str?
:rating (int-in 0 5)}))
This would make migrating a lot less annoying.Clojure is not going to provide a schema transpiler
b/c we have other things to do
people are more than welcome to make one
would we also create a truss transpiler?
and a herbert transpiler?
things in core are forever
gen is just a dynamically loaded skin around test.check
Well, so there's tiny little monad in a framework where everyone did a great job of dodging that sort of thing.
you can ignore it’s monadic nature if you like
it does what it does
All I'm saying is that great pains were made to wrap test.check
, but the lesser pain of wrapping schema
type syntax was ignored for some reason.
It's your product, everyone picks and chooses which wheels they want to reinvent I suppose.
test.check is a Clojure contrib library, following the same contributor agreement, license, and dev methodology as Clojure itself (to make things like this possible)
Schema is not (and I mean nothing negative towards Schema in this regard, they are just much different from a core perspective)
spec was designed to solve a set of problems, not to replace Schema (even though it solves an overlapping set of problems)
let
is tricky as it’s actually a macro
Stu did the work on gen, but I suspect that’s the only reason it’s not there
The Clojure contrib libs are primarily standalone libs without external deps (there are exceptions, but that’s the ideal). There is not going to be a contrib lib related to schema. But there is nothing stopping someone from creating a lib to do what you suggest.
if we think something is fit for spec, it will go into spec, not into a lib
Looking here, it looks like you guys rope in bind
: https://github.com/clojure/clojure/blob/master/src/clj/clojure/spec/gen.clj#L92
bind is a function
I think let
would basically have to be copied into gen rather than dynamically loaded
you can file a jira if you like, but no guarantees for anything
Nah, I'll just post it here in a gist or whatever unless you need me to sign something
we don’t take contributions via slack :)
sign the CA, file a jira, supply a patch
Okay, I'll post it here for people to playtest and then do the patch, since that's a lot of tedium for 15 lines of code
Just for fun, I’ve been prototyping a little library to generate EBNF-ish syntax diagrams from specs. Delving into the spec internal representation has been … eeeenteresting. 😕
@mpenet: Sounds like someone else is going to be signing something, issuing a JIRA ticket with a simple patch and waiting days and days for a fix
@alexmiller: https://gist.github.com/ef92987c8160a796b62c95e207ee0ad4 (fixed a typo)
Any ideas as to why clojure.test/check
would return the empty list?
user=> (require '[clojure.spec :as s])
nil
user=> (s/fdef my+
#_=> :args (s/tuple number? number?)
#_=> :ret symbol?
#_=> :fn #(= (:ret %) (apply + (:args %))))
user/my+
user=> (require '[clojure.spec.test :as stest])
nil
user=> (stest/check 'my+)
()
user=>
Am I missing a step here?@mpenet thx, I’ll try to get that fixed the next time someone is paying attention
> Just for fun, I’ve been prototyping a little library to generate EBNF-ish syntax diagrams from specs. Delving into the spec internal representation has been … eeeenteresting. @glv: This is cool. Does it emit EBNF that instaparse can digest? I would absolutely love if I could shake SQL tables out of specs. But that's super hard and probably will never happen.
why not model your data and shake specs and tables out of that?
No, but it’s super basic right now. I’m more concerned with deciphering the structures and building code that can navigate them. It’ll be easy to play with the details of what gets generated later. (Plus, Alex said that those internals are still likely to change.)
I'm trying to spec a config file, like this: https://github.com/juxt/aero#profile
nvm, the problem I'm having is that aero does its own eval of the config file, so I can't really get access to the data before the literals are processed (even if I do spec the config file before I pass it to aero, I don't have aero's data readers loaded, so #profile
and such is unrecognized). The solution is to spec the data after aero processes it and replaces the tagged literals