![]() |
![]() |
![]() |
![]() |
![]() |
Completing the Adaptation
The generated code has to be extended before it is complete in order to test the intended implementation. This section describes how to make these extensions.
Figure 278 displays in a simple way the anatomy of an executable test suite.
When code is generated by the code generator, it does not know anything about the system it is about to test. It assumes that it will have access to certain functions, implemented by the user. This is the "Test Support Functions" module in Figure 278.
One important thing to remember is that the previously defined interface called GCI, is a standardized set of functions.See The GCI Interface. Adaptation should be made using the functions and data types defined by that interface.
The Test Support Functions
This is the "glue" between the TTCN Runtime Behavior and the IUT. It is a set of functionality (functions) that is adaptation specific and should be provided by the user.
The following areas have to be covered and they are described in more detail in the sections they refer to.
Implementation and Handling of Timers
The timers defined in the Test Suite must have a real representation in the test environment. The TTCN Runtime Behavior will, when necessary, ask the adaptation about the status of a timer. See Representation and Handling of PCO and CP Queues.
Representation and Handling of PCOs [and CPs]
Messages are communicated through Points of Control and Observation and Connection Points. The actual buffering of incoming IUT messages must be supplied by the adaptation. See Representation and Handling of PCO and CP Queues.
Communication with Test Equipment [and PTCs]
To be able to test the IUT at all, there must be actual communication channels to and from it. The actual communication is of course totally depending on the system environment. See IUT Communication.
Encoding and Decoding
Values in the test suite (constraints, variables, constants, etc.) must be properly encoded and decoded for sending them to the IUT. The actual protocol for encoding decoding is up to the user. See Encoding and Decoding and Encoding and Decoding Using BER for more details.
Implementing the Adaptor the Efficient Way - the Adaptation Framework
The implementation of timer handling and communication primitives can be made by calls to the Adaptation Framework API, which permits the underlying "hard" protocol and timer implementation to change without having to change the high-level adaptation code. See The Adaptation Framework for a thorough explanation.
Timers
Timers need some special attention. As timers are implemented differently on different systems the implementation of the timers might differ. See the timers adaptation template in the installation for ideas.
Timers are simply a number of constructions to keep track of each of the test suite timers. In the generated code a timer is represented by an integer descriptor which uniquely identifies it. The timer implementation supplied with the Adaptation Framework implements the timeout functionality by maintaining an ordered list of the running timers, which means that retrieving the time left to timeout and the next timer to expire are quick operations.
To refresh the current status of timers, the timer lists need to be synchronized to the system time. This is made automatically by the snapshot function ACMWaitForEvent() (more on this function later) or can be made explicitly by a call to the function ACMSynchronizeTimers(). This function simply checks all running timers with the system clock, moving timers that are due to the list of expired timers.
There are four functions to consider regarding timers:
GciStartTimerGciCancelTimerGciReadTimerGciSnaphotThe interfaces to timers that can (and should) be called in the TTCN Runtime Behavior are:
GciTimeoutGciGetNoOfTimersGetTimerGciGetTimerNameGetTimerIndexFor details concerning these functions, see GCI C Code Reference.
The Adaptation Framework interfaces to timer functionality that can be used to implement the GCI functions are:
ACMStartTimerACMCancelTimerACMCurrentTimeACMGetTimedOutPosACMGetNextTimedOutACMReadTimerACMTimeLeftACMTimerStatusACMWaitForEventThese functions map to a real-time timer implementation that is provided with the installation of TTCN Suite and can be used "as-is". See Release Notes for details on which platforms are supported by this timer package.
Timer Adaptation Example
The timers adaptation template is the simple adaptation template with timers implemented. It demonstrates in general how to use the Adaptation Framework to implement timer functionality, and in particular the use of the call ACMSynchronizeTimers() that must be used in snapshot when ACMWaitForEvent() cannot be used.
IUT Communication
From an abstract point of view, sending and receiving is done over PCOs (Points of Control and Observation). The physical representation of these PCOs has to be defined by the user. It can be shared memory, serial communication, sockets, etc. This, of course, is only done on the controlling side. The PCOs have to be connected somewhere to the test equipment and the responsibility for this is put upon the user.
The GCI functions that must perform the communication is (at least):
GciSendGciReceiveFor details concerning these functions, see GCI C Code Reference.
Representation and Handling of PCO and CP Queues
PCOs are constructions to handle the PCO queues. Each PCO should have buffers for sending and receiving, a method for retrieving the status of the receive buffer and additional information such as channels and ports must be provided for the physical channels.
If the ETS is to run within the test equipment, i.e. the communication between the ETS and the IUT resides within the test equipment, the ETS has to be moved (cross compiling or if possible compiling within the test equipment).
This queue initialization should of course be made before any test case are run. The function that concerns the PCO's and CPs are:
GciSnapshotGciSendGciSnapshot() can be implemented using for instance ACMWaitForEvent(), and GciSend() can be implemented using ACMSend(). Today, the ACM layer builds upon a TCP/IP socket implementation but other protocol implementations can be easily added.
The interface that should be used when a message has been received (after proper decoding) in the TTCN Runtime Behavior is:
GciReceiveNote that if the ACM layer function ACMWaitForEvent() is used in the snapshot function, the user never has to call GciReceive() since this is done automatically from within ACMWaitForEvent()!
For details concerning these functions, see GCI C Code Reference.
Encoding and Decoding
tBuffer is the subsystem of UCF that is used to represent values of ASN.1 types in the transmitted bit pattern. This section describes the interface for tBuffer.
Functions
To invoke functions of tBuffer, the following macros are to be called:
Initialization of buffer
- You should initialize tBuffer before using it.
void BufInitBuf(tBuffer buf, tDirectionType dirtype, UCFStatus status)
Status of initialization. Variable "status" is equal to UCF_Ok if no errors were encountered during initialization or an error code otherwise.
Closing of buffer
- You should close tBuffer after working with it.
void BufCloseBuf(tBuffer buf)Initialization in write mode
- This function initializes tBuffer in write mode. You can write data to the buffer after calling this function.
UCFStatus BufInitWriteMode(tBuffer buf)Initialization in read mode
- This function initializes tBuffer in read data. You can read data from the buffer after calling this function.
UCFStatus BufInitReadMode(tBuffer buf)Closing write mode
- This function closes write mode of tBuffer. You should call this function after the `write to buffer' operation is completed.
void BufCloseWriteMode(tBuffer buf)Closing read mode
- This function closes read mode of tBuffer. You should call this function after reading from the buffer is finished.
void BufCloseReadMode(tBuffer buf)Getting direction type of the buffer
- This function returns the direction type of tBuffer.
tDirectionType BufGetDirType(tBuffer buf)
REVERSE mode is not supported in the buffers anymore, although the BufGetDirType function is still present in the buffer interface for backwards compatibility. It will always return DIRECT mode.
Creating copy of buffer
- This function makes a copy of the buffer.
UCFStatus BufCopyBuf(tBuffer dst, tBuffer srs)Getting the data byte length
- This function returns the buffer's data length in bytes.
tLength BufGetDataLen(tBuffer buf)Getting the data bit length
- This function returns the buffer's data length in bits.
tLength BufGetDataBitLen(tBuffer buf)Getting the available length
- This function returns the maximal data length you can write into the buffer in one piece (in bytes).
tLength BufGetAvailableLen(tBuffer buf)Getting byte
- This function reads one byte from the buffer. The buffer should be initialized in read mode.
unsigned char BufGetByte(tBuffer buf)Getting data segment
- This function reads a data segment from the buffer. The buffer should be initialized in read mode.
unsigned char* BufGetSeg(tBuffer buf, tLength seglen)Peeking byte
- This function peeks (reads, but does not remove) one byte from the buffer. The buffer should be initialized in read mode.
unsigned char BufPeekByte(tBuffer buf)Peeking data segment
- This function peeks (reads, but does not remove) a data segment from the buffer. The buffer should be initialized in read mode.
unsigned char* BufPeekSeg(tBuffer buf, tLength seglen)Putting byte
- This function writes one byte into the buffer. The buffer should be initialized in write mode.
void BufPutByte(tBuffer buf, unsigned char byte)Putting data segment
- This function writes a data segment into the buffer. The buffer should be initialized in write mode.
void BufPutSeg(tBuffer buf, unsigned char* data, tLength seglen)Putting bit
- This function writes one bit into the buffer. The buffer should be initialized in write mode.
void BufPutBit(tBuffer bud, unsigned char bit)Getting bit
- This function reads one bit from the buffer. The buffer should be initialized in read mode.
unsigned char BufGetBit(tBuffer buf)Putting bits
- This function writes bits into the buffer. The buffer should be initialized in write mode.
void BufPutBits(tBuffer buf, unsigned char bits, unsigned char num)Getting bits
- This function reads bits from the buffer. The buffer should be initialized in read mode.
unsigned char BufGetBits(tBuffer buf, unsigned char num)Putting padding bits
- This function writes padding bits in PER ALIGN variant of encoding. The buffer should be initialized in write mode.
void BufPutAlign(tBuffer buf)Getting padding bits
- This function reads padding bits in PER ALIGN variant of encoding. The buffer should be initialized in read mode.
void BufGetAlign(tBuffer buf)Set encoding variant (PER only)
- This function sets the encoding variant.
void BufSetEncVar(tBuffer buf, UCFEncVariant encVar)Get encoding variant (PER Only)
- This function returns the encoding variant.
UCFEncVariant BufGetEncVar(tBuffer buf)Setting up error catcher
- This function sets up error catcher. Returns 0 or code of error if any.
unsigned int BufSetCatcher(tBuffer buf)Getting error mode
- This function gets error mode (bem_Off or bem_On).
tBufferErrorMode BufGetErrorMode(tBuffer buf)Supported ASN.1 Types
- BOOLEAN
- INTEGER
- ENUMERATED
- REAL
- OBJECT IDENTIFIER
- NULL
- BIT STRING
- OCTET STRING
- IA5String
- NumericString
- PrintableString
- VisibleString
- UTCTime
- GeneralizedTime
- SEQUENCE
- SET
- SEQUENCE OF
- SET OF
- CHOICE
- Tagged types
- Open types
Encoding, and in Particular Decoding within ACM and GCI
The standard way to implement the encoder and decoder functions are to put at least one function of each in the file encoder.c and declare them in the file encoder.h. With every adaptation that is provided with the TTCN Suite installation are those files included, often with empty encode/decode functions where the user is supposed to enter the appropriate functionality. The functions should be of the following structure:
The Encoding Function
GciStatus Encoder_<name>(const GciValue*,
GciBuffer** )If encoding was successful, the GciBuffer should be loaded with the encoded data and GciOk returned by the function. Otherwise, the function should return GciNotOk.
The Decoding Function
GciStatus Decoder_<name>(const GciBuffer*,
int*,
GciValue**)If decoding was successful, the GciValue should be loaded with the decoded GCI value, the integer should contain the number of bytes that were consumed in the decoding process and the return status GciOk. Upon failure, the function should simply return GciNotOk.
Note that several encode/decode functions can exist, and the user can switch between them at will, either by calling different functions from GciSnapshot() or by registering different functions prior to calling ACMSnapshot(). See Registering the Active Decode Function for details.
General
A test suite has no knowledge of the encoding and decoding rules of the actual application protocol. The definition of signal components and the description of the signal flows are done in an abstract and high-level manner. The physical representation of the signal components and the definition of the actual transfer syntax is not defined within the test suite.
The encoding and decoding rules (functions) simply define a common transfer syntax between the test equipment and the executable test suite.
It is up to the user to write his/her own encoding and decoding rules using the GCI value representation. Even if the TTCN to C Compiler comes with an adaptation template that includes a general encoder and decoder, these rules can not be used at all times.
For test applications that need to send the same type of messages back and forth through a communication channel, the encoding and decoding functions must be related to each other in such way that the decoding function is the inverse function of the encoding function. This gives the following simple rule:
Message = Decode( Encode ( Message ) )This simply states, that if you decode an encoded message, you will get the original message back.
For applications that send and receive messages of different types (for example an application sending commands to an interface one way and receiving command results the other way), the encoding and decoding rules might not be related at all.
It is up to the user to identify how he/she needs to encode and decode messages to successfully be able to communicate with his/her test equipment.
Encoding and Decoding Using BER
TTCN Suite can generate encode/decode function definitions, that give an opportunity for representing values of each ASN.1 type as a string of eight-bits octets and transfer them between the environment and the ETS. TTCN Suite now have support for BER (Basic Encoding Rules) standard, defined in X.690 for the subset of ASN.1 that is defined by TTCN.
BER Encoder/Decoder Support Library
The BER support comes in the form of a static library that supports encoding/decoding functionality of ASN.1. Profiles of all the functions that a user can use are located in the ucf.h in the static files directory. This file should be included in the adaptation and the library should be linked together with the adaptation.
The library contains two basic functions that provide functions for encoding/decoding types, computing length of values and buffer handling procedures: BEREncode(...) and BERDecode(...).
How to Use BER Support in the TTCN Suite?
First, select Generate BER encoders/decoders in the Make options dialog. A file will be generated called asn1ende.h in your target directory. This file defines the encoding and decoding functions for the ASN.1 definitions in the test suite.
Second, you should implement the usage of the encoding functions in your adaptor.c. This is done in the GciSend and GciSnapshot functions. Also, you should define buffers and initialize them (this can be done in main.
The generated file <ModuleName_of_ASN.1-specification>_asn1icoder.h contains the encoding/decoding function definitions for ASN.1 objects in the Test Suite. For example, if you have ASN.1 ASP Type definition by name of ASPType1, two functions will be generated:
UCFStatus Encode_ASPType1(tBuffer, GciValue*);UCFStatus Decode_ASPType1(tBuffer, tLength*, GciValue**);The first parameter of the encode function is the buffer where the encoded data is placed. The decoding is made from the buffer to a GCI value.
The encoding/decoding functions are specific for the ASP/PDU types in question so when calling them in GciSend and GciSnapshot, you need to make sure that they are called with a value of the correct ASP/PDU type. One way to get around this problem is to define a single ASP/PDU type that is a CHOICE of all types that you are sending or receiving in your test suite.
Example
For example, we have a test suite with ASN.1 ASP Type
ASPType1 ::= INTEGERAnd ASN.1 ASP constraint of ASPType1 named
ASPCon1 ::= 5We have PCO named PCO1 and dynamic behavior like this:
PCO1 ! ASPType1 | Constraint: ASPCon1PCO1 ? ASPType1 | Constraint: ASPCon1And we want to use BER for encoding/decoding. After code generation we have a <ModuleName_of_ASN.1-specification>_asn1icoder.h file with the following definitions:
tLength Encode_ASPType1(tBuffer, GciValue*);tLength Decode_ASPType1(tBuffer, GciValue**);So in our adaptation file we need to declare, initialize and close BER buffers:
tBuffer InBuffer;tBuffer OutBuffer;.. (rest of the adaptation).int main(int argc, char* argv[]){BufInitBuf(InBuffer, DIRECT, status);BufInitBuf(OutBuffer, DIRECT, status);.. (ETS control).BufCloseBuf(InBuffer);BufCloseBuf(OutBuffer);return 0;}In GciSend() we should initialize buffer in WriteMode and encode value:
GciStatus GciSend( int pcod, GciValue* object ){UCFStatus status;BufInitWriteMode(OutBuffer);..Encode_ASPType1(OutBuffer, object);status = Encode_ASPType1(OutBuffer, object);if(status != UCF_Ok){UCFPrintErrorMessage(stderr,status);/* warning or exit */}..BufCloseWriteMode(OutBuffer);return GciOK;}In GciSnapshot() we should initialize buffer in ReadMode and decode value:
GciStatus GciSnapshot( ){UCFStatus status;tLength DecLength;BufInitReadMode(InBuffer);..Decode_ASPType1(InBuffer, &result);status = Decode_ASPType1(InBuffer, &DecLength, &result);if(status != UCF_Ok){UCFPrintErrorMessage(stderr,status);/* warning or exit */}..BufCloseReadMode(InBuffer);return GciOK;}How to Use the PER Support in the TTCN Suite
The PER support is used almost exactly like the BER support, so read above first to learn how to use the BER support. The PER Encoding/Decoding functions have been changed and now return a UCFStatus value:
UCFStatus Encode_ASPType1(tBuffer, GciValue*)UCFStatus Decode_ASPType1(tBuffer, tLength*, GciValue**)The primary difference to the BER support is that the PER support includes supporting different variants of PER. The variants supported are Unalign, Align and NoEndPad with the Unalign variant being the default. The Unalign and Align variants are, just like BER, eight-bit octet-oriented, but the NoEndPad variant is bit oriented which necessitates use of the bit-oriented access to the buffer instead of the byte-oriented access.
The mechanism to select PER encoding variants is based on the use of pre-processor symbols. By defining the symbol UCF_PER_DEFAULT_ENCODING_VARIANT to the value associated with the selected encoding variant before including the asn1ende.h file, all types will get the selected encoding variant unless explicitly overridden.
Overriding the default encoding variant can be done on a per type basis by defining the pre-processor symbol EncodingVariant_<type name> to the selected variant value before including the asn1ende.h file.
The following example shows a snippet of the adaptor.c file in a situation where all types but the type Type1 is to be encoded/decoded with the Align variant and Type1 is to use the Unalign variant.
Example 299 : Selecting PER encoding variant
#define UCF_PER_DEFAULT_ENCODING_VARIANT UCF_Align#define EncodingVariant_Type1 UCF_Unalign#include <asn1ende.h>The Adaptation Framework
To make it even easier to connect the generated code to "the real world", the Adaptation Framework (ACM) is introduced, a platform-independent API that encapsulates communication and timer handling provided by plug-in components. These components can be communication protocol implementations, simulated timer modules, etc. and can easily be replaced without having to rewrite the adaptation - provided the Adaptation Framework has been used to implement the GCI functions in the adaptor.
Currently, the TTCN Suite is delivered with the following plug-in modules for the framework:
This means that if the adaptation is written using the Adaptation Framework, the generated code can instantly be used with TCP/IP communication. As the framework also hides all internal mechanisms of the run-time system, the resulting adaptation will be easier to maintain, more general and much smaller in size.
Example 300 : An Implementation of GciSend with ACM
GciStatus GciSend (int pcod, GciValue* msg){GciBuffer encoded_value;GciStatus status;encoded_value.buffer = (char *) malloc( sizeof(char) * MAX_ENCODING_BUFFER + 1);encoded_value.current_length = 0;encoded_value.max_length = MAX_ENCODING_BUFFER;status = Encode(&encoded_value, msg);if (status != GciOk) {fprintf(stderr, "%s\n",ACMGetErrorMessage(ACMGetLastError()));return GciNotOk;}status = ACMSend(pcod, &encoded_value);free(&encoded_value);if (status != GciOk) {fprintf(stderr, "%s\n",ACMGetErrorMessage(ACMGetLastError()));return GciNotOk;}return status;}Adaptation Templates
An empty adaptation is copied to the code directory if the user has no prior adaptation.
It is up to the user to implement the functions in the GCI operational interface. These functions are called from the TTCN runtime behavior and should not be removed even if they are empty.
The function bodies and declarations are found in the empty adaptation files, but the function bodies are empty. They only contain a print statement about the function not being implemented.
The ACM adaptor also uses the Adaptation Framework for communication and timer handling and is a good example to study on how to use the Adaptation Framework to implement GCI functions.
Auxiliary Adaptation Functionality
This section describes some extra functionality which is included in the adaptation templates.
FILE* logStream;
- This is the stream to where log messages are written. The default value is stdout and is set in the main function, but can be set to whatever stream the user wishes to use.
extern const int Gc<tablename>D = {int}
- Constant numbers for all tables (test case table, PCO table, timer table, etc...) in TTCN. Used to search in the IcSymTab array (see symbol table below). An example is for GcTestCaseD = 517.
The simple adaptation template (in the installation) includes all empty functions for the previously described GCI operational functions to be defined by the user.
http://www.ibm.com/rational |
![]() |
![]() |
![]() |
![]() |