Important for:
No standard methodology, language, approach, tool
Personal story: During past years,
But: Until 2015, only proprietary tools / compilers :(
eclipse-titan
part of standard Debian / Ubuntu archive, only one apt-get away
Great, we can finally use TTCN-3 in FOSS!
integer
, float
, boolen
bitstring
, octetstring
, hexstring
, charstring
(IA5) and universal charstring
(UCS-4).
record
, set
, record of
, set of
verdicttype
none
, pass
, inconc
, fail
, or error
pass
→ fail
) but never improve (error
→ pass
)
verdicttype
A structured type is an abstract type comprised of other types, whcih can be nested.
An example for a record
type (similar to a C-language struct
) is shown below
type record MyMessageType {
integer field1 optional<1>,
charstring field2,
boolean field3
};
optional members may be present or not
A union expresses a set of alternative types of which one alternative must be chosen.
type union MyMessageUnion {
integer field1,
charstring field2,
};
Difference to C-language union: ischosen()
can be used to learn which of the union members is
chosen/defined!
omit
value must be assigned!
Sub-typing can be used to further constrain a given type. Typical examples include constrained number ranges, and string patterns
type integer MyIntRange (1..100);
type integer MyIntRange8 (0..infinity);
type charstring MyCharRange (”k”..”w");
type charstring SideType (”left”, ”right”);
type integer MyIntListRange (1..5,7,9);
type record length(0..10) of integer RecOfInt;
type charstring CrLfTermStrin (pattern ”*\r\n”);
// Value list template
template charstring tr_SingleABorC := (”A”, ”B”, ”C”);
// Value range
template float tr_NearPi := (3.14 .. 3.15);
template integer tr_FitsToOneByte := (0 .. 255);
template integer tr_GreaterThanZero := (1 .. infinity);
// Intermixed value list and range matching
template integer tr_Intermixed := ((0..127), 200, 255);
// Using any element matching inside a bitstring value
// Last 2 bits can be '0' or '1'
template bitstring tr_AnyBSValue := ’101101??’B;
// Matches charstrings with the first character "a"
// and the last one "z"
template charstring tr_0 := pattern "a*z";
complement
, ifpresent
, subset
, superset
, permutation
constructs not
covered here
See below for an example of a parametric template:
type record MyMessageType {
integer field1 optional,
charstring field2,
boolean field3
};
template MyMessageType trMyTemplte(boolean pl_param) := {
field1 : = ?, // present, but any value
field2 : = (”B”, ”O”, ”Q”) ,
field3 := pl_param
};
The built-in match()
function can be used to check if a given value matches a given template. Some TTCN-3
statements such as receive()
have built-in capabilities for template matching, avoiding even the explicit
call of match()
in many cases.
Using modified templates, one can build a hierarchy of templates: From the specific to the unspecific
template MyMsgType t_MyMsgAny := {
msg_type := ?,
foo := bar
};
template MyMsgType t_MyMsg23 modifies t_MyMsgAny := {
msg_type := 23,
};
where
raw
codec for binary protocols (e.g. GTP)
text
codec for text based protocols (e.g. HTTP, MGCP, IMAP, …)
How to express an UDP header using TITAN raw codec
type integer LIN2_BO_LAST (0..65535) with {
variant ”FIELDLENGTH(16), COMP(nosign), BYTEORDER(last)”
};
type record UDP_header {
LIN2_BO_LAST srcport,
LIN2_BO_LAST dstport,
LIN2_BO_LAST len,
LIN2_BO_LAST cksum
} with { variant ”FIELDORDER(msb)” };
type record UDP packet {
UDP_header header
octetstring payload
} with {
variant (header) ”LENGTHTO(header, payload), LENGTHINDEX(len)”
};
How to express an GTP header using TITAN raw codec
type record GRE_Header {
BIT1 csum_present,
BIT1 rt_present,
BIT1 key_present,
...
OCT2 protocol_type,
OCT2 checksum optional,
OCT2 offset optional,
OCT4 key otional,
...
} with {
variant (checksum) "PRESENCE(csum_present='1', rt_present='1'B)"
variant (offset) "PRESENCE(csum_present='1'B, rt_present='1'B)"
variant (key) "PRESENCE(key_present='1'B)"
}
type charstring MgcpVerb ("EPCF", "CRCX", "MDCX", "DLCX", "RQNT", "NTFY",
"AUEP", "AUCX", "RSIP") with {
variant "TEXT_CODING(,convert=upper_case,,case_insensitive)"
};
type charstring MgcpTransId (pattern "\d#(1,9)");
type charstring MgcpEndpoint (pattern "*@*");
type charstring MgcpVersion (pattern "\d.\d") with {
variant "BEGIN('MGCP ')"
};
type record MgcpCommandLine {
MgcpVerb verb,
MgcpTransId trans_id,
MgcpEndpoint ep,
MgcpVersion ver
} with {
variant "SEPARATOR(' ', '[\t ]+')"
variant "END('\r\n', '([\r\n])|(\r\n)')"
};
if
/ else
like in C
select
statement similar to C switch
for
, while
, do-while
loops like in C
goto
and label
break
and continue
like in C
<port>.send(<ValueRef>)
performs non-blocking send
<port>.receive(<TemplateRef>)
or <port>.receive
performs blocking receive
'… but if receive blocks, how can we wait for any of N events?
// x must be the first on queue P, y the second
P.receive(x); // Blocks until x appears on top of queue P
P.receive(y); // Blocks until y appears on top of queue P
// When y arrives first then P.receive(x) blocks -> error
This is what leads to the alt
statement:
alt
declares a seto alternatives covering all events, which
alt
are blocking operations
alt
statementP.send(req)
T.start;
// ...
alt {
[] P.receive(resp) { /* actions to do and exit alt */ }
[] any port.receive { /* handle unexpected event */ }
[] T.timeout { /* handle timer expiry and exit */ }
}
[]
equals [true]
[x > 0]
alt
and repeat
statementsThe repeat
statement
P.send(req)
T.start;
alt {
[] P.receive(resp) { /* actions to do and exit alt */ }
[] P.receive(keep_alive) { /* handle keep alive message */
repeat }
[] any port.receive { /* handle unexpected event */ }
[] T.timeout { /* handle timer expiry and exit */ }
}
interleave
statementThe interleave
statement
interleave {
[] P.receive(1) { /* actions, optional */ }
[] Q.receive(4) { /* actions, optional */ }
[] R.receive(6) { /* actions, optional */ }
}
altstep
constructalt
alt
statements or activated as default
Definition of an altstep
for PING/PONG handling and guard timer:
altstep my_altstep() {
[] P.receive(tr_PING) { P.send(ts_PONG); }
[] T_guard.timeout { setverdict(fail, "Guard timer timed out"); }
}
altstep
constructExplicit Usage of the my_altstep
defined on previous slide:
P.send(foo)
alt {
[] P.receive(bar)
[] my_altstep()
}
this dynamically adds the alternatives from my_altstep
at the given location
and priority of the above alt
, ensuring that one doesn’t have to repeat
ping/pong handling as well as global guard timer timeout handling in every alt
or every single test case all over again.
altstep
activationaltstep
invocation was explicit
altstep as_pingpong() {
[] P.receive(tr_PING) { P.send(ts_PONG); }
}
...
var default d1 := activate(as_pingpong());
... /* code executing with as_pingpong activated */
deactivate(d1);
TTCN-3 code is written in modules
Let’s have a look at some real-world examples and do a bit of a walk-through before continuing with the slides…
log()
built-in function
ttcn3_logformat
tool for pretty-printing log files
ttcn3_logmerge
tool for merging/splicing multiple logs
Log file format example:
// abstract data type before encode
13:30:41.243536 Sent on GTPC to system @GTP_CodecPort.Gtp1cUnitdata : { peer := { connId := 1, remName := "127.0.23.1", remPort := 2123 }, gtpc := { pn_bit := '0'B, s_bit := '1'B, e_bit := '0'B, spare := '0'B, pt := '1'B, version := '001'B, messageType := '01'O, lengthf := 0, teid := '00000000'O, opt_part := { sequenceNumber := '3AAC'O, npduNumber := '00'O, nextExtHeader := '00'O, gTPC_extensionHeader_List := omit }, gtpc_pdu := { echoRequest := { private_extension_gtpc := omit } } } }
// 'msg' contains encoded binary data actually sent via socket
13:30:41.243799 Outgoing message was mapped to @IPL4asp_Types.ASP_SendTo : { connId := 1, remName := "127.0.23.1", remPort := 2123, proto := { udp := { } }, msg := '32010004000000003AAC0000'O }
The same log file lines if run through ttcn3_logformat
13:30:41.243536 Sent on GTPC to system @GTP_CodecPort.Gtp1cUnitdata : {
peer := {
connId := 1,
remName := "127.0.23.1",
remPort := 2123
},
gtpc := {
pn_bit := '0'B,
s_bit := '1'B,
e_bit := '0'B,
spare := '0'B,
pt := '1'B,
version := '001'B,
messageType := '01'O,
lengthf := 0,
teid := '00000000'O,
opt_part := {
sequenceNumber := '3AAC'O,
npduNumber := '00'O,
nextExtHeader := '00'O,
gTPC_extensionHeader_List := omit
},
gtpc_pdu := {
echoRequest := {
private_extension_gtpc := omit
}
}
}
}
13:30:41.243799 Outgoing message was mapped to @IPL4asp_Types.ASP_SendTo : {
connId := 1,
remName := "127.0.23.1",
remPort := 2123,
proto := {
udp := { }
},
msg := '32010004000000003AAC0000'O
}
End of File