OpenTTCN/Training/2008-06-18

From OpenTTCN

Jump to: navigation, search

  OpenTTCN DocZone

  Home | Developer's corner | Knowledge base | Working documents | Documentation | OpenTTCN IDE | Tutorials | Training | How do I | Frequently asked questions | Technical support


Workshop material, June 18-19, 2008


Pictures from workshop >>


Contents

Introduction

Adapter initialization

int main(...)
{

    // tries to find openttcnd daemon on 127.0.0.1, port 5500:

    int rv = otInitAdapter(0); // 127.0.0.1, 5500

    if (rv) { /* report error */ }

}

include/isl/TTCN3.h: otInitAdapter()

Other way to initialize adapter:

// openttcnd is on 192.168.1.1:
otInitAdapter("192.168.1.1:5500");
// openttcnd is on 192.168.1.1, port 5600:
otInitAdapter("192.168.1.1:5600");

Way to change the openttcnd startup port

C:\Program Files\OpenTTCN\Tester3\etc\OpenTTCN.ini:

//change this line to change port name:

port = 5500

More complex init function

int otResolveInitialReferences(const char* id, const char** args);

(documentation for it is available in include\isl\TTCN3.h)

Way to register adapter to the session

int otRegisterAdapter(const char* sessionName, const char* id);

(specified in include\tri\tri_ext.h)

The simplest adapter config

otRegisterAdapter("MySessionName", "TRI");

Two adapters

// Adapter A:

otRegisterAdapter("MySessionName", "TRI");

// Adapter B:

otRegisterAdapter("MySessionName", "TRI:tsiP2");

Standalone coding and decoding

otRegisterAdapter("MySessionName", "TCI_CD");

Multipurpose adapters

otRegisterAdapter("MySessionName", "TRI:tsiP1");
otRegisterAdapter("MySessionName", "TRI:tsiP2");
otRegisterAdapter("MySessionName", "TCI_CD");
otRegisterAdapter("MySessionName", "TRI_PA:ext_F1");
otRegisterAdapter("MySessionName", "TRI_PA:ext_F2");

Full list of adapter roles (documentation)

Comments for:

int otRegisterAdapter(const char* sessionName, const char* id);

in C:\Program Files\OpenTTCN\SDK\include\tri\tri_ext.h

triSend()

C:\Program Files\OpenTTCN\SDK\include\tri\tri.h:

TriStatus triSend
(const TriComponentId* componentId,
 const TriPortId* tsiPortId,
 const TriAddress* sutAddress,
 const TriMessage* sendMessage);

DOC

typedef BinaryString TriAddress;
typedef BinaryString TriMessage;

tciEncode()

DOC

triEnqueueMsg

tri/tri.h

void triEnqueueMsg
(const TriPortId* tsiPortId,
 const TriAddress* sutAddress,
 const TriComponentId* componentId,
 const TriMessage* receivedMessage);

tciDecode

DOC

Procedure-based communication

void triEnqueueCall()

void triEnqueueReply()

void triEnqueueException()

Multicast, broadcast

(Rarely used currently.)

  • triSendMC() - multicast
  • triSendBC() - broadcast
  • same for call, reply, exception
  • for more details, see: tri\tri.h

