OpenTTCN/Knowledge base/IDL to TTCN-3 mapping explained

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

Last modified December 13, 2007

IDL to TTCN-3 mapping explained


This article is based on the ETSI ES 201 873-8 [1] standard. In this article we explain how the IDL to TTCN-3 conversion works in practice, clarify the unclear parts of the original standard and present a consistent view of the IDL to TTCN-3 mapping.

What is IDL to TTCN-3 conversion?

IDL to TTCN-3 conversion is performed to facilitate testing of interfaces specified using OMG interface definition language, usually accessible through CORBA middleware.

Conversion is performed by a specialized translator (IDL converter) that is usually shipped by a TTCN-3 tool vendor to the end user. Given input IDL files, the converter generates a set of TTCN-3 source files in core language notation containing TTCN-3 modules as its output. Conversion rules are defined in the ETSI ES 201 873-8 [1] standard document and are explained in subsequent sections.

In addition to the IDL converter, you will need an IDL adapter written using standard TTCN-3 TRI [2] and TCI-CD [3] APIs. Since the adapter contains network interface code, it is implemented for a particular IDL middleware platform, most commonly CORBA middleware. You can implement the IDL adapter either by yourself using standard TRI [2] and TCI-CD [3] API, or a test system vendor may have a ready implementation that would be suitable for your needs and that can be integrated into your system.

Test configurations

Since IDL is a language to describe types, values, and interfaces, and not dynamic behaviour, output of the IDL converter is a set of TTCN-3 types and constants. Run-time provisions for (a) binding test harness to a tested IDL interface implementation or for (b) binding tested client to an IDL interface implementation simulated by a test harness are outside of the scope of the ETSI standard and are specific to a TTCN-3 tool vendor and middleware platform in use.

Case (a) of the previous paragraph is referred to as server testing, where the SUT has a server role, and case (b) of the previous paragraph is referred to as client testing, where the SUT has a client role. Since multiple IDL interfaces may be involved in testing with different roles, a generalized test model shall be referred to as mixed-mode client-server testing, as some SUT entities may have client roles, and some may have server roles. This is even more true for various callback scenarios, when a called servant calls back an interface operation collocated with the original calling client process. In this case client process also contains server-side interface implementation.

In terms of CORBA, client invokes server-side operation by initiating a CORBA request. CORBA servant implementing the operation receives the request. User-implemented code on the servant side sees this as an invocation of a callback handler that it needs to handle. Result of the processing is returned back to the client that sees this a completion of an operation call. For both client and server, it can be completely transparent that the operation call was actually a remote, not local one.

TTCN-3 design clearly derives some of its concepts from OMG IDL and CORBA, especially when it comes to procedure-based communication in TTCN-3. This makes TTCN-3 particularly well-suited for CORBA and IDL testing.

Getting started

Example IDL input file

Here is a simple IDL interface definition example (file Count.idl):

module Arithmetic {

interface Count {

    // Returns arg++:
    long increment(in long arg);
};

};

Converting IDL to TTCN-3

Now we need to convert the IDL input in the above example to TTCN-3. This can be done by invoking the idlconvert command-line utility from the command line:

idlconvert -d generated Count.idl

This converts IDL definitions contained in the Count.idl file to a set of TTCN-3 files that are put to the generated directory. Conversion is performed according to the mapping rules set forth in the ETSI ES 201 873-8 [1] standard.

The idlconvert utility is scheduled for release by OpenTTCN in the first quarter of the year 2008. Please write us to sales@openttcn.fi to find out more about availability and schedules.

The idlconvert utility produces the Arithmetic.ttcn file as its output, with the following content:

module Arithmetic
{

import from UsefulTypes all;
import from IDLDefs all;

group CountInterface
{

type Object CountObject;

type port Count procedure
{
    inout Count__incrementSignature;
};

signature Count__incrementSignature(in long arg) return long;

} // group CountInterface

};

You will need to add files UsefulTypes.ttcn and IDLDefs.ttcn to the test suite to be able to compile the generated module above. It can be compiled using the compile.bat script with the following content:

importer3 load idl_test generated\Arithmetic.ttcn library\IDLDefs.ttcn library\UsefulTypes.ttcn

Restricting ports

Note that presence of the inout specifier in the definition of the port type Count may relax your resulting TTCN-3 definitions more than you would like to. If you were testing exclusively server-side interfaces, then it should have been out only, to restrict the use of the port to testing servants only. If you were testing exclusively clients, then it should have been in only, to restrict the use of the port to simulating servants on the test execution side only.

