This chapter contains information about the Generic Compiler Interpreter (GCI) interface and how to adapt the generated code.
The first part of this chapter describes for example the GCI interface model, the GCI interface using natural language and C and it provides details for the implementor of the interface. The different parts are explained in detail with several recommendations and examples of use.
The second part of this chapter describes how adapt the generated C code with the system that is to be tested. This includes for example descriptions of the use of a code generator, the adaption of the generated code, the running of the C Code Generator and the structure of the generated code.
The Generic Compiler Interpreter (GCI) interface standardizes the communication between a TTCN component supplied by a vendor and other test system components supplied by the customer, together forming a MOT, Method Of Test.
The GCI interface focuses on what an ATS needs in order to execute in term of functionality, and on what is needed in order to integrate TTCN into a larger system. This chapter contains a natural language description of the interface and a GCI C Reference.
The main purpose of the GCI interface is to separate TTCN behavior from protocol and test equipment specific code. The GCI interface shall be a standardized set of functions. Communication shall be done by calling functions, passing arguments to the functions and return values from the functions, and in no other way. The interface is bidirectional which means that both parties (the TTCN Runtime Behavior and the Test Adaptor) must provide services to each other. The TTCN Runtime Behavior shall at least provide services for handling values and managing tests (evaluating test cases etc.) and the Test Adaptor shall provide the protocol/test equipment specific parts of an executable (send, snapshot, timer functions etc.). The implementation of the functions in the TTCN Runtime Behavior may only depend on the ATS and 9646-3, no extra information shall be needed to implement them.
|
This section contains an informal description of how a test run might look like using the GCI interface. A test run is defined as a complete test session, where a selection of test cases are run to ensure a specific behavior of the IUT.
A test run begins when the user decides to run a test case or a collection of test cases. He will then use the Supervisory functions to start test cases and monitor their verdicts. The TTCN Runtime Behavior then executes TTCN, occasionally using the operational interface (send, snapshot, etc.) to gather information or to request some service. The TTCN Runtime Behavior handles verdicts internally during a test case and returns the verdict at the end of a test case.
Before using the TTCN Runtime Behavior, it must be initialized. After that the user chooses which test cases to run using the supervisory functions. The test cases returns a verdict which the user can use to form reports or stop test runs. The TTCN Runtime Behavior or the GCI interface does not impose any restrictions on this part. Any number of test cases can be run and each is commanded using the Supervisory functions.
Figure 265 : Start of test run
|
The test case typically at some point will try to send a message on some PCO. The TTCN Runtime Behavior then passes the information which message should be sent and on which PCO to the Test Adaptor. It is the responsibility of the Test Adaptor to properly encode the message and actually send it on some media (e.g. sockets, screen, printer port, pipe). Note that control is in the Test Adaptor until it returns to the TTCN Runtime Behavior. When control does return to the TTCN Runtime Behavior it assumes that the message was sent correctly and continues execution of the test case.
Eventually the test case will have emptied its possibilities to act and needs input from the environment. It therefore passes control to the Test Adaptor in order to take a snapshot of the IUT. Within the snapshot, the Test Adaptor then typically checks all PCO's for incoming messages and all timers for time-outs. If a message has arrived on a PCO, the Test Adaptor must decode the message and translate it into a proper GCI value. If a timer has timed out, the Test Adaptor must record which timer. The Test Adaptor acts as a filter between the IUT and the TTCN Runtime Behavior. Note that the actual reception of a message or timeout could be handled somewhere else (e.g. in an interrupt routine), only the official communication that the event has taken place must be done in SNAPSHOT.
Figure 266 : Send and Snapshot
|
Preliminary verdicts may be set during the test execution and when a final verdict is set, the test case returns to the Test Adaptor immediately. The Test Adaptor then has freedom to decide to continue the test run or not. A final verdict has meaning only in the context of the current test purpose. The meaning of a final verdict is not specified in the context of an entire collection of test cases. For example, when doing regression tests, all test cases should be run regardless of how many failed in order to get a complete picture of the status of the IUT.
The table below summarizes the responsibilities of the two GCI parties, the TTCN Runtime Behavior and the Test Adaptor. It is meant to describe the model and basic assumptions being made.
This section contains case studies of a send and a receive. For the case studies, we use an example PDU shown in Figure 267.
|
Nr | Lbl | Statement Line | Cref | Comment |
---|---|---|---|---|
|
|
L ! CR |
CR_c |
|
The semantic of the TTCN send statement is as follows:
The SendObject above is a temporary object containing the object to be sent. All steps above are initiated by the TTCN Runtime Behavior. Steps 3 to 5 require communication with the environment (IUT). The TTCN Runtime Behavior will build a SendObject using the constraint CR_c above. Then it will call the Test Adaptor function Send. By doing this, the TTCN Runtime Behavior has ensured that the message gets sent on whatever PCO was stated. The Test Adaptor function Send then encodes the message using the transfer syntax of the protocol and then sends the message on the medium that represents the PCO. The arguments passed to Send is the PCO and the message in the GCI representation, and the side effect of Send is to send the message in protocol representation on the PCO in test system representation. Note that the encoding and sending on a PCO might very well be represented as a function call.
|
Nr | Lbl | Statement Line | Cref | Comment |
---|---|---|---|---|
|
|
L ? CC |
CC_c |
|
The receive is a little more complicated than the send. Send is initiated by the TTCN Runtime Behavior while receive is an internal event in the TTCN Runtime Behavior. The actual reception of messages is done in the Test Adaptor and these actions are communicated to the TTCN Runtime Behavior in SNAPSHOT. Note that there may be a difference between the reception of a message and the notification to the TTCN Runtime Behavior that the message has arrived. The TTCN Runtime Behavior calls the function SNAPSHOT and when it returns, the TTCN Runtime Behavior expects all time-outs to have been recorded and all received messages to be put on the correct PCO queues in the correct format. In this case we study the message event only and not the timeout.
If SNAPSHOT does what is stated above, the TTCN Runtime Behavior will do the following on the receive line in the test case:
In Figure 269, we assume that decoding is done in SNAPSHOT. The message is decoded using the value manipulating primitives of the GCI interface. When the message has been decoded into something known to the TTCN Runtime Behavior, it is easy to assign it to a PCO using the GCI function receive.
Figure 269 : The receive event
|
This section contains general thoughts on methods used for designing the GCI interface. Choices have been made such as to provide reasonable trade-offs between speed and ease of use. An efficient implementation in C should be possible with the choices made.
One problem is how to identify global objects in TTCN, PCOs, CPs, ASPs etc. There are a number of requirements that should be met:
There are at least three alternative solutions to be considered:
#define TC1 192 #define TCVAR1 1211 Verdict = GciStartTestCase( TC1 ); Display( GciGetValue( TCVAR1 ));
Verdict = GciStartTestCase( "TC1" ); Display( GciGetValue( "TCVAR1" ));
Solution 1 gives the fastest execution but is very unsuitable for integration into a larger application. Solution 2 is at least possible to integrate into a GUI, and almost as fast as solution 1. Solution 3 is excellent for integration but gives slow execution.
In the GCI interface, we choose to use solution 3 (string references) in the management functions and solution 2 in the TTCN behavior functions. It would then be very easy to integrate an ETS into an already existing test system since the interface is string based while we still maintain speed in the execution of test cases.
A similar problem is how to identify fields in structured types. The simplest solution is to address them using integers field 0, field 1, etc, but then the Test Adaptor writer would get very little support from the compiler. From a the Test Adaptor writers point of view, he might want to address the fields using names and also get type checking aid from the compiler. The Test Adaptor writer also wants to write generic functions for encode/decode. The GCI interface supports both views, one view where substructures are identified by numbers, and one which in effect use name addressing.
The TTCN Runtime Behavior requires that all test suite operations are defined as functions at run-time. Arguments to and return values from the test suite operation functions shall use the GciValue* format. The name of the test suite operation function shall be the same as the name of the test suite operation in TTCN.
The log will provide a view of a test run. Most things happening in the system will need to be logged. We have identified a number of requirements on the log functionality:
The method that the GCI interface suggests is massive logging. Everything is logged at any time and every type of event (send, receive etc.) is assigned a number. That way it is easy for the Test Adaptor writer to write a log function that only logs interesting log messages.
The GCI interface must provide a stable way of manipulating values. This is most important for the Test Adaptor writer to be able to access and build values in his encode and decode functions. The GCI interface proposes not to specify the value representation but rather the methods which can be used on it. The GCI interface specifies what the Test Adaptor writer is able to do with values. The reason for this is that most vendors supporting the GCI interface would like to have their own value representation within their TTCN Runtime Behavior, and the representation used within TTCN has different requirements than the representation needed for the Test Adaptor.
This section contains the description of the GCI interface in natural language. The interface functionality is sorted by responsibility. The management, behavior and value interfaces are interfaces to the TTCN Runtime Behavior. The operational interface is the interface to the Test Adaptor.
The management interface consists of those functions necessary for initiating and managing test runs. They provide an API to the ATS. These functions are used by the Supervisory functions to govern test runs.
Must be called before the TTCN Runtime Behavior is used in any way. It will read test suite variables (using primitives in the operational i/f), set up test case selection references, test suite constants and any other initialization necessary. It should only be called once.
Shall run a test case according to the dynamic behavior in 9646-3. Input shall be a test case name or test case group name. The test case or all the test cases in the test group shall then be run in the order which they were listed in the ATS. If the selection reference of a test case or test case group is false, that test case or test case group shall not be executed. Verdict shall be set and communicated back to the caller. In case several test cases have been run, the verdict shall be the most negative of the verdicts from each test case (if one test case is FAIL, the verdict would be FAIL, if all are PASS, the verdict would be PASS).
This function works just line the function to start test cases, but starts test components for example when concurrent TTCN is used.
A list of names of test cases.
A list of names of test case groups.
Two functions are needed to return information about the amount of timers and PCOs in the system.
Help functions are needed to get some extra information about timers and PCOs. (For example name and type.)
A set of functions is needed to retrieve information about configurations when running concurrent TTCN.
Values of TTCN (and ASN.1) objects must be accessible from the Test Adaptor. A general access function will be provided. Information passed shall be an object name and the information passed back shall be the value of the object. Valid objects shall be test suite parameters, test case selection expressions, test suite constants, test suite variables and test case variables.
The behavior interface consists of functions used to notify the TTCN Runtime Behavior of events in the IUT. The functions are a formalism of what the TTCN Runtime Behavior needs to know about the status of the IUT. The functions are implemented in the TTCN Runtime Behavior and must be used in Snapshot.
Whenever a message has been received and decoded it must be passed to the TTCN Runtime Behavior in SNAPSHOT in order for it to match it with the constraint. The information passed shall be the PCO descriptor and the value. Note that the value passed in receive must be a GciValue*.
Whenever a timer has timed out the TTCN Runtime Behavior must be notified of it in SNAPSHOT. The information passed shall be the timer descriptor.
Whenever a test component has terminated its execution the TTCN Runtime Behavior needs to know about this. The information passed is the descriptor for the given test component.
The operational interface consists of those primitives necessary for the TTCN Runtime Behavior to implement TTCN, i.e. Send, Snapshot, StartTimer etc. This part of GCI can be compared to POSIX. TTCN Runtime Behavior requires that the functions are defined.
The TTCN Runtime Behavior shall call the Test Adaptor once for each test suite parameter during the initialization of the TTCN Runtime Behavior. Information passed shall be a handle for the value, the parameter name and the PICS/PIXIT reference.
Each test suite operation must be defined in the Test Adaptor. The name of the test suite operation function shall be the same as in the ATS, and the order of the parameters shall also be the same.
When a test component needs to be created this function will be called from the TTCN Runtime Behavior.
When different tests are run, different configurations might be needed. This function is called from the TTCN Runtime behavior to set up a configuration before the test goes on.
The status of the IUT must be read. Events in the IUT must be translated for the TTCN Runtime Behavior to be aware of them. The communicating interface between the TTCN Runtime Behavior and the IUT (environment) is made up of messages that has arrived and timers that have timed out. Therefore there are only two requirements on SNAPSHOT: 1) any received messages shall be recorded and 2) any timers that have timed out shall be recorded. The recording shall be done by using the provided GCI functions described in the Behavior i/f.
This function is used by the TTCN Runtime Behavior to ensure that messages get sent. Send probably involves encoding and physical sending but might as well translate to encoding of some parts of the message and use of a lower layer service. The information passed to the function shall be a PCO descriptor and the value to be sent.
When the TTCN Runtime Behavior needs to start a timer. Information passed shall be the timer descriptor, the integer timeout duration and the timer unit.
Shall pass the current timer value back to the caller. Information passed to the function shall be a timer descriptor.
Shall stop a timer identified by a timer descriptor.
Shall log events on an appropriate format. An integer specifying log message type and a log string shall be passed as parameters.
The interface to values is a simple API in which the user is allowed to build and access GciValues in an ordered way. The actual values used are not specified, only the methods that can be used on them are included. This way will allow vendors to have their own value representation within their TTCN Runtime Behavior and still conform to GCI.
Three types of value primitives are needed. Primitives to access structured values:
Value primitives can be further divided in the groups:
Please note that SET and SET OF values are treated as SEQUENCE and SEQUENCE OF values because there is no semantic difference at this level of abstraction. The unordered behavior of SETs is considered a decoder problem and left to the decoder writer.
This section is a description of GCI. It describes what each function does and why it is used. Some of the functions must be implemented by you in your adaptor. See section Adaption of the Generated Code.
This is two GCI value types with their respective value set:
GciStatus = { GciNotOk, GciOk }
GciVerdict = { GciFail, GciInConc, GciPass, GciNone}
The management interface consists of those functions necessary for initiating and managing test runs. They provide an API to the ATS. These functions are used by the Supervisory functions to govern test runs.
GciStatus GciInit()
GciVerdict GciStartTestCase( const char* testCaseName )
GciVerdict GciStartTestComponent(const char* testCompName)
GciTCList GciGetTestCaseList()
GciTCList GciGetTestCaseGroupList()
GciValue* GciGetValue( const char* name )
int GciGetNoOfTimers( )
int GciGetNoOfPCOs( )
char* GciGetTimerName( int timer )
char* GciGetPCOName( int pco )
int GciGetNoOfComponents( GciConf conf )
GciComponent* GciGetComponent( GciConf conf, int index )
char* GciGetComponentName( GciComponent* conp )
int GciGetComponentType( GciComponent* conp )
int GciGetComponentDescriptor( GciComponent* conp )
char** GciGetComponentCPs( GciComponent* conp )
char** GciGetComponentPCOs( GciComponent* conp )
The behavior interface consists of functions used to notify the TTCN Runtime Behavior of events in the IUT. The functions are a formalism of what the TTCN Runtime Behavior needs to know about the status of the IUT. The functions are implemented in the TTCN Runtime Behavior and must be used in Snapshot.
GciStatus GciTimeout( int timerd )
GciStatus GciReceive( int pcod, GciValue* msg )
GciStatus GciDone( int ptc )
The operational interface consists of those primitives necessary for the TTCN Runtime Behavior to implement TTCN, i.e. Send, Snapshot, StartTimer etc. This part of GCI can be compared to POSIX. TTCN Runtime Behavior requires that the functions are defined.
GciValue* GciReadTSPar(const char* name, const char* pRef)
GciStatus GciConfig( GciConf )
GciStatus GciCreate(int ptc, char* tree, GciValue* args)
GciValue* <TS_Op>( <Arguments> )
GciStatus GciSnapshot()
GciStatus GciSend( int pcod, GciValue* obj )
GciStatus GciImplicitSend( GciValue* value )
GciStatus GciStartTimer( int timerd, long timerDuration,
int timerUnit)
GciStatus GciCancelTimer( int timerd )
long GciReadTimer( int timerd )
GciStatus GciLog( int logId, const char* logString)
logId | Description | The logString must contain: |
---|---|---|
Msg |
General message |
|
StartTC |
Start test case |
The name of the test case |
StopTC |
Stop test case |
The name of the test case |
StartTS |
Start test step |
The test step name |
StopTS |
Stop test step |
The test step name |
StartDEF |
Start default |
The default name |
StopDEF |
Stop default |
The default name |
Verdict |
Final verdict set |
The verdict set |
PVerdict |
Prel. verdict set |
The verdict set |
Match |
Line tried matches |
Line number |
NoMatch |
Line tried does not match |
Line number |
SendE |
Send Event |
The PCO name |
RecE |
Receive Event1 |
The PCO name |
OtherE |
Otherwise Event |
The PCO name |
TimeoutE |
Timeout Event |
The timer name |
Assign |
Assignment |
The Left hand side |
StartT |
Start timer |
The timer name |
StopT |
Stop timer |
The timer name |
CancelT |
Cancel timer |
The timer name |
ReadT |
Read timer |
The timer name |
Attach |
Attachment |
The name of the attached test step |
ImplSend |
Implicit send |
The PCO name |
Goto |
Goto |
The line number to which the jump will be made |
Rec |
Receive2 |
The PCO descriptor number |
Timeout |
Timeout3 |
The timer descriptor number |
The interface to values is a simple API in which the user is allowed to build and access GciValues in an ordered way. The actual values used are not specified, only the methods that can be used on them are included. This way will allow vendors to have their own value representation within their TTCN Runtime Behavior and still conform to GCI.
These functions are used to transform actual values into the GCI representation. The interface is on base types only, so TTCN simple types are not visible in the interface.
GciValue* GciMkINTEGER( int num )
int GciGetINTEGER( GciValue* value )
GciValue* GciMkBOOLEAN( int bool )
int GciGetBOOLEAN( GciValue* value)
GciValue* GciMkREAL(int mantissa, int base, int exponent)
GciReal GciGetREAL( GciValue* value)
GciValue* GciMkBIT_STRING( const char* str )
char* GciGetBIT_STRING( GciValue* value)
GciValue* GciMkHEXSTRING( const char* str )
char* GciGetHEXSTRING( GciValue* value)
GciValue* GciMkOCTET_STRING( const char* str )
char* GciGetOCTET_STRING( GciValue* value)
GciValue* GciMkNumericString( const char* str )
char* GciGetNumericString( GciValue* value)
GciValue* GciMkPrintableString( const char* str )
char* GciGetPrintableString( GciValue* value)
GciValue* GciMkTeletexString( const char* str )
char* GciGetTeletexString( GciValue* value)
GciValue* GciMkVideotexString( const char* str )
char* GciGetVideotexString( GciValue* value)
GciValue* GciMkVisibleString( const char* str )
char* GciGetVisibleString( GciValue* value)
GciValue* GciMkIA5String( const char* str )
char* GciGetIA5String( GciValue* value)
GciValue* GciMkT61String( const char* str )
char* GciGetT61String( GciValue* value)
GciValue* GciMkISO646String( const char* str )
char* GciGetISO646String( GciValue* value)
GciValue* GciMkGraphicalString( const char* str )
char* GciGetGraphicalString( GciValue* value)
GciValue* GciMkGeneralString( const char* str )
char* GciGetGeneralString( GciValue* value)
GciValue* GciMkENUMERATED( int value )
int GciGetENUMERATED( GciValue* value)
GciValue* GciMkCHOICE(const char* name, GciValue* value)
GciValue* GciGetCHOICE( GciValue* value)
char* GciGetCHOICEName( GciValue* value)
GciValue* GciMkOBJECT_IDENTIFIER()
int GciOBJECT_IDENTIFIERSize( GciValue* value)
GciValue* GciAddOBJECT_IDENTIFIERComponent(
GciValue* value, int comp)
int GciGetOBJECT_IDENTIFIERComponent( GciValue* value,
int index)
GciValue* GciMkObjectDescriptor( const char* str )
char* GciGetObjectDescriptor( GciValue* value)
GciValue* GciMkNULL( )
int GciGetNULL( GciValue* value)
GciValue* GciMkANY( GciValue* value )
GciValue* GciGetANY( GciValue* value)
GciValue* GciMkR_TYPE( int value )
int GciGetR_TYPE( GciValue* value)
GciValue* GciMkPDU( GciValue* value )
GciValue* GciGetPDU( GciValue* value)
int GciGetType( GciValue* val )
GciValue* GciSetType( int type, GciValue* val )
The functions are divided into two meta sets derived from ASN.1: The sequence, and the sequence of, corresponding to struct and array in C. The choice type of ASN.1 is not represented as a meta class because all values in the GCI value representation can be typed and therefore is the choice value implicit in GCI.
The functions only works within their intended set (e.g. GciSetField(GciMkSEQUENCE(2), 1, GciMkINTEGER()) is well defined but GciSetField(GciMkSEQUENCEOF(), 0, GciMkINTEGER()) is not defined and certainly is unpredictable.
GciValue* GciMkSEQUENCE( int size )
GciValue* GciSetField( GciValue* seq, int index,
GciValue* fld )
GciValue* GciGetField( GciValue* seq, int index )
int GciSeqSize( GciValue* seq )
GciValue* GciMkSEQUENCEOF()
GciValue* GciAddElem( GciValue* seqOf, GciValue* elem )
GciValue* GciGetElem( GciValue* seqOf, int index )
int GciSeqOfSize( GciValue* seqOf )
These functions work correctly for values of correct type, and returns NULL or 0 if a value of the wrong type is used. <type> and <field> are the names found on types and fields (parameters etc.) found in the ATS.
GciValue* GciMk_<type>()
GciValue* GciGet_<type>_<field>( GciValue* seq )
GciStatus GciSet_<type>_<field>( GciValue* seq,
GciValue* fld )
GciStatus GciAdd_<type>_Elem(GciValue* seq, GciValue* elem)
GciValue* GciGet_<type>_Elem( GciValue* seq, int ix,
GciValue* elem )
int GciSize_<type>( GciValue* seq)
This section contains examples of how the mapping between TTCN/ASN.1 and their GCI representation could be done. The examples use a conceptual model for encoding/decoding, no error handling is done and no attention is paid to the fact that some functions would use pointers because of allocation issues.
Global objects (PCOs and types) are identified with an unique number. This number is given a symbolic reference which is its TTCN name with a D for Descriptor appended to the end of it. The number for the PCO L is therefore referenced as LD.
Each example consists of a table and the corresponding encode (and/or decode) functions. The examples focus on the value representation so the transfer syntax is simple: Each value is preceded by a header. The header contains the type of the value (as a number) and in some cases its length (element count, not size in bytes), in real ASN.1, the header would be built using tags. The header is written/read using primitives BufWriteType, BufWriteSeqOfSize, etc. They are not encode/decode primitives but rather buffer primitives.
An encode function is a function that translates the GCI value onto a buffer, and a decode function is one that reads a value from a buffer and builds a value in the GCI representation. Encoding functions are called from SEND and decoding functions are called from SNAPSHOT. The buffer has type Buffer and could be anything behaving as a sequence of bytes. The primitives BufReadInt, BufReadBool are used to read and write GCI basic values.
|
void EncodeT1( Buffer buf, GciValue* v) { BufWriteType( buf, T1d ); BufWriteInt( buf, GciGetField( v, 1 )); BufWriteBool( buf, GciGetField( v, 2 )); }
void EncodeT1( Buffer buf, GciValue* v ) { BufWriteType( buf, T1d ); BufWriteInt( buf, GciT1_a( v )); BufWriteBool( buf, GciT1_b( v )); }
void DecodeT1( Buffer buf, GciValue* v) { int i; v = GciMkSEQUENCE( 2 ); if ( BufReadType( buf ) != T1d ) Error(); GciSetType( T1d, v ); i = BufReadInt( buf ); GciSetField( v, GciMkINTEGER( i ), 1 ); i = BufReadBool( buf ); GciSetField( v, GciMkBool( i ), 2 ); }
|
void EncodeT2( Buffer buf, GciValue* v ) { int i; BufWriteType( buf, T2d ); BufWriteSeqOfSize( buf, GciSize( v ) ); for (i = 1 ; i <= GciSize( v ) ; i++ ) { EncodeT1( buf, GciGetElem( v, i ) ); } }
void DecodeT2( Buffer buf, GciValue* v ) { int i, seqOfSize; GciValue* elem; if ( BufReadType( buf ) != T2d ) Error(); GciSetType( T2d, v ); seqOfSize = BufReadSeqOfSize( buf ); for ( i = 1 ; i <= seqOfSize ; i++ ) { DecodeT1( buf, elem ); GciAddElem( v, elem ); } }
|
void EncodeT3( Buffer buf, GciValue* v ) { switch ( GciGetType( v )) { case T1d: EncodeT1( buf, v ); break; case T2d: EncodeT2( buf, v ); break; default: Error(); } }
void DecodeT3( Buffer buf, GciValue* v ) { switch ( BufGetType( v )) { case T1d: DecodeT1( buf, v ); break; case T2d: DecodeT2( buf, v ); break; default: Error(); } }
|
void EncodeT4( Buffer buf, GciValue* v ) { GciValue* tmp; BufWriteType( T4d ); /* Encode first field */ EncodeT1( GciGetField( v, 1 )); /* Now encode inline type definition */ tmp = GciGetField( v, 2 ); BufWriteInt( buf, GciGetField( tmp, 0 )); EncodeT2( buf, GciGetField( tmp, 1 )); }
void DecodeT4( Buffer buf, GciValue* v) { int i; GciValue* tmp; v = GciMkSEQUENCE( 2 ); if ( BufReadType( buf ) != T4d ) Error(); GciSetType( T4d, v ); DecodeT1( buf, tmp ); GciSetField( v, 1, tmp ); tmpseq = GciMkSEQUENCE( 2 ); i = BufReadInt( buf ); GciSetField( tmpseq, 1, GciMkINTEGER( i ) ); DecodeT2( buf, tmp ); GciSetField( tmpseq, 2, tmp ); }
myQueue
below is the Test Adaptor writers own representation of the PCO queue(s). In this example there is only one PCO, called L
(Ld
for its number). Note that the number Ld
can be any number (most certainly not zero).
Snapshot must read the status of the IUT and tell this to the TTCN Runtime Behavior.
void GciSnapshot() { GciValue* v; /* Check if anything has arrived, */ /* This would be a while statement */ /* in a blocking context */ if ( ! myQueue[ 0 ].empty ) { switch ( BufPeekType( myQueue[0].buf )) { case T1d: DecodeT1( myQueue[0].buf, v ); break; case T2d: DecodeT2( myQueue[0].buf, v ); break; default: Error(); } GciReceive( Ld, v ); } /* Nothing has happened. */ }
For the send example the following ASP table is used to show an API view of encode.
|
/* Two examples of send functionality */ GciStatus GciSend( int pcoDescr, GciValue* msg ) { if ( pcoDescr == PcoToSocket ) { /* Encode first */ if ( GciGetType( msg ) == T1d ) { EncodeT1( buf, msg ); BufSocketSend( buf ); } } else if ( pcoDescr == PcoToAPI ) { if ( GciGetType( msg ) == TCONreqd ) { Buffer pdu; EncodeT1( pdu, GciGetField( msg, 1 )); /* Call to lower layer */ T_CONreq( GciGetField( msg, 0 ), pdu ); } } }
After the TTCN code has been translated, there are a couple of things that has to be done in order adapt the generated code to the system it intends to test. This section describes how such an adaptation should be performed.
Figure 275 displays in a very simple way the anatomy of an executable test suite.
Figure 275 : 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. In any way, it assumes it will have access to special functions which will be implemented by the user to support the testing of the given system.
The test support functions module is responsible for providing functionality as:
The test support functions are the functions that the user must write to adapt the TTCN runtime behavior to his/her test equipment. Typically, among these functions are functions which must supply services for communication between the TTCN runtime behavior and the IUT.
We must be able to send encoded messages which the test system can understand, and also decode received messages into a representation which the TTCN runtime behavior can interpret. Timers and PCOs must be implemented, as well as other help functions which might be needed for a successful adaptation. Figure 276 shows the test support functions module in more detail.
Figure 276 : A better picture of the test support functions module
|
The TTCN runtime behavior must clearly be separated from protocol and test specific code. To succeed in doing this, we use the GCI interface model. In this section we will only shortly introduce this model, for further details, see Overview of the GCI Interface.
The GCI interface is a standardized set of functions. Communication shall only be done by calling functions, passing arguments to functions, returning values from the functions, and in no other way.
The interface is bidirectional in the meaning that both the TTCN runtime behavior and the test adaptor (which is part of the test support functions module) must provide services to each other. The TTCN runtime behavior shall at least provide services for handling values and managing tests and the test adaptor shall provide the protocol/test equipment specific parts of an executable.
With this information in mind, it is time to update Figure 276, into Figure 277 which introduces the GCI interface model to the system.
Figure 277 : Introducing the GCI interface model
|
As can be seen in Figure 277 the GCI interface is divided into a management and an operational interface. These two interfaces will be described in more detail in The Adaptor Functionality.
In this section we will concentrate on the functionality of the adaptor. We will do so by looking at the GCI interfaces; the GCI management, behavior and operational interface. Observe by looking at Figure 277 that the only values passing these interfaces must be values as defined by Overview of the GCI Interface. The GCI interface also allows the user to use a soft or a hard representation of these values. For more details in the subject, see Value Representation and Value Interface.
The GCI management interface describes the management functionality (functions) that the user may use in the adaptor. These functions are already implemented and may be used freely in the adaptor files.
The GCI management functionality consists of functions for initializing and managing test runs. These functions provide an API to the TTCN runtime behavior.
GciStatus GciInit()
GciVerdict GciStartTestCase(char* TestCaseOrTestGroup)
GciNotOk
and log the following message:No such Test Case or Test Group as <name> to run!
GciVerdict GciStartTestComponent( char* StepName,
GciValue* args )
GciTCList* GciGetTestCaseList()
GciTCList* GciGetTestGroupList()
GciValue* GciGetValue(char* TTCNObjectName)
GciValue*
. If the object name is not a TTCN name (name defined within the test suite) the function will return NULL and log the following message:GciGetValue: Could not find <name>! NULL returnedIf the object name is a TTCN name, but not an object (variable, constant, etc...) the function will return NULL and log the following message:
GciGetValue: <name> not an instance! NULL returned
int GciGetNoOfTimers( )
int GciGetNoOfPCOs( )
char* GciGetTimerName( timer )
char* GciGetPCOName( pco )
int GciGetPCOType( pco )
int GciGetNoOfComponents( GciConf conf )
GciComponent* GciGetComponent( GciConf conf, int index )
char* GciGetComponentName( GciComponent* comp )
int GciGetComponentDescriptor( GciComponent* comp )
int GciGetComponentType( GciComponent* comp )
char** GciGetComponentCPs( GciComponent* comp )
char** GciGetComponentPCOs( GciComponent* comp )
The GCI behavior functionality consists of functions that have to be called within the GciSnapshot
function (described in GCI operational interface). These functions are to be used when the user wishes to report a change of the IUT status to the TTCN runtime behavior.
GciStatus GciReceive( int pcod, GciValue* value )
pcod
.GciStatus GciTimeout( int timerd )
timerd
. The timer will be marked as timed out.GciStatus GciDone( int ptc )
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 template adaptor files, but the function bodies are empty. They only contain a print statement about the function not being implemented.
In the ITEX installation directory, there is a subdirectory containing different adaptor templates. The adaptor template called simple is the default one which is copied to the code directory if the user has no adaptor at all. A different adaptor template, called timers adaptor, contains a simple implementation of timers. Finally, an adaptor template called general adaptor, contains an adaptor example with a general encoder and decoder. Useful hints might be found by looking at these small code segments.
The GCI operational interface is the part of the ETS which is dependent on the protocol/test equipment. The process of adaptation is simply to implement these functions so that the TTCN runtime behavior can communicate with the system it intends to test.
GciValue* GciReadTSPar( char* name, char* pRef )
name
is the name of the test suite parameter and pRef
is the PICS/PIXIT field (ex. file). The value returned by this function must be a pointer to a valid GciValue
structure which can be successfully used by the TTCN runtime behavior.GciStatus GciConfig( GciConf conf )
GciStatus GciSnapshot()
GciReceive
function with appropriate PCO descriptor. To build the desired object the user can choose to either use the soft or the hard GCI value interface. If the hard interface is used, the Generate GCI Hard Values toggle button must be set when running the CCG to force it to generate this type of value interface.GciTimeout
to mark a timer as timed out. If the user uses the timers adaptor template he/she can use the AdSetTime
function (see below) to have this work done automatically.GciStatus GciSend(int pcod, GciValue* value)
GciStatus GciStartTimer(int timerd, long duration,
int unit)
GciStatus GciCancelTimer(int timerd)
long GciReadTimer(int timerd)
GciStatus GciLog(int logId, char* LogString)
This section describes some extra functionality which is included in the adaptor.
FILE* logStream;
main
function, but can be set to whatever stream the user wishes to use.CCGPRINT(string, file, string2)
#define)
for:fprintf(stderr, "%s%s%s", string, file, string2)and may be changed or deleted by the user. This is the macro that prints the messages:
Function <name> in file: adaptor.c is not written!for all functions not implemented by the user.
enum IcObjectEnum
extern const int Gc<tablename>D = {int}
extern IcSymTabEntry IcSymTab[]
char* Name; int Type; GciValue** Val;The
Name
is the name of the object, the Type
field is Gc<tablename>D
and Val
is a pointer to the value, if it is a variable.extern int IcSymTabLen;
extern int IcSymTabOffset;
The simple adaptor template includes all empty functions for the previously described GCI operational functions.
The timers adaptor template is the simple adaptor template with timers implemented. The timer functionality is described below.
The implemented timer structure looks as follows:
typedef struct AdTimeStruct { int Id; int Status; long Duration; long StartTime; int Unit; struct AdTimeStruct* Next; } AdTimeStruct;
The fields are described below:
The status field above can take the values defined below:
#define ADRUNNING 0 #define ADNOTRUNNING 1These values are the different status values that the implemented timers may have. If a timer has started the timer will have the status
ADRUNNING
, otherwise it will have ADNOTRUNNING
.AdTimeStruct* AdTimers;
long AdGlobalTime = 0;
Bool AdSetTime(long)
long AdNow()
AdGlobalTime
variable.Bool AdInitTimers()
The time in the Adaptor is a long int and should be measured as milli seconds.
A test suite has no knowledge of the encoding and decoding rules of the actual application protocol. The definition of signal components and description of the signal flows is 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 CCG comes with an adaptor 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.
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, UNIX 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.
In some cases it is important to have a good knowledge about the test equipment/environment.
If the executable test suite is to run within the test equipment, i.e. the communication between the ETS and the IUT is done within the test equipment, the ETS has to be moved (cross compiling or if possible compiling within the test equipment).
Timers might also need some attention. As timers are implemented differently on different systems the implementation of the timers might differ.
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. One way to implement the timeout functionality is to maintain an ordered list of the running timers. In this case it is easy to see how long we must wait until the next timeout is scheduled.
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.