Use new gen-specs macro for other test cases

This moved the new `gen-specs` macro to the top of the file, and uses it to simplify the generation for the existing specifications. This allowed us to remove quite a lot of code.

We also had to change the order of arguments for all the "old" check functions so that `value-type` comes first. We renamed a few check functions so the naming is more consistent.
This commit is contained in:
Nic McPhee 2020-12-28 15:45:45 -06:00
parent d30f7c195e
commit 957af4fc06

View File

@ -12,15 +12,45 @@
['gen/boolean "boolean"] ['gen/boolean "boolean"]
['gen/string "string"]]) ['gen/string "string"]])
(defn generator-for-arg-type
[arg-type generator]
(case arg-type
:boolean 'gen/boolean
:integer 'gen/small-integer
:float 'gen/double
:string 'gen/string
; This is for "generic" vectors where the element is provided by
; the `generator` argument.
:vector `(gen/vector ~generator)
:item generator
:vector_boolean '(gen/vector gen/boolean)
:vector_integer '(gen/vector gen/small-integer)
:vector_float '(gen/vector gen/double)
:vector_string '(gen/vector gen/string)))
(defmacro gen-specs
[spec-name check-fn & arg-types]
(let [symbol-names (repeatedly (count arg-types) gensym)]
`(do ~@(for [[generator value-type] gen-type-pairs
:let [name (symbol (str spec-name "-spec-" value-type))]]
`(defspec ~name
(prop/for-all
[~@(mapcat
(fn [symbol-name arg-type]
[symbol-name (generator-for-arg-type arg-type generator)])
symbol-names
arg-types)]
(~check-fn ~value-type ~@symbol-names)))))))
;;; vector/_contains ;;; vector/_contains
(defn check-expected-contains (defn check-contains
"Creates an otherwise empty Push state with the given vector on the "Creates an otherwise empty Push state with the given vector on the
appropriate vector stack (assumed to be :vector_<value-type>), and appropriate vector stack (assumed to be :vector_<value-type>), and
the given value on the appropriate stack (determined by value-type). the given value on the appropriate stack (determined by value-type).
It then runs the vector/_contains instruction, and confirms that the It then runs the vector/_contains instruction, and confirms that the
result (on the :boolean stack) is the expected value." result (on the :boolean stack) is the expected value."
[vect value value-type] [value-type vect value]
(let [stack-type (keyword (str "vector_" value-type)) (let [stack-type (keyword (str "vector_" value-type))
start-state (state/push-to-stack start-state (state/push-to-stack
(state/push-to-stack state/empty-state (state/push-to-stack state/empty-state
@ -28,36 +58,16 @@
vect) vect)
(keyword value-type) value) (keyword value-type) value)
end-state (vector/_contains stack-type start-state) end-state (vector/_contains stack-type start-state)
expected-result (not (= (.indexOf vect value) -1))] expected-result (not= (.indexOf vect value) -1)]
(= expected-result (= expected-result
(state/peek-stack end-state :boolean)))) (state/peek-stack end-state :boolean))))
(defmacro contains-vector-spec (gen-specs "contains" check-contains :vector :item)
[generator value-type]
`(do
(defspec ~(symbol (str "contains-vector-spec-" value-type))
; Should this be smaller for booleans? (Ditto for below.)
100
(prop/for-all [vect# (gen/vector ~generator)
value# ~generator]
(check-expected-contains vect# value# ~value-type)))
; For float and string vectors, it's rather rare to actually have a random value that
; appears in the vector, so we don't consistently test the case where it should
; return TRUE. So maybe we do need a separate test for those?
(defspec ~(symbol (str "contains-vector-spec-has-value-" value-type))
100
(prop/for-all [vect# (gen/not-empty (gen/vector ~generator))]
(check-expected-contains vect# (rand-nth vect#) ~value-type)))))
(contains-vector-spec gen/small-integer "integer")
(contains-vector-spec gen/double "float")
(contains-vector-spec gen/boolean "boolean")
(contains-vector-spec gen/string "string")
;;; vector/_emptyvector ;;; vector/_emptyvector
(defn check-empty-vector (defn check-empty-vector
[vect value-type] [value-type vect]
(let [stack-type (keyword (str "vector_" value-type)) (let [stack-type (keyword (str "vector_" value-type))
start-state (state/push-to-stack state/empty-state start-state (state/push-to-stack state/empty-state
stack-type stack-type
@ -66,22 +76,12 @@
(= (empty? vect) (= (empty? vect)
(state/peek-stack end-state :boolean)))) (state/peek-stack end-state :boolean))))
(defmacro empty-vector-spec (gen-specs "empty-vector" check-empty-vector :vector)
[generator value-type]
`(defspec ~(symbol (str "empty-vector-spec-" value-type))
100
(prop/for-all [vect# (gen/vector ~generator)]
(check-empty-vector vect# ~value-type))))
(empty-vector-spec gen/small-integer "integer")
(empty-vector-spec gen/double "float")
(empty-vector-spec gen/boolean "boolean")
(empty-vector-spec gen/string "string")
;;; vector/_first ;;; vector/_first
(defn check-first (defn check-first
[vect value-type] [value-type vect]
(let [stack-type (keyword (str "vector_" value-type)) (let [stack-type (keyword (str "vector_" value-type))
start-state (state/push-to-stack state/empty-state start-state (state/push-to-stack state/empty-state
stack-type stack-type
@ -96,22 +96,12 @@
(state/peek-stack end-state (keyword value-type))) (state/peek-stack end-state (keyword value-type)))
(state/empty-stack? end-state stack-type))))) (state/empty-stack? end-state stack-type)))))
(defmacro first-spec (gen-specs "first" check-first :vector)
[generator value-type]
`(defspec ~(symbol (str "first-spec-" value-type))
100
(prop/for-all [vect# (gen/vector ~generator)]
(check-first vect# ~value-type))))
(first-spec gen/small-integer "integer")
(first-spec gen/double "float")
(first-spec gen/boolean "boolean")
(first-spec gen/string "string")
;;; vector/_last ;;; vector/_last
(defn check-last (defn check-last
[vect value-type] [value-type vect]
(let [stack-type (keyword (str "vector_" value-type)) (let [stack-type (keyword (str "vector_" value-type))
start-state (state/push-to-stack state/empty-state start-state (state/push-to-stack state/empty-state
stack-type stack-type
@ -126,27 +116,17 @@
(state/peek-stack end-state (keyword value-type))) (state/peek-stack end-state (keyword value-type)))
(state/empty-stack? end-state stack-type))))) (state/empty-stack? end-state stack-type)))))
(defmacro last-spec (gen-specs "last" check-last :vector)
[generator value-type]
`(defspec ~(symbol (str "last-spec-" value-type))
100
(prop/for-all [vect# (gen/vector ~generator)]
(check-last vect# ~value-type))))
(last-spec gen/small-integer "integer")
(last-spec gen/double "float")
(last-spec gen/boolean "boolean")
(last-spec gen/string "string")
;;; vector/_indexof ;;; vector/_indexof
(defn check-expected-index (defn check-indexof
"Creates an otherwise empty Push state with the given vector on the "Creates an otherwise empty Push state with the given vector on the
appropriate vector stack (assumed to be :vector_<value-type>), and appropriate vector stack (assumed to be :vector_<value-type>), and
the given value on the appropriate stack (determined by value-type). the given value on the appropriate stack (determined by value-type).
It then runs the vector/_indexof instruction, and confirms that the It then runs the vector/_indexof instruction, and confirms that the
result (on the :integer stack) is the expected value." result (on the :integer stack) is the expected value."
[vect value value-type] [value-type vect value]
(let [stack-type (keyword (str "vector_" value-type)) (let [stack-type (keyword (str "vector_" value-type))
start-state (state/push-to-stack start-state (state/push-to-stack
(state/push-to-stack state/empty-state (state/push-to-stack state/empty-state
@ -158,27 +138,7 @@
(= expected-index (= expected-index
(state/peek-stack end-state :integer)))) (state/peek-stack end-state :integer))))
(defmacro indexof-spec (gen-specs "indexof" check-indexof :vector :item)
[generator value-type]
`(do
(defspec ~(symbol (str "indexof-spec-" value-type))
; Should this be smaller for booleans? (Ditto for below.)
100
(prop/for-all [vect# (gen/vector ~generator)
value# ~generator]
(check-expected-index vect# value# ~value-type)))
; For float and string vectors, it's rather rare to actually have a random value that
; appears in the vector, so we don't consistently test the case where it should
; return -1. So maybe we do need a separate test for those?
(defspec ~(symbol (str "indexof-spec-has-value-" value-type))
100
(prop/for-all [vect# (gen/not-empty (gen/vector ~generator))]
(check-expected-index vect# (rand-nth vect#) ~value-type)))))
(indexof-spec gen/small-integer "integer")
(indexof-spec gen/double "float")
(indexof-spec gen/boolean "boolean")
(indexof-spec gen/string "string")
;;; vector/_concat ;;; vector/_concat
@ -201,16 +161,7 @@
(= (concat second-vect first-vect) (= (concat second-vect first-vect)
(state/peek-stack end-state stack-type)))) (state/peek-stack end-state stack-type))))
(defmacro concat-spec (gen-specs "concat" check-concat :vector :vector)
[]
`(do ~@(for [[generator value-type] gen-type-pairs
:let [name (symbol (str "concat-spec-" value-type))]]
`(defspec ~name
(prop/for-all [first-vect# (gen/vector ~generator)
second-vect# (gen/vector ~generator)]
(check-concat first-vect# second-vect# ~value-type))))))
; (concat-spec)
;;; vector/_subvec ;;; vector/_subvec
@ -244,47 +195,4 @@
(= expected-subvec (= expected-subvec
(state/peek-stack end-state stack-type)))) (state/peek-stack end-state stack-type))))
(defmacro subvec-spec
[]
`(do ~@(for [[generator value-type] gen-type-pairs
:let [name (symbol (str "subvec-spec-" value-type))]]
`(defspec ~name
(prop/for-all [vect# (gen/vector ~generator)
start# gen/small-integer
stop# gen/small-integer]
(check-subvec vect# start# stop# ~value-type))))))
; (subvec-spec)
(defn generator-for-arg-type
[arg-type generator]
(case arg-type
:boolean 'gen/boolean
:integer 'gen/small-integer
:float 'gen/double
:string 'gen/string
; This is for "generic" vectors where the element is provided by
; the `generator` argument.
:vector `(gen/vector ~generator)
:vector_boolean '(gen/vector gen/boolean)
:vector_integer '(gen/vector gen/small-integer)
:vector_float '(gen/vector gen/double)
:vector_string '(gen/vector gen/string)
))
(defmacro gen-specs
[spec-name check-fn & arg-types]
(let [symbol-names (repeatedly (count arg-types) gensym)]
`(do ~@(for [[generator value-type] gen-type-pairs
:let [name (symbol (str spec-name "-spec-" value-type))]]
`(defspec ~name
(prop/for-all
[~@(mapcat
(fn [symbol-name arg-type]
[symbol-name (generator-for-arg-type arg-type generator)])
symbol-names
arg-types) ]
(~check-fn ~value-type ~@symbol-names)))))))
(gen-specs "concat" check-concat :vector :vector)
(gen-specs "subvec" check-subvec :vector :integer :integer) (gen-specs "subvec" check-subvec :vector :integer :integer)