Communication: adapter - test component

  • CORBA-based (TRI-PA, TRI-SA, TCI-CD, TCI-TM) (SDK for C, SDK for Java, SDK for C#)
    • tri.h
  • TRI-UDP (TRI-PA, TRI-SA) (you need to have CORBA-based TCI-CD)
    • tri_udp.h

APIs

  • C:\Program Files\OpenTTCN\SDK\include\tci - C
  • C:\Program Files\OpenTTCN\SDK\include\tri - C
  • C:\Program Files\OpenTTCN\SDK\include\isl - C++

Timer example

C:\Program Files\OpenTTCN\SDK\examples\timer

Enabling adapter debug output

tri/tri_ext.h

otSetSAFlags(OT_SA_FLAG_VERBOSE);

Other ways to debug the adapter

tester run:

    --log-encoded-as-text, -t    log encoded messages in text format
    --log-encoded-as-hex, -h     log encoded messages in hex format

TCI-TM:

tci/tci_ext.h:

#define OT_TM_LOG_ENCODED_AS_HEX     0x00000020L
#define OT_TM_LOG_ENQUEUED_EVENTS    0x00000040L
...

int otSetTMFlags(unsigned long flags);

Other ways to debug the test suite

tester run:

   --log-src-line-numbers, -l   enable logging of source file line numbers
   --log-src-file-name, -f      enable logging of source file name

   --print-stack-trace (added recently)

   --log-enqueued-events, -e    log events that arrived to the port queue
   --log-mismatch-events, -m    log mismatching communication statements
   --smart-mismatch, -M         log mismatch that preceded non-pass verdict

More info: tester help more

Also in TCI-TM.

odb (debugger)

Validation of decoder result

tci/tci_ext.h:

#define OT_CD_FLAG_VALIDATE_DECODED_VALUE 0x00000004L

Enabled by default.

To disable (for performance reasons): int otResetCDFlags(unsigned long flags);

How to programmatically control test execution options (TCI-TM)

In C:\Program Files\OpenTTCN\SDK\include\tci\tci_ext.h:

Set of options starting with OT_TM_LOG

(use otSetTMFlags(), otResetTMFlags() to set or reset these options)

Options need to be set before the commencement of the test campaign (i.e. before you run tciStartTestCase() or tciStartControl()).

How to get a list of test cases programmatically in TCI-TM

TciTestCaseIdListType tciGetTestCases();

(specified in tci\tci.h)

(you need to call otSelectSession() beforehand to select active session)

How to start openttcnd

ot start (command-line)

C:\Program Files\OpenTTCN\SDK\include\isl\TTCN3.h:

int otStartSystemServices(const char* installPath,
    const char* port, int nowait,
    otStatusIndicationListener listener);

Create session

cmd-line:

session create MySession

API:

C:\Program Files\OpenTTCN\SDK\include\isl\TTCN3.h:

int otCreateSession(const char* sessionName);

TCI-TM: Logging

Callback (tci/tci.h):

void tciLog(String message);

Session

session create dsrc
importer2 load dsrc MySuite.mp

isl/TTCN3.h:

otCreateSession();
otDeleteSession();
otListSessions();

otInitAdapter

DOC

otRegisterAdapter

tri/tri_ext.h:

DOC

  • in case there is a conflict related to multiple registrations, a best match rule is used, and the best matching adapter is selected to serve a particular request

Persistent, non-persistent data

(persistent repository data is physically stored in Tester3\var directory)

persistent:

  • precompiled test suites
  • module parameters (TTCN-2: test suite parameters)
  • sessions created

non-persistent data:

  • adapter registrations

Stoppping an adapter

session stop

isl/TTCN3.h:

otStopAdapter()

Stopping openttcnd

ot stop

isl/TTCN3.h:

otStopSystemServices();

Message not received - what to do?

  • enable adapter debug output (see above)
  • use -e option in 'tester run' (this will show the messages as they arrive from the adapter)
    • this probably would not work if the component port and TSI port are not mapped
  • make sure that the message is decoded OK (tciDecode())
  • make sure that the TSI port name specified in triEnqueueMsg() is correct and port index is (typically) -1
  • make sure that test component port was mapped to TSI port
  • use -m to see if there is some mismatch

How do I report an error that happened inside an adapter?

int otReportError(const char* errorReason);

(defined in isl\TTCN3.h - see comments in the header for more detail)

Structure of header files in the SDK

  • isl - general-purpose functionality (TTCN3.h is the major header file)
  • tri - TRI: TRI-SA (network interface code), TRI-PA (external functions, timers)
    • proprietary extensions are in tri_ext.h
  • tci - TCI: TCI-CD (coding, decoding), TCI-TM (test management)
    • proprietary extensions are in tci_ext.h

Structure of header files in the SDK

signature SimpleProc(
    in integer p1,
    out bitstring p2,
    inout octetstring p3)
return integer
exception(
    BS_0_1024,
    RecordType,
    EquivalentRecordType,
    integer,
    octetstring);

Developing adapter example

  • start Visual C++ 9.0 Express Edition
  • File|New|Project
  • Win32 console application
  • Example project location: C:\courses\wsp6
  • Name: simple
  • no precompiled header

After the project is created:

  • remove all unnecessary files generated by MS project wizard
  • create empty main.cxx and add it to the project
  • add the following content to main.cxx:
/* for Sleep() call */
#include <windows.h>

#include <tri/tri.h>
#include <isl/TTCN3.h>

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int status = otInitAdapter(0);

    if (status)
    {
        printf("main(): otInitAdapter() failed.\n");
        exit(status);
    }

    /* Register the adapter to OpenTTCN server */

    status = otRegisterAdapter("simple", "TRI");

    if (status)
    {
        printf("main(): otRegisterAdapter() failed.\n");
        exit(status);
    }

    /* Enable printout of library diagnostic information */
    otSetSAFlags(OT_SA_FLAG_VERBOSE);

    printf("Init OK!\n");

    /* Enter the infinite loop */
    while (1) Sleep(100000);

    /* Avoid compiler complaints */
    return 0;
}

Using Release mode only

Build | Configuration Manager | Active solution configuration: Release

Configuring Visual C++ project

  • assumption that OpenTTCN SDK is installed to:
C:\Program Files\OpenTTCN\SDK

If you have another installation location, then project settings are different

Right-click on simple, then Properties, then:

  • Configuration properties|C/C++|General|Additional include directories:
    • Add C:\Program Files\OpenTTCN\SDK\include
  • Configuration properties|C/C++|Code generation|Runtime library:
    • Change /MD (default) to /MT (SDK libraries use this runtime library)
  • Configuration properties|Linker|General|Additional library directories:
    • Add C:\Program Files\OpenTTCN\SDK\lib
  • Configuration properties|Linker|Input|Additional dependencies:
    • Add omniDynamic4.lib omniORB4.lib omnithread.lib pthreads-2.7.0-mt.lib openttcn-tcd-mt.lib openttcn-tsa-mt.lib openttcn-isl-mt.lib openttcn-msg-mt.lib ws2_32.lib

