From 957af4fc068f2d0042ab32deea3e0879fc14a330 Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Mon, 28 Dec 2020 15:45:45 -0600 Subject: [PATCH] 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. --- .../push/instructions/vector_spec.clj | 180 +++++------------- 1 file changed, 44 insertions(+), 136 deletions(-) diff --git a/test/propeller/push/instructions/vector_spec.clj b/test/propeller/push/instructions/vector_spec.clj index 388db47..697d627 100644 --- a/test/propeller/push/instructions/vector_spec.clj +++ b/test/propeller/push/instructions/vector_spec.clj @@ -12,15 +12,45 @@ ['gen/boolean "boolean"] ['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 -(defn check-expected-contains +(defn check-contains "Creates an otherwise empty Push state with the given vector on the appropriate vector stack (assumed to be :vector_), and the given value on the appropriate stack (determined by value-type). It then runs the vector/_contains instruction, and confirms that the 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)) start-state (state/push-to-stack (state/push-to-stack state/empty-state @@ -28,36 +58,16 @@ vect) (keyword value-type) value) end-state (vector/_contains stack-type start-state) - expected-result (not (= (.indexOf vect value) -1))] + expected-result (not= (.indexOf vect value) -1)] (= expected-result (state/peek-stack end-state :boolean)))) -(defmacro contains-vector-spec - [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") +(gen-specs "contains" check-contains :vector :item) ;;; vector/_emptyvector (defn check-empty-vector - [vect value-type] + [value-type vect] (let [stack-type (keyword (str "vector_" value-type)) start-state (state/push-to-stack state/empty-state stack-type @@ -66,22 +76,12 @@ (= (empty? vect) (state/peek-stack end-state :boolean)))) -(defmacro empty-vector-spec - [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") +(gen-specs "empty-vector" check-empty-vector :vector) ;;; vector/_first (defn check-first - [vect value-type] + [value-type vect] (let [stack-type (keyword (str "vector_" value-type)) start-state (state/push-to-stack state/empty-state stack-type @@ -96,22 +96,12 @@ (state/peek-stack end-state (keyword value-type))) (state/empty-stack? end-state stack-type))))) -(defmacro first-spec - [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") +(gen-specs "first" check-first :vector) ;;; vector/_last (defn check-last - [vect value-type] + [value-type vect] (let [stack-type (keyword (str "vector_" value-type)) start-state (state/push-to-stack state/empty-state stack-type @@ -126,27 +116,17 @@ (state/peek-stack end-state (keyword value-type))) (state/empty-stack? end-state stack-type))))) -(defmacro last-spec - [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") +(gen-specs "last" check-last :vector) ;;; vector/_indexof -(defn check-expected-index +(defn check-indexof "Creates an otherwise empty Push state with the given vector on the appropriate vector stack (assumed to be :vector_), and the given value on the appropriate stack (determined by value-type). It then runs the vector/_indexof instruction, and confirms that the 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)) start-state (state/push-to-stack (state/push-to-stack state/empty-state @@ -158,27 +138,7 @@ (= expected-index (state/peek-stack end-state :integer)))) -(defmacro indexof-spec - [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") +(gen-specs "indexof" check-indexof :vector :item) ;;; vector/_concat @@ -201,16 +161,7 @@ (= (concat second-vect first-vect) (state/peek-stack end-state stack-type)))) -(defmacro concat-spec - [] - `(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) +(gen-specs "concat" check-concat :vector :vector) ;;; vector/_subvec @@ -244,47 +195,4 @@ (= expected-subvec (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)