But because the IDL converter does not know in advance what kind of testing you are going to perform, and because a test configuration can mix clients and servers rather freely, the IDL converter generates inout specifier for all.

A more strict validation of a test case logic can be desirable if exact test scenarios and test configuration are known in advance. For example, you may know for a particular test case that a certain CORBA interface is going to be used in the context of server-side testing only or in the context of client-side testing only. In this case stricter validation can be achieved by specifying extension attributes for a test system interface used by the test case in question. These attributes can be on a per-port basis, and since IDL interfaces map to TTCN-3 ports, these attributes are therefore be also on a per-interface basis. These extension attributes define role to the test port in a particular test configuration. Proposed attribute values per port are as follows: "client", "servant". Ports with the "client" role are used for client testing, and ports with the "servant" role are used for testing CORBA servants. If no "role" extension attribute is specified for a particular TSI port, then no additional validation of types of procedural primitives going through that port is performed.

Writing example test case

Now suppose we would like to make sure that the operation Arithmetic::Count::increment() is correctly implemented in our CORBA servant that we are going to test. To do this, we need two things:

  • test suite/test case that tests CORBA servant functionality in question;
  • CORBA/IDL adapter that is capable of communicating with the CORBA servant according to instructions it receives from the test suite.

Operation of CORBA/IDL adapter is discussed in subsequent sections. Now we will focus on writing a simple test case for our CORBA servant that tests increment().

The test case should be concerned with two major tasks:

  • finding object reference of the CORBA servant somehow, maybe through test suite parameters, or by using some other means;
  • invoking CORBA operation on the located servant and ensuring that the return value is according to the specification.

Let us see how all this is done in our example test case TC_test_increment. We assume that a named CORBA servant implementing the required increment() operation is up and running. We also assume that it is collocated with the test harness on the same host and is accessible through the following persistent corbaloc reference:

corbaloc::127.0.0.1:7432/Arithmetic_Count_if_1

TCP port 7432 was selected deliberately for this example and it has no special meaning.

Here is the Main.ttcn file containing TC_test_increment that tests increment():

module Main
{

import from Arithmetic all; // definitions generated from Count.idl

import from UsefulTypes all; // ETSI library of useful types
import from IDLDefs all; // library definitions for IDL testing

// Default value of the module parameter is hard-coded here for
// simplicity, but it can be changed externally by the end user.
//
modulepar charstring PX_ARITHMETIC_COUNT_SERVANT_CORBALOC :=
"corbaloc::127.0.0.1:7432/Arithmetic_Count_if_1";

// wait at most this much time for a response from servant
const float MAX_WAIT_INTERVAL := 2.0;

type component ComponentType
{
    port Arithmetic.Count p;
    timer T_GUARD := 10.0;

    var address servant_addr;
}

type component TestSystemInterface
{
    port Arithmetic.Count tsiPort;
}

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

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

    servant_addr := corbaloc2ior(
        PX_ARITHMETIC_COUNT_SERVANT_CORBALOC);

    p.call(Arithmetic.Count__incrementSignature :
        { arg := 6 }, MAX_WAIT_INTERVAL) to servant_addr
    {
        // Note that defaults are NOT activated in this section

        [] p.getreply(Arithmetic.Count__incrementSignature :
            { - } value 7) from servant_addr
        {
            setverdict(pass);
        }
        [] p.getreply
        {
            setverdict(fail);
        }
        [] p.catch(timeout)
        {
            setverdict(inconc);
            stop;
        }   
    }
}

}

Note that it is the end user who creates the content of Main.ttcn, not the IDL converter idlconvert.

The corbaloc2ior() conversion function used in the code example above is defined in the IDLDefs module. It converts corbaloc to Interoperable Object Reference (IOR). In fact, CORBA object IOR is used to provide CORBA object identity and is used for values of the address type used in to and from clauses. The address type is also defined in IDLDefs.

We need to update our compile.bat as follows to be able to compile also Main.ttcn:

importer3 load idl_test generated\Arithmetic.ttcn library\IDLDefs.ttcn library\UsefulTypes.ttcn Main.ttcn

Running example test case

Before running the test case, we need to start the IDL adapter. From the command-line, it can be done this way:

start idladapter --session idl_test

This launches the IDL adapter binary that registers itself upon startup to the idl_test session in the OpenTTCN server running on localhost.