Adding default content to SA_impl.cxx, PA_impl.cxx

  • Add two more empty source files to the project: PA_impl.cxx and SA_impl.cxx

Content of SA_impl.cxx:

#include <tri/tri.h>

/***************************************************************************
 * Implementation of TRI interface (SA, user-provided part).
 */

TriStatus triSend
(const TriComponentId* componentId,
 const TriPortId* tsiPortId,
 const TriAddress* sutAddress,
 const TriMessage* sendMessage)
{
    return TRI_ERROR;
}

TriStatus triSAReset()
{
    return TRI_OK;
}

TriStatus triExecuteTestCase
(const TriTestCaseId* testCaseId,
 const TriPortIdList* tsiPortList)
{
    return TRI_OK;
}

TriStatus triMap
(const TriPortId* compPortId,
 const TriPortId* tsiPortId)
{
    return TRI_OK;
}

TriStatus triUnmap
(const TriPortId* compPortId,
 const TriPortId* tsiPortId)
{
    return TRI_OK;
}

TriStatus triCall
(const TriComponentId* componentId,
 const TriPortId* tsiPortId,
 const TriAddress* sutAddress,
 const TriSignatureId* signatureId,
 const TriParameterList* parameterList)
{
    return TRI_ERROR;
}

TriStatus triReply
(const TriComponentId* componentId,
 const TriPortId* tsiPortId,
 const TriAddress* sutAddress,
 const TriSignatureId* signatureId,
 const TriParameterList* parameterList,
 const TriParameter* returnValue)
{
    return TRI_ERROR;
}

TriStatus triRaise
(const TriComponentId* componentId,
 const TriPortId* tsiPortId,
 const TriAddress* sutAddress,
 const TriSignatureId* signatureId,
 const TriException* exception)
{
    return TRI_ERROR;
}

TriStatus triSUTActionInformal
(const char* description)
{
    return TRI_ERROR;
}

TriStatus triSUTActionTemplate
(const TriActionTemplate* templateValue)
{
    return TRI_ERROR;
}

Content of PA_impl.cxx:

#include <tri/tri.h>

#include <isl/TTCN3.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/***************************************************************************
 * Implementation of TRI interface (PA, user-provided part).
 */

TriStatus triPAReset()
{
    return TRI_OK;
}

TriStatus triExternalFunction
(const TriFunctionId* functionId, /* in parameter */
 TriParameterList* parameterList, /* inout parameter */
 TriParameter* returnValue /* out parameter */)
{
    // Example:

    if (!strcmp(functionId->objectName, "MyFunct1"))
    {
        // implement MyFunct1
    }
    else if (!strcmp(functionId->objectName, "MyFunct2"))
    {
        // implement MyFunct2
    }
    // ...
    else
    {
        otReportError("Unrecognized external function");
    }

    return TRI_ERROR;
}

TriStatus triStartTimer
(const TriTimerId* timerId,
 TriTimerDuration timerDuration)
{
    return TRI_ERROR;
}

TriStatus triStopTimer
(const TriTimerId* timerId)
{
    return TRI_ERROR;
}

TriStatus triReadTimer
(const TriTimerId* timerId,
 TriTimerDuration* elapsedTime)
{
    return TRI_ERROR;
}

TriStatus triTimerRunning
(const TriTimerId* timerId,
 unsigned char* running)
{
    return TRI_ERROR;
}

ot start, session create

Start openttcnd daemon:

ot start

Create a session named "simple":

session create simple

Adding CD

Add CD_impl.cxx to the project with the following content:

#include <tci/tci.h>

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>

/***************************************************************************
 * Implementation of TCI interface (CD, user-provided encoding part).
 */

BinaryString tciEncode(TciValue value)
{
    BinaryString result;
    memset(&result, 0, sizeof(result));
    return result;
}

/***************************************************************************
 * Implementation of TCI interface (CD, user-provided decoding part).
 */

TciValue tciDecode(BinaryString message, TciType decHypothesis)
{
    TciValue result = 0;
    return result;
}


Add the following before adapter registration (main.cxx):

otSetEncoder(0, &tciEncode);
otSetDecoder(0, &tciDecode);

Add the following include to main.cxx:

#include <tci/tci.h>

(Documentation for otSetEncoder() is here)

Background info

  • SUT protocol definition: DOC

Example test case

Added C:\courses\wsp6\simple\ttcn3\Main.ttcn file with the following content:

