OpenTTCN/Developer corner/Creating adapter with SDK for Java 2010 archive

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


Creating adapter using Java



For archived version of this article describing adapter development and running tests using command-line tools click here. Command-line tools are still available and are fully functional in OpenTTCN Tester 2010 and OpenTTCN Tester 2011.


In this article we create an adapter in Java with OpenTTCN SDK for Java. We use OpenTTCN Tester 2010 IDE to develop the adapter in question.

Contents

Creating Eclipse Java project

After you have started OpenTTCN Tester 2010 IDE with a workspace of your selection, you can create projects within the current workspace.

  • go to File | New | Project...
  • pick the Java Project wizard
  • specify java_wiki_example in the Project name field of the wizard, otherwise keep the default project settings as they are and click Finish
  • make sure you are using the Java perspective of Eclipse. Proper perspective can be selected by clicking the Open perspective button in the top-right corner of the IDE.


Creating Java project in OpenTTCN Tester 2010

Adding Main.java to the project

  • right-click on the src subfolder of the newly created project and select New | Package option
  • enter com.openttcn.sdk.example.wiki in place of the package name and click Finish
  • right-click on the name of the newly created package and choose New | File option
  • enter Main.java in place of the file name and click Finish
  • add the following code to the newly created Main.java file:


package com.openttcn.sdk.example.wiki;

import com.openttcn.sdk.api.StartHere;
import com.openttcn.sdk.api.GeneralFailure;

public class Main {

    private static final int SLEEP_INTERVAL_IN_MILLIS = 100000;

    private static void sleepForever() {

        while (true) {

            try { Thread.sleep(SLEEP_INTERVAL_IN_MILLIS); } catch (Exception e) { }
        }
    }

    public static void main(String[] args) {

        try {

            StartHere.initialize();

            // Register the adapter to OpenTTCN server:
            StartHere.registerSelfToSession("java_wiki_example");

            // Enable printout of library diagnostic information:
            StartHere.setVerbose(true);
        }
        catch (GeneralFailure e) {

            System.err.println("Error: " + e.getErrorCode() + ": " + e.getMessage());
            System.exit(1);
        }

        System.out.println("Init OK!");

        sleepForever();
    }
}
  • StartHere.initialize() initializes the SDK library, what includes establishing a communication path between the adapter, a dedicated process in its own right, and OpenTTCN server containing TTCN-3 runtime environment;

Customizing project settings

After you save the Main.java file, assuming Project | Build Automatically option is enabled, you will see compilation errors in the Problems tab due to incompletely performed project configuration. This can be fixed by right-clicking the project name and selecting OpenTTCN Tester | Setup Java SDK build path. After doing so, everything is configured correctly and the adapter shall build automatically without errors.

Preparing to run adapter for the first time

Before running the adapter, we need to create an Eclipse run configuration:

  • right-click on the project name and select Run As | Run Configurations...
  • click on the Java Application section, then click on the New launch configuration button in the top-left corner of the window
  • enter SimpleAdapter in place of configuration name, java_wiki_example in place of project name, com.openttcn.sdk.example.wiki.Main in place of main class and click Apply, then Close


Creating Java Run Configuration in OpenTTCN Tester 2010


We also need to add an example TTCN-3 test suite to be used jointly with the developed adapter:

  • right-click on the java_wiki_example project and select New | Folder
  • enter ttcn as folder name and click Finish
  • right-click on the newly created ttcn folder and select New | Other | OpenTTCN | TTCN-3 File, then click Next
  • enter example as module name and click Finish
    • do NOT opt for creation of template code

Add the following preliminary code to example.ttcn:

module Main
{
}

and save the module. If automatic build is enabled, then the file shall compile automatically, if not, you need to compile it explicitly through Project | Build Project command.



Note: an alternative way to add example.ttcn to the ttcn folder is as follows:

  • right-click on the newly created ttcn folder and select New | File
  • enter example.ttcn as file name and click Finish
    • if asked about T-Rex nature, answer Yes (enable)
  • right-click on the java_wiki_example project and select OpenTTCN Tester | Toggle OpenTTCN compiler to enable compilation of TTCN-3 modules

