Hegel 0.13.1
Property-based testing for C++
Loading...
Searching...
No Matches
hegel::stateful Namespace Reference

Stateful (model-based) property testing. More...

Classes

class  VariablesGenerator
class  Invariant
 An invariant is a predicate that must hold at any point in the stateful test. They are evaluated in full on the initial and final state and sampled at intermediate join points. The invariant function should throw when the invariant is violated. More...
class  ConcurrentRule
 An action that a worker can apply to a concurrent state machine. More...
class  ConcurrentStateMachine
 Base class for a concurrent state machine. More...
class  Pool
 A pool of previously generated values. A pool lets data flow from one rule to another, so a rule can act on a handle or identifier that an earlier rule produced rather than on a freshly drawn value. More...
class  ConcurrentPool
 A pool of values that concurrent workers can share. More...
class  Rule
 A rule is one possible action in a stateful test. More...
struct  StateMachineParams
 Arguments for the StateMachine constructor. More...
class  StateMachine
 Base class for a state machine. Holds the state and declares the rules and the invariants that act on it. More...
struct  RunParams
 Options for run. More...
class  Generator
 The base class of all generators. More...
struct  IGenerator
 Base interface for generators. More...

Functions

Variable pools
template<typename T>
Generator< T > values_consumed (Pool< T > &p)
 Returns a value from the pool and removes it.
template<typename T>
Generator< T > values_reusable (Pool< T > &p)
 Returns a value from the pool without removing it.
template<typename T>
Generator< T > values_consumed (ConcurrentPool< T > &pool)
 Returns a value from a concurrent pool and removes it.
template<typename T>
Generator< T > values_reusable (ConcurrentPool< T > &pool)
 Returns a copy of a value from a concurrent pool.
template<typename M>
void run_concurrent (M &machine, TestCase &tc, int64_t min_concurrency, int64_t max_concurrency)
 Executes a concurrent stateful test with rules from machine.
template<typename M>
void run (M &machine, TestCase &tc, const RunParams &params={})
 Executes a stateful test by repeatedly applying randomly chosen rules from machine to it. Invariants run in full before the first step and after the final step. Between steps, each invariant is sampled independently. Rules mutate machine in place. Raises std::invalid_argument if the machine declares no rules.

Variables

constexpr const char * anonymous_group = "<anonymous>"
 The group name for rules that do not specify a group.

Detailed Description

Stateful (model-based) property testing.

A stateful test exercises a system through a sequence of randomly chosen actions ("rules") applied to a state machine. Derive a machine from StateMachine and define its rules(). Each Rule is a name and a step function that performs one application of the rule, drawing any arguments it needs from the test case and mutating the machine. Invariants are predicates on the machine evaluated in full before any step and after the final step, and sampled at intermediate join points. An invariant must throw when violated.

To run a state machine, pass an instance to run inside a hegel::test. Examples in this documentation assume the alias namespace gs = hegel::generators;.

Example: an integer stack.

struct IntegerStack
: hegel::stateful::StateMachine<IntegerStack, std::vector<int>> {
IntegerStack() : StateMachine({.initial_state = {}}) {}
std::vector<hegel::stateful::Rule<IntegerStack>> rules() {
return {
hegel::stateful::Rule<IntegerStack>(
"push", [](hegel::TestCase& tc, IntegerStack& m) {
m.state.push_back(tc.draw(gs::integers<int>(
{.min_value = 0, .max_value = 100})));
}),
hegel::stateful::Rule<IntegerStack>(
"pop", [](hegel::TestCase& tc, IntegerStack& m) {
tc.assume(!m.state.empty());
m.state.pop_back();
}),
};
}
};
HEGEL_TEST(stack_operations)(hegel::TestCase& tc) {
IntegerStack machine;
hegel::stateful::run(machine, tc);
}
void assume(bool condition) const
Reject the current test case if condition is false.
T draw(const generators::Generator< T > &gen) const
Draw a random value from a generator.
Definition core.h:264
Base class for a state machine. Holds the state and declares the rules and the invariants that act on...
Definition stateful.h:739
#define HEGEL_TEST(name,...)
Define a Hegel property test.
Definition hegel.h:575
void run(M &machine, TestCase &tc, const RunParams &params={})
Executes a stateful test by repeatedly applying randomly chosen rules from machine to it....
Definition stateful.h:1135

Function Documentation

◆ run()

template<typename M>
void hegel::stateful::run ( M & machine,
TestCase & tc,
const RunParams & params = {} )