module Main
{

/*******************************************************************
 * Message type definitions
 */

type integer uint8(0..255);
type integer uint16(0..65535);

type record Message
{
    uint8 typeCode,
    Payload payload optional
}

type record Payload
{
    uint16 len,
    charstring content
}

type octetstring Raw;

/*******************************************************************
 * Other definitions
 */

// Address type used for UDP packets.
type record address
{
    charstring host,
    integer portField
}

modulepar
{
    // IP address and UDP port number of the SUT.
    charstring PX_SUT_IP_ADDR := "127.0.0.1";
    integer PX_SUT_PORT := 7431;
}

type port PortType mixed
{
    inout all;
}

type component ComponentType
{
    port PortType p;
    timer T_GUARD := 10.0;

    var address sut_addr :=
    {
        host := PX_SUT_IP_ADDR,
        portField := PX_SUT_PORT
    };
}

type component SystemInterfaceType
{
    port PortType tsiPort;
}

template Message GreetingRequest
(in template charstring phrase) :=
{
    typeCode := 1,
    payload :=
    {
        len := lengthof(phrase),
        content := phrase
    }
}

template Message GreetingResponse
(in template charstring phrase)
modifies GreetingRequest :=
{
    typeCode := 2
}

/* the above is equivalent to the following:

{
    typeCode := 2,
    payload :=
    {
        len := lengthof(phrase),
        content := phrase
    }
}

*/

altstep DefaultAltstep() runs on ComponentType
{
    [] any port.check
    {
        setverdict(fail);
        stop;
    }
    [] T_GUARD.timeout
    {
        setverdict(fail);
        stop;
    }
}

testcase TC_R2D2_001()
    runs on ComponentType
    system SystemInterfaceType
{
    activate(DefaultAltstep());
    T_GUARD.start;
    map(mtc:p, system:tsiPort);

    p.send(GreetingRequest("Hello, world!")) to sut_addr;
    p.receive(GreetingResponse("Hello, mankind!")) from sut_addr;

    p.send(GreetingRequest("Hello, gentlemen!")) to sut_addr;
    p.receive(GreetingResponse("Reformulate")) from sut_addr;

    setverdict(pass);
}

}

Compile test suite

importer3 load simple Main.ttcn

Run test case

  • start the adapter (Ctrl+F5 in VC++)
  • tester run simple TC_R2D2_001

Adding encoder

Add the following to CD_impl.cxx (in place of current tciEncode()):

/* for otReportError() */
#include <isl/TTCN3.h>

#define MAX_BUFFER_SIZE 4096

BinaryString tciEncode(TciValue value)
{
    int len = 0;
    BinaryString result;

    TciType valType = tciGetType(value);
    String typeName = tciGetName(valType);
    TciTypeClassType typeClass = tciGetTypeClass(valType);

    memset(&result, 0, sizeof(result));

    if (typeClass == TCI_ADDRESS_TYPE)
    {
        len = sizeof(TciValue);
        result.data = (unsigned char *) malloc(len);
        memcpy(result.data, &value, len);
        result.bits = len << 3;
    }
    else if (!strcmp(typeName, "Message"))
    {
        long typeCode = 0, length = 0;
        unsigned char* buffer = 0;

        TciValue payload = 0;

        char* str = 0;
        long str_len = 0;

        buffer = (unsigned char *) malloc(MAX_BUFFER_SIZE);
        memset(buffer, 0, MAX_BUFFER_SIZE);

        len = 0;

        typeCode = tciValueToLong(tciGetRecFieldValue(value, "typeCode"));

        buffer[len++] = (unsigned char) typeCode;

        payload = tciGetRecFieldValue(value, "payload");

        /* 'payload' field is optional, so it may be omitted */
        if (!tciNotPresent(payload))
        {
            long length = tciValueToLong(tciGetRecFieldValue(payload, "len"));

            /* most significant byte first */
            buffer[len++] = (unsigned char) (length >> 8);
            buffer[len++] = (unsigned char) (length >> 0);

            tciValueToCharstring(&str, &str_len,
                tciGetRecFieldValue(payload, "content"));

            memcpy(buffer + len, str, str_len);
            len += str_len;

            free(str);
        }

        result.data = buffer;
        result.bits = len << 3;
    }
    else
    {
        otReportError("tciEncode(): Unrecognized value type.");
    }

    return result;
}

To use this code, you will need to #include "Utilities.h" to CD_impl.cxx and add files Utilities.h and Utilities.c to your Visual Studio project.

You will need to copy the code of Utilities.h and Utilities.c files referred to in the links of the previous paragraph into your Visual C++ project. Click on the links of the previous paragraph to see the content of both files. Note that these are not the same as in the examples\util directory shipped with the C SDK.

Ways to implement coding and decoding

  • simple: using tciGetType(), tciGetName(myType)
  • more complex (more intelligent): using 'encode' and 'variant' attributes
  • example of attribute definition in TTCN-3 test suite:
type record MyMessage
{
    integer f1,
    integer f2
}
with { encode "MyPropName1=MyPropValue1;MyPropName2=MyPropValue2",
       variant "MyPropName4=MyPropValue4;MyPropName3=MyPropValue3" }
  • how to retrieve encoding, variant attributes using TCI API:
String tciGetTypeEncoding(TciType inst);
/* OpenTTCN Specific: MM4RV is as defined in [RM1]. */
String tciGetTypeEncodingVariant(TciType inst);

Full list of TCI type classes

Search for "TciTypeClassType" in tci\tci.h

Adding triSend handler

Replace triSend() implementation in SA_impl.cxx with the following fragment:

#include "Utilities.h"

