Add contract-based testing infrastructure
This MR introduces the ability to define contracts in Allen algorithms. Contracts can now be defined in the form of preconditions
and postconditions
for each algorithm. Pre and post conditions run before and after the operator()
execution of the algorithm and check that the data is in the expected state by guaranteeing developer-defined data tests.
In order to define a contract, it is enough to define a struct that inherits from either Allen::contract::Precondition
or Allen::contract::Postcondition
, implement an operator()
with a concrete signature, and register the contract in the algorithm. For instance, the code below defines a Precondition
for algorithm velo_search_by_triplet_t
:
namespace velo_search_by_triplet {
struct Parameters { ... };
// Define a precondition that checks the cluster container input
struct cluster_container_checks : Allen::contract::Precondition {
void operator()(
const ArgumentReferences<Parameters>&,
const RuntimeOptions&,
const Constants&,
const Allen::Context&) const;
};
struct velo_search_by_triplet_t : public DeviceAlgorithm, Parameters {
// Register contracts for this algorithm
using contracts = std::tuple<cluster_container_checks>;
...
};
} // namespace velo_search_by_triplet
The above contract should be then defined in a source file. Concretely, the above example is defined in https://gitlab.cern.ch/lhcb/Allen/-/blob/dcampora_contracts/device/velo/search_by_triplet/src/SearchByTripletContracts.cu
Changes introduced by this MR:
-
Introduce the namespace
Allen::contract
and two new structsPrecondition
andPostcondition
to allow the definition of contracts. -
Created the first contract for Search by triplet that checks the state of the hit container prior to running the algorithm's
operator()
. -
Added a new CMake option
ENABLE_CONTRACTS
which is disabled by default. If enabled, it guarantees that contracts are run for each algorithm that has registered contracts. -
Added
make_vector
free-standing function that takes an argument and produces anstd::vector
container and populates it with the contents of the argument. If the argument's type isbool
, the resulting datatype isstd::vector<char>
. -
Simplified slightly syntax of Scheduler Machinery to add contract support.
-
Created function
require(bool, std::string)
, which allows to define a condition that should be fulfilled and a string describing the requirement of the condition. This method of writing requirements has several advantages over plainassert
s, since the behaviour can be defined in the backend and it decouples contracts from macroNDEBUG
. Currently, if a requirement is not met, an exception of typeContractException
is thrown with a message similar to the following (thedemangle
trick is used here @raaij ):terminate called after throwing an instance of 'Allen::contract::ContractException' what(): Contract exception in algorithm velo_search_by_triplet, precondition velo_search_by_triplet::cluster_container_checks: Require that y be lower than max value Aborted