The tests of a module are specified in its documentation blocks,
both globally (in the documentation of the module itself) and locally (in the documentation of functions, types, etc.).
The format is dictated by the parsing tools in Platform, specifically \module{Clean.Parse.Comments} and \module{Clean.Doc}.
\makeatletter
\newcommand{\availableon}[1]{%
\vspace{-6mm}%
\hfill\small{\textit{Available on:} #1.}%
\vspace{2mm}\par\noindent%
\@ifnextchar\par\@gobble\relax}
\makeatother
\subsection{Basic properties}
Minimal test suites use only \nameref{sec:property}, though \nameref{sec:property-bootstrap} is virtually always needed.
\subsubsection{\clean{@property}}
\label{sec:property}
\availableon{functions}
In properties, the full Clean syntax can be used to specify Gast properties.
Since Gast is automatically imported (see \cref{sec:property-bootstrap}), you can use its combinators.
The first line of a property indicates its name and arguments.
An example test for the correctness of \clean{member} in \module{Data.Set} is:
\begin{Verbatim}
* @property correctness: A.x :: Int; s :: Set Int:
* member x s <==> isMember x (toList s)
\end{Verbatim}
This can be read as:
\enquote{there is a property \enquote{correctness}, which says that
for all \clean{x} of type \clean{Int} and \clean{s} of type \clean{Set Int},
\clean{member x s} holds if and only if \clean{isMember x (toList s)} holds.}
Obviously, this property should hold for more types than \clean{Int}.
However, Gast needs to have a concrete type in order to be able to generate test values.
It is possible to specify properties more abstractly; see \nameref{sec:property-test-with}.
\subsubsection{\clean{@property-bootstrap}}
\label{sec:property-bootstrap}
\availableon{modules}
In this field, give all the \enquote{bootstrap} that the generated test module should contain.
This includes imports, file-global helper types and functions, necessary instances and derivations, etc.
Some imports are included automatically and do not need to be added here:
\begin{itemize}
\item\module{Gast};
\item\module{Gast.CommandLine};
\item\clean{instance toString \{\#Char\}} from \module{StdString};
\item\module{Control.GenBimap};
\item The tested module itself.
\end{itemize}
\subsection{Advanced properties}
\subsubsection{\clean{@invariant}}
\label{sec:invariant}
\availableon{types}
Invariants are properties of types that should always hold.
This is useful for types of which the values are often assumed to be more restrictive than the type system allows to specify.
For instance, it is assumed that a \clean{Map} or a \clean{Set} is ordered, and that a type for angles has values from $[0,2\pi)$ or $[0,360)$ degrees.
Invariants on types are specified with the exact same syntax as properties on functions.
In the generated test module, they become plain functions and can thus be used in \nameref{sec:property} and \nameref{sec:precondition} fields.
For instance, for the \clean{Set} type we may specify:
\begin{Verbatim}
* @invariant no_duplicates: A.s :: Set a | Eq, genShow{|*|}, gPrint{|*|} a:
* xs =.= removeDup xs where xs = toList s
\end{Verbatim}
Then we can use this invariant in the definition of the correctness property of \clean{insert}:
This section describes ways to specify properties in more abstract ways.
This serves two main functions:
it makes properties more readable as documentation,
and it reduces code duplication in properties.
\subsubsection{\clean{@property-test-generator}}
\label{sec:property-test-generator}
\availableon{modules}
In some cases, Gast's value generation is not suitable for generating useful test cases.
In particular this is the case for types as \clean{Map} and \clean{Set} which assume that some properties hold for their values
(while these properties cannot be ensured by the type system).
When testing such types, it is custom to use a \enquote{shadow type} to generate values and transform the shadow type to the tested type.%
\footnote{Common shadow types are \clean{[(k,v)]} for \clean{Map k v} and \clean{[a]} for \clean{Set a}.}
Inconveniently, classic testing with Gast then requires one to specify properties on the shadow type instead of the actual type and call the value transformer from every test.
Clean-test-properties can automate this process by defining a file-global test generator containing the value transformer.
A typical test generator for \clean{Set} would look like:
\begin{Verbatim}
* @property-test-generator [a] -> Set a | < a
* gen xs = fromList xs
\end{Verbatim}
On the first line, the type of the test generator is specified, including both the shadow type (\clean{[a]}) and the actual type (\clean{Set a}).
The following lines specify the implementation, assuming the generator is called \clean{gen}.
When clean-test-properties encounters a property requiring arguments that can be generated by this generator,
it will automatically insert it into the property (and change the property's type accordingly).
When multiple test generators are applicable, one is picked arbitrarily.
For a generator to be recognised by clean-test-properties it must have the \emph{exact same} result type as the argument used in the property.
This means that a property with arguments of type \clean{Set b} will \emph{not} use the test generator specified above, because the type variables do not match.
\subsubsection{\clean{@property-test-with}}
\label{sec:property-test-with}
\availableon{modules; functions}
With this field it is possible to abstract from concrete types in property signatures and still allow Gast to generate test values.
We can specify the correctness property of \module{Data.Set}'s \clean{member} function (\cref{sec:property}) more abstractly using \clean{a} instead of \clean{Int}:
\begin{Verbatim}
* @property correctness: A.x :: a; s :: Set a:
* member x s <==> isMember x (toList s)
\end{Verbatim}
To tell Gast how to test this property, use:
\begin{Verbatim}
* @property-test-with a = Int
\end{Verbatim}
When \clean{@property-test-with} is given globally (on a module), it is used for all tests described by that module;
otherwise, it is used only locally.
It is possible to give multiple type instantiations for a type variable.
Furthermore, properties can use multiple type variables.
In such cases, all possible configurations will be tested.
(When used unwisely, this can lead to long running times.)