TriStatus triSend
(const TriComponentId* componentId,
 const TriPortId* tsiPortId,
 const TriAddress* sutAddress,
 const TriMessage* sendMessage)
{
    TciValue addrValue = *((TciValue *) sutAddress->data);

    unsigned long ipAddr = 0;
    unsigned short portNumber = 0;

    int result =
        extractHostAndPortFromAddress(
            &ipAddr, &portNumber, addrValue);

    if (result) return TRI_ERROR;

    result = sendDatagramPacket(
        ipAddr,
        portNumber,
        (char*) sendMessage->data,
        sendMessage->bits >> 3);

    return (!result) ? TRI_OK : TRI_ERROR;
}

SUT implementation

SUT implementation can be downloaded here.

Way to start SUT binary (in the zip package): start_sut.bat:

start Release\SystemUnderTest.exe 7431
  • SUT is configured to listen on UDP port 7431

Theory track (continued)

TTCN-2 execution to TTCN-3 adaptation mapping

DOC

  • triMap(), triUnmap() are in fact called automatically

Procedure-based communication

Signatures

Example of signature definition for procedure-based communication:

signature Signature(
    in integer par1,
    out bitstring par2,
    MyRecordType par3,
    inout OS_0_256 par4)
return integer
exception(
    BS_0_1024,
    RecordType);
signature ASP_Type1(
    in integer par1,
    in bitstring par2,
    in MyRecordType par3,
    in OS_0_256 par4);

template ASP_Type1 t1 :=
{
par1 := 1,
par2 := '1101'B,
par3 := { f1 := 1, f2 := 2 },
par4 := 'AF'O
}

...
p.call(t1);

Mapping of procedure-based comm. concepts to message-based concepts

TE - client, SUT - server:

call -> REQ (TE -> SUT)
getreply -> RSP (TE <- SUT)
catch -> ERR IND (TE <- SUT)

TE - server, SUT - client:

getcall -> REQ (TE <- SUT)
reply -> RSP (TE -> SUT)
raise -> ERR IND (TE -> SUT)

triCall

TriStatus triCall
(const TriComponentId* componentId,
 const TriPortId* tsiPortId,
 const TriAddress* sutAddress,
 const TriSignatureId* signatureId,
 const TriParameterList* parameterList);

triEnqueueReply

void triEnqueueReply
(const TriPortId* tsiPortId,
 const TriAddress* sutAddress,
 const TriComponentId* componentId,
 const TriSignatureId* signatureId,
 const TriParameterList* parameterList,
 const TriParameter* returnValue);

triEnqueueException

void triEnqueueException
(const TriPortId* tsiPortId,
 const TriAddress* sutAddress,
 const TriComponentId* componentId,
 const TriSignatureId* signatureId,
 const TriException* exception);

When tciEncode() is called

Message-based communication:

  • called to encode a message and optionally address prior to triSend() call

Procedure-based communication:

  • called to encode in, inout parameters and optionally address prior to triCall() call
  • called to encode out, inout parameters and optionally return value and optionally address prior to triReply() call
  • called to encode exception and optionally address prior to triRaise() call

External functions:

  • called to encode in, inout parameters of external function prior to triExternalFunction() call

SUT actions:

  • called to encode template prior to triSUTActionTemplate()

When tciDecode() is called

Message-based communication:

  • called to decode a message and optionally address when triEnqueueMsg() call is being processed

Procedure-based communication:

  • called to decode in, inout parameters and optionally address when triEnqueueCall() call is being processed
  • called to decode out, inout parameters and optionally return value and optionally address when triEnqueueReply() call is being processed
  • called to decode exception and optionally address when triEnqueueException() call is being processed

External functions:

  • called to decode out, inout parameters of external function and optionally external function return value after triExternalFunction() callback function returns

When decoding hypothesis is provided

It is accurately provided when decoding:

  • out, inout parameters and return value when triEnqueueReply() call is being processed
  • in, inout parameters when triEnqueueCall() call is being processed
  • out, inout parameters and return value after triExternalFunction() callback function returns

Decoding hypothesis may be accurately provided when decoding address.

Decoding hypothesis by default is not provided for the following cases:

  • decoding of a message in message-based communication (when triEnqueueMsg() call is being processed)
  • decoding an exception in procedure-based communication (when triEnqueueException() call is being processed)

Decoding hypothesis for messages

  • none by default
  • see comment for OT_CD_FLAG_MESSAGES_WITH_HYPOTHESIS flag in tci\tci_ext.h for more details
  • this flag can be set using:
int otSetCDFlags(unsigned long flags);

(found in tci/tci_ext.h)

  • more information on exact algorithm for constructing list of expected types during phase 1 (types A, B, C in the drawing) can be found in documentation for OT_CD_FLAG_MESSAGES_WITH_HYPOTHESIS flag in tci\tci_ext.h

Decoding hypothesis for exceptions

  • none by default
  • see comment for OT_CD_FLAG_EXCEPTIONS_WITH_HYPOTHESIS flag in tci\tci_ext.h for more details
  • bitwise combination of flags is possible:
otSetCDFlags(OT_CD_FLAG_MESSAGES_WITH_HYPOTHESIS |
             OT_CD_FLAG_EXCEPTIONS_WITH_HYPOTHESIS);

Preparing TriParameterList for triEnqueueReply

  • consult how many parameters the relevant signature definition in TTCN-3 has
    • e.g. four (see Signature definition above)
