Module eqc_symbolic

This module implements QuickCheck generators and utility functions for symbolic calls.

Copyright © Quviq AB, 2011-2017

Version: 1.42.1

Description

This module implements QuickCheck generators and utility functions for symbolic calls.

In test case generation it is often an advantage to postpone calling functions in the subject under test. In a test one is interested in the actual function that is called as well as its evaluated result. If one would evaluate the result already at generation time, then the actual call is not visible in the QuickCheck counter example shown in a failing test.

For example, when testing a data structure like the OTP library sets.erl, one may need more information than just the value to detect what goes wrong with the following property:

 prop_sets() ->
  ?FORALL({S1,S2},{set(),set()},
          begin
              L1 = sets:to_list(S1),
              L2 = sets:to_list(S2),
              sets:intersection(S1,S2) ==
                  sets:from_list(L1--(L1--L2))
          end).
 
which will fail with for example the following counter example:
  Failed! After 132 tests.
  Shrinking.......(7 times)
  {{set,2,16,16,8,80,48,
        {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
        {{[],[],[],[],[],[],[],[],[],[],[],[-15,33],[],[],[],[]}}},
   {set,3,16,16,8,80,48,
        {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
        {{[0],[],[],[],[],[],[],[],[],[],[],[33,-15],[],[],[],[]}}}}
  false
 
We would really need to understand the internal representation of sets in order to understand which sets we have generated and even if we know that, we have no clue which operations were used to create those sets. This is were symbolic representations help a lot. We would create a recursive generator that creates symbolic sets and use the following property instead:
  prop_sets() ->
    ?FORALL({SymbS1,SymbS2},{set(),set()},
            begin
              S1 = eval(SymbS1),
              S2 = eval(SymbS2),
              L1 = sets:to_list(S1),
              L2 = sets:to_list(S2),
              sets:intersection(S1,S2) ==
                 sets:from_list(L1--(L1--L2))
            end).
 
  This would then result in a more readable error message:
  Shrinking..........(10 times)
  {{call,sets,from_list,[[6,-10]]},{call,sets,from_list,[[0,-10,6]]}}
  false
 
Symbolic representation of function calls provides us with

Data Types

symbolic_call()

abstract datatype: symbolic_call()

A symbolic representation of a function call.

Function Index

call_names/1A symbolic function call can have arguments also calling symbolic functions and that recursively.
defined/1Checks whether a term can be evaluated without raising an exception.
eval/1Evaluates terms of the form {call,Module,Function,Args} anywhere in its argument, replacing them by the result of the corresponding function call.
eval/2Like eval/1, but also replaces symbolic variables, that is, terms of the form {var,V}, by their corresponding values in the property list.
pretty_print/1Pretty printing of symbolic terms.
pretty_print/2Pretty printing of symbolic terms within given environment.
pretty_term_doc/3Pretty-print an Erlang term as a prettypr:document(), ready for rendering by the prettypr library.
well_defined/1Generates a well defined symbolic value.

Function Details

call_names/1

call_names(T::symbolic_call()) -> term()

A symbolic function call can have arguments also calling symbolic functions and that recursively. This function collects all these calls in one symbolic call and represents them as tuples in a list; which is useful when wanting to aggregate or collect the calls in properties.

 ?FORALL(SymSet,set(...),
   begin
     aggregate(call_names(SymSet),
               Res==ok)
   end)
 

defined/1

defined(E::symbolic_call()) -> bool()

Checks whether a term can be evaluated without raising an exception. Some symbolic terms may raise an exception when evaluating, e.g., division by zero would raise an exception, thus eval({call,erlang,'div',[1,0]}) raises an exception as well.

eval/1

eval(Term::symbolic_call()) -> term()

Evaluates terms of the form {call,Module,Function,Args} anywhere in its argument, replacing them by the result of the corresponding function call. This is useful when, for example, test data is of an abstract datatype, and we want to know how it was generated, rather than its representation--it is much clearer to see that a test failed for sets:new() (that is {call,sets,new,[]}), for example, than for its representation. We write ?FORALL(X,TGen,...eval(X)...), where TGen generates terms containing calls, so that test cases are displayed in this form, but the actual test data is the result of evaluating the calls.

eval/2

eval(Env::proplist(), Term::symbolic_call()) -> term()

Like eval/1, but also replaces symbolic variables, that is, terms of the form {var,V}, by their corresponding values in the property list. This should be a list of pairs of atoms and values. For example, eval([{x,3}],{var,x}) evaluates to 3.

pretty_print/1

pretty_print(Symb::symbolic_call()) -> string()

Pretty printing of symbolic terms. A symbolic value like {call,sets,union,[{call,sets,new,[]},{call,sets,from_list,[[1,2]]}]} is transformed to the string \"sets:union(sets:new(),sets:from_list([1,2]))\".

pretty_print/2

pretty_print(Env::proplist(), Symb::symbolic_call()) -> string()

Pretty printing of symbolic terms within given environment. Like pretty_print/1, but also replaces symbolic variables, that is, terms of the form {var,V}, by their corresponding values in the property list. This should be a list of pairs of atoms and values. For example, pretty_print([{x,3}],{var,x}) is pretty printed to \"3\".

pretty_term_doc/3

pretty_term_doc(Special, Fields, T) -> any()

Pretty-print an Erlang term as a prettypr:document(), ready for rendering by the prettypr library. A handler for special cases can be supplied.

well_defined/1

well_defined(G::gen(A)) -> A

Generates a well defined symbolic value. A value is well defined if evaluating it does not raise an exception.


Generated by EDoc, Sep 18 2017, 16:17:37.