This requires toggling OpenTTCN compiler, so this way is less preferred.



If TTCN-3 module compilation does not work, right-click on the java_wiki_example project and select OpenTTCN Tester | Toggle OpenTTCN compiler or OpenTTCN Tester | Add/remove OpenTTCN TTCN-3 Compiler from Project to enable compilation of TTCN-3 modules. Make sure you have TTCN-3 perspective enabled.

Running adapter for the first time

Now we can launch our adapter as follows:

  • right-click on the java_wiki_example project and select Run As | Run Configurations... | SimpleAdapter | Run

If everything goes fine, you should see a console output in the Console tab with the following text message: "Init OK!"

The launched adapter can be stopped by clicking the Stop button in the Console tab. The adapter console output window itself can be closed by clicking the Remove launch button.

Adding default callback handlers

Now we need to create a skeleton of our adapter code. One of the adapter roles is handling various test execution (TE) requests, including requests to send a frame to the SUT. In the adapter this is done by implementing callback handlers that are invoked by the SDK library in the background when a relevant request arrives from the TE.

Let's add some dummy default implementation of these callbacks for now and improve it later. For this we will need to add one more source file to the src folder into the same package next to Main.java, TriCommunicationSA.java. It will contain implementation of the SUT adapter including calls to network interface code.

Content of TriCommunicationSA.java:

package com.openttcn.sdk.example.wiki;

import com.openttcn.sdk.tri.Factory;

public class TriCommunicationSA extends com.openttcn.sdk.tri.TriCommunicationSA {

    public org.etsi.ttcn.tri.TriStatus triSend(
            org.etsi.ttcn.tri.TriComponentId componentId,
            org.etsi.ttcn.tri.TriPortId tsiPortId,
            org.etsi.ttcn.tri.TriAddress address,
            org.etsi.ttcn.tri.TriMessage sendMessage) {

        return Factory.createTriStatus(org.etsi.ttcn.tri.TriStatus.TRI_ERROR);
    }
}

Here we implement one important callback handler from the standard TRI interface that is invoked when the TE wishes to send a frame to the SUT:

In addition, we will need to add TciCDProvided.java source file to the src folder next to Main.java and add implementation of the following encoding and decoding handlers from the standard TCI interface:

Content of TciCDProvided.java:

package com.openttcn.sdk.example.wiki;

public class TciCDProvided
implements org.etsi.ttcn.tci.TciCDProvided {

    public org.etsi.ttcn.tri.TriMessage encode(
        org.etsi.ttcn.tci.Value value) {

        return null;
    }

    public org.etsi.ttcn.tci.Value decode(
        org.etsi.ttcn.tri.TriMessage message,
        org.etsi.ttcn.tci.Type decodingHypothesis) {

        return null;
    }
}

Now when we added skeletons of TRI SA and TCI CD handlers, let's register them in the SDK library in the main() function. Add the following section of code to Main.java after StartHere.registerSelfToSession("java_wiki_example");:

com.openttcn.sdk.tri.StartHereSA.
    setCallbackHandler(new TriCommunicationSA());

com.openttcn.sdk.tri.StartHerePA.
    setCallbackHandler(new com.openttcn.sdk.tri.TriPlatformPA());

com.openttcn.sdk.tci.StartHereCD.
    setCallbackHandler(new TciCDProvided());

This also registers default implementation of TRI PA handler that does nothing useful. Since we are not planning to implement TTCN-3 external functions this time, we did not add our own version of TriPlatformPA class to our codebase as we did for TriCommunicationSA. We use default implementation provided by the SDK library instead.

Everything should compile OK and "Init OK!" message should show up when you start new version of the adapter.

Now we can move forward and add real content to our dummy adapter implementation. To do this, we need to agree on what kind of protocol and SUT we are testing.

SUT definition

Main article: SUT definition

We will be testing a simple UDP-based protocol with a few defined frames, simple behaviour, and simple network interface.

For precise protocol definition, please read the main article of this section.