TriParameterList pl;
pl.parList = (TriParameter **) malloc(sizeof(TriParameter*) * 4);
pl.length = 4;

TriParameter** parList = pl.parList;

parList[0] = (TriParameter*) malloc(sizeof(TriParameter));
memset(&(parList[0]->par), 0, sizeof(BinaryString));
parList[0]->mode = TRI_IN;

parList[1] = (TriParameter*) malloc(sizeof(TriParameter));
memset(&(parList[1]->par), 0, sizeof(BinaryString));
/* Add code to set the value of parList[1]->par which is a
   BinaryString and which should contain encoding of the
   parameter in binary form */
parList[1]->mode = TRI_OUT;

/* Do similar thing for the rest of IN parameters as
   we did for parList[0].

   Do similar thing for the rest of INOUT, OUT parameters as
   we did for parList[1].
 */

  • use malloc() to allocate data holder for encoding for e.g. parList[1]->par.data
  • use malloc() to allocate data holder for encoding for signature return value:
TriParameter rv;
rv.par.data = (...) malloc(...);
rv.mode = TRI_IN;

Preparing TriParameterList for triEnqueueCall

  • consult how many parameters the relevant signature definition in TTCN-3 has
    • e.g. four (see Signature definition above)
TriParameterList pl;
pl.parList = (TriParameter **) malloc(sizeof(TriParameter*) * 4);
pl.length = 4;

TriParameter** parList = pl.parList;

parList[0] = (TriParameter*) malloc(sizeof(TriParameter));
memset(&(parList[0]->par), 0, sizeof(BinaryString));
/* Add code to set the value of parList[0]->par which is a
   BinaryString and which should contain encoding of the
   parameter in binary form */
parList[0]->mode = TRI_IN;

parList[1] = (TriParameter*) malloc(sizeof(TriParameter));
memset(&(parList[1]->par), 0, sizeof(BinaryString));
parList[1]->mode = TRI_OUT;

/* Do similar thing for the rest of IN, INOUT parameters as
   we did for parList[0].

   Do similar thing for the rest of OUT parameters as
   we did for parList[1].
 */

C module testing

Example C header file (My_C_Intf.h):

struct A
{
    int f1;
    char f2
};

int MyF(A p1, int p2);

Idea (similar to CORBA and IDL-to-C compiler):

  • generate 'stubs' on the TTCN-3 source code side (TTCN-3 typedefs and signatures)
  • generate 'skeletons' on the adapter side (portions of generated adapter code that call C functions directly locally)

This implies that C adapter is linked statically with the SUT C interface (same binary).

Example of generated 'stub' in TTCN-3 code:

type record A
{
    c_int f1,
    c_char f2
}

signature MyF(in A p1, in c_int p2) return c_int;