We also need to make sure that the CORBA servant under test is up and running.

Now we can run the test case:

tester run idl_test TC_test_increment
This concludes our introductory tutorial on the IDL to TTCN-3 mapping.

Essentials of IDL to TTCN-3 mapping

Type mapping

ETSI ES 201 873-8 [1] defines mapping of IDL types to TTCN-3 types. This means that for every type definition in an IDL module, the idlconvert utility generates a corresponding typedef in the relevant TTCN-3 module. Type references are also converted during the IDL to TTCN-3 translation to fit the TTCN-3 language rules. Type mapping rules used by the idlconvert utility are presented in Table 1.

Table 1: IDL to TTCN-3 type conversion rules
IDL type TTCN-3 type Example in IDL Output in TTCN-3 after conversion
Basic types

boolean

boolean[1]

typedef boolean T1;
type boolean T1;

char

iso8859char[2] 1)

typedef char T2;
type iso8859char T2;

float

IEEE754float[3] 2)

typedef float T3;
type IEEE754float T3;

double

IEEE754double[3] 2)

typedef double T4;
type IEEE754double T4;

long double

IEEE754extdouble[3] 2)

typedef long double T5;
type IEEE754extdouble T5;

long

long[3] 2)

typedef long T6;
type long T6;

long long

longlong[3] 2)

typedef long long T7;
type longlong T7;

octet

octet[4] 2) 3)

typedef octet T8;
type octet T8;

short

short[3] 2)

typedef short T9;
type short T9;

string

iso8859string[5] 2)

typedef string T10;
type iso8859string T10;

unsigned long

unsignedlong[3] 2)

typedef unsigned long T11;
type unsignedlong T11;

unsigned long long

unsignedlonglong[3] 2)

typedef unsigned long long T12;
type unsignedlonglong T12;

unsigned short

unsignedshort[3] 2)

typedef unsigned short T13;
type unsignedshort T13;

wchar

uchar[2] 2)

typedef wchar T14;
type uchar T14;

wstring

universal charstring[5]

typedef wstring T15;
type universal charstring T15;
Constructed types

enum

enumerated[6]

enum Order
{
    first,
    second,
    third
};
type enumerated Order
{
    first,
    second,
    third
}

sequence

record of[7]

typedef sequence<long> IntArray;
type record of long IntArray;

struct

record[8]

struct MyStruct
{
    boolean f1;
    long f2;
};
type record MyStruct
{
    boolean f1,
    long f2
}

union

union inside record[9] 4) 5)

union Register switch (char) 
{
    case 'a':
    case 'b': short AX;

    case 'c': long EAX;

    default:  octet AL;
};
type union RegisterType
{
    short AX,
    long EAX,
    octet AL
}

type record Register
{
    iso8859char discr,
    RegisterType val
}

array

record of[10] 6)

typedef long Table[10][5];
type record length(10) of record length(5) of long Table;
Special types

fixed[11][12]

IDLfixed[13] 2)

typedef fixed<7, 4> MyFixedPoint;
type IDLfixed MyFixedPoint;

Value example:[12]

const MyFixedPoint MyConst1 = 012.3450d;

Value example after conversion:

const MyFixedPoint MyConst1 :=
{
    digits := 7,
    scale := 4,
    value_ := "012.3450"
}

Object

Object[14] 1) 7)

typedef Object S1;
type Object S1;

any

anytype[15] 8)

typedef any MyOpenType;
type anytype MyOpenType;

exception

record[16]

exception MyException
{
    string reason;
    unsigned long minor;
};
type record MyException
{
    iso8859string reason,
    unsignedlong minor
}

native[17] 10)

native[18] 1) 9)

native S2;
type native S2;

valuetype[19] 10)

record[20]

valuetype MyValueType
{
    private string f1;
    private string f2;

    // initializer
    factory init(
        in string arg1,
        in string arg2);
};
type record MyValueType
{
    iso8859string f1,
    iso8859string f2
}

NOTES

1) The type is defined in the IDLDefs module, and its fully-qualified type reference includes the IDLDefs module reference. For example, a fully-qualified name of iso8859char is IDLDefs.iso8859char. You need to import the IDLDefs module into the module that uses this type.

2) The type is defined in the UsefulTypes module, and its fully-qualified type reference includes the UsefulTypes module reference. For example, a fully-qualified name of IEEE754float is UsefulTypes.IEEE754float. You need to import the UsefulTypes module into the module that uses this type.

