|
|
# The most simple test with standard input/output
|
|
|
|
|
|
In the following example the standard input and output is used as an artificial network device:
|
|
|
1. sending out a test messages -> printing out to the standard output
|
|
|
2. expecting a response message from standard input (this will be the system’s response)
|
|
|
3. evaluating the response message and set verdict</li>
|
|
|
|
|
|
## Prerequisites
|
|
|
|
|
|
1. Please install TITAN first. You can find the installation guide in the <a href="https://www.eclipse.org/downloads/download.php?file=/titan/TitanDocuments_6_2_0.zip" target="new">TITAN documentation package</a>.
|
|
|
|
|
|
2. Create your project folder [example1] and create the following subdirectories:
|
|
|
|
|
|
```bash
|
|
|
> mkdir example1 # project folder
|
|
|
> cd example1
|
|
|
> mkdir bin # build folder (TITAN generates c++ and binary files here)
|
|
|
> mkdir src # source code folder
|
|
|
```
|
|
|
|
|
|
3. Download or clone standard input/output TITAN port <a href="https://github.com/eclipse/titan.TestPorts.STDINOUTmsg" target="new">implementation</a>
|
|
|
|
|
|
```bash
|
|
|
> cd src
|
|
|
> git clone https://github.com/eclipse/titan.TestPorts.STDINOUTmsg.git
|
|
|
```
|
|
|
|
|
|
4. In order to use this TITAN port implementation, we need to place all corresponding files into the same folder (building folder). We can perform it with links:
|
|
|
|
|
|
```bash
|
|
|
> cd ../bin
|
|
|
> ln -s ../src/titan.TestPorts.STDINOUTmsg/src/STDINOUTmsg_PortType.ttcn # port type definition
|
|
|
> ln -s ../src/titan.TestPorts.STDINOUTmsg/src/STDINOUTmsg_PT.hh # additional h ans c files
|
|
|
> ln -s ../src/titan.TestPorts.STDINOUTmsg/src/STDINOUTmsg_PT.cc
|
|
|
```
|
|
|
|
|
|
## Testfile
|
|
|
|
|
|
Now, you can start your favoured text editor and create you first TTCN3 files with the following content:
|
|
|
|
|
|
```ttcn
|
|
|
module StdTest
|
|
|
{
|
|
|
import from STDINOUTmsg_PortType all; // importing SRD port type definitions
|
|
|
|
|
|
type component testComponent // create a simple test component
|
|
|
{
|
|
|
port STDINOUTmsg_PT testPort; // with STDINOUTmsg_PT port type
|
|
|
}
|
|
|
|
|
|
testcase TC_1() runs on testComponent // define a testcase usint testComponent
|
|
|
{
|
|
|
map(mtc:testPort, system:testPort); // mapping MTC's port to system's port
|
|
|
|
|
|
testPort.send("test_message"); // sending out a message "testMessage"
|
|
|
|
|
|
alt // check possible answers and evaluate them
|
|
|
{
|
|
|
[] testPort.receive("good_reply_message")
|
|
|
{
|
|
|
setverdict(pass);
|
|
|
}
|
|
|
[] testPort.receive("bad_reply_message")
|
|
|
{
|
|
|
setverdict(fail);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
control
|
|
|
{
|
|
|
execute(TC_1());
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Save the file as <code>StdTest.ttcn</code> to your <code>src</code> folder and create a symbolic link also
|
|
|
```bash
|
|
|
> ln -s ../src/StdTest.ttcn
|
|
|
```
|
|
|
|
|
|
## Building
|
|
|
|
|
|
First generate the <code>Makefile</code> with TITAN (-s: single mode, -e name of the binary, -f overwrite existing MakeFile)
|
|
|
|
|
|
```bash
|
|
|
> makefilegen -s -f -e StdTest *.ttcn *.hh *.cc
|
|
|
Generating Makefile skeleton...
|
|
|
Makefile skeleton was generated.
|
|
|
```
|
|
|
Now you can build your code...
|
|
|
|
|
|
```bash
|
|
|
> make
|
|
|
/home/ekrigul/TTCN3/titan.core/Install/bin/compiler -L \
|
|
|
STDINOUTmsg_PortType.ttcn StdTest.ttcn - STDINOUTmsg_PortType.ttcn StdTest.ttcn
|
|
|
Notify: Parsing TTCN-3 module `STDINOUTmsg_PortType.ttcn'...
|
|
|
Notify: Parsing TTCN-3 module `StdTest.ttcn'...
|
|
|
Notify: Checking modules...
|
|
|
Notify: Generating code...
|
|
|
Notify: File `STDINOUTmsg_PortType.hh' was generated.
|
|
|
Notify: File `STDINOUTmsg_PortType.cc' was generated.
|
|
|
Notify: File `StdTest.hh' was generated.
|
|
|
Notify: File `StdTest.cc' was generated.
|
|
|
Notify: 4 files were updated.
|
|
|
touch compile
|
|
|
g++ -c -DLINUX -I/home/ekrigul/TTCN3/titan.core/Install/include -Wall -o STDINOUTmsg_PortType.o STDINOUTmsg_PortType.cc
|
|
|
g++ -c -DLINUX -I/home/ekrigul/TTCN3/titan.core/Install/include -Wall -o StdTest.o StdTest.cc
|
|
|
g++ -c -DLINUX -I/home/ekrigul/TTCN3/titan.core/Install/include -Wall -o STDINOUTmsg_PT.o STDINOUTmsg_PT.cc
|
|
|
if g++ -o StdTest STDINOUTmsg_PortType.o StdTest.o STDINOUTmsg_PT.o \
|
|
|
-L/home/ekrigul/TTCN3/titan.core/Install/lib -lttcn3 \
|
|
|
-L/home/ekrigul/TTCN3/titan.core/Install/lib -lcrypto \
|
|
|
-L/home/ekrigul/TTCN3/titan.core/Install/lib -lxml2 -lpthread -lrt; \
|
|
|
then : ; else /home/ekrigul/TTCN3/titan.core/Install/bin/titanver STDINOUTmsg_PortType.o StdTest.o STDINOUTmsg_PT.o; exit 1; fi
|
|
|
```
|
|
|
... and run it
|
|
|
|
|
|
```bash
|
|
|
> ./StdTest
|
|
|
TTCN-3 Test Executor (single mode), version CRL 113 200/6 R2A
|
|
|
Execution of control part in module StdTest started.
|
|
|
Test case TC_1 started.
|
|
|
test_message
|
|
|
```
|
|
|
|
|
|
As you can see TC_1 has been started and it sent the "test_message" message to the standard input. Now TTCN is waiting for the response message, so you can write it into the console...
|
|
|
If you send a "good_reply_message" message back then the verdict will be _pass_
|
|
|
|
|
|
```bash
|
|
|
> ./StdTest
|
|
|
TTCN-3 Test Executor (single mode), version CRL 113 200/6 R2A
|
|
|
Execution of control part in module StdTest started.
|
|
|
Test case TC_1 started.
|
|
|
test_message
|
|
|
```
|
|
|
|
|
|
and of course it is _fail_ in case of "bad_reply_message"
|
|
|
|
|
|
```bash
|
|
|
bad_reply_message
|
|
|
Test case TC_1 finished. Verdict: fail
|
|
|
Execution of control part in module StdTest finished.
|
|
|
Verdict statistics: 0 none (0.00 %), 0 pass (0.00 %), 0 inconc (0.00 %), 1 fail (100.00 %), 0 error (0.00 %).
|
|
|
Test execution summary: 1 test case was executed. Overall verdict: fail
|
|
|
```
|
|
|
|
|
|
# The raw encoder
|
|
|
|
|
|
One of the most powerful feature of the TTCN is the very flexible and easy to use encoder and decoder functionality.
|
|
|
|
|
|
## Ethernet header
|
|
|
|
|
|
The Ethernet header has the following fields:
|
|
|
* Destination MAC address, 6 octets
|
|
|
* Source MAC address, 6 octets
|
|
|
* Ethernet type, 2 octets
|
|
|
|
|
|
First, define these limited length octetstrings:
|
|
|
```ttcn
|
|
|
type octetstring oct2 with { variant "FIELDLENGTH(2)" }; // 2 length octetstring
|
|
|
type octetstring oct6 with { variant "FIELDLENGTH(6)" }; // 6 lenght octetstring
|
|
|
```
|
|
|
With _variant_ field we can define the encoded variable's different properties, e.g. how to encode each field to bits.
|
|
|
|
|
|
Define an alias to oct6 as MACAddress for easier reading:
|
|
|
```ttcn
|
|
|
type oct6 MACAddress; // alias to oct6
|
|
|
```
|
|
|
Define the structure itself
|
|
|
```ttcn
|
|
|
type record EthernetHeader {
|
|
|
MACAddress destination,
|
|
|
MACAddress source,
|
|
|
oct2 etherType
|
|
|
} with { variant "" };
|
|
|
```
|
|
|
Based on the definition above TTCN3 will generates the conversion functions automatically if you create their declaration:
|
|
|
```ttcn
|
|
|
external function encEthH (in EthernetHeader header) return octetstring
|
|
|
with { extension "prototype(convert) encode(RAW)" }
|
|
|
|
|
|
external function decEthH (in octetstring octets) return EthernetHeader
|
|
|
with { extension "prototype(convert) decode(RAW)" }
|
|
|
```
|
|
|
|
|
|
Put everything together and check the encoded results with the following a simple TTCN3 code:
|
|
|
```ttcn
|
|
|
module test
|
|
|
{
|
|
|
type octetstring oct2 with { variant "FIELDLENGTH(2)" };
|
|
|
type octetstring oct6 with { variant "FIELDLENGTH(6)" };
|
|
|
|
|
|
type oct6 MACAddress;
|
|
|
|
|
|
type record EthernetHeader {
|
|
|
MACAddress destination,
|
|
|
MACAddress source,
|
|
|
oct2 etherType
|
|
|
} with { variant " " };
|
|
|
|
|
|
external function encEthH (in EthernetHeader header) return octetstring
|
|
|
with { extension "prototype(convert) encode(RAW)" }
|
|
|
|
|
|
external function decEthH (in octetstring octets) return EthernetHeader
|
|
|
with { extension "prototype(convert) decode(RAW)" }
|
|
|
|
|
|
control {
|
|
|
var EthernetHeader ethH := {
|
|
|
destination := '00010a002700'O,
|
|
|
source := '000108000604'O,
|
|
|
etherType := '0800'O
|
|
|
}
|
|
|
|
|
|
log("Ethernet header: ", ethH);
|
|
|
log("Encoded Ethernet header: ", encEthH(ethH));
|
|
|
}
|
|
|
} with { encode "RAW" }
|
|
|
```
|
|
|
|
|
|
Create a simple configuration file with the following content to see the log results also:
|
|
|
```ttcn
|
|
|
[LOGGING]
|
|
|
ConsoleMask := USER
|
|
|
```
|
|
|
### Make and run
|
|
|
|
|
|
```bash
|
|
|
> ./test test.cfg
|
|
|
TTCN-3 Test Executor (single mode), version CRL 113 200/6 R2A
|
|
|
Using configuration file: `test.cfg'
|
|
|
Ethernet header: { destination := '00010A002700'O, source := '000108000604'O, etherType := '0800'O }
|
|
|
Encoded Ethernet header: '00010A0027000001080006040800'O
|
|
|
```
|
|
|
|
|
|
## Vlan and IP header
|
|
|
|
|
|
Let us consider more complex data structure like 802.1q vlan header and IP header. Define data structures and encoder/decoder functions as:
|
|
|
```ttcn
|
|
|
// bitfields with different lenght
|
|
|
type integer bit1 with { variant "FIELDLENGTH(1), BYTEORDER(last)" };
|
|
|
type integer bit2 with { variant "FIELDLENGTH(2), BYTEORDER(last)" };
|
|
|
type integer bit3 with { variant "FIELDLENGTH(3), BYTEORDER(last)" };
|
|
|
type integer bit4 with { variant "FIELDLENGTH(4), BYTEORDER(last)" };
|
|
|
type integer bit6 with { variant "FIELDLENGTH(6), BYTEORDER(last)" };
|
|
|
type integer bit8 with { variant "FIELDLENGTH(8), BYTEORDER(last)" };
|
|
|
type integer bit12 with { variant "FIELDLENGTH(12), BYTEORDER(last)" };
|
|
|
type integer bit13 with { variant "FIELDLENGTH(13), BYTEORDER(last)" };
|
|
|
type integer bit16 with { variant "FIELDLENGTH(16), BYTEORDER(last)" };
|
|
|
|
|
|
// 4 lenght bit8 field for IPv4 address
|
|
|
type record of bit8 IPv4Address with { variant "FIELDLENGTH(4), BYTEORDER(last)" };
|
|
|
|
|
|
// 802.1q header's Tag Control Information (TCI) part
|
|
|
type record Tci
|
|
|
{
|
|
|
bit3 pcp,
|
|
|
bit1 dei,
|
|
|
bit12 vid
|
|
|
} with { variant "FIELDORDER(msb)" };
|
|
|
|
|
|
// 802.1q header
|
|
|
type record VlanHeader
|
|
|
{
|
|
|
oct2 tpid, // Tag Protocol Id: 0x8100 or 0x88a8
|
|
|
Tci tci // Tag Control Information
|
|
|
} with { variant " " };
|
|
|
|
|
|
// Encoder and decoder functions
|
|
|
external function encVlanH (in VlanHeader header) return octetstring
|
|
|
with { extension "prototype(convert) encode(RAW)" }
|
|
|
|
|
|
external function decVlanH (in octetstring octets) return VlanHeader
|
|
|
with { extension "prototype(convert) decode(RAW)" }
|
|
|
|
|
|
// IPv4 header
|
|
|
type record IPv4Header {
|
|
|
bit4 version,
|
|
|
bit4 IHL,
|
|
|
bit6 DSCP,
|
|
|
bit2 ECN,
|
|
|
bit16 totalLength,
|
|
|
bit16 identification,
|
|
|
bit3 flags,
|
|
|
bit13 fragmentOffset,
|
|
|
bit8 timeToLive,
|
|
|
bit8 protocol,
|
|
|
bit16 checksum,
|
|
|
IPv4Address source,
|
|
|
IPv4Address destination
|
|
|
} with { variant "FIELDORDER(msb)" };
|
|
|
|
|
|
// Encoder and decoder functions
|
|
|
external function encIPv4H (in IPv4Header header) return octetstring
|
|
|
with { extension "prototype(convert) encode(RAW)" }
|
|
|
|
|
|
external function decIPv4H (in octetstring octets) return IPv4Header
|
|
|
with { extension "prototype(convert) decode(RAW)" }
|
|
|
```
|
|
|
|
|
|
Adding some data to encode and decode:
|
|
|
```ttcn
|
|
|
control {
|
|
|
var VlanHeader vlanH := {
|
|
|
tpid := '8100'O,
|
|
|
tci := {
|
|
|
pcp := 5,
|
|
|
dei := 0,
|
|
|
vid := 100
|
|
|
}
|
|
|
}
|
|
|
|
|
|
log("VLAN header: ", vlanH);
|
|
|
log("Encoded VLAN header: ", encVlanH(vlanH));
|
|
|
|
|
|
log("Incomming IP header: ", "4500003c075b4000ff112e799f6bc575e00000fb");
|
|
|
log("Decoded IP header: ", decIPv4H('4500003c075b4000ff112e799f6bc575e00000fb'O));
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### Make and run
|
|
|
|
|
|
```bash
|
|
|
> ./test test.cfg
|
|
|
TTCN-3 Test Executor (single mode), version CRL 113 200/6 R2A
|
|
|
Using configuration file: `test.cfg'
|
|
|
VLAN header: { tpid := '8100'O, tci := { pcp := 5, dei := 0, vid := 100 } }
|
|
|
Encoded VLAN header: '8100A064'O
|
|
|
Incomming IP header: 450000280f444000800691f091fea0ed41d0e4df
|
|
|
Decoded IP header: { version := 4, IHL := 5, DSCP := 0, ECN := 0, totalLength := 40, identification := 3908, flags := 2, fragmentOffset := 0,
|
|
|
timeToLive := 128, protocol := 6, checksum := 37360, source := { 145, 254, 160, 237 }, destination := { 65, 208, 228, 223 } }
|
|
|
``` |