Say I have a generator users-genthat generates a group of 1 or more users. And one more parameterized generator, called user-actions-gen, which takes a sequence of one or more users and generates a sequence of actions that these users can perform.
(def user-gen
;; generates a user
...)
(def users-gen
;; sequences of 1 or more users
(gen/such-that not-empty (gen/vector gen/users))
(defn user-actions-gen [users]
;; a action performed by some user from the `users argument
...)
If I want to generate one action for one sequence of users created by gen-users, then it's simple, just gen / bind users-gen directly in user-actions-gen.
However, I want to create many actions from the same sequence of users. I have this problem because I basically just try to say: "Here is the state, let any random action come in, let's apply the action to the state, let's confirm that the state is still valid, do it for all the actions." I have the following code.
(defspec check-that-state-is-always-valid
100
(let [state-atm (atom {})]
(prop/for-all
[[actions users]
(gen/bind users-gen
(fn [users]
(gen/tuple
(gen/vector (user-actions-gen users))
(gen/return users))))]
(doseq [action actions
:let [state (swap! state-atm state-atm-transform-fx action)]]
(is (state-still-valid? state))))))
Such work. The problems are as follows:
- It seems to be fully evaluating the dose, rather than stopping at the first error.
- It just looks wrong. The code is everywhere, it's not entirely obvious what it does.
- , , , -action-gen -gen, -gen? ? , , -, , .
, . . /?