3) The conversion rule for the octet type is OpenTTCN specific. ETSI ES 201 873-8 [1] defines IDL octet as TTCN-3 octetstring.[4]

4) The conversion rule for union is OpenTTCN specific. ETSI ES 201 873-8 [1] additionally uses enumerated to convert IDL union discriminator.[9] The original mapping however contains syntax errors like use of reserved keyword value. More important, it unnecessarily complicates mapping of the IDL union discriminator and limits its use considerably. If the purpose had been to provide additional validation for values used as a discriminator, then a typedef that is a subtype of the original union discriminator type with attached value list restriction enumerating allowed discriminator values would have done it better. Moreover, if the original IDL union declaration contains a default branch, then a full range of discriminator values within the original IDL discriminator type is allowed. Therefore, an attempt to map IDL discriminator type to enumerated shall be regarded as conceptually flawed and may even indicate lack of understanding of what an IDL union discriminator is.

5) Note that in order to access the default section of the original IDL type Register presented in the example, the discr field of mapped TTCN-3 Register type shall contain any value not mentioned in case sections of the original IDL type Register.

6) The conversion rule for IDL arrays is OpenTTCN specific. ETSI ES 201 873-8 [1] defines IDL array as TTCN-3 array.[10] Doing so however may greatly complicate use of TCI-compliant encoders and decoders or even make such use unfeasible, as ETSI ES 201 873-6 [3] that defines the TCI interface has no explicit support for TTCN-3 arrays.

7) The conversion rule for the IDL Object type is OpenTTCN specific. ETSI ES 201 873-8 [1] defines IDL Object as TTCN-3 address.[14] Such mapping is however regarded as being not in line with TTCN-3 testing methodology in which use of the address type is semantically reserved for the purposes of identification of remote SUT peer or peer test component to be used almost exclusively in to and from clauses of communication operations, and not for the purposes of being a part of payload data transfers. While TTCN-3 Object and address are indeed semantically very close in the context of the IDL to TTCN-3 mapping and resolve to the same parent type charstring, it has been regarded as the most clean solution to consider them separately nonetheless.

8) Limitations[21] of TTCN-3 anytype may require reconsidering the current conversion rule for the IDL any type in the future. For example, references to types defined in different modules but having colliding type names may not be properly handled by anytype.[21] In addition, TTCN-3 defines anytype relative to the module where it is used, while IDL any is defined in absolute terms. TTCN-3 defines anytype as a collection of all types known in the current module.[22]

9) The conversion rule for the IDL native type is OpenTTCN specific. ETSI ES 201 873-8 [1] defines IDL native as TTCN-3 address.[18] See Note 7 for general discussion on use of TTCN-3 address in value type mappings. OpenTTCN version of native is effectively defined as octetstring to enable proper handling of IDL language-dependent mappings for native. For example, in IDL to C++ mapping [8], native is often used as a pointer[23][24], and a C pointer itself can be conveniently represented using octetstring. In addition, since OMG IDL core specification [5] strongly discourages use of native in service or application interfaces[25], practical applicability of the native type mapping in the context of service interface testing is expected to be negligible.

10) Not supported by OmniORB 4.0.7.

Type references to IDL basic types are converted to type references to TTCN-3 useful types in line with the mapping presented in Table 1. For example, IDL type reference long long is converted to the TTCN-3 useful type longlong.

Type references to IDL user-defined constructed types are converted to type references to corresponding generated TTCN-3 types.

Effect of scope on name conversions

While IDL identifiers per se are not subject to any particular conversion rules due to a possible difference between sets of allowed characters in IDL and TTCN-3 identifiers, mapping rules for IDL module and interface definitions have an impact on the resulting symbol names in TTCN-3.

OMG IDL introduces two classes of units of scope:

  • modules;
  • interfaces.

IDL modules can be nested, that is, a module can be contained in another module. While interfaces cannot be nested, they can be contained in nested modules. As TTCN-3 does not have similar scoping tools, the IDL to TTCN-3 converter mimics IDL scopes by flattening identifiers of target definitions it generates.

Since IDL modules and interfaces can be nested and they introduce a unit of scope that cannot be conveniently represented using TTCN-3 native constructs, symbols generated in TTCN-3 incorporate module and interface scope information in the target symbol identifier.