In our test configuration SUT has server role and test harness simulates a client. Frame exchange between the two peers, client and server, uses UDP communication.

SUT pre-compiled binary can be downloaded here. Source code is also included for reference.

TTCN-3 message type definitions

Add the following content to example.ttcn instead of what it currently contains:

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;

}

Type Message is used to represent requests and responses in both format A and format B.

Type Raw is introduced to simplify construction of invalid frames and to provide sensible means for an adapter to communicate to test execution those frames that it is unable to decode.

Adding example test case

Add the following content to example.ttcn in addition to what it currently contains:

/*******************************************************************
 * 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
}

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);
}

Building test suite

If Project | Build Automatically option is enabled, then the test suite in OpenTTCN Tester 2010 IDE shall build automatically after you save the .ttcn file. If this option is disabled, then the test suite can be built manually using Project | Build Project command.

Preparing to run test case for the first time

Before running the test case, we need to create another Eclipse run configuration, this time for TTCN-3, not Java:

  • right-click on the java_wiki_example project name and select Run As | Run Configurations...
  • click on the OpenTTCN Test Suite section, then click on the New launch configuration button in the top-left corner of the window
  • enter SimpleTestSuite in place of configuration name, java_wiki_example in place of project name, then click the Test Plan tab, select Run selected test cases, select Main.TC_R2D2_001 test case, then click Apply, then Close


Creating OpenTTCN Test Suite Run Configuration in OpenTTCN Tester 2010


Running test case (first try)

Launch the adapter if it is not yet running (see Running adapter for the first time section of this article for more details), and run the test case that we have just added as follows:

  • right-click on the java_wiki_example project and select Run As | Run Configurations... | SimpleTestSuite | Run

If you see adapter executable output and not the campaign text log output in the Console tab of the IDE, you can switch between consoles by clicking the Display Selected Console icon in the top right corner of the Console tab and selecting the hello_run console from the list.


The console output for the test campaign shall complain with this kind of error message:

mtc : {15:07:09.375} : // VERDICT Main.TC_R2D2_001 TEST CASE ERROR: E4205: triSend() return status is not TRI_OK.

Indeed, we return TRI_ERROR from triSend() to indicate that we did not implement the TTCN-3 send request.

We also did not implement proper encoding of the message content and address field. This will be done later in this article.


From the command line, test case can be run as follows:

 tester run hello TC_R2D2_001


Creating encoder

Currently, functionality for encoding message content and address field is not implemented in our version of TciCDProvided.encode(). Before we go for implementing triSend(), we need to implement the encoding part first, because triSend() receives message and address parameters in already encoded form.

The TTCN-3 standard puts a test system developer in quite tight framework. Your triSend() handler always receives important parameters in encoded form, even if the business logic of the adapter implementation makes this undesirable.

Here is the code that needs to replace the earlier version of encode() in TciCDProvided.java:

public static final int MAX_BUFFER_SIZE = 4096;

public org.etsi.ttcn.tri.TriMessage encode(
    org.etsi.ttcn.tci.Value value) {

    int len = 0;

    org.etsi.ttcn.tci.Type valType = value.getType();
    String typeName = valType.getName();
    int typeClass = valType.getTypeClass();

    if (typeClass == org.etsi.ttcn.tci.TciTypeClass.ADDRESS) {

        byte[] buffer = new byte[MAX_BUFFER_SIZE];

        len = 0;
    	
        org.etsi.ttcn.tci.Value portField =
            ((org.etsi.ttcn.tci.RecordValue) value).getField("portField");

        int port =
            ((org.etsi.ttcn.tci.IntegerValue) portField).getInteger();

        buffer[len++] = (byte) (port >> 8);
        buffer[len++] = (byte) (port >> 0);

        org.etsi.ttcn.tci.Value hostField =
            ((org.etsi.ttcn.tci.RecordValue) value).getField("host");

        String host =
            ((org.etsi.ttcn.tci.CharstringValue) hostField).getString();

        byte[] hostAsBytes = null;

        try {
            hostAsBytes = host.getBytes("US-ASCII");
        }
        catch (java.io.UnsupportedEncodingException e) {

            Utilities.reportLocalError("Error: Unrecognized encoding.");
            return null;
        }

        System.arraycopy(
            hostAsBytes, 1, buffer, len, hostAsBytes.length - 2);

        len += hostAsBytes.length - 2;

        byte[] finalArray = new byte[len];

        System.arraycopy(buffer, 0, finalArray, 0, len);

        return com.openttcn.sdk.tri.Factory.createTriMessage(finalArray);
    }

    if (typeName.equals("Message")) {

        byte[] buffer = new byte[MAX_BUFFER_SIZE];

        len = 0;

        org.etsi.ttcn.tci.Value typeCodeField =
            ((org.etsi.ttcn.tci.RecordValue) value).getField("typeCode");

        int typeCode =
            ((org.etsi.ttcn.tci.IntegerValue) typeCodeField).getInteger();

        buffer[len++] = (byte) typeCode;

        org.etsi.ttcn.tci.Value payload =
            ((org.etsi.ttcn.tci.RecordValue) value).getField("payload");

        if (!payload.notPresent()) {

            org.etsi.ttcn.tci.Value lenField =
                ((org.etsi.ttcn.tci.RecordValue) payload).getField("len");

            int length =
                ((org.etsi.ttcn.tci.IntegerValue) lenField).getInteger();

            buffer[len++] = (byte) (length >> 8);
            buffer[len++] = (byte) (length >> 0);

            org.etsi.ttcn.tci.Value contentField =
                ((org.etsi.ttcn.tci.RecordValue) payload).getField("content");

            String content =
                ((org.etsi.ttcn.tci.CharstringValue) contentField).getString();

            byte[] contentAsBytes = null;

            try {
                contentAsBytes = content.getBytes("US-ASCII");
            }
            catch (java.io.UnsupportedEncodingException e) {

                Utilities.reportLocalError("Error: Unrecognized encoding.");
                return null;
            }

            System.arraycopy(
                contentAsBytes, 1, buffer, len, contentAsBytes.length - 2);

            len += contentAsBytes.length - 2;
        }

        byte[] finalArray = new byte[len];

        System.arraycopy(buffer, 0, finalArray, 0, len);

        return com.openttcn.sdk.tri.Factory.createTriMessage(finalArray);
    }

    Utilities.reportLocalError("Error: Unrecognized message type: " + typeName + ".");

    return null;
}

To use this code, you will need to add Utilities.java file to the src directory.

Sending frame to the SUT

Now everything is ready to add proper implementation of the triSend() handler to our code. It should extract IP address and UDP port of the SUT from the address parameter, and then send encoded data frame contained in the sendMessage parameter to the SUT using extracted address fields.

Here is a new triSend() handler for TriCommunicationSA.java that does all this:

public org.etsi.ttcn.tri.TriStatus triSend(
        org.etsi.ttcn.tri.TriComponentId componentId,
        org.etsi.ttcn.tri.TriPortId tsiPortId,
        org.etsi.ttcn.tri.TriAddress address,
        org.etsi.ttcn.tri.TriMessage sendMessage) {

    byte[] ipAddr = new byte[4];
    int[] portNumberHolder = new int[1];

    int result =
        Utilities.extractHostAndPortFromAddress(
            ipAddr, portNumberHolder, address);

    if (result != 0) return Factory.createTriStatus(
        org.etsi.ttcn.tri.TriStatus.TRI_ERROR);

    java.net.InetAddress inetAddr = null;

    try {
        inetAddr = java.net.InetAddress.getByAddress(ipAddr);
    }
    catch (java.net.UnknownHostException e) {

        Utilities.reportLocalError("Error: Invalid IP address format.");

        return Factory.createTriStatus(
            org.etsi.ttcn.tri.TriStatus.TRI_ERROR);
    }

    result = Utilities.sendDatagramPacket(
        inetAddr,
        portNumberHolder[0],
        sendMessage.getEncodedMessage());

    return Factory.createTriStatus((result == 0) ?
        org.etsi.ttcn.tri.TriStatus.TRI_OK :
        org.etsi.ttcn.tri.TriStatus.TRI_ERROR);
}

SUT implementation

Main article: SUT implementation

SUT implementation is available both in source (C/C++) and binary form. Main article of this section explains how to download, install, and run the SUT. Follow the instructions contained in the main article of this section before moving forward. You do not have to recompile the SUT because the downloadable package already contains the SUT binary.

SUT pre-compiled binary can be downloaded here. Source code is also included for reference.

Running test case (second try)

Before running the test case we need to make sure that the following conditions are met:

  • SUT is up and running (use start_sut.bat to launch it)
  • adapter is up and running (see Running adapter for the first time section of this article for more details)

Note that you need to restart the adapter every time you change and rebuild its source code. So make sure that the new version of the adapter has been built and restart it now.

Now run the test case as it was explained in Running test case (first try) section.

Sending now succeeds, but the test case fails after expiry of a guard timer started with the duration of 10 seconds. This happens because we currently do not handle responses from the SUT server in the adapter, so the test execution never sees them.

If you switch to the SUT window, you will see that it indeed received a valid encoded frame from the test harness containing a request and it responded back with a valid response.

Here is what the SUT window shall contain:

--------------------------------------------------------------------------
RECEIVED REQUEST MESSAGE (UDP PACKET) FROM PEER

  Remote host (source):     127.0.0.1
  Remote UDP port (source): 54758
  Local UDP port (dest):    7431

<<<<<
01 00 0D 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21
<<<<<
--------------------------------------------------------------------------
SENDING RESPONSE MESSAGE (UDP PACKET) TO PEER

  Local UDP port (source): 7431
  Remote host (dest):      127.0.0.1
  Remote UDP port (dest):  54758

>>>>>
02 00 0F 48 65 6C 6C 6F 2C 20 6D 61 6E 6B 69 6E 64 21
>>>>>

Creating UDP port listener thread

Now when we have the sending channel working, we need to implement a subsystem listening for incoming frames from the SUT. This is typically done by launching a dedicated thread that uses DatagramSocket.receive(), a standard Java API function call. This function blocks until a UDP frame arrives from the remote peer.

To launch a dedicated thread, we will implement Java Runnable interface and instantiate a new Thread object as explained in Sun's concurrency tutorial.

First, let's add SocketListener.java file containing implementation of the Runnable interface with the following content:

package com.openttcn.sdk.example.wiki;

public class SocketListener implements Runnable {

    public static final int MAX_BUFFER_SIZE = 65536;

    public void run() {

        if (Utilities._socketDesc == null) {

            Utilities._socketDesc = Utilities.bindSocketToAnyPort();

            if (Utilities._socketDesc == null) {

                Utilities.reportLocalError("Error: Cannot bind socket.");
                return;
            }
        }

        org.etsi.ttcn.tri.TriCommunicationTE te =
            com.openttcn.sdk.tri.StartHereSA.getRequestServer();

        java.net.DatagramPacket packet =
            new java.net.DatagramPacket(
                new byte[MAX_BUFFER_SIZE], MAX_BUFFER_SIZE);

        /* Loop forever */
        while (true) {

            /* Receive a packet from the SUT */

            try {

                Utilities._socketDesc.receive(packet);
            }
            catch (Exception e) {

                // Error during receiving, try again:
                continue;
            }

            // Process and encode address:

            java.net.InetAddress srcAddr = packet.getAddress();

            String host = srcAddr.getHostAddress();

            int port = packet.getPort();

            byte[] buffer = new byte[MAX_BUFFER_SIZE];

            int len = 0;
	    	
            buffer[len++] = (byte) (port >> 8);
            buffer[len++] = (byte) (port >> 0);

            byte[] hostAsBytes = null;

            try {
                hostAsBytes = host.getBytes("US-ASCII");
            }
            catch (java.io.UnsupportedEncodingException e) {

                Utilities.reportLocalError("Error: Unrecognized encoding.");
            }

            System.arraycopy(
                hostAsBytes, 0, buffer, len, hostAsBytes.length);

            len += hostAsBytes.length;

            byte[] finalArray = new byte[len];

            System.arraycopy(buffer, 0, finalArray, 0, len);

            org.etsi.ttcn.tri.TriAddress sutAddrEncoded =
                com.openttcn.sdk.tri.Factory.createTriAddress(finalArray);

            // Process message:
            
            byte[] buffer2 = new byte[packet.getLength()];

            System.arraycopy(packet.getData(),
                packet.getOffset(), buffer2, 0, packet.getLength());

            org.etsi.ttcn.tri.TriMessage msg =
                com.openttcn.sdk.tri.Factory.createTriMessage(buffer2);

            org.etsi.ttcn.tri.TriPortId p =
                com.openttcn.sdk.tri.Factory.
                    createTriPortId("tsiPort"); /* port name is hard-coded */

            te.triEnqueueMsg(p, sutAddrEncoded, null, msg);
        }
    }
}