Executes a stateful test by repeatedly applying randomly chosen rules from machine to it. Invariants run in full before the first step and after the final step. Between steps, each invariant is sampled independently. Rules mutate machine in place. Raises std::invalid_argument if the machine declares no rules.

On a failing replay, each applied rule prints as "Step N: <name>". A violated invariant identifies whether it was observed after a step or in the initial or final state.

Step 1: add
Step 2: add
Invariant nonneg violated after step 2
An invariant is a predicate that must hold at any point in the stateful test. They are evaluated in f...
Definition stateful.h:662
Template Parameters
MThe state-machine type, deriving from StateMachine
Parameters
machineThe state machine, initialized by the caller and mutated by its rules
tcThe test case object
paramsOptions for the run. See RunParams.

◆ run_concurrent()

template<typename M>
void hegel::stateful::run_concurrent ( M & machine,
TestCase & tc,
int64_t min_concurrency,
int64_t max_concurrency )

Executes a concurrent stateful test with rules from machine.

The runner selects a concurrency level from min_concurrency through max_concurrency. Execution proceeds in rounds. For each round, libhegel picks a random concurrency group. Every worker thread then runs a short (possibly empty) random sequence of rules from only that group concurrently with the other workers. The runner waits for all workers to finish before it starts the next round.

All invariants run before the first round and after the final round. Between rounds, the runner samples each invariant independently.

Raises std::invalid_argument if the machine declares no rules or if the concurrency bounds are invalid.

On a failing replay, the output shows the concurrency level, each round, the active group, and the rules that each worker applies, but the falsifying test case is not shrunk since the failure may be dependent on thread scheduling.

Template Parameters
MThe state-machine type, deriving from ConcurrentStateMachine
Parameters
machineThe state machine that the workers share
tcThe test case object
min_concurrencyThe minimum number of workers
max_concurrencyThe maximum number of workers

◆ values_consumed() [1/2]

template<typename T>
Generator< T > hegel::stateful::values_consumed ( ConcurrentPool< T > & pool)

Returns a value from a concurrent pool and removes it.

The draw rejects the test case if the pool is empty.

Template Parameters
TThe type of the values in the pool
Parameters
poolThe pool to draw from
Returns
A generator that consumes values from the pool

◆ values_consumed() [2/2]

template<typename T>
Generator< T > hegel::stateful::values_consumed ( Pool< T > & p)

Returns a value from the pool and removes it.

HEGEL_TEST(pool_round_trip)(hegel::TestCase& tc) {
auto original_set = tc.draw(
"original_set", gs::sets(gs::integers<int>(), {.max_size =
10}));
for (int num : original_set) {
pool.add(num);
}
std::set<int> returned_set;
for (int i = 0; i < original_set.size(); i++) {
returned_set.insert(
}
assert(original_set == returned_set);
}
Handle to the currently-executing test case.
Definition test_case.h:40
A pool of previously generated values. A pool lets data flow from one rule to another,...
Definition stateful.h:329
Generator< T > values_consumed(Pool< T > &p)
Returns a value from the pool and removes it.
Definition stateful.h:449
Template Parameters
TElement type
Parameters
pThe pool to draw from
Returns
An element of the pool

◆ values_reusable() [1/2]

template<typename T>
Generator< T > hegel::stateful::values_reusable ( ConcurrentPool< T > & pool)

Returns a copy of a value from a concurrent pool.

The draw does not remove the value. It rejects the test case if the pool is empty.

Template Parameters
TThe type of the values in the pool
Parameters
poolThe pool to draw from
Returns
A generator that reuses values from the pool

◆ values_reusable() [2/2]

template<typename T>
Generator< T > hegel::stateful::values_reusable ( Pool< T > & p)

Returns a value from the pool without removing it.

HEGEL_TEST(pool_reuse)(hegel::TestCase& tc) {
auto sz = tc.draw("sz", gs::integers<uint8_t>());
auto original_set = tc.draw(
"original_set", gs::sets(gs::integers<int>(), {.max_size =
sz}));
for (int num : original_set) {
pool.add(num);
}
for (int i = 0; i < original_set.size(); i++) {
}
assert(pool.size() == original_set.size());
}
Generator< T > values_reusable(Pool< T > &p)
Returns a value from the pool without removing it.
Definition stateful.h:480
Template Parameters
TElement type
Parameters
pThe pool to draw from
Returns
An element of the pool