The identifier conversion rules are as follows:

  • generated TTCN-3 module identifier incorporates identifiers of all modules in which the target generated module is contained; all identifier elements are separated by two underscores from each other;
  • identifiers of generated TTCN-3 top-level definitions like typedefs, constant definitions, operation signatures and so on, in addition to possible identifier postfix generation rules, incorporate identifier of an interface in which the target top-level definition is contained as a prefix separated by two underscores from the original identifier as it was defined in the original IDL input source.

Here is an example that illustrates the above said:

IDL input TTCN-3 output after conversion
module M1
{
    typedef long T1;

    module M2
    {
        typedef char T1;

        interface I1
        {
            typedef string T1;
        };
    };

    typedef octet T2;
};
module M1
{
import from UsefulTypes all;
import from IDLDefs all;

type long T1;
type octet T2;
}

module M1__M2
{
import from UsefulTypes all;
import from IDLDefs all;

type iso8859char T1;
type iso8859string I1__T1;
}

Resources

You can download examples used in this article by following this link.

Bibliography

  • [1] ETSI ES 201 873-8 V3.2.1 (2007-02): Methods for Testing and Specification (MTS); The Testing and Test Control Notation version 3; Part 8: The IDL to TTCN-3 Mapping
  • [2] ETSI ES 201 873-5 V3.2.1 (2007-02): Methods for Testing and Specification (MTS); The Testing and Test Control Notation version 3; Part 5: TTCN-3 Runtime Interface (TRI)
  • [3] ETSI ES 201 873-6 V3.2.1 (2007-02): Methods for Testing and Specification (MTS); The Testing and Test Control Notation version 3; Part 6: TTCN-3 Control Interface (TCI)
  • [6] ETSI ES 201 873-1 V3.2.1 (2007-02): Methods for Testing and Specification (MTS); The Testing and Test Control Notation version 3; Part 1: TTCN-3 Core Language

References

  1. ETSI ES 201 873-8 [1], clause 8.1.3 Boolean type
  2. 2.0 2.1 ETSI ES 201 873-8 [1], clause 8.1.2 Char and wide char type
  3. 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 ETSI ES 201 873-8 [1], clause 8.1.1 Integer and floating-point types
  4. 4.0 4.1 ETSI ES 201 873-8 [1], clause 8.1.4 Octet type
  5. 5.0 5.1 ETSI ES 201 873-8 [1], clause 8.3.2 String and wstring
  6. ETSI ES 201 873-8 [1], clause 8.2.3 Enumerations
  7. ETSI ES 201 873-8 [1], clause 8.3.1 Sequence
  8. ETSI ES 201 873-8 [1], clause 8.2.1 Struct
  9. 9.0 9.1 ETSI ES 201 873-8 [1], clause 8.2.2 Discriminated unions
  10. 10.0 10.1 ETSI ES 201 873-8 [1], clause 8.4.1 Arrays
  11. CORBA IDL Syntax and Semantics [5], clause 3.11.3.4 Fixed Type
  12. 12.0 12.1 CORBA IDL Syntax and Semantics [5], clause 3.10.1 Syntax. This clause explains use of literals and values of the IDL fixed type. It also effectively explains semantics of the IDL fixed type.
  13. ETSI ES 201 873-8 [1], clause 8.3.3 Fixed types
  14. 14.0 14.1 ETSI ES 201 873-8 [1], Annex B.2 Comparison of IDL, ASN.1, TTCN-2 and TTCN-3 data types
  15. ETSI ES 201 873-8 [1], clause 8.1.5 Any type
  16. ETSI ES 201 873-8 [1], clause 9 Exception declaration
  17. CORBA IDL Syntax and Semantics [5], clause 3.11.5 Native Types
  18. 18.0 18.1 ETSI ES 201 873-8 [1], clause 8.4.2 Native types
  19. CORBA IDL Syntax and Semantics [5], clause 3.9.1.6 Value Type Example
  20. ETSI ES 201 873-8 [1], clause 7.3 Value declaration
  21. 21.0 21.1 ETSI ES 201 873-1 [6], clause 6.2.6 The anytype, NOTE 1
  22. ETSI ES 201 873-1 [6], clause 6.2.6 The anytype
  23. IDL to C++ Language Mapping [8], clause 1.17.10.1 ValueFactoryBase Class
  24. IDL to C++ Language Mapping [8], clause 1.41.1 Mapping for Cookie
  25. CORBA IDL Syntax and Semantics [5], clause 3.11.5 Native Types, Note
Personal tools