Generate from regular expression with Plumatic Schema generators - clojure

Regexes seem to be accepted schemas:
(require '[schema.core :as schema])
(schema/validate #"^..$" "hi") ; => "hi"
But schema-generators can't seem to generate from them:
(require '[schema-generators.generators :as gen])
(gen/generate #"^..$")
; 1. Unhandled java.lang.RuntimeException
; You must provide a leaf generator for
; schema.spec.leaf.LeafSpec#3d398cfd
Is it possible to work around this in some way?

If we use the miner/strgen library we can indeed work up a solution:
(require '[schema.core :as schema]
'[miner.strgen :as strgen]
'[schema-generators.generators :as gen])
(def two-char #"^..$")
(schema/validate two-char "hi") ; => "hi"
(gen/generate two-char {two-char (strgen/string-generator #"^..$")})
; => "x["
Though it should be noted that this only provides a generator for the regex #"^..$" in perticular and not regular expressions in general. We need a better solution, perhaps extend some protocol somewhere.

Related

Clojure Spec - Register a spec pointing at another spec

Could someone shed a light on the following behavior please?
Let's assume I have this namespace with a spec:
(ns user.specs
(:require [clojure.alpha.spec :as s]
[clojure.alpha.spec.gen :as gen]
[clojure.string :as str]))
# Non-blank string of 20 to 50 ascii chars.
(s/def ::text (s/with-gen
(s/and string? #(not (str/blank? %)))
#(gen/such-that
(complement str/blank?)
(gen/fmap
clojure.string/join
(gen/vector
(gen/char)
20 50)))))
Now I want to reuse this spec.
(in-ns 'user)
(require '[user.specs :as su])
=> nil
(def kws [::dir
::ns])
=> #'user/kws
(s/def ::dir ::su/text)
=> :user/dir
(s/def ::ns string?)
=> :user/ns
(s/register ::spec (s/schema* kws))
=> :user/spec
When exercising the last spec, I get an error:
(s/exercise ::spec)
Error printing return value (IllegalArgumentException) at clojure.core/-cache-protocol-fn (core_deftype.clj:583).
No implementation of method: :conform* of protocol: #'clojure.alpha.spec.protocols/Spec found for class: clojure.lang.Keyword
However, if I redef the ::dir spec usinig s/register and s/get-spec instead of s/def, no problem:
(s/register ::dir (s/get-spec ::su/text))
=> :user/dir
(s/exercise ::spec)
=>
([#:user{:dir "teôÆ>EüáéNj¬u}zþs²DÍ$", :ns ""}
#:user{:dir "teôÆ>EüáéNj¬u}zþs²DÍ$", :ns ""}]
[#:user{:dir ":éû,#Î|)Q«óCS\t´ÿ4ÚÝܺ»Ân5Zq", :ns ""}
#:user{:dir ":éû,#Î|)Q«óCS\t´ÿ4ÚÝܺ»Ân5Zq", :ns ""}]
... elided
I'm assuming, from the error message, that with s/def, spec resolves ::dir as the literal ::su/text keyword instead of the associated spec.
1) Why?
2) Is s/register + s/get-spec an appropriate solution?
I'm trying to reuse a "utility" spec in a few places under domain specific names.
FWIW, I'm using spec-alpha2 in order to build specs dynamically and benefit from schema + select.
Aliasing specs like (s/def ::dir ::su/text) is not currently working in spec 2, which is still a work in progress.

Configure symbol quote expansion in clojure zprint

Is there a way to avoid having zprint write 'my-symbol as (quote my-symbol)? I am aware it is the reader that converts it to that form.
However I would expect zprint to be configurable to produce the more idiomatic format which is the default for clojure.pprint.
(require '[zprint.core :as zp])
(zp/zprint '(def foo 'my-symbol))
;; (def foo (quote my-symbol))
(require '[clojure.pprint :as pp])
(pp/pprint '(def foo 'my-symbol))
;; (def foo 'my-symbol)
See related issue:
https://github.com/kkinnear/zprint/issues/121
Following bfabry's comment, as a workaround, we can use pprint to create the nicely quoted string, and then pass it through zprint for formatting:
(require '[zprint.core :as zp])
(require '[clojure.pprint :as pp])
(-> '(def foo 'my-symbol)
pp/pprint
with-out-str
(zp/zprint 40 {:parse-string? true}))
(def foo 'my-symbol)
nil

How to generative test function-under-test with must-to-test distinct edge cases for each execution?

Hello Clojure experts..!,
Update: 08/03/2018 1:25PM CDT - Rephrased the question to make it more descriptive.
Update: 08/03/2018 4:10PM CDT - Added negative scenario test to make more sense.
I have a function/spec-under-test (jvm-languages), i want to test positive scenario(for now) using Clojure generative test API.
Positive scenario test: Given the jvm-language as one of the five {"clojure" "haskell" "erlang" "scala" "python"}, result should be true.
My Question/requirement: I should want to test my function-under-test with each and every possible valid scenario (which i listed in a set below) for each execution (each time i run, lein test) so that i can get rid of regular unit tests.
As per my little testing knowledge, i think there is no value(for me) to test my function multiple times with the same test-scenario in the same execution. Please educate me, if you think my thought process is wrong.
Is there a way to fulfill my use-case using Clojure generative
testing?
If not, is it not a great feature to have?
(ns spec.gen-test.explore
(:require [clojure.test :refer :all]
[clojure.spec :as spec]
[clojure.test.check.generators :as gen]
[clojure.test.check.properties :as prop :include-macros true]
[clojure.test.check.clojure-test :refer [defspec]]))
(def languages-set #{"clojure" "haskell" "erlang" "scala" "python"})
(spec/def ::jvm-languages
(spec/and string?
languages-set))
;;Generative test case for jvm-languages spec:
(defspec jvm-languages-positive-generative-test
5
(prop/for-all [language (gen/elements ["clojure" "haskell" "erlang"
"scala" "python"])]
(println "Testing for language:" language)
(spec/valid? ::jvm-languages language)))
(defspec jvm-languages-negative-generative-test
100
(prop/for-all [language (gen/such-that
#(not (contains? languages-set %))
(gen/one-of
[ gen/string
(gen/elements ["" nil])
gen/int
gen/keyword
gen/char-alphanumeric
gen/double ]))]
(println "Testing for language:" language)
(not (spec/valid? ::jvm-languages language))))
(run-tests)
Output:
#'spec.gen-test.explore/jvm-languages-positive-generative-test
Testing for language: scala
Testing for language: haskell
Testing for language: erlang
Testing for language: erlang
Testing for language: scala
{:result true, :num-tests 5, :seed 1533232724897, :test-var "jvm-languages-generative-test"}
I'd just iterate the collection since you want to test each case anyway.
You could get some reasonably nice-reading code using is and every?:
(ns your-ns
(:require [clojure.test :refer [is testing]]))
(let [test-cases ["clojure" "haskell" "erlang" "scala" "python"]]
(testing "if my things are working."
; Just iterating the test cases using every?
(is (every? #(your-test-predicate-here %) test-cases))))

clojure.spec custom generator for Java objects

I just saw one of Rich's talks on clojure.spec, and really want to give it a try on my project. I'm writing a series of tools for parsing C code using the eclipse CDT library, and I would like to spec that my functions accept and emit AST objects.
I think a very basic spec could be written for a function that takes the root of an AST and emits all the tree's leaves like this:
(import '(org.eclipse.cdt.core.dom.ast IASTNode))
(require '[clojure.spec :as s])
(defn ast-node? [node] (instance? IASTNode node))
(s/def ::ast-node ast-node?)
(s/fdef leaves :args ::ast-node :ret (s/coll-of ::ast-node))
However when I try to exercise the code (s/exercise leaves) I get an error:
Unable to construct gen at: [] for:
xxx.x$leaves#xxx
#:clojure.spec{:path [], :form #function[xxx.xxx/leaves], :failure :no-gen}
How can I write a custom generator for Java objects to fully spec and exercise my code?
You can attach a custom generator to a spec using s/with-gen. You'll need to write a generator that produces all the node variants that you need. You might find it easier to write one generator per node type and then combine them, either with s/or or possibly by using something like s/multi-spec instead (which would make this open to extension).
An example of writing a generator that produces a Java object would be something like this:
(s/def ::date
(s/with-gen #(instance? java.util.Date %)
(fn [] (gen/fmap #(java.util.Date. %) (s/gen pos-int?)))))
fmap takes a function and applies that to each result from the generator you give it. If you have a Java object with a constructor that takes multiple values, you can use a source generator like (s/gen (s/tuple int? string? int?)).
For completeness, here's my code after applying Alex's answer to spec a "LiteralExpression" AST node:
(ns atom-finder.ast-spec
(:import [org.eclipse.cdt.internal.core.dom.parser.cpp CPPASTLiteralExpression])
(:require [clojure.spec :as s]
[clojure.spec.gen :as gen]))
(def gen-literal-expression-args
(gen/one-of
[
(gen/tuple (s/gen #{CPPASTLiteralExpression/lk_char_constant})
(gen/char-ascii))
(gen/tuple (s/gen #{CPPASTLiteralExpression/lk_float_constant})
(gen/double))
(gen/tuple (s/gen #{CPPASTLiteralExpression/lk_integer_constant})
(s/gen (s/int-in -2147483648 2147483647)))
(gen/tuple (s/gen #{CPPASTLiteralExpression/lk_string_literal})
(gen/string))]))
(def gen-literal-expression
(gen/fmap
(fn [[type val]]
(CPPASTLiteralExpression. type (.toCharArray (str val))))
gen-literal-expression-args))
(s/def ::literal-expression
(s/with-gen
(partial instance? CPPASTLiteralExpression)
(fn [] gen-literal-expression)))
(s/exercise :atom-finder.ast-spec/literal-expression 10

Why can't I use regular expressions to validate strings as map keys?

In my Clojure project, I have these:
:dependencies [
[org.clojure/clojure "1.8.0"]
[prismatic/schema "1.0.5"]]
This is considered valid:
(require '[schema.core :as s])
(def pos (s/pred #(re-matches #"\d+,\d+" %)))
(s/validate pos "0,0")
; "0,0"
So based on that, I would have thought the following would also be valid (but it isn't):
(require '[schema.core :as s])
(def pos (s/pred #(re-matches #"\d+,\d+" %)))
(def structure {(s/optional-key pos) s/Any})
(s/validate structure {"0,0" true, "2,0" false})
; Value does not match schema: {"0,0" disallowed-key, "2,0" disallowed-key}
And as often happens with these things, I discovered the answer soon after posting this question; I got what I expected when I used the below definition for structure instead:
(require '[schema.core :as s])
(def pos (s/pred #(re-matches #"\d+,\d+" %)))
(def structure {pos s/Any})
(s/validate structure {"0,0" true, "2,0" false})
; {"0,0" true, "2,0" false}