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’:returna1 ≺{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’:return100 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.