Copyright © Quviq AB, 2008-2017
Version: 1.42.1
eqc_temporal
requires a timed trace of events to analyse--a list
of pairs of times (positive integers) and events. For example,
Trace = [{1,{login,alice,office}}, {2,{login,bob,home}}, {8,{login,bob,phone}}, {10,{send,alice,bob,hello}}, {11,{logout,alice,office}}, {20,{deliver,home,hello}}, {30,{deliver,phone,hello}}].might represent Alice and Bob using an instant messaging application. Typically events are recorded during the execution of a test case, time-stamped with the time since the start of the test, and then analysed using
eqc_temporal
after the test has finished.
from_timed_list
:
51> Events=eqc_temporal:from_timed_list(Trace). [{0,1,[]}, {1,2,[{login,alice,office}]}, {2,3,[{login,bob,home}]}, {3,8,[]}, {8,9,[{login,bob,phone}]}, {9,10,[]}, {10,11,[{send,alice,bob,hello}]}, {11,12,[{logout,alice,office}]}, {12,20,[]}, {20,21,[{deliver,home,hello}]}, {21,30,[]}, {30,31,[{deliver,phone,hello}]}, {31,infinity,[]}]
A temporal relation specifies a set of values related to each
time. For example, the third line above says that the relation
contains {login,bob,home}
at all times between 2
and 3
,
(not including 3
). That is, the relation contains
{login,bob,home}
at time 2. This relation contains the same
information as the list of events, but in a form which is easier to
work with.
Note that the representation of temporal relations may change in future versions, so while displaying the representation as diagnostic information is useful, user code should not manipulate temporal relations except using the functions in this module.
stateful
function:
4> LoggedIn=eqc_temporal:stateful( fun({login,User,Where})->[{User,Where}] end, fun({User,Where},{logout,User,Where})->[] end, Events). [{0,1,[]}, {1,2,[{alice,office}]}, {2,8,[{alice,office},{bob,home}]}, {8,11,[{alice,office},{bob,home},{bob,phone}]}, {11,infinity,[{bob,home},{bob,phone}]}]We pass
stateful
two functions:
{login,User,Where}
start the state {User,Where}
in
the LoggedIn
relation,{logout,User,Where}
transforms the state
{User,Where}
into no states (the empty list), terminating the
state in the LoggedIn
relation.The relation constructed shows us exactly which users were logged in at each time, and where.
product
of
Events
and LoggedIn
contains pairs of events and
{User,Where}
states, which we can use to construct a relation of
message lifetimes. We use stateful
again to recognise the
beginning and end of each message lifetime.
8> Messages=eqc_temporal:stateful( fun({{send,A,B,Msg},{B,Where}})->[{Msg,Where}] end, fun({Msg,Where},{{deliver,Where,Msg},{B,Where}})->[] end, eqc_temporal:product(Events,LoggedIn)). [{0,10,[]}, {10,21,[{hello,home},{hello,phone}]}, {21,31,[{hello,phone}]}, {31,infinity,[]}]The first function says that when
A
sends a message to B
,
who is logged in at Where
, then a message to Where
is
created in the Messages
relation. The second function says that
this message is deleted again when a matching deliver
event
occurs. Looking at the result,
we can see that {hello,home}
was in flight from time 10 to 20,
while {hello,phone}
was in flight from 10 to 30.
all_past
, which contains values which have been active for a
given period in the past. This effectively shortens the lifetime of
each value by the period given.
9> Overdue=eqc_temporal:all_past(15,Messages). [{0,25,[]},{25,31,[{hello,phone}]},{31,infinity,[]}]
Overdue
contains all messages which have been in flight
throughout the previous 15 time units--and so, are now overdue. In
this example, {hello,phone}
was overdue between times 25 and 31.
Overdue
relation, and check that it is empty, either via
Overdue == eqc_temporal:empty()or via
eqc_temporal:is_false(Overdue)(a temporal relation is considered to be false if it contains no values).
A complete property based on this idea might look like this:
prop_instant_messenger() -> ?FORALL(Cmds,commands(?MODULE), begin start_trace_recording(), run_commands(?MODULE,Cmds), Trace = recorded_trace(), Events = from_timed_list(Trace), LoggedIn = stateful( fun({login,User,Where})->[{User,Where}] end, fun({User,Where},{logout,User,Where})->[] end, Events), Messages = stateful( fun({{send,A,B,Msg},{B,Where}})->[{Msg,Where}] end, fun({Msg,Where},{{deliver,Where,Msg},{B,Where}})->[] end, product(Events,LoggedIn)), Overdue = all_past(15,Messages), is_false(Overdue) end).(where
start_trace_recording
and recorded_trace
are assumed
to be defined by the user, in any appropriate way).
Note that by including the header file
-include_lib("eqc/include/eqc_temporal.hrl").then we can import all the eqc_temporal functions automatically.
CertainlyLoggedIn = all_past(4,LoggedIn)containing those users whose logins are certainly complete. By using
CertainlyLoggedIn
instead of LoggedIn
in the definition of
Messages
above, we avoid creating the overdue message and thus
allow the test to pass.
abstract datatype: rel()
A relation between times and values (arbitrary terms). We say that a value
X
is true in R
at time T
, if R
relates T
to X
. A
temporal relation R
is true at time T
if it relates T
to some X
.
time() = integer()
A non-negative integer.
all_future/1 | Relate values that will stay true for eternity. |
all_future/2 | Relate values that will stay true for a given time. |
all_past/1 | Relate values that have been true since time 0. |
all_past/2 | Relate values that have been true for a given time. |
any_future/1 | Relate values that will be true at some point. |
any_future/2 | Relate values that will become true at some point within the given time. |
any_past/1 | Relate values that have been true at some point. |
any_past/2 | Relate values that have been true at some point within the given time. |
at/2 | Compute the values related to Time by R . |
bind/2 | The bind operation of the temporal relation monad. |
coarsen/2 | Coarsen the resolution of time in a temporal relation, by the factor K. |
complement/1 | The complement of a temporal relation. |
completion/1 | Turn a temporal relation into a total temporal relation. |
constant/1 | The constant temporal relation. |
domain_size/1 | The total time during which a temporal relation is true. |
elems/1 | Flatten a set valued temporal relation. |
empty/0 | The empty temporal relation. |
filter/2 | Filter a temporal relation by a predicate on the range. |
flatmap/2 | Apply a list valued partial function to the values of a temporal relation. |
from_timed_list/1 | Construct a temporal relation from a list of time value pairs. |
implies/2 | Relational implication. |
intersection/2 | Intersection of two temporal relations. |
is_false/1 | Check if a temporal relation is empty. |
is_subrelation/2 | Check if a temporal relation is a subrelation of another temporal relation. |
is_true/1 | Check if a temporal relation is total. |
last/1 | Extend a temporal relation to include the most recently related value. |
map/2 | Apply a (partial) function to the values of a temporal relation. |
negation/1 | The negation of a temporal relation. |
negation/2 | The negation of a temporal relation. |
partition/2 | Partition a temporal relation based on its range. |
product/2 | The product of two temporal relations. |
range/1 | The range of a temporal relation. |
restrict/2 | Restrict a temporal relation to the domain of another temporal relation. |
ret/1 | A singleton temporal relation. |
set/1 | Converting a temporal relation to a set valued temporal relation. |
shift/2 | Shift a temporal relation by a given time. |
slice/3 | Restrict a temporal relation to a particular time slice. |
split_at/2 | Split a temporal relation into two temporal relations with disjoint domains. |
stateful/3 | Apply a simple state machine to a temporal relation. |
subtract/2 | Subtract one temporal relation from another. |
union/2 | Union of two temporal relations. |
unions/1 | Take the union of a list of temporal relations. |
Relate values that will stay true for eternity.
all_future(R)
relates T
to X
, if R
relates T
to
X
for every T'
in the range T
to infinity
.
Relate values that will stay true for a given time.
all_future(Time, R)
relates T
to X
, if R
relates T
to
X
for every T'
in the range T
to T + Time
.
Relate values that have been true since time 0.
all_past(R)
relates T
to X
, if R
relates T
to
X
for every T'
in the range 0
to T
.
Relate values that have been true for a given time.
all_past(Time, R)
relates T
to X
, if R
relates T
to
X
for every T'
in the range T - Time
to T
.
Relate values that will be true at some point. any_future(R)
relates
T
to X
, if R
relates T'
to X
for some T' >= T
.
Conceptually equivalent to any_future/2
with an infinite time bound.
Relate values that will become true at some point within the given time.
any_future(Time, R)
relates T
to X
, if R
relates T
to
X
for any T'
in the range T
to T + Time
.
Relate values that have been true at some point. any_past(R)
relates
T
to X
, if R
relates T'
to X
for some T' =< T
.
Conceptually equivalent to any_past/2
with an infinite time bound.
Relate values that have been true at some point within the given time.
any_past(Time, R)
relates T
to X
, if R
relates T
to
X
for any T'
in the range T - Time
to T
.
Compute the values related to Time
by R
.
The bind operation of the temporal relation monad. bind(R, F)
is the union of shift(T, F(X))
for each
X
and T
such that F(X)
is defined and R
relates T
to X
.
F
can be partial.
Coarsen the resolution of time in a temporal relation, by the factor K. For example, coarsen(1000,R) would convert a temporal relation with times in microseconds to a relation with times in milliseconds.
The complement of a temporal relation.
complement(R)
relates T
to X
, if X
is in the range of R
and R
does not relate T
to X
.
Equivalent to union(R, negation(R)).
Turn a temporal relation into a total temporal relation.
constant(X::term()) -> rel()
The constant temporal relation. constant(X)
relates all times to X
.
The total time during which a temporal relation is true.
Flatten a set valued temporal relation. elems(R)
relates T
to X
, if
R
relates T
to Xs
and X
is a member of Xs
.
empty() -> rel()
The empty temporal relation.
Filter a temporal relation by a predicate on the range. filter(P, R)
relates
T
to X
, if R
relates T
to X
and P(X) == true
. P
can be partial.
Equivalent to elems(map(F, R)).
Apply a list valued partial function to the values of a temporal relation.
map(F, R)
relates T
to Y
if R
relates T
to X
and
F(X)
contains Y
. Values in the range of R
for which F
is not
defined are discarded.
Construct a temporal relation from a list of time value pairs.
Equivalent to union(negation(R1), R2).
Relational implication. Relates T
to none
when the condition is
not satisfied.
Intersection of two temporal relations. intersection(R1, R2)
relates T
to X
, if
both R1
and R2
relates T
to X
.
is_false(R::rel()) -> bool()
Check if a temporal relation is empty. is_false(R)
is true if R
is the
empty temporal relation.
Equivalent to is_false(subtract(R1, R2)).
Check if a temporal relation is a subrelation of another temporal relation.
is_true(R::rel()) -> bool()
Check if a temporal relation is total. is_true(R)
is true if R
relates
every time to a value.
Extend a temporal relation to include the most recently related value.
last(R)
relates T
to X
, if R
relates T'
to X
for the
last time T' =< T
in the domain of R
.
Apply a (partial) function to the values of a temporal relation. map(F, R)
relates
T
to Y
if R
relates T
to X
and F(X) == Y
. Values in the
range of R
for which F
is not defined are discarded.
Equivalent to negation(none, R).
The negation of a temporal relation.
The negation of a temporal relation. negation(None, R)
relates T
to
None
, if there is no X
such that R
relates T
to X
.
Partition a temporal relation based on its range. Returns one temporal relation with all
the values from R
satifying P
and one with the values not satisfying
P
.
The product of two temporal relations. product(R1, R2)
relates T
to
{X, Y}
if R1
relates T
to X
and R2
relates T
to Y
.
range(R::rel()) -> [term()]
The range of a temporal relation. Returns the ordered list of all X
such that
R
relates T
to X
for some T
.
Restrict a temporal relation to the domain of another temporal relation.
restrict(R, When)
relates T
to X
, if R
relates T
to X
and When
relates T
to some Y
.
ret(X::term()) -> rel()
A singleton temporal relation. ret(X)
just relates 0
to X
.
(It is the return operation for the temporal relation monad.)
Converting a temporal relation to a set valued temporal relation. set(R)
relates T
to at(T, R)
.
Shift a temporal relation by a given time.
shift(Time, R)
relates T
to X
, if R
relates T - Time
to X
.
Note that if Time < 0
the initial part of R
will be discarded, since
the domain of a temporal relation only includes non-negative times.
Restrict a temporal relation to a particular time slice.
slice(T0, T1, R)
is the restriction of R
to the domain T0 =< T < T1
.
Split a temporal relation into two temporal relations with disjoint domains. The domain of the input temporal relation is split at the earliest occurrence of a value matching the given predicate.
stateful(Start::fun((term()) -> [State]), Transform::fun((State, term()) -> [State]), R::rel()) -> rel()
Apply a simple state machine to a temporal relation. The Start
function maps
values to sets of initial states, and Transform
maps a state and a value
to a (potentially empty) set of new states. A state S
is started at time
T
, if R
relates T
to X
and Start(X)
contains S
.
Whenever Transform(S, Y)
is defined for any Y
in R
, then S
is
replaced by all the states in Transform(S, Y)
. Both Start
and
Transform
can be partial.
Subtract one temporal relation from another.
subtract(R1, R2)
relates T
to X
, if R1
relates T
to X
and R2
does not relate T
to X
.
Union of two temporal relations. union(R1, R2)
relates T
to X
, if one
or both of R1
and R2
relates T
to X
.
Take the union of a list of temporal relations.
Generated by EDoc, Sep 18 2017, 16:17:38.