13#include "hegel/core.h"
122 template <
typename T>
class Pool {
137 void add(
const T& element) {
138 int64_t var_id = pool_handle_.add(tc_);
140 if (pool_.find(var_id) != pool_.end()) {
141 throw std::runtime_error(
"unexpected variable id in map");
144 pool_.emplace(var_id, element);
153 int64_t var_id = pool_handle_.add(tc_);
155 if (pool_.find(var_id) != pool_.end()) {
156 throw std::runtime_error(
"unexpected variable id in map");
159 pool_.emplace(var_id, std::move(element));
167 std::size_t
size()
const {
return pool_.size(); }
173 hegel::internal::PoolHandle pool_handle_;
174 std::map<int64_t, T> pool_;
181 template <
typename T>
class VariablesGenerator :
public IGenerator<T> {
183 explicit VariablesGenerator(Pool<T>& p,
bool consume)
184 : p_(p), consume_(consume) {}
186 T do_draw(
const TestCase& tc)
const override {
187 int64_t variable = p_.pool_handle_.draw_variable(tc, consume_);
189 auto it = p_.pool_.find(variable);
191 if (it == p_.pool_.end()) {
192 throw std::runtime_error(
193 "Pool state diverged between the engine and the "
194 "client, or a bug in the pool bookkeeping.");
198 auto val = std::move(it->second);
289 template <
typename T>
class Rule {
301 : name_(std::move(
name)), step_(std::move(
step)) {}
308 const std::string&
name()
const {
return name_; }
314 const std::function<void(
TestCase&, T&)>&
step()
const {
return step_; }
318 std::function<void(
TestCase&, T&)> step_;
339 : name_(std::move(
name)), invariant_(std::move(
invariant)) {}
346 const std::string&
name()
const {
return name_; }
353 const std::function<void(
const T&)>&
invariant()
const {
359 std::function<void(
const T&)> invariant_;
420 :
state(std::move(params.initial_state)) {}
462 template <
typename M>
465 tc.
note(
"state = " + internal::repr(machine.state));
470 template <
typename T>
471 void check_invariants(TestCase& tc,
const std::string& origin,
473 const std::vector<Invariant<T>>& invariants) {
474 for (
const auto& inv : invariants) {
476 (inv.invariant())(state);
478 tc.note(
"Invariant " + inv.name() +
" violated " + origin);
507 template <
typename M>
510 std::is_base_of<StateMachine<M, typename M::state_type>, M>::value,
511 "run() requires a machine deriving from "
512 "StateMachine<M, State>.");
513 std::vector<Rule<M>> rules = machine.rules();
514 std::vector<Invariant<M>> invariants = machine.invariants();
516 throw std::invalid_argument(
517 "Cannot run a state machine with no rules.");
519 std::vector<std::string> rule_names;
520 rule_names.reserve(rules.size());
521 for (
const Rule<M>& rule : rules)
522 rule_names.push_back(rule.name());
524 std::vector<std::string> invariant_names;
525 invariant_names.reserve(invariants.size());
526 for (
const Invariant<M>& invariant : invariants)
527 invariant_names.push_back(invariant.name());
529 print_state(tc, machine, params);
530 check_invariants(tc,
"in the initial state", machine, invariants);
532 internal::StateMachineHandle machine_handle(tc, rule_names,
534 int64_t steps_run = 0;
537 internal::start_span(tc, internal::SpanLabel::StatefulRule);
538 int64_t next_rule_idx = machine_handle.next_rule(tc);
539 if (next_rule_idx == internal::state_machine_done) {
543 if (next_rule_idx < 0 ||
544 static_cast<size_t>(next_rule_idx) >= rules.size()) {
545 throw std::runtime_error(
546 "state_machine_next_rule returned out-of-range "
547 "rule index. Please report this as a bug.");
551 const Rule<M>& rule = rules[
static_cast<size_t>(next_rule_idx)];
552 tc.
note(
"Step " + std::to_string(steps_run) +
": " + rule.name());
557 internal::NoteIndentScope indent(tc);
558 rule.step()(tc, machine);
560 print_state(tc, machine, params);
561 check_invariants(tc,
"after step " + std::to_string(steps_run),
562 machine, invariants);
563 internal::stop_span(tc);
564 }
catch (
const internal::HegelReject&) {
565 tc.
note(
"Rule stopped early due to violated assumption.");
566 machine_handle.rule_rejected(tc);
567 internal::stop_span(tc,
true);
569 internal::stop_span(tc);
Handle to the currently-executing test case.
Definition test_case.h:40
void note(std::string_view message) const
Record a message that will be printed on the final replay of a failing test case.
The base class of all generators.
Definition core.h:67
Invariant(std::string name, std::function< void(const T &)> invariant)
Declare a new invariant.
Definition stateful.h:337
const std::string & name() const
Returns the name of the invariant.
Definition stateful.h:346
const std::function< void(const T &)> & invariant() const
Returns the function representing the predicate of the invariant.
Definition stateful.h:353
A pool of previously generated values. A pool lets data flow from one rule to another,...
Definition stateful.h:122
std::size_t size() const
Returns the number of variables in the pool.
Definition stateful.h:167
Pool(const TestCase &tc)
Creates an empty pool. Pools are tied to a test case. Do not reuse one across test cases.
Definition stateful.h:130
void add(T &&element)
Adds element to the pool. Overload for moving rvalues.
Definition stateful.h:152
void add(const T &element)
Adds element to the pool. Overload for copying lvalues.
Definition stateful.h:137
A rule is one possible action in a stateful test.
Definition stateful.h:289
Rule(std::string name, std::function< void(TestCase &, T &)> step)
Declares a new Rule.
Definition stateful.h:300
const std::function< void(TestCase &, T &)> & step() const
Returns the underlying step function of the rule.
Definition stateful.h:314
const std::string & name() const
Returns the name of the rule.
Definition stateful.h:308
Base class for a state machine. Holds the state and declares the rules and the invariants that act on...
Definition stateful.h:406
std::vector< Invariant< Derived > > invariants()
Invariants checked before the first step and after every valid step. Override to add them....
Definition stateful.h:432
State state
Definition stateful.h:424
State state_type
The type of the state the rules act on.
Definition stateful.h:409
StateMachine(StateMachineParams< State > params)
Builds a machine holding the given initial state.
Definition stateful.h:419
Stateful (model-based) property testing.
Definition stateful.h:59
Generator< T > values_consumed(Pool< T > &p)
Returns a value from the pool and removes it.
Definition stateful.h:242
Generator< T > values_reusable(Pool< T > &p)
Returns a value from the pool without removing it.
Definition stateful.h:273
void run(M &machine, TestCase &tc, const RunParams ¶ms={})
Executes a stateful test by repeatedly applying randomly chosen rules from machine to it and checking...
Definition stateful.h:508
Base interface for generators.
Definition core.h:29
Base interface for generators.
Definition core.h:29
Options for run.
Definition stateful.h:441
bool print_state
Definition stateful.h:457
Arguments for the StateMachine constructor.
Definition stateful.h:367
State initial_state
The state the first rule acts on.
Definition stateful.h:369