class TrackerC

Transcription

class TrackerC
UML Checker: Formal Specification in VDM++
TR-SDBT-2013-03, SERG, FEUP, November 2013
João Pascoal Faria 1, 2, Ana C. R. Paiva 1
1
Software Engineering Research Group, Department of Informatics Engineering,
Faculty of Engineering of the University of Porto, Porto, Portugal
2
INESC TEC, Porto, Portugal
[email protected], [email protected]
Contents
1
2
3
4
5
6
7
8
Introduction ..................................................................................................................................................... 1
Preliminaries: Values, Value Specifications and Actions ................................................................................ 1
Interactions (Sequence Diagrams) ................................................................................................................... 3
Parallel Finite Automata (PFA) ....................................................................................................................... 4
Conversion of Interactions to Parallel Finite Automata ................................................................................... 5
Automata Simplification (optional) ............................................................................................................... 10
Automata Execution ...................................................................................................................................... 12
Test Cases ...................................................................................................................................................... 15
8.1
Convenience definitions ........................................................................................................................ 15
8.2
Example 1: weak sequencing ................................................................................................................ 15
8.3
Example 2: ATM .................................................................................................................................. 20
8.4
Example 3: asynchronous user interaction ............................................................................................ 23
8.5
Loop testing .......................................................................................................................................... 24
8.6
Test execution ....................................................................................................................................... 25
9
Conclusion ..................................................................................................................................................... 25
References .............................................................................................................................................................. 25
1
Introduction
This document contains the formal specification of the conformance checking engine presented in our ICTSS 2013
paper (‎[1]). The specification is written in VDM++ ‎[2]. This documented (RTF version) can be executed with the
VDM++ Toolbox. Is was prepared following the literate programming paradigm.
All the specification is organized inside a single class (classes are required by VDM++), although the specification
follows a functional style.
class UMLChecker
Whenever possible or convenient, we follow the names used in the UML specification ‎[3].
2
Preliminaries: Values, Value Specifications and Actions
Value specifications (‎‎[3], page 139) are used in UML sequence diagrams for specifying message parameters, target
objects, return values and guard conditions. By contrast, values occur at run-time. A few actions are used in the
automata that we generate from sequence diagrams, for manipulating loop counters.
types
UML Checker: Formal Specification in VDM++
2
-- these are the kinds of values we need ...
public Value = Primitive | Instance;
public Primitive = nat | bool | real | string;
public string = seq of char;
public Instance ::
className : string
object
: token;
-- these are the kinds of variables we need ...
public Variable = Lifeline | Counter | Parameter;
public Lifeline ::
className : string
instanceName : [string];
public Counter :: nat; -- loop counter identifier
public Parameter :: string; -- interaction parameter name
-- Bindings of variables to actual values (usually stored separatedly)
public Bindings = map Variable to Value;
-- these are the kinds of value specifications we need ...
public ValueSpecification = Value | Variable | Expression | <Unknown>;
public Expression = Negate | Equals | Plus | Minus | Lt | Lte | Gt| Gte;
public Negate ::
arg : ValueSpecification;
public Lt ::
lhs : ValueSpecification
rhs : ValueSpecification;
public Lte ::
lhs : ValueSpecification
rhs : ValueSpecification;
public Gt ::
lhs : ValueSpecification
rhs : ValueSpecification;
public Gte ::
lhs : ValueSpecification
rhs : ValueSpecification;
public Equals ::
lhs : ValueSpecification
rhs : ValueSpecification;
public Plus ::
lhs : ValueSpecification
rhs : ValueSpecification;
public Minus ::
lhs : ValueSpecification
UML Checker: Formal Specification in VDM++
rhs : ValueSpecification;
3
functions
private eval(vs: ValueSpecification, b: Bindings) r: [Value] ==
if is_Lifeline(vs) or is_Counter(vs) or is_Parameter(vs)
then if vs in set dom b then b(vs) else nil
else if vs = <Unknown> then nil
else if is_Negate(vs) then not eval(vs.arg, b)
else if is_Equals(vs) then eval(vs.lhs, b) = eval(vs.rhs, b)
else if is_Lt(vs) then eval(vs.lhs, b) < eval(vs.rhs, b)
else if is_Lte(vs) then eval(vs.lhs, b) <= eval(vs.rhs, b)
else if is_Gt(vs) then eval(vs.lhs, b) > eval(vs.rhs, b)
else if is_Gte(vs) then eval(vs.lhs, b) >= eval(vs.rhs, b)
else if is_Plus(vs) then eval(vs.lhs, b) + eval(vs.rhs, b)
else if is_Minus(vs) then eval(vs.lhs, b) - eval(vs.rhs, b)
else vs; -- Value
types
public Action = Assign | Increment;
public Assign ::
var : Variable
val : ValueSpecification;
public Increment ::
var : Variable;
functions
-- Bind a variable to a value, updating the set of bindings
private bind(var: Variable, val: Value, bind: Bindings) r: Bindings ==
bind ++ {var |-> val};
-- Apply an action, updating the set of bingings
private apply(a: [Action], b: Bindings) r: Bindings ==
if a = nil then b
else if is_Assign(a) then bind(a.var, eval(a.val, b), b)
else if is_Increment(a) then bind(a.var, b(a.var) + 1, b)
else b;
3
Interactions (Sequence Diagrams)
In UML, a sequence diagram is a visual representation of an Interaction.
types
-- order number (not necesserily consecutive) for an event in a Lifeline,
-- such as send/receive message, begin/end combined fragment or operand
-- (e.g., can be assigned using the y-coordinate)
public Location = nat;
public LifelineLocation = Lifeline * Location;
-- message sorts defined in the UML standard
UML Checker: Formal Specification in VDM++
public MessageSort = <synchCall> | <asynchCall> | <asynchSignal> |
<createMessage> | <reply>;
4
public Message ::
id
: nat -- connector id
groupId
: nat -- pairs or call/reply messages
source
: LifelineLocation
target
: LifelineLocation
messageSort : MessageSort
signature
: string
arguments
: seq of ValueSpecification; -- return value if reply message
public InteractionConstraint ::
minint
: [ValueSpecification] -- loop
maxint
: [ValueSpecification] -- loop
specification: [ValueSpecification];
-- subset of the interaction operators defined in the UML standard
public InteractionOperatorKind = <seq> | <alt> | <opt> | <par> | <strict> |
<loop>;
public InteractionOperand ::
guard
: [InteractionConstraint]
startLocations
: set of LifelineLocation
finishLocations : set of LifelineLocation;
public CombinedFragment ::
id
: nat
interactionOperator : InteractionOperatorKind
operands
: seq1 of InteractionOperand
lifelines
: set of Lifeline
inv f ==
cases f.interactionOperator:
<loop>, <opt> -> len f.operands = 1,
<alt>, <par>, <strict>, <seq> -> len f.operands > 1
end
and (forall o in set elems f.operands &
{lf | mk_(lf, -) in set o.startLocations} = f.lifelines
and {lf | mk_(lf, -) in set o.finishLocations} = f.lifelines)
and (forall i in set {1, ..., len f.operands - 1} &
f.operands(i+1).startLocations = f.operands(i).finishLocations);
public Interaction ::
lifelines
:
messages
:
combinedFragments :
parameters
:
4
set
set
set
set
of
of
of
of
Lifeline
Message
CombinedFragment
Parameter;
Parallel Finite Automata (PFA)
In our approach, for the incremental conformance checking, we first translate sequence diagramas to finite
automata with parallelism, or parallel finite automata (PFA). Here we define parallel finite automata.
types
public State = nat;
-- The events of interest here are related with message sending and receiving.
UML Checker: Formal Specification in VDM++
public EventType = <Send> | <Receive> | <Call> | <Reply>;
5
public Event ::
type
: EventType
message : Message;
public Label ::
event : [Event]
guard : [ValueSpecification]
action : [Action];
public Transition ::
source : set of State
label : [Label]
target : set of State
inv t == t.source <> {} and t.target <> {};
public PFA ::
states
: set of State
starts
: State
finish
: set of State
transitions: set of Transition
inv a == a.starts in set a.states
and a.finish subset a.states
and forall t in set a.transitions &
t.source subset a.states and t.target subset a.states;
5
Conversion of Interactions to Parallel Finite Automata
Here we present the formal specification of the translation rules presented in Table 2 of our ICTSS 2013 paper and
replaced below.
UML Checker: Formal Specification in VDM++
functions
-- Auxiliary function to get the asynchronous messages in a SD
private getAsynch(sd: Interaction) r: set of Message ==
6
UML Checker: Formal Specification in VDM++
7
{m | m in set sd.messages &
m.messageSort = <asynchCall> or m.messageSort = <asynchSignal>};
-- Get the lifeline location that refers to a lifeline
private getLoc(locs: set of LifelineLocation, lf: Lifeline) r : LifelineLocation
==
iota loc in set locs & loc.#1 = lf;
-- Get all lifeline locations in an interaction
private getLifelineLocations(sd: Interaction) r: set of LifelineLocation ==
{m.source | m in set sd.messages}
union {m.target | m in set sd.messages}
union dunion {dunion {o.startLocations union o.finishLocations
| o in set elems f.operands}
| f in set sd.combinedFragments};
-- Generates a mapping of locations in a single lifeline to before/after states,
-- starting with a minimum state number
private mapLocationsLf(locs: set of LifelineLocation, min: State) r: map
LifelineLocation to (State * State) ==
if locs = {} then { |-> }
else let loc in set locs be st
not exists loc2 in set locs & loc2.#2 < loc.#2
in {loc |-> mk_(min, min+1)}
munion mapLocationsLf(locs \ {loc}, min+1);
-- Generates a mapping of locations to before/after states,
-- starting with a minimum state number
private mapLocations(locs: set of LifelineLocation, min: State) r: map
LifelineLocation to (State * State) ==
if locs = {} then { |-> }
else let loc in set locs
in let locsLf = {l | l in set locs & l.#1 = loc.#1}
in mapLocationsLf(locsLf, min)
munion mapLocations(locs \ locsLf, min + card locsLf + 1);
-- Generates a mapping from asynchronous messages to channel states
private mapMessages(msgs: set of Message, min: State) r: map Message to State
==
if msgs = {} then { |-> }
else let m in set msgs
in { m |-> min } munion mapMessages(msgs \ {m}, min+1);
-- Get the state immediatly before a lifeline location,
-- using a pre-computed mapping
private stateBefore(loc: LifelineLocation, mapping: map LifelineLocation to
(State * State)) r : State ==
let ss = mapping(loc) in ss.#1;
-- Get the state immediatly after a lifeline location,
-- using a pre-computed mapping
private stateAfter(loc: LifelineLocation, mapping: map LifelineLocation to
(State * State)) r : State ==
let ss = mapping(loc) in ss.#2;
-- Get the first lifeline states, using a pre-computed mapping
private getFirstLifelineStates(mapping: map LifelineLocation to (State *
State)) r : set of State ==
{stateBefore(loc, mapping) | loc in set dom mapping &
not exists loc2 in set dom mapping &
UML Checker: Formal Specification in VDM++
loc2.#1 = loc.#1 and loc2.#2 < loc.#2};
8
-- Get the last lifeline states, using a pre-computed mapping
private getLastLifelineStates(mapping: map LifelineLocation to (State * State))
r : set of State ==
{ stateAfter(loc, mapping) | loc in set dom mapping &
not exists loc2 in set dom mapping &
loc2.#1 = loc.#1 and loc2.#2 > loc.#2};
-- Generates transitions for a message, considering pre-computed
-- state generator mappings
private msg2trans(m: Message, mapping: map LifelineLocation to (State * State),
mapAsynch: map Message to State) r : set of Transition ==
cases m.messageSort:
<synchCall>, <createMessage> ->
{mk_Transition(
{stateBefore(m.source, mapping), stateBefore(m.target, mapping)},
mk_Label(mk_Event(<Call>, m), nil, nil),
{stateAfter(m.source, mapping), stateAfter(m.target, mapping)})},
<reply> ->
{mk_Transition(
{stateBefore(m.source, mapping), stateBefore(m.target, mapping)},
mk_Label(mk_Event(<Reply>, m), nil, nil),
{stateAfter(m.source, mapping), stateAfter(m.target, mapping)})},
<asynchCall>, <asynchSignal> ->
{mk_Transition(
{stateBefore(m.source, mapping)},
mk_Label(mk_Event(<Send>, m), nil, nil),
{stateAfter(m.source, mapping), mapAsynch(m)}),
mk_Transition(
{stateBefore(m.target, mapping), mapAsynch(m)},
mk_Label(mk_Event(<Receive>, m), nil, nil),
{stateAfter(m.target, mapping)})}
end;
-- Generates transitions for a combined fragment, considering pre-computed
-- state generator mappings
private cf2trans(f: CombinedFragment, mapping: map LifelineLocation to (State
* State)) r: set of Transition ==
cases f.interactionOperator:
<opt> ->
let o = f.operands(1)
in {mk_Transition(
{stateBefore(loc, mapping) | loc in set o.startLocations},
if o.guard = nil then nil
else mk_Label(nil, o.guard.specification, nil),
{stateAfter(loc, mapping) | loc in set o.startLocations}),
mk_Transition(
{stateBefore(loc, mapping) | loc in set o.startLocations},
if o.guard = nil then nil
else mk_Label(nil, mk_Negate(o.guard.specification), nil),
{stateAfter(loc, mapping) | loc in set o.finishLocations})
}
union
{mk_Transition({stateBefore(loc, mapping)}, nil,
{stateAfter(loc, mapping)})
| loc in set o.finishLocations},
<par> ->
dunion
{{mk_Transition(
UML Checker: Formal Specification in VDM++
9
{stateBefore(getLoc(f.operands(1).startLocations, lf), mapping)},
nil,
{stateAfter(getLoc(o.startLocations, lf), mapping) |
o in set elems f.operands}),
mk_Transition(
{stateBefore(getLoc(o.finishLocations, lf), mapping) |
o in set elems f.operands},
nil,
{stateAfter(
getLoc(f.operands(len f.operands).finishLocations,lf),
mapping)})}
| lf in set f.lifelines},
<seq> ->
{mk_Transition({stateBefore(loc, mapping)}, nil,
{stateAfter(loc, mapping)})
| loc in set dunion {o.startLocations union o.finishLocations |
o in set elems f.operands}},
<strict> ->
{mk_Transition(
{stateBefore(loc, mapping) | loc in set o.startLocations},
nil,
{stateAfter(loc, mapping) | loc in set o.startLocations})
| o in set elems f.operands}
union
{mk_Transition(
{stateBefore(loc, mapping) | loc in set o.finishLocations},
nil,
{stateAfter(loc, mapping) | loc in set o.finishLocations})
| o in set elems f.operands},
<alt> ->
{mk_Transition(
{stateBefore(l0, mapping)| l0 in set f.operands(1).startLocations},
if o.guard = nil then nil
else mk_Label(nil, o.guard.specification, nil),
{stateAfter(loc, mapping) | loc in set o.startLocations})
| o in set elems f.operands}
union
dunion
{ {mk_Transition(
{stateBefore(getLoc(o.finishLocations, lf), mapping)},
nil,
{stateAfter(getLoc(f.operands(len f.operands).finishLocations,
lf), mapping)})
| o in set elems f.operands}
| lf in set f.lifelines},
<loop> ->
let o = f.operands(1),
c = mk_Counter(f.id)
in {
-- enter loop
mk_Transition(
{stateBefore(loc, mapping) | loc in set o.startLocations},
if o.guard = nil then nil
else mk_Label(nil, nil, mk_Assign(c, 0)),
{stateAfter(loc, mapping) | loc in set o.startLocations}),
-- skip loop
mk_Transition(
{stateBefore(loc, mapping) | loc in set o.startLocations},
if o.guard = nil or o.guard.minint = nil then nil
else mk_Label(nil, mk_Equals(o.guard.minint, 0), nil),
UML Checker: Formal Specification in VDM++
10
{stateAfter(loc, mapping) | loc in set o.finishLocations}),
-- exit loop
mk_Transition(
{stateBefore(loc, mapping) | loc in set o.finishLocations},
if o.guard = nil or o.guard.minint = nil then nil
else mk_Label(nil, mk_Gte(mk_Plus(c, 1), o.guard.minint), nil),
{stateAfter(loc, mapping) | loc in set o.finishLocations}),
-- continue loop
mk_Transition(
{stateBefore(loc, mapping) | loc in set o.finishLocations},
if o.guard = nil then nil
else mk_Label(nil,
if o.guard.maxint = nil then nil
else mk_Lt(mk_Plus(c, 1), o.guard.maxint),
mk_Increment(c)),
{stateAfter(loc, mapping) | loc in set o.startLocations})}
end;
-- Generates needed states for a sequence diagram, discrimitaing the start
-- and finished states, together with a mapping from locations to lifelines
private generateStates(sd: Interaction) r: (set of State) * State * State * (map
LifelineLocation to (State * State)) * (map Message to State) ==
let locs = getLifelineLocations(sd),
numStates = card locs + card sd.lifelines + card getAsynch(sd) + 2,
states = {1, ..., numStates},
starts = 1,
finishes = numStates,
mapping = mapLocations(locs, 2),
mapAsynch = mapMessages(getAsynch(sd), 2 + card locs + card sd.lifelines)
in mk_(states, starts, finishes, mapping, mapAsynch);
-- Generating all transitions
private generateTransitions(sd: Interaction, starts: State, finishes: State,
mapping: map LifelineLocation to (State * State), mapAsynch: map Message to
State) r : set of Transition ==
{mk_Transition({starts}, nil, getFirstLifelineStates(mapping)),
mk_Transition(getLastLifelineStates(mapping), nil, {finishes})}
union dunion {cf2trans(f, mapping) | f in set sd.combinedFragments}
union dunion {msg2trans(m, mapping, mapAsynch) | m in set sd.messages};
-- Main conversion function from sequence diagram to PFA
public sd2pfa(sd : Interaction) r: PFA ==
let mk_(states, s0, finishes, mapping, mapAsynch) = generateStates(sd),
transitions = generateTransitions(sd, s0, finishes, mapping, mapAsynch)
in mk_PFA(states, s0, {finishes}, transitions);
6
Automata Simplification (optional)
functions
private incoming(a: PFA, s: State) r: set of Transition ==
{t | t in set a.transitions & s in set t.target};
private outgoing(a: PFA, s: State) r: set of Transition ==
{t | t in set a.transitions & s in set t.source};
-- Remove intermmediate state s
private exclude(a: PFA, s: State) r: PFA ==
UML Checker: Formal Specification in VDM++
11
mk_PFA(a.states \ {s}, a.starts, a.finish,
{mk_Transition(t.source\{s}, t.label, t.target\{s}) |
t in set a.transitions & t.source <> {s} and t.target <> {s}})
pre s <> a.starts and s not in set a.finish;
-- Remove "parallel" states and its transitions
private simplify1(a: PFA) r: PFA ==
let candidates =
{s | s in set a.states \ a.finish \ {a.starts} &
exists s2 in set a.states \ a.finish \ {a.starts, s} &
(forall t1 in set incoming(a, s) & s2 in set t1.target)
and (forall t2 in set outgoing(a, s) & s2 in set t2.source)}
in if candidates <> {}
then let s in set candidates in exclude(a, s)
else a;
-- Merge states s1 and s2 (removing s1),
-- all transitions to s1 are redirected to s2
-- (except automatic transitions between s1 and s2)
private join(a: PFA, s1, s2: State) r: PFA ==
mk_PFA(a.states \ {s1},
if s1 = a.starts then s2 else a.starts,
a.finish,
{mk_Transition(
if s1 in set t.source then t.source \ {s1} union {s2} else t.source,
t.label,
if s1 in set t.target then t.target \ {s1} union {s2} else t.target) |
t in set a.transitions &
t <> mk_Transition({s1}, nil, {s2})
and t <> mk_Transition({s2}, nil, {s1})});
-- Remove some simple automatic transitions
private simplify3(a: PFA) r: PFA ==
let candidates = {mk_(s1, s2) | s1, s2 in set a.states &
outgoing(a, s1) = {mk_Transition({s1}, nil, {s2})}
or incoming(a, s2) = {mk_Transition({s1}, nil, {s2})} }
in if candidates <> {}
then let mk_(s1, s2) in set candidates in join(a, s1, s2)
else a;
private closure(rel: set of (Transition * Transition)) r : set of (Transition
* Transition) ==
let rel2 = {mk_(t1, t4) | mk_(t1, t2), mk_(t3, t4) in set rel & t2 = t3}
in if rel2 subset rel then rel
else closure(rel union rel2);
private precRel(a: PFA, states : set of State) r: set of (Transition * Transition)
==
closure(dunion {{mk_(t1, t2) | t1 in set incoming(a, s),
t2 in set outgoing(a, s)}
| s in set states & card incoming(a, s) = 1});
private simplify2(a: PFA) r: PFA ==
let candidates =
{s | s in set a.states \ a.finish \ {a.starts} &
card incoming(a, s) = 1 and card outgoing(a, s) = 1
and let t1 in set incoming(a, s)
in let t2 in set outgoing(a, s)
in mk_(t1, t2) in set precRel(a, a.states \ {s})
UML Checker: Formal Specification in VDM++
and not t1 in set reachable(a, {t2})}
in if candidates <> {}
then let s in set candidates in exclude(a, s)
else a;
12
-- recurively computes transition nodes that are reachable from a given set
private reachable(a: PFA, trans: set of Transition) r: set of Transition ==
let res = dunion { dunion {outgoing(a,s) | s in set t.target} | t in set trans}
in if res subset trans then res
else reachable(a, res union trans);
-- try to apply 3 simplification rules
public simplify(a: PFA) r : PFA ==
let a1 = simplify1(a)
in if a1 <> a then simplify(a1)
else let a2 = simplify3(a)
in if a2 <> a then simplify(a2)
else let a3 = simplify2(a)
in if a3 <> a then simplify(a3)
else a;
7
Automata Execution
types
public RunState :: active
: set of State
bindings : Bindings
pending
: map nat to nat
covers
: set of nat;
-- see ICTSS paper with explanations
public Occurrence ::
id
: nat
groupId
: nat -- to identify pairs or call/reply or send/receive
target
: OccurrenceTarget
type
: EventType
signature
: string
arguments
: seq of Value; -- return value, in case of Reply
public OccurrenceTarget = Instance | Classifier;
public Classifier ::
className : string;
public ConformanceMode = <LooseConf> | <StrictConf>;
functions
-- currently only checks same type (enough for prototyping)
private isSubtype(type1 : string, type2: string) r : bool ==
type1 = type2;
-- Matches a value against a value specification, considering a set of variable
-- bindings. Returns a tuple with a success flag and the new bindings.
private matchAndBindVal(actual: [Value], expected: [ValueSpecification], b:
Bindings) r : bool * Bindings ==
cases true:
(expected = nil)
-> mk_(actual = nil, b),
(expected = <Unknown>) -> mk_(true, b),
UML Checker: Formal Specification in VDM++
13
(is_Lifeline(expected)) ->
if actual = nil or not is_Instance(actual)
then mk_(false, b)
else if expected in set dom b
then mk_(b(expected) = actual, b)
else if isSubtype(actual.className, expected.className)
then mk_(true, bind(expected, actual, b))
else mk_(false, b),
others -> mk_(eval(expected, b) = actual, b)
end;
-- Matches a sequence of values against a sequence a value specifications,
-- considering a set of bindings of variables to actual values.
-- Returns a tuple with a success flag and new bindings
private matchAndBindSeq(actual: seq of [Value], expected: seq of
[ValueSpecification], b: Bindings) r : bool * Bindings ==
if len actual <> len expected then mk_(false, b)
else if actual = [] then mk_(true, b)
else let mk_(r1, b1) = matchAndBindVal(hd actual, hd expected, b)
in if r1
then matchAndBindSeq(tl actual, tl expected, b1)
else mk_(r1, b1);
-- Check if a transition guard holds
private guardHolds(t: Transition, b: Bindings) r: bool ==
t.label = nil or t.label.guard = nil or eval(t.label.guard, b) = true;
-- Check if the transition is automatic
private isAuto(t: Transition) r: bool ==
t.label = nil or t.label.event = nil;
-- Match and bind the target of a message
private matchAndBindTarget(o: Occurrence, e: Event, b: Bindings) r: bool *
Bindings==
let m = e.message,
mt = if o.type = <Reply> then m.source.#1 else m.target.#1
in if is_Instance(o.target)
then if mt.instanceName = nil
then mk_(false, b)
else matchAndBindVal(o.target, mt, b)
else mk_(mt.instanceName = nil, b);
-- Matches an occurrence against an event.
-- Returns a tuple with a success flag and new bindings.
private matchAndBind(o: Occurrence, e: Event, b: Bindings) r: bool * Bindings
==
let m = e.message in
if e.type = o.type and m.signature = o.signature
then let mk_(r2, b2) = matchAndBindTarget(o, e, b)
in if r2
then matchAndBindSeq(o.arguments, m.arguments, b2)
else mk_(false, b)
else mk_(false, b);
-- Recursively advances run states following automatic transitions.
-- Uses an accumulator of already visited run states to avoid infinite loops.
private auto(pfa: PFA, toVisit: set of RunState, visited: set of RunState) r:
set of RunState ==
if toVisit = {} then {}
else let keep = {r | r in set toVisit &
UML Checker: Formal Specification in VDM++
14
r.active subset pfa.finish
or forall s in set r.active &
exists t in set outgoing(pfa, s) &
not (t.source subset r.active
and isAuto(t)
and guardHolds(t, r.bindings))},
newToVisit = dunion
{{let newA = (r.active \ t.source) union t.target,
newB = if t.label = nil or t.label.action = nil
then r.bindings
else apply(t.label.action, r.bindings)
in mk_RunState(newA, newB, r.pending, r.covers)
| t in set pfa.transitions &
t.source subset r.active
and isAuto(t)
and guardHolds(t, r.bindings)}
| r in set toVisit}
in keep union
auto(pfa,newToVisit\(visited union toVisit),visited union toVisit);
-- Performs an automaton step, i.e., processes a single input occurrence,
-- departing from a current set of run states, and returns the new set of
-- run states.
private step(pfa: PFA, o: Occurrence, rr: set of RunState, mode:
ConformanceMode) r: set of RunState ==
let newRR = dunion
{{let newA = (r.active \ t.source) union t.target,
b2 = matchAndBind(o, t.label.event, r.bindings).#2,
newB = apply(t.label.action, b2),
newP = (if o.type = <Reply> or o.type = <Receive>
then {o.groupId} <-: r.pending
else r.pending munion
{o.groupId |-> t.label.event.message.groupId}),
newCov = r.covers union {t.label.event.message.id}
in mk_RunState(newA, newB, newP, newCov)
| t in set pfa.transitions &
t.source subset r.active
and t.label <> nil and t.label.event <> nil
and t.label.event.type = o.type
and guardHolds(t, r.bindings)
and matchAndBind(o, t.label.event, r.bindings).#1
and ((o.type = <Reply> or o.type = <Receive>)
=> t.label.event.message.groupId = r.pending(o.groupId)) }
| r in set rr &
(o.type=<Reply> or o.type =<Receive>) => o.groupId in set dom r.pending}
in if o.type = <Reply> or o.type = <Receive>
then auto(pfa, newRR, {})
union {r | r in set rr & o.groupId not in set dom r.pending}
else if mode = <LooseConf>
then auto(pfa, newRR, {}) union rr
else auto(pfa, newRR, {});
-- Generates the initial set of run states for an automaton
private startAutomaton(pfa: PFA, params: Bindings) r: set of RunState ==
auto(pfa, {mk_RunState({pfa.starts},params, {|->}, {})}, {});
-- Checks if a set of run states represents an irrevocable failure
private failed(pfa: PFA, runStates: set of RunState) r: bool ==
runStates = {};
UML Checker: Formal Specification in VDM++
-- Checks if a set of run states represents acceptance
private succeeded(pfa: PFA, runStates: set of RunState) r: bool ==
exists r in set runStates & r.active subset pfa.finish;
15
-- Recursively checks if an automaton accepts an execution trace in a given
-- conformance mode, departing from a given set of initial run states.
-- Returns the final set of run states.
private steps(pfa: PFA, trace: seq of Occurrence, mode: ConformanceMode,
runStates: set of RunState) r : set of RunState ==
if failed(pfa, runStates) then runStates
else if trace = [] then runStates
else steps(pfa, tl trace, mode, step(pfa, hd trace, runStates, mode));
-- Checks if an automaton accepts an execution trace in a given conformance mode
public accepts(pfa: PFA, trace: seq of Occurrence, params: Bindings, mode:
ConformanceMode) r: bool ==
succeeded(pfa, steps(pfa, trace, mode, startAutomaton(pfa, params)));
-- Computes the set of covered messages
public coverage(pfa: PFA, trace: seq of Occurrence, params: Bindings, mode:
ConformanceMode) r: set of nat ==
let runStates = steps(pfa, trace, mode, startAutomaton(pfa, params))
in if not succeeded(pfa, runStates) then {}
else let r1 in set runStates be st
r1.active subset pfa.finish
and not exists r2 in set runStates &
r2.active subset pfa.finish
and card r2.covers > card r1.covers
in r1.covers;
-- TODO: coverage and error location information in case of failure (based on
-- closest match)
8
Test Cases
8.1
Convenience definitions
operations
-- convenience operation for writing test cases (pre-condition checking
-- must be enabled)
private Assert : bool ==> ()
Assert(a) == return
pre a;
8.2
Example 1: weak sequencing
The next example is based on Fig. 5 of our ICTSS paper. It illustrates weak sequencing, i.e., implicit parallelism
between lifelines, as well as the overall conformance checking process.
UML Checker: Formal Specification in VDM++
Step 2: Generate transitions
Start: 1
Step 1: Generate states
1
o1 :C1
Step 3: Simplify
Start:
o2 :C2
2
1
5
14
m0
m0
Client
2
m0()
6
6
5
m1()
m2
9
3
opt
8
m2()
16
ret m2
17
11
9
17
10
12
11
18
4
16
ret m2
ret m3
17
11
ret m1
ret m1
18
ret m0
12
m3
9
ret m3
10
16
15
m2
m3
8
m3()
7
15
3
15
7
m1
m1
14
7
6
4
16
10
ret m0
13
19
13
19
19
values
public sdFig5 : Interaction =
let l1 = mk_Lifeline("Client", nil),
l2 = mk_Lifeline("C1", nil),
l3 = mk_Lifeline("C2", "o2"),
o1 = mk_InteractionOperand(nil, {mk_(l2, 3)}, {mk_(l2, 6)}),
f1 = mk_CombinedFragment(1, <opt>, [o1], {l2}),
m0C = mk_Message(1, 1, mk_(l1, 1), mk_(l2, 1), <synchCall>, "m0", []),
m0R = mk_Message(2, 1, mk_(l2, 8), mk_(l1, 2), <reply>, "m0", []),
m1C = mk_Message(3, 2, mk_(l2, 2), mk_(l3, 1), <synchCall>, "m1", []),
m1R = mk_Message(4, 2, mk_(l3, 4), mk_(l2, 7), <reply>, "m1", []),
m2C = mk_Message(5, 3, mk_(l2, 4), mk_(l2, 4), <synchCall>, "m2", []),
m2R = mk_Message(6, 3, mk_(l2, 5), mk_(l2, 5), <reply>, "m2", []),
m3C = mk_Message(7, 4, mk_(l3, 2), mk_(l3, 2), <synchCall>, "m3", []),
m3R = mk_Message(8, 4, mk_(l3, 3), mk_(l3, 3), <reply>, "m3", [])
in
mk_Interaction({l1,l2,l3}, {m0C,m0R,m1C,m1R,m2C,m2R,m3C,m3R},{f1}, {});
public trace1 : seq of Occurrence =
let obj1 = mk_Classifier("C1"),
obj2 = mk_Instance("C2", mk_token(2)),
obj3 = mk_Instance("C3", mk_token(3))
in
[ mk_Occurrence(1, 1, obj1, <Call>, "m0", []),
mk_Occurrence(2, 2, obj2, <Call>, "m1", []),
mk_Occurrence(3, 3, obj1, <Call>, "m2", []),
mk_Occurrence(4, 4, obj2, <Call>, "m3", []),
mk_Occurrence(5, 4, obj2, <Reply>, "m3", []),
mk_Occurrence(6, 3, obj1, <Reply>, "m2", []),
mk_Occurrence(7, 2, obj2, <Reply>, "m1", []),
mk_Occurrence(8, 1, obj1, <Reply>, "m0", []) ];
UML Checker: Formal Specification in VDM++
private p1 : Parameter = mk_Parameter("balance");
private p2 : Parameter = mk_Parameter("amount");
operations
public testFig5Accept()==
(
Assert( accepts(sd2pfa(sdFig5), trace1, {|->}, <StrictConf>) )
);
public testFig5Simplify() ==
(
Assert( simplify(sd2pfa(sdFig5)) =
mk_UMLChecker`PFA(
{ 2,6,7,8,11,13,14,16,17,19 },
2,
{ 19 },
{ mk_UMLChecker`Transition(
{ 2 },
mk_UMLChecker`Label(
mk_UMLChecker`Event(
<Call>,
mk_UMLChecker`Message(
1,
1,
mk_( mk_UMLChecker`Lifeline(
"Client",
nil ),
1 ),
mk_( mk_UMLChecker`Lifeline(
"C1",
nil ),
1 ),
<synchCall>,
"m0",
[ ] ) ),
nil,
nil ),
{ 11 } ),
mk_UMLChecker`Transition(
{ 6 },
mk_UMLChecker`Label(
mk_UMLChecker`Event(
<Call>,
mk_UMLChecker`Message(
7,
4,
mk_( mk_UMLChecker`Lifeline(
"C2",
"o2" ),
2 ),
mk_( mk_UMLChecker`Lifeline(
"C2",
"o2" ),
2 ),
<synchCall>,
"m3",
[ ] ) ),
nil,
nil ),
17
UML Checker: Formal Specification in VDM++
{ 7 } ),
mk_UMLChecker`Transition(
{ 7 },
mk_UMLChecker`Label(
mk_UMLChecker`Event(
<Reply>,
mk_UMLChecker`Message(
8,
4,
mk_( mk_UMLChecker`Lifeline(
"C2",
"o2" ),
3 ),
mk_( mk_UMLChecker`Lifeline(
"C2",
"o2" ),
3 ),
<reply>,
"m3",
[ ] ) ),
nil,
nil ),
{ 8 } ),
mk_UMLChecker`Transition(
{ 11 },
mk_UMLChecker`Label(
mk_UMLChecker`Event(
<Call>,
mk_UMLChecker`Message(
3,
2,
mk_( mk_UMLChecker`Lifeline(
"C1",
nil ),
2 ),
mk_( mk_UMLChecker`Lifeline(
"C2",
"o2" ),
1 ),
<synchCall>,
"m1",
[ ] ) ),
nil,
nil ),
{ 6,13 } ),
mk_UMLChecker`Transition(
{ 13 },
nil,
{ 16 } ),
mk_UMLChecker`Transition(
{ 13 },
mk_UMLChecker`Label(
mk_UMLChecker`Event(
<Call>,
mk_UMLChecker`Message(
5,
3,
mk_( mk_UMLChecker`Lifeline(
"C1",
nil ),
18
UML Checker: Formal Specification in VDM++
4 ),
mk_( mk_UMLChecker`Lifeline(
"C1",
nil ),
4 ),
<synchCall>,
"m2",
[ ] ) ),
nil,
nil ),
{ 14 } ),
mk_UMLChecker`Transition(
{ 14 },
mk_UMLChecker`Label(
mk_UMLChecker`Event(
<Reply>,
mk_UMLChecker`Message(
6,
3,
mk_( mk_UMLChecker`Lifeline(
"C1",
nil ),
5 ),
mk_( mk_UMLChecker`Lifeline(
"C1",
nil ),
5 ),
<reply>,
"m2",
[ ] ) ),
nil,
nil ),
{ 16 } ),
mk_UMLChecker`Transition(
{ 17 },
mk_UMLChecker`Label(
mk_UMLChecker`Event(
<Reply>,
mk_UMLChecker`Message(
2,
1,
mk_( mk_UMLChecker`Lifeline(
"C1",
nil ),
8 ),
mk_( mk_UMLChecker`Lifeline(
"Client",
nil ),
2 ),
<reply>,
"m0",
[ ] ) ),
nil,
nil ),
{ 19 } ),
mk_UMLChecker`Transition(
{ 8,16 },
mk_UMLChecker`Label(
mk_UMLChecker`Event(
<Reply>,
19
UML Checker: Formal Specification in VDM++
mk_UMLChecker`Message(
4,
2,
mk_( mk_UMLChecker`Lifeline(
"C2",
"o2" ),
4 ),
mk_( mk_UMLChecker`Lifeline(
"C1",
nil ),
7 ),
<reply>,
"m1",
[ ] ) ),
nil,
nil ),
{ 17 } ) } ))
20
);
8.3
Example 2: ATM
The next example was presented in Fig.3 of our ICTSS 2013 paper. Numbers in red were added to identify lifeline
locations.
1
2 (ret)
3
4
5
8
6
7 (ret)
9
10
11
12
16
17
15
13
14 (ret)
18
19
20
21
values
public sdFig3 : Interaction =
let l1 = mk_Lifeline("Client", nil),
l2 = mk_Lifeline("Account", "a"),
l3 = mk_Lifeline("Movement", "m"),
l4 = mk_Lifeline("Movement", "n"),
UML Checker: Formal Specification in VDM++
21
o11 = mk_InteractionOperand(mk_InteractionConstraint(nil, nil, mk_Lte(p2,
p1)), {mk_(l1, 3), mk_(l2, 3), mk_(l3, 3), mk_(l4, 3)}, {mk_(l1, 18), mk_(l2,
18), mk_(l3, 18), mk_(l4, 18)}),
o12 = mk_InteractionOperand(mk_InteractionConstraint(nil, nil,mk_Gt(p2,
p1)), {mk_(l1, 18), mk_(l2, 18), mk_(l3, 18), mk_(l4, 18)}, {mk_(l1, 21), mk_(l2,
21), mk_(l3, 21), mk_(l4, 21)}),
o21 = mk_InteractionOperand(nil, {mk_(l2, 5), mk_(l3, 5), mk_(l4, 5)},
{mk_(l2, 8), mk_(l3, 8), mk_(l4, 8)}),
o22 = mk_InteractionOperand(nil, { mk_(l2, 8), mk_(l3, 8), mk_(l4, 8)},
{mk_(l2, 16), mk_(l3, 16), mk_(l4, 16)}),
o31 = mk_InteractionOperand(nil, {mk_(l2, 9), mk_(l3, 9), mk_(l4, 9)},
{mk_(l2, 12), mk_(l3, 12), mk_(l4, 12)}),
o32 = mk_InteractionOperand(nil, {mk_(l2, 12), mk_(l3, 12), mk_(l4, 12)},
{mk_(l2, 15), mk_(l3, 15), mk_(l4, 15)}),
f1 = mk_CombinedFragment(1, <alt>, [o11, o12], {l1, l2, l3, l4}),
f2 = mk_CombinedFragment(1, <par>, [o21, o22], {l2, l3, l4}),
f3 = mk_CombinedFragment(1, <alt>, [o31, o32], {l2, l3, l4}),
m0C = mk_Message(1, 1, mk_(l1, 1), mk_(l2, 1), <createMessage>, "Account",
[p1]),
m0R = mk_Message(2, 1, mk_(l2, 2), mk_(l1, 2), <reply>, "Account", [l2]),
m1C = mk_Message(3, 2, mk_(l1, 4), mk_(l2, 4), <synchCall>, "withdraw",
[p2]),
m1R = mk_Message(4, 2, mk_(l2, 17), mk_(l1, 17), <reply>, "withdraw",
["OK"]),
m2C = mk_Message(5, 3, mk_(l2, 6), mk_(l2, 6), <synchCall>, "setBalance",
[mk_Minus(p1,p2)]),
m2R = mk_Message(6, 3, mk_(l2, 7), mk_(l2, 7), <reply>, "setBalance", []),
m3C = mk_Message(7, 4, mk_(l2, 10), mk_(l3, 10), <createMessage>,
"Movement", [l2, p2, "withdraw"]),
m3R = mk_Message(8, 4, mk_(l3, 11), mk_(l2, 11), <reply>, "Movement", [l3]),
m4C = mk_Message(9, 5, mk_(l2, 13), mk_(l4, 13), <createMessage>,
"Movement", [l2, mk_Minus(0, p2)]),
m4R = mk_Message(10, 5, mk_(l4, 14), mk_(l2, 14), <reply>, "Movement",
[l4]),
m5C = mk_Message(11, 6, mk_(l1, 19), mk_(l2, 19), <synchCall>, "withdraw",
[p2]),
m5R = mk_Message(12, 6, mk_(l2, 20), mk_(l1, 20), <reply>, "withdraw",
["INSUF_BALANCE"])
in
mk_Interaction({l1,l2,l3, l4}, {m0C,m0R,m1C,m1R,m2C,m2R,m3C,m3R, m4C, m4R,
m5C, m5R},{f1, f2, f3}, {p1, p2});
The next execution trace corresponds to the execution trace indicated in Fig. 7 of the our ICTSS 2013 paper.
UML Checker: Formal Specification in VDM++
Execution Trace
22
Automaton Run States(s)
≺A, L, , C≻ = ≺{1}, {}, {},{}≻
P={balance ↦ 100, amount ↦ 50}
1: Account(100)
≺{2}, {}, {1↦1}, {1}≻
1’:‎return‎a1
≺{4}, {a↦a1}, {}, {1,1’}≻
2: a1.withdraw(50)
≺{5,11}, {a↦a1}, {2↦2}, {1,1’,2}≻
≺{5,9}, {a↦a1}, {2↦2}, {1,1’,2}≻
(unchanged, call ignored)
(unchanged, call ignored)
(unchanged, reply ignored)
(unchanged, reply ignored)
≺{5,12}, {a↦a1}, {2↦2, 4↦4}, {1,1’,2,4}≻
(unchanged, call ignored)
≺{5,13}, {a↦a1, m↦m1}, {2↦2}, {1,1’,2,4,4’}≻
(unchanged, reply ignored)
3: a1.getBalance()
3’:‎return‎100
4: Movement(a1,
50, “withdraw”)
4’:‎return m1
5: a1.setBalance(50)
≺{6,13},{a↦a1,m↦m1},{2↦2,5↦3},{1,1’,2,4,4’,3}≻ ≺{6,9},{a↦a1},{2↦2,5↦3},{1,1’,2,3}≻
5’:‎return
≺{7,13},{a↦a1,m↦m1},{2↦2},{1,1’,2,4,4’,3,3’}≻
≺{7,9},{a↦a1},{2↦2},{1,1’,2,3,3’}≻
≺{16}, {a↦a1,m↦m1},{},{1,1’,2,4,4’,3,3’,2’}≻
must consider 2’, but
active states don’t accept it
2’:‎return‎“Ok”
values
public traceFig7 : seq of Occurrence =
let obj1 = mk_Instance("Account", mk_token(1)),
obj2 = mk_Instance("Movement", mk_token(2))
in
[ mk_Occurrence(1, 1, obj1, <Call>, "Account", [100]),
mk_Occurrence(2, 1, obj1, <Reply>, "Account", [obj1]),
mk_Occurrence(3, 3, obj1, <Call>, "withdraw", [50]),
mk_Occurrence(4, 4, obj1, <Call>, "getBalance", []),
mk_Occurrence(5, 4, obj1, <Reply>, "getBalance ", [100]),
mk_Occurrence(6, 5, obj2, <Call>, "Movement", [obj1, 50, "withdraw"]),
mk_Occurrence(7, 5, obj2, <Reply>, "Movement", [obj2]),
mk_Occurrence(8, 6, obj1, <Call>, "setBalance", [50]),
mk_Occurrence(9, 6, obj1, <Reply>, "setBalance", []),
mk_Occurrence(10, 3, obj1, <Reply>, "withdraw", ["OK"]) ];
operations
public testFig7Accept() ==
(
Assert( accepts(sd2pfa(sdFig3), traceFig7, {p1 |-> 100, p2 |-> 50},
<LooseConf>) );
Assert( not accepts(sd2pfa(sdFig3), traceFig7, {p1 |-> 100, p2 |-> 50},
<StrictConf>) );
Assert( coverage(sd2pfa(sdFig3), traceFig7, {p1 |-> 100, p2 |-> 50},
<LooseConf>) = {1, 2, 3, 4, 5, 6, 7, 8});
);
sd UserInteractionTesting
UML Checker: Formal Specification in VDM++
8.4
23
Example 3: asynchronous user interaction
This example refers to Fig. 9 in our ICTSS 2013 paper.
sd InteractionTestingSpec
Implementation & Tracing
SD
AUT
Test
Driver
(thread 0)
User
start(args)
enter(x)
display(y)
Input
Blocking
Queue
Output
Blocking
Queue
Tracing
Aspect
send- start(app, args)
main(args)
possibly
start
internal
poll(timeout)
interactinos send- enter(x)
put(x)
(wait)
here
:x
enter
Automaton
send- sendstart(args) enter(x)
Console
Simulator
check()
rcvdisplay(y)
scan()
:x
rcvstart
rcventer
poll(timeout)
:y
rcvdisplay
AUT
(thread 1)
(wait)
put(y)
print(y)
senddisplay
:y
assertEquals(exp, y)
rcv- rcv- sendstart(args) enter(x) display(y)
stop()
join(timeout)
operations
public testAsynchUI() ==
(
let
x = mk_Parameter("x"),
y = mk_Parameter("y"),
User = mk_Lifeline("User", nil),
AUT = mk_Lifeline("AUT", nil),
m1 = mk_Message(1, 1, mk_(User, 1), mk_(AUT, 1), <asynchSignal>, "start",
[]),
m2 = mk_Message(2, 2, mk_(User, 2), mk_(AUT, 2), <asynchSignal>, "enter",
[x]),
m3 = mk_Message(3, 3, mk_(AUT, 3), mk_(User, 3), <asynchSignal>,
"display", [y]),
pfa1 = sd2pfa(mk_Interaction({User, AUT}, {m1, m2, m3}, {}, {x, y})),
U = mk_Classifier("User"),
A = mk_Classifier("AUT"),
o1 = mk_Occurrence(1, 1, A,
o2 = mk_Occurrence(2, 1, A,
o3 = mk_Occurrence(3, 2, A,
o4 = mk_Occurrence(4, 2, A,
o5 = mk_Occurrence(5, 3, U,
o6 = mk_Occurrence(6, 3, U,
<Send>, "start", []),
<Receive>, "start", []),
<Send>, "enter", [1]),
<Receive>, "enter", [1]),
<Send>, "display", [2]),
<Receive>, "display", [2])
in
(
Assert(accepts(pfa1, [o1,o2,o3,o4,o5,o6], {x|->1, y|->2}, <StrictConf>));
Assert(accepts(pfa1, [o1,o3,o2,o4,o5,o6], {x|->1, y|->2}, <StrictConf>));
Assert(not accepts(pfa1, [o1,o2,o3,o5,o4,o6], {x|->1, y|->2},
<StrictConf>))
)
UML Checker: Formal Specification in VDM++
24
);
8.5
Loop testing
operations
public testLoop() ==
(
let l1 = mk_Lifeline("C1", "o1"),
l2 = mk_Lifeline("C2", "o2"),
o1 = mk_InteractionOperand(mk_InteractionConstraint(1, 2, nil),
{mk_(l1, 1), mk_(l2, 1)}, {mk_(l1, 4), mk_(l2, 4)}),
o2 = mk_InteractionOperand(mk_InteractionConstraint(nil, 2, nil),
{mk_(l1, 1), mk_(l2, 1)}, {mk_(l1, 4), mk_(l2, 4)}),
o3 = mk_InteractionOperand(mk_InteractionConstraint(1, nil, nil),
{mk_(l1, 1), mk_(l2, 1)}, {mk_(l1, 4), mk_(l2, 4)}),
o4 = mk_InteractionOperand(mk_InteractionConstraint(nil, nil, nil),
{mk_(l1, 1), mk_(l2, 1)}, {mk_(l1, 4), mk_(l2, 4)}),
f1 = mk_CombinedFragment(1, <loop>, [o1], {l1, l2 }),
f2 = mk_CombinedFragment(1, <loop>, [o2], {l1, l2 }),
f3 = mk_CombinedFragment(1, <loop>, [o3], {l1, l2 }),
f4 = mk_CombinedFragment(1, <loop>, [o4], {l1, l2 }),
m0C = mk_Message(1, 1, mk_(l1, 2), mk_(l2, 2), <synchCall>, "m0", []),
m0R = mk_Message(2, 1, mk_(l2, 3), mk_(l1, 3), <reply>, "m0", []),
pfa1 = sd2pfa(mk_Interaction({l1,l2}, {m0C, m0R},{f1}, { })),
pfa2 = sd2pfa(mk_Interaction({l1,l2}, {m0C, m0R},{f2}, { })),
pfa3 = sd2pfa(mk_Interaction({l1,l2}, {m0C, m0R},{f3}, { })),
pfa4 = sd2pfa(mk_Interaction({l1,l2}, {m0C, m0R},{f4}, { })),
obj1 = mk_Instance("C1", mk_token(1)),
obj2 = mk_Instance("C2", mk_token(2)),
oc1 = mk_Occurrence(1, 1, obj2, <Call>, "m0", []),
oc2 = mk_Occurrence(2, 1, obj2, <Reply>, "m0", []),
oc3 = mk_Occurrence(3, 2, obj2, <Call>, "m0", []),
oc4 = mk_Occurrence(4, 2, obj2, <Reply>, "m0", []),
oc5 = mk_Occurrence(5, 3, obj2, <Call>, "m0", []),
oc6 = mk_Occurrence(6, 3, obj2, <Reply>, "m0", [])
in
(
-- loop(1,2)
Assert(not accepts(pfa1, [], {|->}, <StrictConf>) );
Assert(accepts(pfa1, [oc1, oc2], {|->}, <StrictConf>) );
Assert(accepts(pfa1, [oc1, oc2, oc3, oc4], {|->}, <StrictConf>) );
Assert(not accepts(pfa1, [oc1, oc2, oc3, oc4, oc5, oc6], {|->},
<StrictConf>));
Assert(accepts(pfa1, [oc1, oc2, oc3, oc4, oc5, oc6], {|->}, <LooseConf>));
-- loop(nil,2)
Assert(accepts(pfa2, [], {|->}, <StrictConf>) );
Assert(accepts(pfa2, [oc1, oc2], {|->}, <StrictConf>) );
Assert(accepts(pfa2, [oc1, oc2, oc3, oc4], {|->}, <StrictConf>) );
Assert(not accepts(pfa2, [oc1, oc2, oc3, oc4, oc5, oc6], {|->},
<StrictConf>));
Assert(accepts(pfa2, [oc1, oc2, oc3, oc4, oc5, oc6], {|->}, <LooseConf>));
-- loop(1,nil)
Assert(not accepts(pfa3, [], {|->}, <StrictConf>) );
Assert(accepts(pfa3, [oc1, oc2], {|->}, <StrictConf>) );
UML Checker: Formal Specification in VDM++
25
Assert(accepts(pfa3, [oc1, oc2, oc3, oc4], {|->}, <StrictConf>) );
Assert(accepts(pfa3, [oc1, oc2, oc3, oc4, oc5, oc6], {|->}, <StrictConf>));
-- loop(nil,nil)
Assert(accepts(pfa4,
Assert(accepts(pfa4,
Assert(accepts(pfa4,
Assert(accepts(pfa4,
[], {|->}, <StrictConf>) );
[oc1, oc2], {|->}, <StrictConf>) );
[oc1, oc2, oc3, oc4], {|->}, <StrictConf>) );
[oc1, oc2, oc3, oc4, oc5, oc6], {|->}, <StrictConf>))
)
);
8.6
Test execution
public testAll() ==
(
-testFig5Simplify();
testFig5Accept();
testFig7Accept();
testAsynchUI();
testLoop()
);
9
-- too slow
Conclusion
This concludes this report and the VDM++ specification.
end UMLChecker
References
[1]
[2]
[3]
J. P.Faria, A. Paiva and Mário V. Castro, Techniques and Toolset for Conformance Testing against UML Sequence Diagrams, ICTSS
2013.
The IFAD VDM++ Language - Revised for V6.6, IFAD, 2000
OMG Unified Modeling LanguageTM (OMG UML), Superstructure, Version 2.4.1, OMG, August 2011.

Similar documents