From d30f7c195ead437d95d540c629be502efad582b5 Mon Sep 17 00:00:00 2001 From: Nic McPhee Date: Mon, 28 Dec 2020 15:27:10 -0600 Subject: [PATCH] Write generalized spec generation macro This add `gen-spec` which provides a fairly general macro for creating `test.check` specifications for vector instructions. It takes a string used to generate specification names, a function used to check the state after running the function under test, and a list of keywords indicating types for arguments generated by `test.check` and passed to the check function. Keywords like `:integer` and `:string` are associated with "simple" generators such as `gen/small-integer` and `gen/string`. The keyword `:vector` is used to generate a vector of items whose type is specified by the `generator` argument to `generator-for-arg-type`. This allows us to loop over all of the supported vector item types in the macro and generate a separate specification for each type. We moved the `value-type` argument for the `check` functions to the front to simplify passing in the value type arguments, which can vary in number. --- .../push/instructions/vector_spec.clj | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/test/propeller/push/instructions/vector_spec.clj b/test/propeller/push/instructions/vector_spec.clj index 5b630d3..388db47 100644 --- a/test/propeller/push/instructions/vector_spec.clj +++ b/test/propeller/push/instructions/vector_spec.clj @@ -190,7 +190,7 @@ The order of concatenation is that the top of the stack will be _second_ in the concatenation, i.e., its elements will come _after_ the elements in the vector one below it in the stack." - [first-vect second-vect value-type] + [value-type first-vect second-vect] (let [stack-type (keyword (str "vector_" value-type)) start-state (state/push-to-stack (state/push-to-stack state/empty-state @@ -210,7 +210,7 @@ second-vect# (gen/vector ~generator)] (check-concat first-vect# second-vect# ~value-type)))))) -(concat-spec) +; (concat-spec) ;;; vector/_subvec @@ -229,7 +229,7 @@ the given values on the integer stack. It then runs the vector/_subvec instruction, and confirms that the result (on the :vector_ stack) is the expected value." - [vect start stop value-type] + [value-type vect start stop] (let [stack-type (keyword (str "vector_" value-type)) start-state (state/push-to-stack (state/push-to-stack @@ -254,4 +254,37 @@ stop# gen/small-integer] (check-subvec vect# start# stop# ~value-type)))))) -(subvec-spec) +; (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)