Signature parameters are then encoded into direct in-memory representation in the adapter (see relevant article in http://wiki.openttcn.com for more details on how to do that, 'C to TTCN-3 mapping').

Example of generated 'skeleton' on the adapter side (function parameters are already encoded into in-memory representation):

TriStatus triCall(...)
{
    // Launch a new thread to make a C call:
    ...
}

// The following code is automatically generated by a translator
void MyNewThread(... /* same list of parameters as in triCall*/)
{
...
if (!strcmp(signatureId->objectName, "MyF"))
{
    MyF(*((A *) parameterList->parList[0].data),
        *((int *) parameterList->parList[1].data));
}
else if (!strcmp(signatureId->objectName, "MyF2"))
{
    MyF2(...);
}

...

}

Theory track (continued)

triExternalFunction

TriStatus triExternalFunction
(const TriFunctionId* functionId,
 TriParameterList* parameterList, /* inout parameter */
 TriParameter* returnValue); /* out parameter */

In TTCN-3:

external function F1(
    in integer p1,
    inout RecType p2,
    out boolean p3)
return charstring;

function MyFunct()
{
    ...
    var RecType v1;
    var boolean v2;

    var charstring v3 := F1(2 + 5, v1, v2);

    // After call to F1(), v1 may and v2 should contain value assigned by the adapter.
}

Typical structure of tciEncode()

BinaryString tciEncode(TciValue value)
{
    TciType valType = tciGetType(value);
    String typeName = tciGetName(valType);

    BinaryString result;
    memset(&result, 0, sizeof(BinaryString));

    if (!strcmp(typeName, "T1"))
    {
        // Encoding of T1 value
    }
    else if (!strcmp(typeName, "T2"))
    {
        // Encoding of T2 value
    }
    ...
    else if (!strcmp(typeName, "T30"))
    {
        // Encoding of T30 value
    }
    else
    {
        otReportError("tciEncode(): Cannot recognize typeName");
    }

    return result;
}

otReportError() is defined in isl/TTCN3.h (see comments there for more details):

int otReportError(const char* errorReason);

Structure of tciEncode() that uses variant attribute

BinaryString tciEncode(TciValue value)
{
    TciType valType = tciGetType(value);
    String typeVariantAttr = tciGetTypeEncodingVariant(valType);

    BinaryString result;
    memset(&result, 0, sizeof(BinaryString));

    if (!strcmp(typeVariantAttr, "my variant attr val 1"))
    {
        // Encoding of value with type having 'val 1' variant attribute
    }
    else if (!strcmp(typeVariantAttr, "my variant attr val 2"))
    {
        ...
    }
    ...
    else if (!strcmp(typeVariantAttr, "my variant attr val 3"))
    {
        ...
    }
    else
    {
        otReportError("tciEncode(): Cannot recognize variant attribute");
    }

    return result;
}
  • use STL map to speedup type and variant lookup
  • or use OpenTTCN-supplied solution to speedup correct decoder resolution

otSetEncoder - Setting specific encoder (for performance reasons)

DOC

BinaryString myTciEncode(TciValue value)
{
    // Default (generic) encoder responsible for encoding of values of all types
    // except T1 and MyModule.T2.
    ...
}

BinaryString myTciEncode2(TciValue value)
{
    // This function is responsible solely for encoding of values of type T1
    ...
}

BinaryString myTciEncode3(TciValue value)
{
    // This function is responsible solely for encoding of values of type MyModule.T2
    ...
}

int main()
{
    ...
    otSetEncoder(0, &myTciEncode);
    ...
}

// Problem: tciGetTypeForName() calls
void F()
{
    otSetEncoder(tciGetTypeForName("T1"), &myTciEncode2);
    otSetEncoder(tciGetTypeForName("MyModule.T2"), &myTciEncode3);
}

  • create MTC (TCI-CD request server) (tci/tci_ext.h):
// Creates MTC service TCI-CD query request as a side effect
int otTMSelectSession(const char* sessionName);
  • or put F() into triExecuteTestcase()
  • or check documentation for otRegisterSetCodecFunction() in tci/tci_ext.h

SUT action (implicit send in TTCN-2)

In TTCN-3:

action("Please dial the number +1234567");

Callback in TRI:

TriStatus triSUTActionInformal
(const char* description);

In TTCN-3:

action(myTmplRef);

Callback in TRI:

TriStatus triSUTActionTemplate
(const TriActionTemplate* templateValue);

Memory management rules for TRI interface

  • discussed in the preamble for tci/tci.h
  • for TRI callbacks ('provided functions' is the standard term used throughout TCI specification) like triSend(), the rules are quite simple:
  • you (the adapter developer) do not own pointers and their content supplied to you by the SDK in the callback functions like triSend(), triCall(), triExecuteTestcase() etc.
  • supplied parameters are thread-safe and they are safe to use for the duration of the user-implemented callback handling
  • it is unsafe to use a stored shallow copy of a pointer supplied to the callback like triSend() by the framework after the callback finishes and returns control back to the SDK, because there is no guarantee that the pointer still exists after the callback returns (SDK can get rid of it at any moment of time as it sees fit)
  • if you want to use some parameter after callback returns, you need to make your own deep private copy if the parameter in question
  • a deep copy can be made for almost any TRI parameter using OpenTTCN-supplied otDuplicate...() family of functions defined in tri/tri_ext.h
  • don't forget to deallocate your own private copy after you no longer use it to avoid a memory leak
  • this could be done using otRelease...() family of OpenTTCN-supplied functions found in tri/tri_ext.h (they do a deep deallocation of relevant data structures)
  • for TRI SDK-supplied (or TE-supplied) API functions ('required functions' is the standard term used throughout TCI specification) memory management rules are as folows:
  • you (the adapter developer) own data that you pass to API function in place of function formal parameters
  • memory management rules for return value of 'provided' and 'required' TRI operations are trivial and they do not need to be defined, because all of them return exclusively either void or TriStatus which is defined as long int (primitive non-pointer data type)

Memory management rules for TCI-CD interface

  • tciEncode()
  • you (the adapter developer) do not own TciValue passed as a parameter
  • from the perspective of adapter developer TciValue passed as a parameter is immutable and shall not be changed (so you can only introspect it)
  • you do not own returned BinaryString; in particular, you do not own the 'data' field after you return it back to the caller
  • you are still responsible for allocating the 'data' field using malloc(), but it is not you but the SDK that deallocates the 'data' field using free() at some convenient time
  • the 'aux' field is not interpreted by the SDK in any way
  • the SDK will not try to deallocate data referred to by the 'aux' field by itself
  • the SDK will pass the content of the 'aux' field unmodified to the other entity (e.g. triSend()), unless standalone codec is applicable, in which case content of 'aux' field will be lost
  • tciDecode()
  • you do not own message and decHypothesis parameters
  • it is a rule of thumb that the SDK will always own any data structure of type TciType (stored in the internal cache buffers)
  • you stop owning TciValue at the moment you return it back to the caller of tciDecode()

Multiplication by eight is equal to shift left by three

int lenInOctets = 5;
BinaryString bs;
memset(&bs, 0, sizeof(BinaryString));
bs.data = (unsigned char *) malloc(lenInOctets);
bs.bits = lenInOctets << 3; // equivalent to lenInOctets * 8

Encode and variant attributes

Example of typedef with encode and variant attributes:

type RecType record
{
    integer f1,
    integer f2
}
with
{
   encode "ENC:123",
   variant "VAR:123
A:1,
B:2,
bits=3"
}

Retrieval of encode, variant attributes for typedefs:

In tci/tci.h:

/* OpenTTCN Specific: MM4RV is as defined in [RM1]. */
String tciGetTypeEncoding(TciType inst);

/* OpenTTCN Specific: MM4RV is as defined in [RM1]. */
String tciGetTypeEncodingVariant(TciType inst);

Example of value with encode and variant attributes:

template RecType t1 := { f1 := 1, f2 := 2}
// with { encode "ABC", variant "DEF" }
...
p.send(t1);

-> tciEncode(t1); (invocation inside the SDK for C code)

Retrieval of encode, variant attributes for values:

In tci/tci.h:

String tciGetValueEncoding(TciValue inst);

String tciGetValueEncodingVariant(TciValue inst);

Example of adapter code:

// String is defined as char*
String myEnc = tciGetTypeEncoding(tciGetTypeForName("MyTestSuiteType"));

if (!strcmp(myEnc, "MyEncodingValue"))
{
  ....
}

// When the function is exited, you do not need to
// deallocate memory occupied by myEnc.

Encode and variant attributes: example of use

TTCN-3 code:

type integer INT_16
with { variant "type=integer;length-in-bits=16;octet-order=msb-first" }

type integer INT_32
with { variant "type=integer;length-in-bits=32;octet-order=msb-first" }

type record Message
{
    INT_16 f1,
    INT_32 f2
}
with { variant "type=record-container" }

Conventional approach to tciEncode():

BinaryString tciEncode(TciValue value)
{
    TciType valType = tciGetType(value);
    String typeName = tciGetName(valType);

    BinaryString result;
    memset(&result, 0, sizeof(BinaryString));

    if (!strcmp(typeName, "Message"))
    {
        // Encode field f1 using hard-coded knowledge that it is 16 bit
        // Encode field f2 using hard-coded knowledge that it is 32 bit
    }
    else
    {
        otReportError("tciEncode(): Cannot recognize typeName");
    }

    return result;
}

Approach to tciEncode() using variant info (generic encoder):

BinaryString tciEncode(TciValue value)
{
    TciType valType = tciGetType(value);
    String variant = tciGetTypeEncodingVariant(TciType inst);

    map<string, string> variantItems = parseVariant(variant);

    BinaryString result;
    memset(&result, 0, sizeof(BinaryString));

    if (variantItems["type"] == "record-container")
    {
        // Call tciEncode() recursively for all fields of the record
        // container and concatenate result of encoding into single
        // result buffer.

        ...
    }
    else if (variantItems["type"] == "integer")
    {
        int lenInBits = stringToLong(variantItems["length-in-bits"]);
        string octetOrder = variantItems["octet-order"];

        if (octetOrder == "msb-first")
        {
            // Encode integer value into number of bits specified in lenInBits,
            // most significant byte first

            ...
        }
        else if (octetOrder == "lsb-first")
        {
            // Encode integer value into number of bits specified in lenInBits,
            // least significant byte first

            ...
        }
    }
    else
    {
        otReportError("tciEncode(): Cannot recognize variant type");
    }

    return result;
}

Memory management for TCI containers

  • when you return from tciDecode() you stop owning the return value
  • when you establish a container-contained relationship between two TciValue's, e.g. using:
  • tciSetRecFieldValue()
  • tciSetRecOfFieldValue()
  • tciAppendRecOfFieldValue()
  • tciSetUnionVariant()

... then you transfer ownership of the newly contained TciValue from yourself to the container

  • with this system you almost never need to deallocate TciValue by yourself
  • if in some case you need to manually deallocate TciValue, use otReleaseTciValue() from tci/tci_ext.h
  • typically you might need to call otReleaseTciValue() only in case of erroneous input frame, when the decoding failed half-way
  • never try to reuse contained elements in more than one container-contained relationship, as this may lead to a segmentation fault when the corresponding container is deallocated. Example (don't do this):
TciValue lenField = tciNewInstance(tciGetTypeForName("uint16"));
TciValue contentField = tciNewInstance(tciGetTypeForName("MyContent"));

TciValue payload = tciNewInstance(tciGetTypeForName("Payload"));

tciSetRecFieldValue(payload, "len", lenField);
tciSetRecFieldValue(payload, "content", contentField);

// The following will lead to segmentation fault:
tciSetRecFieldValue(payload, "alt_content", contentField);

How to organize test material most efficiently

  • use modular design
  • typical TTCN-3 test suite contains the following modules:
  • type definitions (message or procedure definitions)
  • could be combined with test system configuration (definition of port types and component types)
  • module parameters (PICS and PIXIT parameters in terms of TTCN-2)
  • definition of message templates (typically parameterized templates)
  • library of reusable functions and altsteps
  • test cases can be split into several modules if there are too many of them
  • control part

Distributed infrastructure

  • more information and documentation on DI is available from OpenTTCN on demand

SDK for Java

  • SDK for Java contains an example that can be used as a base for development

Final remarks

More technical documentation is available at:

http://wiki.openttcn.com

Thank you!


Follow-up: adapter example

Full adapter example (adapter source code in C, TTCN-3 test case, example SUT) can be downloaded here.

Views
Personal tools