Upload
others
View
11
Download
0
Embed Size (px)
Citation preview
UML Checker: Formal Specification in VDM++ of the Petri Net-based
Conformance Checking Engine
TR-SDBT-2013-04, SERG, FEUP, December 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 Introduction ..................................................................................................................................................... 2 2 Preliminaries .................................................................................................................................................... 2
2.1 Values ..................................................................................................................................................... 2 2.2 Variables ................................................................................................................................................. 2 2.3 Value specifications ................................................................................................................................ 3
3 Interactions (Sequence Diagrams) ................................................................................................................... 4 4 Petri Nets ......................................................................................................................................................... 7 5 Conversion of Interactions to Petri Nets .......................................................................................................... 8
5.1 Introduction and semantic options .................................................................................................................. 8 5.2 Place generation.............................................................................................................................................. 8 5.3 Transition generation .................................................................................................................................... 10 5.4 Main conversion logic .................................................................................................................................. 19
6 Petri Net Execution ........................................................................................................................................ 19 6.1 Definition of execution traces ....................................................................................................................... 19 6.2 Definition of run states ................................................................................................................................. 19 6.3 Matching and binding values and messages ................................................................................................. 20 6.4 Operations on markings ................................................................................................................................ 21 6.5 Operations on transitions .............................................................................................................................. 21 6.6 Main execution logic .................................................................................................................................... 22
7 Test Cases ...................................................................................................................................................... 24 7.1 Convenience definitions ........................................................................................................................ 24 7.2 Simple asynchronous message .............................................................................................................. 25 7.3 Weak sequencing .................................................................................................................................. 25 7.4 ATM example with synchronous messages, 'alt' and 'par' .................................................................... 27 7.5 Asynchronous user interaction .............................................................................................................. 30 7.6 Loops with synchronous messages ....................................................................................................... 31 7.7 Loops with asynchronous messages ..................................................................................................... 32 7.8 Break ..................................................................................................................................................... 34 7.9 General ordering ................................................................................................................................... 35 7.10 Co-region .............................................................................................................................................. 36 7.11 Critical region ....................................................................................................................................... 37 7.12 Interaction use ....................................................................................................................................... 39 7.13 Assert and negate fragments ................................................................................................................. 39 7.14 Consider and ignore fragments ............................................................................................................. 41 7.15 Test execution ....................................................................................................................................... 42
8 Conclusion ..................................................................................................................................................... 42 References .............................................................................................................................................................. 42
UML Checker: Formal Specification in VDM++ 2
1 Introduction
This document contains a formal specification of the conformance checking engine of the UML Checker toolset
based on Extended Petri Nets presented in our paper [4]. The specification is written in VDM++ [2]. This
documented (RTF version) can be executed with the VDM++ Toolbox.
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
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. Value specifications are also
generated in our approach in the translating of sequence diagrams to Extended Petri Nets, to represent additional
guards in transitions and expressions in transition arcs.
2.1 Values types
-- these are the kinds of values we need ...
public Value = Primitive | Instance | OccurrenceGroupId;
public Primitive = nat | bool | real | string;
public string = seq of char;
-- a run-time instance of a class
public Instance :: className : string
object : token; -- a VDM token may contain anything
-- an identifier for a group of runtime occurrences (send/receive or call/reply
-- pair), encapsulated in a record type to be distinguishable from other nats
public OccurrenceGroupId :: id: nat;
functions
-- Checks if a type is a subtype of another type
-- Here only checks that they are same type (enough for prototyping).
private isSubtype(type1 : string, type2: string) r : bool ≜ type1 = type2;
2.2 Variables types
-- these are the kinds of variables we need ...
public Variable = Lifeline | Parameter | PNVariable;
-- a Petri Net variable used with the scope of a single transition
public PNVariable :: string;
UML Checker: Formal Specification in VDM++ 3
-- a lifeline representing a classifier or a named instance
-- (to be bound to an actual instance during execution)
public Lifeline ::
className : string
instanceName : [string];
-- an interaction parameter (identified by its name)
public Parameter ::
name: string
direction: ParameterDirectionKind;
public ParameterDirectionKind = <InParam> | <OutParam>;
-- Bindings of variables to actual values
public Bindings = map Variable to Value;
functions
-- Bind a variable to a value, updating the set of bindings
private bind(var: Variable, val: Value, b: Bindings) r: Bindings ≜
b ++ {var val};
2.3 Value specifications types
-- these are the kinds of value specifications we need ...
public ValueSpecification = Value | Variable | Expression | <Unknown>;
public Expression :: symbol: ExpSymbol
operands: seq of [ValueSpecification];
-- there are the kinds of expression symbols we need
public ExpSymbol = <Neg> | <Eq> | <Plus> | <Minus> | <Lt> | <Lte> | <Gt> | <Gte>
| <And> | <Or>;
functions
-- evaluates a value specification, given bindings of variables to values
private eval(vs: ValueSpecification, b: Bindings) r: [Value] ≜ cases true:
(is_Lifeline(vs) is_Parameter(vs)
is_PNVariable(vs)) -> if vs dom b then b(vs) else nil,
(vs = <Unknown>) -> nil, -- shouldn't be evaluated ...
(is_Expression(vs)) ->
cases vs.symbol:
<Neg> -> eval(vs.operands(1), b),
<Eq> -> eval(vs.operands(1), b) = eval(vs.operands(2), b),
<And> -> op elems vs.operands eval(op, b) = true,
<Or> -> op elems vs.operands eval(op, b) = false,
<Plus> -> eval(vs.operands(1), b) + eval(vs.operands(2), b),
<Minus> -> eval(vs.operands(1), b) - eval(vs.operands(2), b),
<Lt> -> eval(vs.operands(1), b) < eval(vs.operands(2), b),
<Lte> -> eval(vs.operands(1), b) eval(vs.operands(2), b),
<Gt> -> eval(vs.operands(1), b) > eval(vs.operands(2), b),
<Gte> -> eval(vs.operands(1), b) eval(vs.operands(2), b)
UML Checker: Formal Specification in VDM++ 4
end,
(is_OccurrenceGroupId(vs)) -> vs.id,
other -> vs -- Value
end;
-- 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),
(is_Lifeline(expected))->
if actual = nil is_Instance(actual)
then mk_(false, b)
else if expected 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),
(is_Parameter(expected) is_PNVariable(expected)) ->
if expected dom b
then mk_(b(expected) = actual, b)
else mk_(true, bind(expected, actual, b)),
others -> mk_(eval(expected, b) = actual, b)
end;
private replace(vs: ValueSpecification, rep: map ValueSpecification to
[ValueSpecification]) r: ValueSpecification ≜
if vs dom(rep) then rep(vs)
else if is_Expression(vs)
then mk_Expression(vs.symbol,[replace(vs.operands(i), rep) |
i inds vs.operands])
else vs;
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
-- (can be assigned using the y-coordinate)
public Location = nat;
public MessageId = nat;
public Gate ::
name : nat;
public MessageSignature = string;
public LifelineLocation = Lifeline Location;
-- message sorts defined in the UML standard
public MessageSort = <synchCall> | <asynchCall> | <asynchSignal> |
<createMessage> | <reply>;
UML Checker: Formal Specification in VDM++ 5
public Message ::
id : MessageId -- connector id
groupId : nat -- to identify pairs call/reply messages
source : LifelineLocation
target : LifelineLocation
messageSort : MessageSort
signature : MessageSignature
arguments : seq of ValueSpecification; -- return value if reply message
public InteractionConstraint ::
minint : [ValueSpecification] -- loop
maxint : [ValueSpecification] -- loop
specification: [ValueSpecification] | <else>;
-- interaction operators defined in the UML standard
public InteractionOperatorKind = <seq> | <alt> | <opt> | <par> | <strict> | <loop>
| <break> | <critical> | <assert> | <neg> | <consider> | <ignore>;
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>, <break>, <critical>, <assert>, <neg> -> len f.operands = 1,
<alt>, <par>, <strict>, <seq> -> len f.operands 1
end
( o elems f.operands
{lf | mk_(lf, -) o.startLocations} = f.lifelines
{lf | mk_(lf, -) o.finishLocations} = f.lifelines)
( i {1, ..., len f.operands - 1}
f.operands(i+1).startLocations = f.operands(i).finishLocations);
public ConsiderIgnoreFragment::
id : nat
interactionOperator : InteractionOperatorKind -- consider ignore
lifelines : set of Lifeline
messages : set of MessageSignature
startLocations : set of LifelineLocation
finishLocations : set of LifelineLocation;
public GeneralOrdering ::
before : LifelineLocation
after : LifelineLocation;
public InteractionUse ::
id : nat
refersTo : Interaction
arguments : seq of ValueSpecification
lifelines : set of Lifeline
locations : set of LifelineLocation;
UML Checker: Formal Specification in VDM++ 6
public Interaction ::
lifelines : set of Lifeline
messages : set of Message
combinedFragments : set of CombinedFragment
generalOrderings : set of GeneralOrdering
interactionUses : set of InteractionUse
considerIgnoreFragments: set of ConsiderIgnoreFragment
parameters : seq of Parameter;
functions
-- Get all lifeline locations in an interaction
private getLifelineLocations(sd: Interaction) r: set of LifelineLocation ≜
{m.source | m sd.messages}
∪ {m.target | m sd.messages}
∪ ⋃ {⋃ {o.startLocations ∪ o.finishLocations | o elems f.operands}
| f sd.combinedFragments}
∪ ⋃ {r.locations | r sd.interactionUses};
-- Get the lifeline location that refers to a lifeline
private getLoc(locs: set of LifelineLocation, lf: Lifeline) r : LifelineLocation
≜ iota loc locs loc.#1 = lf;
private contains(f: CombinedFragment, loc: LifelineLocation) r: bool ≜
loc.#1 f.lifelines
loc.#2>getLoc(f.operands(1).startLocations, loc.#1).#2
loc.#2<getLoc(f.operands(len f.operands).finishLocations, loc.#1).#2;
private contains(f1: CombinedFragment, f2: CombinedFragment) r: bool ≜
f2.lifelines ⊆ f1.lifelines
( loc f2.operands(1).startLocations
getLoc(f1.operands(1).startLocations, loc.#1).#2 < loc.#2)
( loc f2.operands(len f2.operands).finishLocations
getLoc(f1.operands(len f1.operands).finishLocations, loc.#1).#2
> loc.#2);
private container(sd: Interaction, f: CombinedFragment) r: [CombinedFragment]
≜ let ff = {f2 | f2 sd.combinedFragments contains(f2, f)}
in if ff = {} then nil
else iota f2 ff f3 ff contains(f2, f3);
private contains(f1: ConsiderIgnoreFragment, f2: ConsiderIgnoreFragment) r:
bool ≜
f2.lifelines ⊆ f1.lifelines
( loc f2. startLocations
getLoc(f1. startLocations, loc.#1).#2 < loc.#2)
( loc f2.finishLocations
getLoc(f1.finishLocations, loc.#1).#2 > loc.#2);
private contains(f: ConsiderIgnoreFragment, loc: LifelineLocation) r: bool ≜
loc.#1 f.lifelines
UML Checker: Formal Specification in VDM++ 7
loc.#2 > getLoc(f.startLocations, loc.#1).#2
loc.#2 < getLoc(f.finishLocations, loc.#1).#2;
4 Petri Nets
In our approach, for the incremental conformance checking, we first translate sequence diagrams to Coloured Petri
Nets with inhibitor arcs, reset arcs and LIFO/FIFO places. Here we define such nets.
types
public PlaceType = <FIFOPlace> | <LIFOPlace> | <CountingPlace> | <CriticalPlace>
| <FailurePlace>;
public Place ::
id: nat
type: PlaceType;
-- The events of interest here are related with message sending and receiving.
public EventType = <Send> | <Receive> | <Call> | <Reply> | <AnyEvent> |
<AnySignatureInSet> | <AnySignatureNotInSet>;
public Event ::
type : EventType
message : [Message | set of MessageSignature]
inv e ≜ e.type = <AnyEvent> e.message = nil;
public Label ::
event : [Event]
guard : [ValueSpecification];
public Transition ::
source : map Place to [ValueSpecification]
label : [Label]
target : map Place to [ValueSpecification]
inhibitor: set of Place
resetArcs: set of Place
inv t ≜ t.source {} t.target {};
public PetriNet ::
places : set of Place
starts : Place
finish : set of Place
transitions: set of Transition
inv a ≜ a.starts a.places
a.finish ⊆ a.places
t a.transitions
dom t.source ⊆ a.places
t.inhibitor ⊆ a.places
dom t.target ⊆ a.places;
functions
private mkTransition(source: map Place to [ValueSpecification], label:
[Label],
UML Checker: Formal Specification in VDM++ 8
target : map Place to [ValueSpecification], inhibitor: set of Place) r :
Transition ≜ mk_Transition(source, label, target, inhibitor, {});
private incoming(a: PetriNet, p: Place) r: set of Transition ≜
{t | t a.transitions p dom t.target};
private outgoing(a: PetriNet, p: Place) r: set of Transition ≜
{t | t a.transitions p dom t.source};
instance variables
private static placeCounter : nat := 0;
operations
public static newPlace(type: PlaceType) res: Place ≜ (
placeCounter := placeCounter + 1;
return mk_Place(placeCounter, type)
);
5 Conversion of Interactions to Petri Nets
5.1 Introduction and semantic options
Two semantic options are supported in the conversion of Interactions to PetriNets, with without lifeline
synchronize at decision points of 'alt', 'opt', 'loop' or 'break' combined fragments.
values
public synchronizeLifelinesAtDecisionPoints : bool = false;
5.2 Place generation functions
-- auxliary function to sort locations in the same lifeline
private sortLocs(locs: set of LifelineLocation) r: seq of LifelineLocation ≜ if locs={} then []
else let loc1 locs be st loc2 locs loc2.#2 < loc1.#2
in [loc1] ↷ sortLocs(locs \ {loc1});
-- Generates a mapping of locations in a single lifeline to before/after places
private mapLocationsLf(locs: set of LifelineLocation) r: map LifelineLocation
to (Place Place) ≜
if locs = {} then {}
else let locs2 = sortLocs(locs),
places = [newPlace(<CountingPlace>) | i {1, ...,len locs2+1}]
in {locs2(i) mk_(places(i), places(i+1)) | i inds locs2};
UML Checker: Formal Specification in VDM++ 9
-- Generates a mapping of locations to before/after places
private mapLocations(locs: set of LifelineLocation) r: map LifelineLocation to
(Place Place) ≜
if locs = {} then { }
else let loc locs
in let locsLf = {l | l locs l.#1 = loc.#1}
in mapLocationsLf(locsLf) munion mapLocations(locs \ locsLf);
-- Generates a mapping from messages to related queuing places
private mapMessages(msgs: set of Message) r: map Message to Place ≜
if msgs = {} then { }
else let m msgs
in let t = if m.messageSort {<asynchCall>, <asynchSignal>}
then <FIFOPlace> else <LIFOPlace>
in {m newPlace(t)} munion mapMessages(msgs \ {m});
-- Generates a mapping from general orderings to related places
private mapGeneralOrderings(genOrd: set of GeneralOrdering) r: map
GeneralOrdering to Place ≜
if genOrd = {} then { }
else let k genOrd
in {k newPlace(<CountingPlace>)}
munion mapGeneralOrderings(genOrd \ {k});
private mapDecCf(f: CombinedFragment, lfs: set of Lifeline, n: nat) r: map
(CombinedFragment Lifeline nat) to Place ≜
if lfs = {} then { }
else let lf lfs
in {mk_(f, lf, n) newPlace(<FIFOPlace>) }
munion mapDecCf(f, lfs\ {lf}, n);
private mapDecisionPlaces(cfs: set of CombinedFragment) r: map
(CombinedFragment Lifeline nat) to Place ≜
if cfs = {} then { }
else let f cfs
in if f.interactionOperator {<opt>, <alt>, <break>, <neg>}
then mapDecCf(f, f.lifelines, 1)
munion mapDecisionPlaces(cfs \ {f})
else if f.interactionOperator = <loop>
then mapDecCf(f, f.lifelines, 1)
munion mapDecCf(f, f.lifelines, 2)
munion mapDecisionPlaces(cfs \ {f})
else mapDecisionPlaces(cfs \ {f});
-- Generates a mapping from special Fragments to places
private mapCombFrag(cfs: set of CombinedFragment) r: map CombinedFragment to
Place ≜
if cfs = {} then { }
else let f cfs
in cases f.interactionOperator:
<critical> -> {f newPlace(<CriticalPlace>)}
munion mapCombFrag(cfs \ {f}),
<loop> -> {f newPlace(<LIFOPlace>) }
munion mapCombFrag(cfs \ {f}),
UML Checker: Formal Specification in VDM++ 10
<neg> -> {f newPlace(<FailurePlace>) }
munion mapCombFrag(cfs \ {f}),
other -> mapCombFrag(cfs \ {f})
end;
-- Generates needed places for a sequence diagram, described by mappings
-- from SD features to places
private generatePlaces(sd: Interaction) r: (set of Place) Place Place (map
LifelineLocation to (Place Place)) (map Message to Place) (map
(CombinedFragment Lifeline nat) to Place) (map GeneralOrdering to Place)
(map CombinedFragment to Place) ≜ let starts = newPlace(<CountingPlace>),
finishes = newPlace(<CountingPlace>),
mapLocs = mapLocations(getLifelineLocations(sd)),
mapMsgs = mapMessages({m | m sd.messages
m.messageSort {<asynchCall>,<asynchSignal>,<reply>}}), mapDecs = mapDecisionPlaces(sd.combinedFragments),
mapFrag = mapCombFrag(sd.combinedFragments),
mapGenOrd = mapGeneralOrderings(sd.generalOrderings),
places = {starts, finishes}
∪ ⋃ {{p1, p2} | mk_(p1, p2) rng mapLocs}
∪ rng mapMsgs ∪ rng mapDecs ∪ rng mapGenOrd ∪ rng mapFrag in mk_(places,starts,finishes,mapLocs,mapMsgs,mapDecs,mapGenOrd,mapFrag);
5.3 Transition generation
-- Get the state immediatly before a lifeline location,
-- using a pre-computed mapping
private placeBefore(loc: LifelineLocation, mapping: map LifelineLocation to
(Place Place)) r : Place ≜ let ss = mapping(loc) in ss.#1;
-- Get the place immediatly after a lifeline location,
-- using a pre-computed mapping
private placeAfter(loc: LifelineLocation, mapping: map LifelineLocation to
(Place Place)) r : Place ≜ let ss = mapping(loc) in ss.#2;
-- Get the first lifeline places, using a pre-computed mapping
private getFirstLifelinePlaces(mapping: map LifelineLocation to (Place
Place)) r : set of Place ≜
{placeBefore(loc, mapping) | loc dom mapping
loc2 dom mapping loc2.#1 = loc.#1 loc2.#2 < loc.#2};
-- Get the last lifeline places, using a pre-computed mapping
private getLastLifelinePlaces(mapping: map LifelineLocation to (Place Place))
r : set of Place ≜
{ placeAfter(loc, mapping) | loc dom mapping
loc2 dom mapping loc2.#1 = loc.#1 loc2.#2 > loc.#2};
-- Get the last lifeline locations in a SD
private getLastLifelineLocs(sd: Interaction) r : set of LifelineLocation ≜ let locs = getLifelineLocations(sd)
UML Checker: Formal Specification in VDM++ 11
in {loc | loc locs
loc2 locs loc2.#1 = loc.#1 loc2.#2 > loc.#2};
-- Generate, from general ordering constraints, input arcs for a PN transition
-- related to a set of SD locations
private genOrdInputs(mapGenOrd : map GeneralOrdering to Place, locs: set of
LifelineLocation) r : map Place to nat ≜
{mapGenOrd(i) 1 | i dom mapGenOrd i.after locs};
-- Generate, from general ordering constraints, outpur arcs for a PN transition
-- related to a set of SD locations
private genOrdOutputs(mapGenOrd : map GeneralOrdering to Place, locs: set of
LifelineLocation) r : map Place to nat ≜
{mapGenOrd(i) 1 | i dom mapGenOrd i.before locs};
-- Generates transitions for a message, considering pre-computed
-- place generator mappings
private msg2trans(m: Message, mapping: map LifelineLocation to (Place Place),
mapMsg: map Message to Place, mapGenOrd : map GeneralOrdering to Place) r : set
of Transition ≜ cases m.messageSort:
<synchCall>, <createMessage> ->
let r dom mapMsg be st
r.messageSort = <reply> r.groupId = m.groupId
in {mkTransition(
{placeBefore(m.source,mapping) 1,
placeBefore(m.target,mapping) 1}
munion genOrdInputs(mapGenOrd, {m.source, m.target}),
mk_Label(mk_Event(<Call>, m), nil),
{placeAfter(m.source,mapping) 1,
placeAfter(m.target,mapping) 1,
mapMsg(r) OccurrenceGroup}
munion genOrdOutputs(mapGenOrd, {m.source, m.target}),
{})
},
<reply> ->
{mkTransition(
{placeBefore(m.source,mapping) 1,
placeBefore(m.target,mapping) 1,
mapMsg(m) OccurrenceGroup }
munion genOrdInputs(mapGenOrd, {m.source, m.target}),
mk_Label(mk_Event(<Reply>, m), nil),
{placeAfter(m.source,mapping) 1,
placeAfter(m.target,mapping) 1}
munion genOrdOutputs(mapGenOrd, {m.source, m.target}),
{})
},
<asynchCall>, <asynchSignal> ->
{mkTransition(
{placeBefore(m.source, mapping) 1}
munion genOrdInputs(mapGenOrd, {m.source}),
mk_Label(mk_Event(<Send>, m), nil),
{placeAfter(m.source, mapping) 1, mapMsg(m) OccurrenceGroup}
UML Checker: Formal Specification in VDM++ 12
munion genOrdOutputs(mapGenOrd, {m.source}),
{}),
mkTransition(
{placeBefore(m.target, mapping) 1,
mapMsg(m) OccurrenceGroup}
munion genOrdInputs(mapGenOrd, {m.target}),
mk_Label(mk_Event(<Receive>, m), nil),
{placeAfter(m.target, mapping) 1}
munion genOrdOutputs(mapGenOrd, {m.target}),
{})}
end;
private getFirstEvents(sd: Interaction, f: CombinedFragment, lf: Lifeline) res
: set of Event ≜ let loc1 = getLoc(f.operands(1).startLocations, lf).#2
in
{ mk_Event(if m.source.#1 = lf then <Send> else <Receive>, m)
| m sd.messages m.messageSort <reply>
(m.source.#1 = lf m.source.#2 > loc1
m.target.#1 = lf m.target.#2 > loc1)};
-- assumes that reply message must be in same fragment of call message
-- Generate transitions for a decision point outcome
private dec2trans(sd: Interaction, f: CombinedFragment, mapLocs: map
LifelineLocation to (Place Place), mapDec: map (CombinedFragment Lifeline
nat) to Place, guard: [ValueSpecification], extraInOut: [(map Place to
[ValueSpecification]) (map Place to [ValueSpecification])], decPointId: nat,
outcomeId: nat, startLocs : set of LifelineLocation, endLocs: set of
LifelineLocation) r: set of Transition ≜
if card f.lifelines = 1 synchronizeLifelinesAtDecisionPoints then {mkTransition(
let m = {placeBefore(loc, mapLocs) 1 | loc startLocs} in if extraInOut = nil then m else m munion extraInOut.#1,
if guard = nil then nil else mk_Label(nil, guard),
let m = {placeAfter(loc, mapLocs) 1 | loc endLocs} in if extraInOut = nil then m else m munion extraInOut.#2,
{})}
else
let decidors = {lf | lf f.lifelines
e getFirstEvents(sd, f, lf) e.type = <Send>}
in
-- as first to decide
{mkTransition(
let m = {placeBefore(getLoc(startLocs, lf), mapLocs) 1}
in if extraInOut=nil then m else m munion extraInOut.#1,
if guard = nil then nil else mk_Label(nil, guard),
(let m = {placeAfter(getLoc(endLocs, lf), mapLocs) 1}
in if extraInOut=nil then m else m munion extraInOut.#2)
munion
{mapDec(mk_(f,lf2,decPointId))outcomeId|lf2 f.lifelines\{lf}}, {mapDec(mk_(f, lf, decPointId))})
| lf decidors}
∪ -- as following others
{mkTransition(
UML Checker: Formal Specification in VDM++ 13
{placeBefore(getLoc(startLocs, lf), mapLocs) 1,
mapDec(mk_(f, lf, decPointId)) outcomeId}, nil,
{placeAfter(getLoc(endLocs, lf), mapLocs) 1},
{})
| lf f.lifelines decidors {lf}};
-- Generates transitions for a combined fragment, considering pre-computed
-- place generator mappings
private cf2trans(sd: Interaction, f: CombinedFragment, mapLocs: map
LifelineLocation to (Place Place), mapDecs: map (CombinedFragment Lifeline
nat) to Place, mapFrag: map CombinedFragment to Place) r: set of Transition
≜ cases f.interactionOperator:
<par> ->
⋃ {{mkTransition(
{placeBefore(getLoc(f.operands(1).startLocations,lf),mapLocs)1},
nil,
{placeAfter(getLoc(o.startLocations, lf), mapLocs) 1 |
o elems f.operands}, {}),
mkTransition(
{placeBefore(getLoc(o.finishLocations, lf), mapLocs) 1 |
o elems f.operands},
nil,
{placeAfter(getLoc(f.operands(len f.operands).finishLocations,lf),
mapLocs) 1}, {})}
| lf f.lifelines},
<seq>, <assert> ->
{mkTransition({placeBefore(loc, mapLocs) 1}, nil,
{placeAfter(loc, mapLocs) 1}, {})
| loc ⋃ {o.startLocations ∪ o.finishLocations |
o elems f.operands}},
<strict> ->
{mkTransition(
{placeBefore(loc, mapLocs) 1 | loc o.startLocations}, nil,
{placeAfter(loc, mapLocs) 1 | loc o.startLocations},{})
| o elems f.operands}
∪ {mkTransition(
{placeBefore(loc, mapLocs) 1 | loc o.finishLocations}, nil,
{placeAfter(loc, mapLocs) 1 | loc o.finishLocations},{})
| o elems f.operands},
<critical> ->
let o = f.operands(1)
in {mkTransition(
{placeBefore(loc, mapLocs) 1 | loc o.startLocations}, nil,
{placeAfter(loc, mapLocs) 1 | loc o.startLocations}, {}),
UML Checker: Formal Specification in VDM++ 14
mk_Transition(
{placeBefore(loc, mapLocs) 1 | loc o.finishLocations}, nil,
{placeAfter(loc, mapLocs) 1 | loc o.finishLocations}, {}, {mapFrag(f)})},
<opt> ->
let o = f.operands(1)
in -- enter fragment
dec2trans(sd, f, mapLocs, mapDecs,
if o.guard = nil then nil else o.guard.specification, nil,
1, 1, o.startLocations, o.startLocations)
∪ -- skip fragment
dec2trans(sd, f, mapLocs, mapDecs,
if o.guard = nil then nil
else mk_Expression(<Neg>, [o.guard.specification]),
nil, 1, 2, o.startLocations, o.finishLocations)
-- exit fragment
∪
{mkTransition({placeBefore(loc, mapLocs) 1}, nil,
{placeAfter(loc, mapLocs) 1}, {})
| loc o.finishLocations},
<neg> ->
let o = f.operands(1)
in -- enter fragment
dec2trans(sd, f, mapLocs, mapDecs, nil, nil,
1, 1, o.startLocations, o.startLocations)
-- in end of frag. accept any event (while waiting for other lifelines)
∪ {mkTransition(
{placeBefore(loc, mapLocs) 1},
mk_Label(mk_Event(<AnyEvent>, nil), nil),
{placeBefore(loc, mapLocs) 1}, {})
| loc o.finishLocations}
-- exit fragment to irrevocable failure state
∪ {mkTransition(
{placeBefore(loc, mapLocs) 1 | loc o.finishLocations},
nil, {mapFrag(f) 1}, {})}
-- skip fragment
∪ dec2trans(sd, f, mapLocs, mapDecs, nil, nil,
1, 2, o.startLocations, o.finishLocations)
-- after skipping, allow any event
∪ {mkTransition(
{placeAfter(loc, mapLocs) 1},
mk_Label(mk_Event(<AnyEvent>, nil), nil),
{placeAfter(loc, mapLocs) 1}, {})
| loc o.finishLocations},
<break> ->
-- same as opt, only different in the destination of 'exit fragment'
let o = f.operands(1),
UML Checker: Formal Specification in VDM++ 15
f2 = container(sd, f),
p2 = if f2 nil f2.interactionOperator = <loop>
then mapFrag(f2) else nil,
locs2 = if f2 = nil then getLastLifelineLocs(sd)
else f2.operands(len f2.operands).finishLocations
in -- enter fragment
dec2trans(sd, f, mapLocs, mapDecs,
if o.guard = nil then nil else o.guard.specification,
if p2 = nil then nil
else mk_({p2 mk_PNVariable("$c")},{}),
1, 1, o.startLocations, o.startLocations)
∪ -- skip fragment
dec2trans(sd, f, mapLocs, mapDecs,
if o.guard = nil then nil
else mk_Expression(<Neg>, [o.guard.specification]),
nil, 1, 2, o.startLocations, o.finishLocations)
-- exit fragment
∪
{mkTransition({placeBefore(loc, mapLocs) 1}, nil,
{placeAfter(getLoc(locs2, loc.#1), mapLocs) 1}, {})
| loc o.finishLocations},
<alt> ->
-- enter alternatives
⋃ {let o = f.operands(i)
in dec2trans(sd, f, mapLocs, mapDecs,
if o.guard = nil then nil
else if o.guard.specification = <else>
then mk_Expression(<Neg>, [mk_Expression(<Or>,
[f.operands(i).guard.specification |
i inds f.operands
f.operands(i).guard nil
f.operands(i).guard.specification <else>])]) else o.guard.specification,
nil, 1, i, f.operands(1).startLocations, o.startLocations)
| i inds f.operands}
∪ -- skip alternatives if none holds
(if op elems f.operands
op.guard = nil op.guard.specification = <else>
then {}
else
dec2trans(sd, f, mapLocs, mapDecs,
mk_Expression(<Neg>, [mk_Expression(<Or>,
[f.operands(i).guard.specification | i inds f.operands
f.operands(i).guard nil])]),
nil, 1, len f.operands + 1,
f.operands(1).startLocations,
f.operands(len f.operands).finishLocations))
∪ -- exit alternatives
⋃ {{mkTransition(
{placeBefore(getLoc(o.finishLocations, lf), mapLocs) 1},
UML Checker: Formal Specification in VDM++ 16
nil,
{placeAfter(getLoc(f.operands(len f.operands).finishLocations,
lf), mapLocs) 1}, {})
| o elems f.operands}
| lf f.lifelines},
<loop> ->
let o = f.operands(1),
c = mk_PNVariable("$c")
in -- enter loop
dec2trans(sd, f, mapLocs, mapDecs,
if o.guard = nil then nil
else mk_Expression(<And>,
[if o.guard.maxint = nil then true
else mk_Expression(<Gt>, [o.guard.maxint, 0]),
mk_Expression(<Or>,
[if o.guard.minint = nil then false
else mk_Expression(<Gt>, [o.guard.minint, 0]),
if o.guard.specification = nil then true
else o.guard.specification])]),
mk_({}, {mapFrag(f) 1}),
1, 1, o.startLocations, o.startLocations)
∪ -- skip loop dec2trans(sd, f, mapLocs, mapDecs,
if o.guard = nil then nil
else mk_Expression(<Or>,
[if o.guard.maxint = nil then false
else mk_Expression(<Eq>, [o.guard.maxint, 0]),
mk_Expression(<And>,
[if o.guard.minint = nil then true
else mk_Expression(<Eq>, [o.guard.minint, 0]),
if o.guard.specification = nil then true
else mk_Expression(<Neg>,[o.guard.specification])])]),
nil,
1, 2, o.startLocations, o.finishLocations)
∪ -- continue loop dec2trans(sd, f, mapLocs, mapDecs,
if o.guard = nil then nil
else mk_Expression(<And>,
[if o.guard.maxint = nil then true
else mk_Expression(<Lt>, [c, o.guard.maxint]),
mk_Expression(<Or>,
[if o.guard.minint = nil then false
else mk_Expression(<Lt>, [c, o.guard.minint]),
if o.guard.specification = nil then true
else o.guard.specification])]),
mk_({mapFrag(f) c},
{mapFrag(f) mk_Expression(<Plus>,[c,1])}),
2, 1, o.finishLocations, o.startLocations)
∪ -- exit loop dec2trans(sd, f, mapLocs, mapDecs,
if o.guard = nil then nil
else mk_Expression(<Or>,
[if o.guard.maxint = nil then false
else mk_Expression(<Gte>, [c, o.guard.maxint]),
mk_Expression(<And>,
[if o.guard.minint = nil then true
UML Checker: Formal Specification in VDM++ 17
else mk_Expression(<Gte>, [c, o.guard.minint]),
if o.guard.specification = nil then true
else mk_Expression(<Neg>,[o.guard.specification])])]),
mk_({mapFrag(f) c}, {}),
2, 2, o.finishLocations, o.finishLocations)
end;
private replace(pn: PetriNet, rep: map Parameter to [ValueSpecification]) r:
PetriNet ≜ let trans2 =
{mkTransition(
{p replace(t.source(p), rep) | p dom t.source},
if t.label = nil then nil
else mk_Label(
if t.label.event = nil then nil
else mk_Event(t.label.event.type,
let m = t.label.event.message
in if m = nil then nil
else mk_Message(m.id, m.groupId, m.source,
m.target, m.messageSort, m.signature,
[replace(m.arguments(i), rep) |
i inds m.arguments])),
if t.label.guard = nil then nil else replace(t.label.guard, rep)),
{p replace(t.target(p), rep) | p dom t.target},
t.inhibitor) | t pn.transitions}
in mk_PetriNet(pn.places, pn.starts, pn.finish, trans2);
-- Generate PN and transitions for an interaction use
private ref2trans(sd: Interaction, f: InteractionUse, mapLocs: map
LifelineLocation to (Place Place)) r: (set of Transition) PetriNet ≜ let refd = replace(sd2pn(f.refersTo),
{f.refersTo.parameters(i) f.arguments(i) |
i inds f.refersTo.parameters}),
trans = {mkTransition(
{placeBefore(loc, mapLocs) 1 | loc f.locations},
nil, {refd.starts 1},{}),
mkTransition({p 1 | p refd.finish}, nil,
{placeAfter(loc, mapLocs) 1 | loc f.locations},
{})}
in mk_(trans, refd);
private considerIgnore2trans(sd: Interaction, f: ConsiderIgnoreFragment,
mapLocs: map LifelineLocation to (Place Place)) r : set of Transition ≜ let etype = if f.interactionOperator = <ignore> then <AnySignatureInSet>
else <AnySignatureNotInSet>,
tlabel = mk_Label(mk_Event(etype, f.messages), nil)
in ⋃ { let p1 = placeBefore(loc, mapLocs), p2 = placeAfter(loc, mapLocs)
in {mkTransition({p1 1}, tlabel, {p1 1}, {}),
mkTransition({p2 1}, tlabel, {p2 1}, {})}
| loc dom mapLocs
contains(f, loc)
f2 sd.considerIgnoreFragments
UML Checker: Formal Specification in VDM++ 18
f2 f contains(f2, loc) contains(f, f2)};
-- Update transition because of critical places
private updateTransCrit(t: Transition, sd: Interaction, mapLocs: map
LifelineLocation to (Place Place), mapFrag : map CombinedFragment to Place)
r: Transition ≜
if t.label = nil t.label.event = nil then t
else let tlocs = {loc | loc dom mapLocs
mapLocs(loc).#1 dom t.source
mapLocs(loc).#2 dom t.target},
inhib = {mapFrag(crit) | crit dom mapFrag
crit.interactionOperator = <critical>
loc tlocs
loc.#1 crit.lifelines
let par = container(sd, crit)
in loc tlocs
contains(par, loc)
contains(crit, loc)},
setCrit = {mapFrag(crit) 1 | crit dom mapFrag crit.interactionOperator = <critical>
loc tlocs
loc.#1 crit.lifelines
loc tlocs contains(crit, loc)}
in mkTransition(t.source, t.label, t.target munion setCrit, t.inhibitor
∪ inhib);
-- Generating all transitions
private generateTransitions(sd: Interaction, starts: Place, finishes: Place,
mapLocs: map LifelineLocation to (Place Place), mapMsgs: map Message to Place,
mapDec : map (CombinedFragment Lifeline nat) to Place, mapGenOrd: map
GeneralOrdering to Place, mapFrag: map CombinedFragment to Place) r : (set of
Transition) (set of PetriNet)≜
let ref2transRes = {ref2trans(sd, f, mapLocs) | f sd.interactionUses},
trans =
{mkTransition({starts 1}, nil,
{p 1 | p getFirstLifelinePlaces(mapLocs)}, {}),
mkTransition({p 1 | p getLastLifelinePlaces(mapLocs)},
nil, {finishes 1}, {})}
∪ ⋃ {cf2trans(sd, f, mapLocs, mapDec, mapFrag) |
f sd.combinedFragments}
∪ ⋃ {considerIgnore2trans(sd, f, mapLocs) |
f sd.considerIgnoreFragments}
∪ ⋃ {trans | mk_(trans, refdpn) ref2transRes}
∪ ⋃ {msg2trans(m,mapLocs, mapMsgs, mapGenOrd) |
m sd.messages},
trans2 = if f dom mapFrag
f.interactionOperator = <critical>
then trans
else {updateTransCrit(t, sd, mapLocs, mapFrag) | t trans}
in mk_(trans2, {refdpn | mk_(trans, refdpn) ref2transRes});
UML Checker: Formal Specification in VDM++ 19
5.4 Main conversion logic -- Converts a sequence diagram (Interaction) to a PetriNet
public sd2pn(sd : Interaction) r: PetriNet ≜ let mk_(places, s0, finishes, mapping, mapMsg, mapDec, mapGenOrd, mapFrag)
= generatePlaces(sd),
mk_(trans, refdpn) = generateTransitions(sd, s0, finishes, mapping,
mapMsg, mapDec, mapGenOrd, mapFrag)
in mk_PetriNet(places ∪ ⋃ {r.places | r refdpn}, s0, {finishes},
trans ∪ ⋃ {r.transitions | r refdpn});
6 Petri Net Execution
6.1 Definition of execution traces types
public Trace = seq of Occurrence;
public Occurrence ::
id : nat
groupId : nat --identifies pairs of call/reply or send/receive occurrences
target : OccurrenceTarget
type : EventType
signature: MessageSignature
arguments: seq of Value; -- return value, in case of Reply
public OccurrenceTarget = Instance | Classifier;
public Classifier ::
className : string;
6.2 Definition of run states public RunState :: marking : Marking
bindings : Bindings
covers : set of nat;
public PNToken = Value;
public Marking = map Place to (nat | seq of PNToken);
public ConformanceMode = <LooseConf> | <StrictConf>;
values
private OccurrenceGroup = mk_PNVariable("$g");
UML Checker: Formal Specification in VDM++ 20
6.3 Matching and binding values and messages functions
-- Recursively matches a sequence of values against a sequence of 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);
-- Similar to above, but receives a set of pairs of actual and expected values.
private matchAndBindPairs(pairs: set of ([Value] [ValueSpecification]), b:
Bindings) r : bool Bindings ≜ if pairs = {}
then mk_(true, b)
else let pair pairs
in let mk_(r1, b1) = matchAndBindVal(pair.#1, pair.#2, b)
in if r1
then matchAndBindPairs(pairs \ {pair}, b1)
else mk_(r1, b1);
-- 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 pattern, and returns a tuple with a
-- success flag and new bindings.
private matchAndBind(o: Occurrence, e: Event, b: Bindings) r: bool Bindings
≜ cases e.type:
<AnyEvent> -> mk_(true, b),
<AnySignatureInSet> -> mk_(o.signature e.message, b),
<AnySignatureNotInSet> -> mk_(o.signature e.message, b),
others -> let m = e.message
in if e.type = o.type 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)
end;
UML Checker: Formal Specification in VDM++ 21
6.4 Operations on markings -- Checks if a place contains a token in a given marking
private containsToken(m: Marking, p: Place, tok: PNToken) res: bool ≜
if p.type = <FIFOPlace> p.type = <LIFOPlace>
then tok elems m(p)
else tok {1, ..., m(p)};
-- Checks if a marking contains a token
private containsToken(m: Marking, tok: PNToken) res: bool ≜
p dom m containsToken(m, p, tok);
-- Gets the next token to come out from a non empty place p in a marking m
private getToken(m: Marking, p: Place) res : PNToken ≜
if p.type = <FIFOPlace> p.type = <LIFOPlace> then hd m(p) else 1;
-- Gets the number of tokens in a marking m for a place p
private getNumTokens(m: Marking, p: Place) res : nat ≜
if p.type = <FIFOPlace> p.type = <LIFOPlace> then len m(p) else m(p);
-- Remove from a marking one token per place in a set
private markingRmv(m: Marking, s: set of Place) r : Marking ≜
{p if p s
then if p.type = <FIFOPlace> p.type = <LIFOPlace> then tl m(p)
else m(p)-1
else m(p)
| p dom m getNumTokens(m, p) > 1 p s};
-- Reset a place (make empty) in a marking, returns the new marking
private markingReset(m: Marking, s: set of Place) r : Marking ≜ s <-: m;
-- Add to a marking one token per output place of a transition
-- returns the new marking.
private markingAdd(m: Marking, a: map Place to ValueSpecification, b: Bindings)
r : Marking ≜
{p if p dom a then if p.type = <FIFOPlace>
then if p dom m then m(p) ↷ [eval(a(p),b)]
else [eval(a(p), b)]
else if p.type = <LIFOPlace>
then if p dom m then [eval(a(p),b)] ↷ m(p)
else [eval(a(p), b)]
else if p dom m then m(p) + 1 else 1
else m(p)
| p dom m ∪ dom a};
6.5 Operations on transitions -- Gets temporary bindings of variables to actual values for a transition.
-- Variables may appear in input arcs; there is also
-- a built-in variable for the occurrence group id.
-- Returns a success flag (no inconsistent bindings) and the bindings.
UML Checker: Formal Specification in VDM++ 22
-- Matches andbinds the tokens extracted from the input places -- againts the value specfications contained in the arcs.
private getTmpBindings(t: Transition, r: RunState, o: [Occurrence]) res : (bool
Bindings) ≜ -- first performs the built-in binding, and then the input arcs
let b = if o = nil then {}
else {OccurrenceGroup mk_OccurrenceGroupId(o.groupId)}
in matchAndBindPairs(
{mk_(getToken(r.marking, s), t.source(s)) | s dom t.source}, b);
-- Check if a transition guard holds, considering current bindings of
-- variables to actual values.
private guardHolds(t: Transition, b: Bindings) r: bool ≜
t.label = nil t.label.guard = nil eval(t.label.guard, b) = true;
-- Checks if a Transition is enabled in a RunState
private isEnabled(t: Transition, r: RunState, o: [Occurrence]) res: bool ≜
dom t.source ⊆ dom r.marking
t.inhibitor ∩ dom r.marking = {}
if o = nil
then if t.label nil t.label.event nil
then false
else let mk_(res, b) = getTmpBindings(t, r, o)
in if res = false then false
else guardHolds(t, r.bindings ++ b)
else if t.label = nil t.label.event = nil
(t.label.event.type {<AnyEvent> , <AnySignatureInSet>, <AnySignatureNotInSet>}
t.label.event.type o.type)
then false
else let mk_(res, b) = getTmpBindings(t, r, o)
in if res = false then false
else let b2 = r.bindings ++ b
in guardHolds(t, b2)
matchAndBind(o, t.label.event, b2).#1;
-- Performs an automatic (o = nil) or event-driven (o nil) Transition
-- from a given RunState and returns the new RunState
private fire(t: Transition, r: RunState, o: [Occurrence]) res: RunState ≜ let tmpB = getTmpBindings(t, r, o).#2,
b1 = r.bindings ++ tmpB,
b2 = if o = nil then b1
else matchAndBind(o, t.label.event, b1).#2,
newM = markingReset(markingAdd(markingRmv(r.marking, dom t.source),
t.target, b2), t.resetArcs),
newCov = if o = nil t.label.event.type
{<AnySignatureInSet>, <AnySignatureNotInSet>, <AnyEvent>}
then r.covers
else r.covers ∪ {t.label.event.message.id} in mk_RunState(newM, dom tmpB <-: b2, newCov);
6.6 Main execution logic
UML Checker: Formal Specification in VDM++ 23
-- Decides if an event occurrence may be considered in a RunState
private mayConsider(r: RunState, o: Occurrence, mode: ConformanceMode) res:
bool ≜ if mode = <StrictConf> then true
else ((o.type = <Reply> o.type = <Receive>)
containsToken(r.marking, mk_OccurrenceGroupId(o.groupId)));
-- Decides if an occurrence may be ignored in a RunState
private mayIgnore(r: RunState, o: Occurrence, mode : ConformanceMode) res: bool
≜ if mode = <StrictConf> then false
else ((o.type=<Reply> o.type =<Receive>)
containsToken(r.marking, mk_OccurrenceGroupId(o.groupId)));
-- Recursively computes the transitive closure of a set of run states rr
-- by firing automatic transitions. Uses an accumulator of already explored
-- run states to avoid infinite loops.
private getAutoFireClosure(pn: PetriNet, rr: set of RunState, explored: set of
RunState) r: set of RunState ≜ if rr = explored then rr
else getAutoFireClosure(pn,
rr ∪ ⋃{{fire(t, r, nil) | t pn.transitions isEnabled(t, r, nil)}
| r rr \ explored},
rr ∪ explored);
-- Determines if a run state of a Petri net is a quiescent state
private isQuiescent(pn: PetriNet, r: RunState) res: bool ≜
t pn.transitions isEnabled(t, r, nil);
-- Advances run states following automatic transitions until quiescent
-- states are reached.
private auto(pn: PetriNet, rr: set of RunState) res: set of RunState ≜
{r | r getAutoFireClosure(pn, rr, {}) isQuiescent(pn, r)};
-- Performs a PetriNet step, i.e., processes a single input occurrence,
-- departing from a set of run states, and returns the new set of run states.
private step(pn: PetriNet, o: Occurrence, rr: set of RunState, mode:
ConformanceMode) r: set of RunState ≜
let newRR = ⋃ {{fire(t, r, o) | t pn.transitions isEnabled(t, r, o)}
| r rr mayConsider(r, o, mode)}
in auto(pn, newRR) ∪ {r | r rr mayIgnore(r, o, mode)};
-- Generates the initial set of run states for a PetriNet
private startPN(pn: PetriNet, params: Bindings) r: set of RunState ≜
auto(pn, {mk_RunState({pn.starts 1}, params, {})});
-- Checks if a set of (final) run states represents acceptance
private succeeded(pn: PetriNet, runStates: set of RunState) r: bool ≜
failed(pn, runStates) r runStates p dom r.marking p pn.finish;
-- Checks if a set of (intermediate final) run states represents failure
private failed(pn: PetriNet, runStates: set of RunState) r: bool ≜
runStates = {} (r runStates p dom r.marking p.type = <FailurePlace>);
-- Recursively checks if a PN accepts an execution trace in a given
-- conformance mode, departing from a given set of initial run states.
UML Checker: Formal Specification in VDM++ 24
-- Returns the final set of run states.
private steps(pn: PetriNet, trace: Trace, mode: ConformanceMode, runStates: set
of RunState) r : set of RunState ≜ if failed(pn, runStates) then runStates
else if trace = [] then runStates
else steps(pn, tl trace, mode, step(pn, hd trace, runStates, mode));
-- Checks if a PN accepts an execution trace in a given conformance mode
public accepts(pn: PetriNet, trace: Trace, params: Bindings, mode:
ConformanceMode) r: bool ≜ succeeded(pn, steps(pn, trace, mode, startPN(pn, params)));
-- Gets the run state with a maximal set of covered messages
public getBestMatch(pn: PetriNet, runStates: set of RunState) r: [RunState] ≜ if runStates = {} then nil
else let r1 runStates be st r2 runStates card r2.covers > card r1.covers
in r1;
-- Recursively checks if a PN accepts an execution trace in a given
-- conformance mode, departing from a given set of initial run states
-- and an initial bestMatch (a run state with maximal coverage).
-- Returns a pair with the final set of run states and the best match.
private stepsWithInfo(pn: PetriNet, trace: Trace, mode: ConformanceMode,
runStates: set of RunState, bestMatch: [RunState]) r : (set of RunState)
[RunState] ≜
if failed(pn, runStates) trace = [] then mk_(runStates, bestMatch)
else let newStates = step(pn, hd trace, runStates, mode),
newMatch = if bestMatch = nil
then getBestMatch(pn, newStates)
else getBestMatch(pn, {bestMatch} ∪ newStates) in stepsWithInfo(pn, tl trace, mode, newStates, newMatch);
-- Checks if a PN accepts an execution trace in a given conformance mode
-- and initial bindings, and returns a tuple with a success flag and
-- a maximal set of messages covered
public acceptsWithInfo(pn: PetriNet, trace: Trace, params: Bindings, mode:
ConformanceMode) res: bool (set of MessageId) ≜ let initRunStates = startPN(pn, params),
initBestMatch = getBestMatch(pn, initRunStates),
mk_(runStates, bestMatch) = stepsWithInfo(pn, trace, mode,
initRunStates, initBestMatch),
bestCover = if bestMatch = nil then {} else bestMatch.covers,
f = {r | r runStates p dom r.marking p.type = <FailurePlace>},
a = {r | r runStates dom r.marking ∩ pn.finish {}}
in if f {} then let r f in mk_(false, r.covers)
else if a = {} then mk_(false, bestCover)
else mk_(true, getBestMatch(pn, a).covers);
7 Test Cases
7.1 Convenience definitions operations
-- convenience operation for writing test cases (pre-condition checking
UML Checker: Formal Specification in VDM++ 25
-- must be enabled)
private Assert : bool = ()
Assert(a) ≜ return pre a;
7.2 Simple asynchronous message
operations
public testTrivial()≜ (
let l1 = mk_Lifeline("L1", nil),
l2 = mk_Lifeline("L2", nil),
m1c = mk_Message(1, 1, mk_(l1,1), mk_(l2,1), <synchCall>, "m1", []),
m1r = mk_Message(2, 1, mk_(l2,2), mk_(l1,2), <reply>, "m1", []),
sd1 = mk_Interaction({l1,l2 }, {m1c, m1r}, {}, {}, {}, {}, []),
pn1 = sd2pn(sd1),
c1 = mk_Classifier("L1"),
c2 = mk_Classifier("L2"),
sm1 = mk_Occurrence(1, 1, c2, <Call>, "m1", []),
rm1 = mk_Occurrence(2, 1, c2, <Reply>, "m1", [])
in
(
Assert(acceptsWithInfo(pn1, [], { }, <StrictConf>) = mk_(false, {}));
Assert(acceptsWithInfo(pn1, [sm1, rm1], { }, <StrictConf>) = mk_(true,
{1,2}));
)
);
7.3 Weak sequencing
The next example is adapted from Fig. 5 of our ICTSS paper. It illustrates weak sequencing, i.e., implicit
parallelism between lifelines, as well as the overall conformance checking process. It was updated to use Petri Nets
instead of Parallel Finite automata.
UML Checker: Formal Specification in VDM++ 26
sd Dynamic View
Client
o1 :C1 o2 :C2
opt
m0()
m1()
m3()
m2()
2
3
4
5
6
8
11
9
14
15
16
17
18
2
3
4
5
6
7
13
12
11
14
15
16
17
18
m0
Start
m1
m3
ret m3
m2
ret m2
ret m1
ret m0
12
1
Step 1: Generate states Step 2: Generate transitions
1
7
10
13
8
10
9
19
19
22
$g
$g
20
$g
$g
23
$g
$g
21
$g
$g
Step 3: Simplify (optional)
7
12
11
15
17
m0
Start
m1
m3
ret m3
m2
ret m2
ret m1
ret m0
1
19
22
$g
$g
20
$g
$g
23
$g
$g
21
$g
$g
6
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++ 27
operations
public testFig5Accept()≜ (
Assert( accepts(sd2pn(sdFig5), trace1, {}, <StrictConf>) )
);
7.4 ATM example with synchronous messages, 'alt' and 'par'
The next example was adapted from Fig.3 of our ICTSS 2013 paper. Numbers in red were added to identify lifeline
locations.
1
3
45
6
7 (ret)8
9
1011
13
12
14 (ret)15
1617
18
19
2021
2 (ret)
values
private p1 : Parameter = mk_Parameter("balance", <InParam>);
private p2 : Parameter = mk_Parameter("amount", <InParam>);
public sdFig3 : Interaction =
let l1 = mk_Lifeline("Client", nil),
l2 = mk_Lifeline("Account", "a"),
l3 = mk_Lifeline("Movement", "m"),
l4 = mk_Lifeline("Movement", "n"),
o11 = mk_InteractionOperand(mk_InteractionConstraint(nil, nil,
mk_Expression(<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)}),
UML Checker: Formal Specification in VDM++ 28
o12 = mk_InteractionOperand(mk_InteractionConstraint(nil, nil,<else>),
{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_Expression(<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_Expression(<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]);
Next, the resulting Petri Net (partially simplified). Underlined events are to be generated by the test harness.
UML Checker: Formal Specification in VDM++ 29
14
4
2
1
1: Account(balance)
3
1’:returna
2: a.withdraw(amount)
3: a.setBalance( balance-amount)
5
3’:return
12
4: Movement(a, amount,“withdraw”)
4’:returnm
16
15
6: a.withdraw(
amount)
6’:return“INSUF_BALANCE”2’:return“Ok”
6
9
7 13
[! (amount<=balance)][amount <= balance]
10
5’:returnn
5: Movement(a, -amount)
8
11
$g
$g
$g
$g
$g
$g
$g
$g $g
$g
17
$g
$x
The next execution trace corresponds to an execution trace adapted from Fig. 7 of our ICTSS 2013 paper.
1: Account(100)
1’:returna1
2: a1.withdraw(50)
3: a1.getBalance()
3’:return100
4: Movement(a1,
50, “withdraw”)
4’:return m1
5: a1.setBalance(50)
5’:return
2’:return“Ok”
≺M, L, C≻ = ≺{1↦1}, {}, {}≻
≺{2↦[1]}, {}, {1}≻
≺{4↦1}, {a↦a1},{1,1’}≻
≺{5↦1,17↦[2],11↦1}, {a↦a1}, {1,1’,2}≻
≺{5↦1,17↦[2],12↦[4]}, {a↦a1}, {1,1’,2,4}≻
≺{5↦1,17↦[2],13↦1},{a↦a1,m↦m1},{1,1’,2,4,4’}≻
≺{6↦[5],17↦[2],13↦1},{a↦a1,m↦m1},{1,1’,2,4,4’,3}≻
≺{7↦1,17↦[2],13↦1},{a↦a1,m↦m1},1,1’,2,4,4’,3,3’}≻
≺{16↦1}, {a↦a1,m↦m1},{1,1’,2,4,4’,3,3’,2’}≻
(unchanged, call ignored)
(unchanged, reply ignored)
Execution Trace EPN Run States(s)
P={balance ↦ 100, amount ↦ 50}
≺{5↦1,17↦[2],9↦1},{a↦a1},{1,1’,2}≻
(unchanged, call ignored)
(unchanged, reply ignored)
(unchanged, call ignored)
(unchanged, reply ignored)
≺{6↦[5],17↦[2],9↦1},{a↦a1},{1,1’,2,3}≻
≺{7↦1,17↦[2],9↦1},{a↦a1},{1,1’,2,3,3’}≻
must consider 2’, but active states don’t accept it
values
UML Checker: Formal Specification in VDM++ 30
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( acceptsWithInfo(sd2pn(sdFig3), traceFig7, {p1 100, p2 50},
<LooseConf>) = mk_(true, {1,2, 3, 4, 5, 6, 7, 8}))
);
7.5 Asynchronous user interaction
This example refers to Fig. 9 in our ICTSS 2013 paper.
sd UserInteractionTesting
Console
Simulator
Output
Blocking
Queue
Test
Driver
(thread 0)
Input
Blocking
Queue
Tracing
Aspect
AUT
(thread 1)
(wait)
(wait)
start(app, args)main(args)
scan()poll(timeout)
enter(x)put(x)
:x :x
check()poll(timeout)
print(y)put(y)
:y
:y
assertEquals(exp, y)
stop() join(timeout)
send-
display
send-
startrcv-
start
rcv-
enter
send-
enter
rcv-
display
sd InteractionTestingSpec
User
AUT
start(args)
enter(x)
display(y)
Implementation & Tracing
send-start(args)
rcv-start(args)
rcv-enter(x)
send-enter(x)
send-display(y)
rcv-display(y)
Automaton
possibly
internal
interactinos
here
SD
operations
public testAsynchUI() ≜
UML Checker: Formal Specification in VDM++ 31
(
let
x = mk_Parameter("x", <InParam>),
y = mk_Parameter("y", <InParam>),
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]),
pn1 = sd2pn(mk_Interaction({User, AUT}, {m1, m2, m3}, {}, {}, {}, {}, [x,
y])),
U = mk_Classifier("User"),
A = mk_Classifier("AUT"),
o1 = mk_Occurrence(1, 1, A, <Send>, "start", []),
o2 = mk_Occurrence(2, 1, A, <Receive>, "start", []),
o3 = mk_Occurrence(3, 2, A, <Send>, "enter", [1]),
o4 = mk_Occurrence(4, 2, A, <Receive>, "enter", [1]),
o5 = mk_Occurrence(5, 3, U, <Send>, "display", [2]),
o6 = mk_Occurrence(6, 3, U, <Receive>, "display", [2])
in
(
Assert(accepts(pn1, [o1,o2,o3,o4,o5,o6], {x1, y2}, <StrictConf>));
Assert(accepts(pn1, [o1,o3,o2,o4,o5,o6], {x1, y2}, <StrictConf>));
Assert(acceptsWithInfo(pn1, [o1,o2,o3,o5,o4,o6], {x1, y2},
<StrictConf>) = mk_(false, {1, 2}) )
)
);
7.6 Loops with synchronous messages
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", []),
pn1 = sd2pn(mk_Interaction({l1,l2}, {m0C, m0R},{f1}, {}, {}, {}, [])),
UML Checker: Formal Specification in VDM++ 32
pn2 = sd2pn(mk_Interaction({l1,l2}, {m0C, m0R},{f2}, {}, {}, {}, [])),
pn3 = sd2pn(mk_Interaction({l1,l2}, {m0C, m0R},{f3}, {}, {}, {}, [])),
pn4 = sd2pn(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( accepts(pn1, [], {}, <StrictConf>) );
Assert(accepts(pn1, [oc1, oc2], {}, <StrictConf>) );
Assert(accepts(pn1, [oc1, oc2, oc3, oc4], {}, <StrictConf>) );
Assert( accepts(pn1, [oc1, oc2, oc3, oc4, oc5, oc6], {}, <StrictConf>));
Assert(accepts(pn1, [oc1, oc2, oc3, oc4, oc5, oc6], {}, <LooseConf>));
-- loop(nil,2)
Assert(accepts(pn2, [], {}, <StrictConf>) );
Assert(accepts(pn2, [oc1, oc2], {}, <StrictConf>) );
Assert(accepts(pn2, [oc1, oc2, oc3, oc4], {}, <StrictConf>) );
Assert( accepts(pn2, [oc1, oc2, oc3, oc4, oc5, oc6], {}, <StrictConf>));
Assert(accepts(pn2, [oc1, oc2, oc3, oc4, oc5, oc6], {}, <LooseConf>));
-- loop(1,nil)
Assert( accepts(pn3, [], {}, <StrictConf>) );
Assert(accepts(pn3, [oc1, oc2], {}, <StrictConf>) );
Assert(accepts(pn3, [oc1, oc2, oc3, oc4], {}, <StrictConf>) );
Assert(accepts(pn3, [oc1, oc2, oc3, oc4, oc5, oc6], {}, <StrictConf>));
-- loop(nil,nil)
Assert(accepts(pn4, [], {}, <StrictConf>) );
Assert(accepts(pn4, [oc1, oc2], {}, <StrictConf>) );
Assert(accepts(pn4, [oc1, oc2, oc3, oc4], {}, <StrictConf>) );
Assert(accepts(pn4, [oc1, oc2, oc3, oc4, oc5, oc6], {}, <StrictConf>))
)
);
7.7 Loops with asynchronous messages
The next figure shows an example that illustrates the need for asynchronous coordination of decisions.
UML Checker: Formal Specification in VDM++ 33
loop m2
L1 L2
send m1 rcv m1$g $g
L1 L2
m1
m3
1 1
send m2 rcv m2$g
22
send m3 rcv m3$g $g
2 2
1 1
sd pn
valid trace
start
L1 L2invalid trace
$g
operations
public testLoopAsynch() ≜ (
let l1 = mk_Lifeline("C1", nil),
l2 = mk_Lifeline("C2", nil),
o1 = mk_InteractionOperand( nil,
{mk_(l1, 2), mk_(l2, 2)}, {mk_(l1, 4), mk_(l2, 4)}),
f1 = mk_CombinedFragment(1, <loop>, [o1], {l1, l2 }),
m1 = mk_Message(1, 1, mk_(l1, 1), mk_(l2, 1), <asynchSignal>, "m1", []),
m2 = mk_Message(2, 2, mk_(l1, 3), mk_(l2, 3), <asynchSignal>, "m2", []),
m3 = mk_Message(3, 3, mk_(l1, 5), mk_(l2, 5), <asynchSignal>, "m3", []),
pn1 = sd2pn(mk_Interaction({l1,l2}, {m1, m2, m3}, {f1}, {}, {}, {}, [])),
c1 = mk_Classifier("C1"),
c2 = mk_Classifier("C2"),
oc1 = mk_Occurrence(1, 1, c2, <Send>, "m1", []),
oc2 = mk_Occurrence(2, 1, c2, <Receive>, "m1", []),
oc3 = mk_Occurrence(3, 2, c2, <Send>, "m2", []),
oc4 = mk_Occurrence(4, 2, c2, <Receive>, "m2", []),
oc5 = mk_Occurrence(5, 3, c2, <Send>, "m2", []),
oc6 = mk_Occurrence(6, 3, c2, <Receive>, "m2", []),
oc7 = mk_Occurrence(7, 4, c2, <Send>, "m3", []),
oc8 = mk_Occurrence(8, 4, c2, <Receive>, "m3", [])
in
(
Assert(accepts(pn1, [oc1, oc2, oc3, oc4, oc5, oc6, oc7, oc8], {},
<StrictConf>));
Assert( synchronizeLifelinesAtDecisionPoints
accepts(pn1, [oc1, oc3, oc2, oc5, oc4, oc7, oc6, oc8], {},
<StrictConf>));
UML Checker: Formal Specification in VDM++ 34
Assert( synchronizeLifelinesAtDecisionPoints
accepts(pn1, [oc1, oc3, oc5, oc7, oc2, oc4, oc6, oc8], {},
<StrictConf>));
Assert( accepts(pn1, [oc1, oc2, oc3, oc6, oc5, oc4, oc7, oc8], {},
<StrictConf>))
)
);
7.8 Break
break m2
L1 L2
SD
m1
m3
loop
m4
Valid trace
L1 L2
Invalid trace
L1 L2
operations
public testBreak() ≜ (
let l1 = mk_Lifeline("L1", nil),
l2 = mk_Lifeline("L2", nil),
o1 = mk_InteractionOperand(nil,
{mk_(l1, 1), mk_(l2, 1)}, {mk_(l1, 7), mk_(l2, 7)}),
f1 = mk_CombinedFragment(1, <loop>, [o1], {l1, l2}),
o2 = mk_InteractionOperand(nil,
{mk_(l1, 3), mk_(l2, 3)}, {mk_(l1, 5), mk_(l2, 5)}),
f2 = mk_CombinedFragment(2, <break>, [o2], {l1, l2}),
m1 = mk_Message(1, 1, mk_(l1, 2), mk_(l2, 2), <asynchSignal>, "m1", []),
m2 = mk_Message(2, 2, mk_(l1, 4), mk_(l2, 4), <asynchSignal>, "m2", []),
m3 = mk_Message(3, 3, mk_(l1, 6), mk_(l2, 6), <asynchSignal>, "m3", []),
m4 = mk_Message(4, 4, mk_(l1, 8), mk_(l2, 8), <asynchSignal>, "m4", []),
pn1 = sd2pn(mk_Interaction({l1,l2}, {m1, m2, m3, m4}, {f1, f2}, {}, {},
{}, [])),
c1 = mk_Classifier("L1"),
c2 = mk_Classifier("L2"),
sm1a = mk_Occurrence(1, 1, c2, <Send>, "m1", []),
rm1a = mk_Occurrence(2, 1, c2, <Receive>, "m1", []),
sm1b = mk_Occurrence(3, 2, c2, <Send>, "m1", []),
rm1b = mk_Occurrence(4, 2, c2, <Receive>, "m1", []),
UML Checker: Formal Specification in VDM++ 35
sm2a = mk_Occurrence(5, 3, c2, <Send>, "m2", []),
rm2a = mk_Occurrence(6, 3, c2, <Receive>, "m2", []),
sm3a = mk_Occurrence(7, 4, c2, <Send>, "m3", []),
rm3a = mk_Occurrence(8, 4, c2, <Receive>, "m3", []),
sm3b = mk_Occurrence(9, 5, c2, <Send>, "m3", []),
rm3b = mk_Occurrence(10, 5, c2, <Receive>, "m3", []),
sm4a = mk_Occurrence(11, 6, c2, <Send>, "m4", []),
rm4a = mk_Occurrence(12, 6, c2, <Receive>, "m4", [])
in
(
Assert(accepts(pn1, [sm1a, sm3a, rm1a, sm1b, rm3a, sm2a, rm1b, sm4a, rm2a,
rm4a], {}, <StrictConf>));
Assert( accepts(pn1, [sm1a, sm2a, rm1a, sm1b, rm2a, sm3a, rm1b, sm4a, rm3a,
rm4a], {}, <StrictConf>));
)
);
7.9 General ordering
L1 L2
SD
m1
L3 L4
m2
L1 L2
Valid trace
L3 L4
L1 L2
Invalid trace
L3 L4
operations
public testGeneralOrdering() ≜
UML Checker: Formal Specification in VDM++ 36
(
let l1 = mk_Lifeline("L1", nil),
l2 = mk_Lifeline("L2", nil),
l3 = mk_Lifeline("L3", nil),
l4 = mk_Lifeline("L4", nil),
m1 = mk_Message(1, 1, mk_(l1, 1), mk_(l2, 1), <asynchSignal>, "m1", []),
m2 = mk_Message(2, 2, mk_(l3, 2), mk_(l4, 2), <asynchSignal>, "m2", []),
g1 = mk_GeneralOrdering(mk_(l1, 1), mk_(l3, 2)),
pn1 = sd2pn(mk_Interaction({l1,l2,l3,l4}, {m1, m2 }, {}, {g1}, {}, {},
[])),
pn2 = sd2pn(mk_Interaction({l1,l2,l3,l4}, {m1, m2 }, {}, { }, {}, {}, [])),
c1 = mk_Classifier("L1"),
c2 = mk_Classifier("L2"),
c3 = mk_Classifier("L3"),
c4 = mk_Classifier("L4"),
oc1 = mk_Occurrence(1, 1, c2, <Send>, "m1", []),
oc2 = mk_Occurrence(2, 1, c2, <Receive>, "m1", []),
oc3 = mk_Occurrence(3, 2, c2, <Send>, "m2", []),
oc4 = mk_Occurrence(4, 2, c2, <Receive>, "m2", [])
in
(
Assert(accepts(pn1, [oc1, oc3, oc4, oc2], {}, <StrictConf>));
Assert( accepts(pn1, [oc3, oc1, oc2, oc4], {}, <StrictConf>));
Assert(accepts(pn2, [oc3, oc1, oc2, oc4], {}, <StrictConf>));
)
);
7.10 Co-region
L1 L2
SD
m1
m2
L1 L2
Valid trace
operations
public testCoregion() ≜ (
let l1 = mk_Lifeline("L1", nil),
l2 = mk_Lifeline("L2", nil),
o1 = mk_InteractionOperand(nil, {mk_(l2, 1)}, {mk_(l2, 3)}),
o2 = mk_InteractionOperand(nil, {mk_(l2, 3)}, {mk_(l2, 5)}),
f1 = mk_CombinedFragment(1, <par>, [o1, o2], { l2}),
m1 = mk_Message(1, 1, mk_(l1, 2), mk_(l2, 2), <asynchSignal>, "m1", []),
m2 = mk_Message(2, 2, mk_(l1, 4), mk_(l2, 4), <asynchSignal>, "m2", []),
pn1 = sd2pn(mk_Interaction({l1,l2}, {m1, m2 }, {f1 }, {}, {}, {}, [])),
c1 = mk_Classifier("L1"),
UML Checker: Formal Specification in VDM++ 37
c2 = mk_Classifier("L2"),
sm1 = mk_Occurrence(1, 1, c2, <Send>, "m1", []),
rm1 = mk_Occurrence(2, 1, c2, <Receive>, "m1", []),
sm2 = mk_Occurrence(3, 2, c2, <Send>, "m2", []),
rm2 = mk_Occurrence(4, 2, c2, <Receive>, "m2", [])
in
(
Assert(accepts(pn1, [sm1, sm2, rm2, rm1], {}, <StrictConf>));
Assert(accepts(pn1, [sm1, sm2, rm1, rm2], {}, <StrictConf>));
Assert( accepts(pn1, [sm2, sm1, rm1, rm2], {}, <StrictConf>))
)
);
7.11 Critical region
operations
public testCriticalRegion()≜ (
let l1 = mk_Lifeline("Emergency", "emergency"),
l2 = mk_Lifeline("Operator", "operator"),
l3 = mk_Lifeline("Caller", "caller"),
l4 = mk_Lifeline("Callee", "callee"),
o1 = mk_InteractionOperand(nil,
{mk_(l1, 1), mk_(l2, 1), mk_(l3, 1), mk_(l4, 1)},
{mk_(l1, 4), mk_(l2, 4), mk_(l3, 4), mk_(l4, 4)}),
o2 = mk_InteractionOperand(nil,
{mk_(l1, 4), mk_(l2, 4), mk_(l3, 4), mk_(l4, 4)},
{mk_(l1, 7), mk_(l2, 7), mk_(l3, 7), mk_(l4, 7)}),
o3 = mk_InteractionOperand(nil,
{mk_(l1, 7), mk_(l2, 7), mk_(l3, 7), mk_(l4, 7)},
UML Checker: Formal Specification in VDM++ 38
{mk_(l1, 12), mk_(l2, 12), mk_(l3, 12), mk_(l4, 12)}),
f1 = mk_CombinedFragment(1, <par>, [o1, o2, o3], {l1, l2, l3, l4}),
o4 = mk_InteractionOperand(nil,
{mk_(l1, 8), mk_(l2, 8)}, {mk_(l1, 11), mk_(l2, 11)}),
f2 = mk_CombinedFragment(2, <critical>, [o4], {l1, l2}),
m1 = mk_Message(1,1,mk_(l3,2),mk_(l2,2),<asynchSignal>,"call",[100]),
m2 = mk_Message(2,2,mk_(l2,3),mk_(l4,3),<asynchSignal>,"call",[100]),
m3 = mk_Message(3,3,mk_(l3,5),mk_(l2,5),<asynchSignal>,"call",[101]),
m4 = mk_Message(4,4,mk_(l2,6),mk_(l4,6),<asynchSignal>,"call",[101]),
m5 = mk_Message(5,5,mk_(l3,9),mk_(l2,9),<asynchSignal>,"call",[911]),
m6 =mk_Message(6,6,mk_(l2,10),mk_(l1,10),<asynchSignal>,"call",[911]),
pn1 = sd2pn(mk_Interaction({l1,l2,l3,l4}, {m1, m2, m3, m4, m5, m6}, {f1,
f2}, {}, {}, {}, [])),
o1 = mk_Instance("Emergency", mk_token(1)),
o2 = mk_Instance("Operator", mk_token(2)),
o3 = mk_Instance("Caller", mk_token(3)),
o4 = mk_Instance("Callee", mk_token(4)),
sm1 = mk_Occurrence(1, 1, o2, <Send>, "call", [100]),
rm1 = mk_Occurrence(2, 1, o2, <Receive>, "call", [100]),
sm2 = mk_Occurrence(3, 2, o4, <Send>, "call", [100]),
rm2 = mk_Occurrence(4, 2, o4, <Receive>, "call", [100]),
sm3 = mk_Occurrence(5, 3, o2, <Send>, "call", [101]),
rm3 = mk_Occurrence(6, 3, o2, <Receive>, "call", [101]),
sm4 = mk_Occurrence(7, 4, o4, <Send>, "call", [101]),
rm4 = mk_Occurrence(8, 4, o4, <Receive>, "call", [101]),
sm5 = mk_Occurrence(9, 5, o2, <Send>, "call", [911]),
rm5 = mk_Occurrence(10, 5, o2, <Receive>, "call", [911]),
sm6 = mk_Occurrence(11, 6, o1, <Send>, "call", [911]),
rm6 = mk_Occurrence(12, 6, o1, <Receive>, "call", [911])
in
(
Assert(accepts(pn1, [sm1, rm1, sm2, rm2, sm5, rm5, sm6, rm6, sm3, rm3, sm4,
rm4], {}, <StrictConf>));
Assert(accepts(pn1, [sm5, rm5, sm6, rm6, sm1, sm3, rm1, rm3, sm4, rm4, sm2,
rm2], {}, <StrictConf>));
Assert(accepts(pn1, [sm1, sm3, rm1, rm3, sm4, rm4, sm2, rm2, sm5, rm5, sm6,
rm6], {}, <StrictConf>));
Assert(accepts(pn1, [sm1, rm1, sm2, rm2, sm3, sm5, rm5, sm6, rm6, rm3, sm4,
rm4], {}, <StrictConf>));
Assert(accepts(pn1, [sm1, rm1, sm3, rm3, sm5, rm5, sm6, rm6, sm4, rm4, sm2,
rm2], {}, <StrictConf>));
Assert(accepts(pn1, [sm1, sm3, rm1, sm5, rm5, sm6, rm6, rm3, sm4, rm4, sm2,
rm2], {}, <StrictConf>));
Assert(accepts(pn1, [sm1, sm3, sm5, rm1, rm5, sm6, rm6, rm3, sm4, rm4, sm2,
rm2], {}, <StrictConf>));
Assert( accepts(pn1, [sm1, sm3, sm5, rm1, rm5, rm3, sm6, rm6, sm4, rm4,
sm2, rm2], {}, <StrictConf>));
)
);
UML Checker: Formal Specification in VDM++ 39
7.12 Interaction use
ref
sd sd1(in x, out y)
sd2(x, y)
L1 L2
m1(x)
:y
L3
sd sd2(in z, out w)
L2 L3
m2(z)
:w
L1 L2
m1(1)
:2
L3
Valid trace
m2(1)
:2
L1 L2
m1(1)
:2
L3
Invalid trace
m2(2)
:1
operations
public testInteractionUse ()≜ (
let l2 = mk_Lifeline("L2", nil),
l3 = mk_Lifeline("L3", nil),
z = mk_Parameter("z", <InParam>),
w = mk_Parameter("w", <OutParam>),
m2c = mk_Message(1, 1, mk_(l2,1), mk_(l3,1), <synchCall>, "m2", [z]),
m2r = mk_Message(2, 1, mk_(l3,2), mk_(l2,2), <reply>, "m2", [w]),
sd2 = mk_Interaction({l2,l3 }, {m2c, m2r}, {}, {}, {}, {}, [z, w]),
l1 = mk_Lifeline("L1", nil),
x = mk_Parameter("x", <InParam>),
y = mk_Parameter("y", <OutParam>),
m1c = mk_Message(3, 2, mk_(l1,1), mk_(l2,1), <synchCall>, "m1", [x]),
m1r = mk_Message(4, 2, mk_(l2,3), mk_(l1,3), <reply>, "m1", [y]),
ref1 = mk_InteractionUse(1, sd2, [x, y], {l2, l3},{mk_(l2,2),mk_(l3,2)}),
sd1 = mk_Interaction({l1, l2,l3 }, {m1c, m1r}, {}, {}, {ref1}, {}, [x,
y]),
pn1 = sd2pn(sd1),
c1 = mk_Classifier("L1"),
c2 = mk_Classifier("L2"),
c3 = mk_Classifier("L2"),
sm1 = mk_Occurrence(1, 1, c2, <Call>, "m1", [1]),
rm1 = mk_Occurrence(2, 1, c2, <Reply>, "m1", [2]),
sm2a = mk_Occurrence(3, 2, c3, <Call>, "m2", [1]),
rm2a = mk_Occurrence(4, 2, c3, <Reply>, "m2", [2]),
sm2b = mk_Occurrence(5, 3, c3, <Call>, "m2", [2]),
rm2b = mk_Occurrence(6, 3, c3, <Reply>, "m2", [1])
in
(
Assert(accepts(pn1, [sm1, sm2a, rm2a, rm1], {x1}, <StrictConf>));
Assert( accepts(pn1, [sm1, sm2b, rm2b, rm1], {x1}, <StrictConf>));
)
);
7.13 Assert and negate fragments
Assert fragments ('assert') are simply handled as weak sequencing fragments (seq), so no test was designed. More
tricky are the 'neg' combined fragments.
UML Checker: Formal Specification in VDM++ 40
neg m2
L1 L2
SD
m1
m3
m4
Valid trace
(strict mode)
L1 L2
Invalid trace
(strict mode)
L1 L2
Valid trace
(strict mode)
L1 L2
Invalid trace
(strict mode)
L1 L2
operations
public testNegative () ≜ (
let l1 = mk_Lifeline("L1", nil),
l2 = mk_Lifeline("L2", nil),
o1 = mk_InteractionOperand(nil,
{mk_(l1, 2), mk_(l2, 2)}, {mk_(l1, 5), mk_(l2, 5)}),
f1 = mk_CombinedFragment(1, <neg>, [o1], {l1, l2 }),
m1 = mk_Message(1,1,mk_(l1,1),mk_(l2,1),<asynchSignal>,"m1",[]),
m2 = mk_Message(2,2,mk_(l1,3),mk_(l2,3),<asynchSignal>,"m2",[]),
m3 = mk_Message(3,3,mk_(l1,4),mk_(l2,4),<asynchSignal>,"m3",[]),
m4 = mk_Message(4,4,mk_(l1,6),mk_(l2,6),<asynchSignal>,"m4",[]),
pn1 = sd2pn(mk_Interaction({l1,l2 }, {m1, m2, m3, m4 }, {f1 }, {}, {},
{}, [])),
c1 = mk_Classifier("L1"),
c2 = mk_Classifier("L2"),
sm1 = mk_Occurrence(1, 1, c2, <Send>, "m1", []),
rm1 = mk_Occurrence(2, 1, c2, <Receive>, "m1", []),
sm2a = mk_Occurrence(3, 2, c2, <Send>, "m2", []),
rm2a = mk_Occurrence(4, 2, c2, <Receive>, "m2", []),
sm2b = mk_Occurrence(5, 3, c2, <Send>, "m2", []),
rm2b = mk_Occurrence(6, 3, c2, <Receive>, "m2", []),
sm3 = mk_Occurrence(7, 4, c2, <Send>, "m3", []),
rm3 = mk_Occurrence(8, 4, c2, <Receive>, "m3", []),
sm4a = mk_Occurrence(9, 5, c2, <Send>, "m4", []),
rm4a = mk_Occurrence(10, 5, c2, <Receive>, "m4", []),
sm4b = mk_Occurrence(11, 6, c2, <Send>, "m4", []),
rm4b = mk_Occurrence(12, 6, c2, <Receive>, "m4", []),
sm5 = mk_Occurrence(13, 7, c2, <Send>, "m5", []),
rm5 = mk_Occurrence(14, 7, c2, <Receive>, "m5", [])
in
(
Assert(accepts(pn1, [sm1, rm1, sm4a, rm4a], {}, <StrictConf>));
Assert( accepts(pn1, [sm1, rm1, sm2a, rm2a, sm3, rm3, sm4a, rm4a], {},
<StrictConf>));
Assert(accepts(pn1, [sm1, sm4a, rm1, rm4a], {}, <StrictConf>));
Assert( accepts(pn1, [sm1, sm2a, sm3, sm4a, rm1, rm2a, rm3,
sm4a, rm4a], {}, <StrictConf>));
UML Checker: Formal Specification in VDM++ 41
Assert(accepts(pn1, [sm1, sm2a, rm1, sm2b, rm2a, sm3, rm2b, sm4a, rm3, sm4b,
rm4a, rm4b], {}, <StrictConf>));
Assert( accepts(pn1, [sm1, sm2a, rm1, sm3, rm2a, sm5, rm3, sm4a, rm5, rm4a],
{}, <StrictConf>));
)
);
7.14 Consider and ignore fragments
ignore y x
L1 L2
SD
z
Valid trace
(strict conf.)
L1 L2
Invalid trace
(strict conf.)
L1 L2
operations
public testConsiderIgnore()≜ (
let l1 = mk_Lifeline("L1", nil),
l2 = mk_Lifeline("L2", nil),
x = mk_Message(1, 1, mk_(l1,2), mk_(l2,2), <asynchSignal>, "x", []),
z = mk_Message(2, 2, mk_(l1,4), mk_(l2,4), <asynchSignal>, "z", []),
f = mk_ConsiderIgnoreFragment(1, <ignore>, {l1, l2}, {"y"},
{mk_(l1,1), mk_(l2,1)}, {mk_(l1, 3), mk_(l2, 3)}),
sd1 = mk_Interaction({l1,l2 }, {x, z}, {}, {}, {}, {f}, []),
pn1 = sd2pn(sd1),
c1 = mk_Classifier("L1"),
c2 = mk_Classifier("L2"),
sx = mk_Occurrence(1, 1, c2, <Send>, "x", []),
rx = mk_Occurrence(2, 1, c2, <Receive>, "x", []),
sya = mk_Occurrence(3, 2, c2, <Send>, "y", []),
rya = mk_Occurrence(4, 2, c2, <Receive>, "y", []),
sz = mk_Occurrence(5, 3, c2, <Send>, "z", []),
rz = mk_Occurrence(6, 3, c2, <Receive>, "z", []),
syb = mk_Occurrence(7, 4, c2, <Send>, "y", []),
ryb = mk_Occurrence(8, 4, c2, <Receive>, "y", [])
in
(
Assert(accepts(pn1, [sx, sz, rx, rz], { }, <StrictConf>));
Assert(accepts(pn1, [sya, sx, rya, syb, rx, sz, ryb, rz], { },
<StrictConf>));
UML Checker: Formal Specification in VDM++ 42
Assert( accepts(pn1, [sya, sx, rya, sz, rx, syb, rz, ryb], { },
<StrictConf>));
)
);
7.15 Test execution
public testAll() ≜ (
testTrivial();
testFig5Accept();
testFig7Accept();
testAsynchUI();
testLoop();
testLoopAsynch();
testGeneralOrdering();
testCoregion();
testBreak();
testCriticalRegion();
testNegative();
testInteractionUse();
testConsiderIgnore()
);
8 Conclusion
This concludes this report the VDM++ specification.
end UMLChecker
References [1] J. P.Faria, A. Paiva and Mário V. Castro, Techniques and Toolset for Conformance Testing against UML Sequence Diagrams, ICTSS
2013.
[2] The IFAD VDM++ Language - Revised for V6.6, IFAD, 2000
[3] OMG Unified Modeling LanguageTM (OMG UML), Superstructure, Version 2.4.1, OMG, August 2011.
[4] J. P.Faria, A. Paiva, A Toolset for Conformance Testing against UML Sequence Diagrams using Extended Petri Nets, International Journal on Software Tools for Technology Transfer, Springer , 2014 (submited)