Fork me on GitHub
#clojure-spec
<
2019-02-14
>
borkdude10:02:00

Seems to work now. I’m generating regexes using a simplified regex spec (that exists solely for generating) and I’m generating strings that match it using test.check. I’m using these combinations to test re-find, etc. Thanks for the suggestions all 🙂

borkdude10:02:49

I think I’ll just replace the generator on CLJS with a simpler one

gfredericks11:02:37

@borkdude there's a PR for that 🙂

gfredericks11:02:51

I'm not sure if it works though

borkdude11:02:40

PR seems out of date. Even if it’s incomplete I think it’s better than nothing? @wilkerlucio

gfredericks11:02:35

you're suggesting I just merge it? I'm not sure what you mean by "out of date"

borkdude11:02:02

that the PR has merge conflicts

wilkerlucio11:02:16

I wouldn't merge it, the range of things it works is quite narrow at this point, I'm using something simpler to generate strings, if we want to get that working in cljs (which would be awesome) that code needs better testing and impl, not good to merge as is IMO.

wilkerlucio11:02:20

what I'm using for cljs these days is a much simpler string generator that knows just some basic patterns (numbers, letters, alphanum):

wilkerlucio11:02:22

(ns string-gen
  (:require [com.wsscode.test.chuck.charsets :as charsets]
            [clojure.test.check.generators :as gen]))

(defn charset->gen [charset]
  (let [size (charsets/size charset)]
    (if (zero? size)
      (throw (ex-info "Cannot generate characters from empty class!"
               {:type ::ungeneratable}))
      (gen/fmap (partial charsets/nth charset)
        (gen/choose 0 (dec size))))))

(def type->charset
  {"D" (charsets/range "0" "9")
   "W" (charsets/union (charsets/range "0" "9") (charsets/range "a" "z") (charsets/range "A" "Z"))
   "A" (charsets/range "A" "Z")})

(defn parse-int [x]
  (js/parseInt x))

(defn token->gen [token]
  (cond
    (string? token)
    (gen/return token)

    (keyword? token)
    (if-let [[_ t n] (re-find #"([DAW])(\d+)" (name token))]
      (gen/fmap #(apply str %)
        (gen/vector (charset->gen (type->charset t)) (parse-int n)))
      (throw (ex-info "Invalid keyword token" {:token token})))

    :else
    (throw (ex-info "Invalid token" {:token token}))))

(defn string-gen [tokens]
  (->> tokens
       (mapv token->gen)
       (apply gen/tuple)
       (gen/fmap #(apply str %))))

borkdude11:02:51

I’ll consider tweaking this. Thanks

borkdude11:02:57

I just realized that core.match could play nice with clojure spec conform results (in general, not related to this regex discussion)? https://stackoverflow.com/a/54687183/6264

borkdude11:02:05

(probably realized a little late)

joshkh13:02:32

i'm probably missing something obvious -- how can i define a spec so that its value conforms to one of a collection of specs? for example, a person can have a pet that's either a mammal or a fish, neither of which share a common attribute.

(s/def :animal.class/mammal (s/keys :req [:utters/count]))
(s/def :animal.class/fish (s/keys :req [:fins/count]))
(s/def :person/pet (s/or :animal.class/mammal :animal.class/fish))

(s/explain :person/pet {:utters/count 4})
val: #:utters{:count 4} fails spec: :animal.class/fish at: [:animal.class/mammal] predicate: (contains? % :fins/count)
=> nil

joshkh13:02:53

whoops - that was meant for #beginner

borkdude13:02:12

(s/or :animal.class/mammal :animal.class/fish) => (s/or :mammal :animal.class/mammal :fish :animal.class/fish)

borkdude13:02:26

you need to tag the alternatives with a key

joshkh13:02:50

i saw that in cat/alt, didn't realise it applied to /or. thanks as usual, borkdude.

andy.fingerhut13:02:22

borkdude, are you basically looking for bugs in the JVM's and/or JavaScript's regex library using spec and generative testing?

andy.fingerhut13:02:02

I wouldn't be surprised if you find bugs in those, and/or test.chuck's generate-a-string-matching-a-regex-from-a-regex code, or all of the above. Just so you realize that the time scale for fixing the JVM and/or JavaScript's regex library might be a bit long 🙂 Those libraries must be challenging to maintain.

borkdude13:02:31

The situation is like this: someone used Orchestra with speculative specs. Orchestra checks ret specs at runtime (in contrast to spec). It turned out the ret specs of regex functions were incomplete and I could have found this if I had used generative testing (which I have for almost all spec’ed functions except these). Now I have almost fixed this generative testing, but I ran into the CLJS limitation of test.chuck as a last issue.

borkdude13:02:23

I can bypass this by generating simpler strings for CLJS. It will still find the case I forgot to spec.

borkdude13:02:01

I’m not using it for anything more serious than this, just wanted to see how for I could go with it.

mpenet14:02:44

any planned changes (or options) about fn as argument triggering gen, something like wrapping their arguments with asserts instead so that they fail at invoke time ?

mpenet14:02:04

I think there was a jira issue about this

Alex Miller (Clojure team)14:02:57

will revisit, haven't yet

👍 5
djtango17:02:10

Is there a way to provide a custom generator for a spec distant to the spec's definition/local to the test context? The docs for instrument say that the :replace only relates to fn-specs and :gen option is only for stubbed vars

borkdude17:02:47

@djtango do you want to test with stest/check or instrument?

djtango17:02:03

I think right now want to manually generate some inputs using gen/sample

djtango17:02:19

though could also do stest/check - was it stest/check that lets you also patch in your own generators?

djtango17:02:21

that seems familiar

borkdude17:02:58

yes,

(stest/check `my-function {:gen {::my-spec (fn [] (gen/…))}})

djtango17:02:03

awesome thanks

djtango17:02:16

but with instrument no luck?

borkdude17:02:49

> :gen overrides are used only for :stub generation.

borkdude17:02:24

I’m not sure what :replace does, I have never used it 🙂

borkdude17:02:11

:replace replaces a fn with a fn that checks args conformance, then
invokes the fn you provide, enabling arbitrary stubbing and mocking.

borkdude17:02:16

:thinking_face: