Fork me on GitHub
#clojure-spec
<
2018-10-30
>
borkdude13:10:16

I have an interesting case where I call stest/check from the REPL (lein + cider nrepl) and it doesn’t finish. Anyone familiar with that one?

borkdude13:10:28

it does terminate when I run the tests from the command line

Alex Miller (Clojure team)13:10:18

how do you know it won’t finish? did you solve the halting problem!?

😛 4
😆 4
borkdude13:10:51

I’m a human, I use heuristics.

😂 4
borkdude13:10:55

seems to work with clj repl

borkdude13:10:44

$ clj -A:test:repl
user=> (stest/check `str)
({:spec #object[clojure.spec.alpha$fspec_impl$reify__2524 0x3a94964 "clojure.spec.alpha$fspec_impl$reify__2524@3a94964"], :clojure.spec.test.check/ret {:result true, :pass? true, :num-tests 1000, :time-elapsed-ms 19215, :seed 1540906061983}, :sym clojure.core/str})
user=> ^D
Borkdude@borkdude ~/Dropbox/dev/clojure/speculative (borkdude*) $ lein repl
nREPL server started on port 57033 on host 127.0.0.1 - 
(require '[REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.10.0-RC1
Java HotSpot(TM) 64-Bit Server VM 1.8.0_45-b14
...

user=> (require '[speculative.core])
nil
user=> (require '[clojure.spec.test.alpha :as stest])
nil
user=> (stest/check `str)
;; still nothing after a looooong time

gfredericks13:10:10

Have you tried at least a few times from the command line? Tests that probabilistically take a Long Time can be misleading like that with just a few data points

borkdude15:10:34

@gfredericks trying. hmm, now clj lingers after printing the result of stests:

$ clj -A:test -e "(require,'[speculative.core])" -e "(require,'[clojure.spec.test.alpha,:as,stest])" -e "(stest/check \`str)"
({:spec #object[clojure.spec.alpha$fspec_impl$reify__2524 0x3e14c16d "clojure.spec.alpha$fspec_impl$reify__2524@3e14c16d"], :clojure.spec.test.check/ret {:result true, :pass? true, :num-tests 1000, :time-elapsed-ms 15108, :seed 1540911818977}, :sym clojure.core/str})

borkdude15:10:52

it just takes a long time before it returns to the cmd line. maybe garbage collecting?

gfredericks15:10:01

is it exactly 60s?

borkdude15:10:10

this is the output with time:

$ time clj -A:test -e "(require,'[speculative.core])" -e "(require,'[clojure.spec.test.alpha,:as,stest])" -e "(stest/check \`str)"
({:spec #object[clojure.spec.alpha$fspec_impl$reify__2524 0x3e14c16d "clojure.spec.alpha$fspec_impl$reify__2524@3e14c16d"], :clojure.spec.test.check/ret {:result true, :pass? true, :num-tests 1000, :time-elapsed-ms 14804, :seed 1540911992271}, :sym clojure.core/str})
clj -A:test -e "(require,'[speculative.core])" -e  -e "(stest/check \`str)"  25.76s user 1.40s system 35% cpu 1:16.86 total

gfredericks15:10:35

I meant 60s following the end of the tests if so, then adding (shutdown-agents) after the stest/check call should help

borkdude15:10:30

Repro:

;; run with:

;; clj -Srepro -Sdeps '{:deps {org.clojure/clojure {:mvn/version "RELEASE"} org.clojure/test.check {:mvn/version "RELEASE"}}}' -i stest_time.clj


(ns stest-time
  (:require
   [clojure.test :as t]
   [clojure.spec.alpha :as s]
   [clojure.spec.test.alpha :as stest]))

(s/fdef clojure.core/str
  :args (s/* any?)
  :ret string?)

(stest/check `str)

(println "after stest")

(shutdown-agents)

(println "after shutdown agents")
Still takes 13s after printing the last message.

borkdude15:10:22

maybe any? just generates loads of garbage?

gfredericks15:10:23

that sounds very strange

borkdude15:10:36

you can try the script…

gfredericks15:10:37

I mean it does, but I don't think a JVM needs to GC on shutdown

gfredericks15:10:55

you just burn it all down and let the OS sort it out

borkdude15:10:13

maybe a System/exit helps

borkdude15:10:35

yeah it does… but for a test namespace that’s not an option maybe

taylor15:10:49

maybe use jstack while it's "hung" to see what it's doing at the moment

gfredericks15:10:52

kill -3 says there's an agent thread running generator stuff

gfredericks15:10:24

so that code launches a future somehow

gfredericks15:10:14

I think shutdown-agents might not be synchronous in its effect

gfredericks15:10:32

so the fact that it waits longer when there's a future in the background that hasn't completed isn't surprising

gfredericks15:10:37

but why there's a future in this case I don't know

gfredericks15:10:01

(I haven't used clojure.spec.test.alpha much)

taylor15:10:43

at clojure.spec.test.alpha$quick_check.invokeStatic(alpha.clj:310)
	at clojure.spec.test.alpha$quick_check.invoke(alpha.clj:303)
	at clojure.spec.test.alpha$check_1.invokeStatic(alpha.clj:336)
	at clojure.spec.test.alpha$check_1.invoke(alpha.clj:324)
	at clojure.spec.test.alpha$check$fn__3088.invoke(alpha.clj:412)
	at clojure.core$pmap$fn__8342$fn__8343.invoke(core.clj:6994)
:thinking_face:

gfredericks15:10:44

ah well there you go then

gfredericks15:10:00

gotta spend 15 seconds generating a few last giant piles of garbage in case somebody wants them

🚮 4
gfredericks15:10:23

@borkdude the most recent alpha of test.check might be a bit better about this. I can't remember, and also don't know what version you get by specifying RELEASE

taylor15:10:04

looks like org/clojure/test.check/0.10.0-alpha3

gfredericks15:10:21

oh; nevermind then

gfredericks15:10:46

for this kind of test it's probably best to use a smaller max-size for the gen/any

borkdude15:10:05

I’ll try that, thanks.

gfredericks15:10:19

I wonder if gen/any should just have a smaller default size universally :thinking_face:

borkdude15:10:30

any snippet handy that I could re-use?

gfredericks15:10:58

no, I'm not sure of the exact syntax for sizing control in spec

borkdude15:10:09

k, I’ll figure something out

gfredericks15:10:12

if you don't mind affecting the whole generator, I think the stest/check call lets you pass max size

borkdude15:10:20

oh that would be nice

gfredericks15:10:24

for finer grained control you'd probably set an override specific to the any? spec

borkdude09:11:08

Any idea how to do this? I tried by using a :gen override for :clojure.core/any? but that doesn’t work, because any? is a builtin I think: https://github.com/clojure/spec.alpha/blob/31165fec69ff86129a1ada8b3f50864922dfc88a/src/main/clojure/clojure/spec/gen/alpha.clj#L118

gfredericks12:11:30

I wouldn't have expected that, but assuming you're right and you can't override builtins, another option would be having your own (def ::any any?) that you can override

gfredericks12:11:46

unfortunate if you have to do that, but should work at least

borkdude15:10:58

@alexmiller if you want I can post that script in an issue if you think it’s something that should be improved over time

Alex Miller (Clojure team)15:10:01

I don’t understand what we’re talking about

Alex Miller (Clojure team)15:10:35

check returns a lazy seq - you need to consume it

Alex Miller (Clojure team)15:10:20

As noted in the doc string

Alex Miller (Clojure team)15:10:53

So wrap a doall around the call to check

borkdude15:10:55

aah, wrapping it in a doall works

borkdude15:10:08

issue solved (see above thread).

borkdude15:10:22

didn’t realize that (pun intended)

👏 16
😏 4
andy.fingerhut21:10:11

I slightly envy your pun-creation skills.

martinklepsch18:10:47

I'm not sure if this is a bad idea or something but I'd love to always have stest/instrument switched on during development. Is there some way to keep functions instrumented after re-defining them in the REPL?

taylor18:10:29

I see there's (defonce ^:private instrumented-vars (atom {})) in spec.alpha, but I don't see it publicly exposed. Something that came to mind was using that to re-instrument things that were previously instrumented?

dadair20:10:58

If you are using something like Component or Integrant you can wrap their system reset functions with calls to instrument

dadair20:10:15

I’ve done that with our duct-based project at work

martinklepsch22:10:48

I’ve considered that but often I’m just working in one namespace in the REPL and restarting the system seems like overkill in a way

martinklepsch22:10:07

I guess I should just bind stest/Instrument to a hotkey

borkdude18:10:27

@martinklepsch I hook up stest/instrument with component. So when I restart the system, my new fns are instrumented.

👍 4
💡 4
borkdude23:10:21

somehow this also lingers after the test has executed, even with doall:

(ns spec-keys-test
  (:require
   [clojure.test :as t :refer [deftest testing is]]
   [clojure.spec.alpha :as s]
   [clojure.spec.test.alpha :as stest]))

(defn foo [n] n)

(s/fdef foo
  :args (s/cat :n number?)
  :ret number?)

(deftest repro-test
  (testing "output of check"
    (let [r (doall (stest/check `foo))
          c (count r)]
      (println "count" c)
      (is (= c (count (keep :spec r))))
      (is (= c (count (keep :sym r))))
      (is (= c (count (keep :clojure.spec.test.check/ret r)))))))

(t/run-tests)

andy.fingerhut23:10:42

I think shutdown-agents was mentioned before, but wasn't sure whether you had tried it. If your code calls future directly (or indirectly through one of several other functions), it can cause a 60-second pause at the end before the JVM exits: http://clojuredocs.org/clojure.core/future

borkdude08:10:43

Right, that’s it. So doall + shutdown-agents it is.

andy.fingerhut23:10:55

e.g. pmap clojure.java.shell/sh