We also need to add the following block of code to Main.java before System.out.println("Init OK!"); line to start the listener thread:

Thread t = new Thread(new SocketListener());
t.start();

Creating decoder

Our listener implementation enqueues frames in encoded form. They need to be decoded prior to supplying them to the TE.

Here is a replacement for the dummy implementation of decode() in TciCDProvided.java:

public org.etsi.ttcn.tci.Value decode(
    org.etsi.ttcn.tri.TriMessage message,
    org.etsi.ttcn.tci.Type decodingHypothesis) {

    org.etsi.ttcn.tci.TciCDRequired cd =
        com.openttcn.sdk.tci.StartHereCD.getRequestServer();

    String typeName =
        (decodingHypothesis != null) ? decodingHypothesis.getName() : "";

    if (typeName.equals("address")) {

    	byte[] srcAsBytes = message.getEncodedMessage();
    	
        /* Extract port */
    	
        int port =
        	((((int) srcAsBytes[0]) << 8) & 0xFF00) |
        	((((int) srcAsBytes[1]) << 0) & 0x00FF);

        /* Extract IP address */

        byte[] hostAsBytes = new byte[srcAsBytes.length - 2];

        System.arraycopy(srcAsBytes, 2, hostAsBytes, 0, srcAsBytes.length - 2);
        
        String host = null;

        try {
            host = new String(hostAsBytes, "US-ASCII");
        }
        catch (java.io.UnsupportedEncodingException e) {

            Utilities.reportLocalError("Error: Unrecognized encoding.");
            return null;
        }

        org.etsi.ttcn.tci.RecordValue addrValue =
            (org.etsi.ttcn.tci.RecordValue)
                cd.getTypeForName("address").newInstance();

        addrValue.setField("host",
            com.openttcn.sdk.tci.Utilities.charstringToTciValue(host));

        addrValue.setField("portField",
            com.openttcn.sdk.tci.Utilities.intToTciValue(port));

        return addrValue;
    }

    if (decodingHypothesis == null) {

        /* Assuming decoding of value of Message type */

        byte[] buffer = message.getEncodedMessage();
        int len = buffer.length;

        if (len == 0) {

            Utilities.reportLocalError("Error: Message content is too short.");
            return null;
        }

        if (len == 2) {

            Utilities.reportLocalError("Error: Message content is malformed.");
            return null;
        }

        org.etsi.ttcn.tci.Value typeCode =
            com.openttcn.sdk.tci.Utilities.intToTciValue((int) buffer[0]);

        org.etsi.ttcn.tci.RecordValue result =
            (org.etsi.ttcn.tci.RecordValue)
                cd.getTypeForName("Message").newInstance();

        result.setField("typeCode", typeCode);

        if (len > 2) {

            int length =
                ((((int) buffer[1]) & 0xFF) << 8) |
                ((((int) buffer[2]) & 0xFF) << 0);

            org.etsi.ttcn.tci.IntegerValue lenField =
                (org.etsi.ttcn.tci.IntegerValue)
                    cd.getTypeForName("uint16").newInstance();

            lenField.setInteger(length);

            byte[] contentBuffer = new byte[len - 3];
            System.arraycopy(buffer, 3, contentBuffer, 0, len - 3);

            String contentAsStr = null;

            try {
                contentAsStr = new String(contentBuffer, "US-ASCII");
            }
            catch (java.io.UnsupportedEncodingException e) {

                Utilities.reportLocalError("Error: Unrecognized encoding.");
                return null;
            }

            org.etsi.ttcn.tci.Value contentField =
                com.openttcn.sdk.tci.Utilities.
                    charstringToTciValue(contentAsStr);

            org.etsi.ttcn.tci.RecordValue payload =
                (org.etsi.ttcn.tci.RecordValue)
                    cd.getTypeForName("Payload").newInstance();

            payload.setField("len", lenField);
            payload.setField("content", contentField);

            result.setField("payload", payload);
        }

        return result;
    }

    Utilities.reportLocalError("Error: Unrecognized type: " + typeName + ".");
    return null;
}

Running test case (third try)

It works now! The test case passes, meaning that we have implemented everything correctly. Here is the output of the Console tab corresponding to the test campaign:

*****************************************************************************
*** MODULE PARAMETER LIST

modulepar
{
    charstring PX_SUT_IP_ADDR := "127.0.0.1";
    integer PX_SUT_PORT := 7431;
} // End of module parameter list.


*****************************************************************************
*** RUNNING TEST CASE Main.TC_R2D2_001

Tester : {16:38:34.486} : // Time: 16:38:34.486. Date: 19/Apr/2010. MOT version: TC: 4.0.0.
mtc : {16:38:34.509} : // CASE TC_R2D2_001 STARTED
mtc : {16:38:34.509} : java_wiki_example\ttcn\example.ttcn : 000102 : T_GUARD.start(10.0); // Timer is started: duration 10 s.
mtc : {16:38:34.510} : java_wiki_example\ttcn\example.ttcn : 000103 : map(mtc:p, system:tsiPort);
mtc : {16:38:34.518} : java_wiki_example\ttcn\example.ttcn : 000105 : p.send(Message GreetingRequest := {
    typeCode := 1,
    payload :=
    {
        len := 13,
        content := "Hello, world!"
    }
}) to {
    host := "127.0.0.1",
    portField := 7431
};
mtc : {16:38:34.549} : java_wiki_example\ttcn\example.ttcn : 000106 : p.receive(Message GreetingResponse := {
    typeCode := 2,
    payload :=
    {
        len := 15,
        content := "Hello, mankind!"
    }
}) to {
    host := "127.0.0.1",
    portField := 7431
};
mtc : {16:38:34.552} : java_wiki_example\ttcn\example.ttcn : 000108 : p.send(Message GreetingRequest := {
    typeCode := 1,
    payload :=
    {
        len := 17,
        content := "Hello, gentlemen!"
    }
}) to {
    host := "127.0.0.1",
    portField := 7431
};
mtc : {16:38:34.577} : java_wiki_example\ttcn\example.ttcn : 000109 : p.receive(Message GreetingResponse := {
    typeCode := 2,
    payload :=
    {
        len := 11,
        content := "Reformulate"
    }
}) to {
    host := "127.0.0.1",
    portField := 7431
};
mtc : {16:38:34.577} : java_wiki_example\ttcn\example.ttcn : 000111 : setverdict(pass);
mtc : {16:38:34.583} : // CASE Main.TC_R2D2_001 FINISHED WITH PASS

/////////////////////////////////////////////////////////////////////////////
/// TEST CASE Main.TC_R2D2_001 COMPLETE VERDICT PASS


*****************************************************************************
*** TEST EXECUTION SUMMARY

Pass    Fail    Inconc  None    Error   Total   Duration
1       0       0       0       0       1       00:00:00

What next

There are many ways to improve the adapter code that we have developed up to this moment. Things to consider:

  • encoding and decoding of invalid frames using Raw data type definition;
  • adding support for multiple test system interface ports; currently we have the only one tsiPort port, what greatly simplified our design; having more TSI ports may mean more UDP port listeners, more threads, and more complexity;
  • integrating adapter code with GUI test management software, if this is required by your project;
  • disabling debug output to improve overall system performance;
  • etc.
Views
Personal tools