9-test_ports.adoc 49.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
= Test Ports
:table-number: 0
:toc:

The Java source code generated by the Java code generator is protocol independent, that is, it does not contain any device specific operations. To provide the connection between the executable test suite and SUT, that is, the physical interface of the test equipmentfootnote:[The test equipment not necessarily requires a special hardware; it can even be a simple PC with an Ethernet interface.], a so-called Test Port is needed.

The Test Port is a software library written in Java language, which is a part of the executable test program. It maps the device specific operations to function calls specified in an API. This chapter describes the Test Port API in details.

== Generating the Skeleton

The functions of Test Ports must be written by the user who knows the interface between the executable test suite and the test equipment. In order to make this development easier, Eclipse features can be used to generate and update Test Port skeletons. A Test Port belongs to one certain TTCN3 port type, so the skeleton is generated based on port type definitions.

A Test Port consists of two parts. One part is generated automatically by the Java code generator, and it is put into the generated Java code. The user has nothing to do with this part.

The other part is a Java class, which is written mainly by the user. This class can be found in a separate Java file (their suffixes are `.java`). It is recommended to store this file in a folder separate from the generated java files (for example called `user_provided`), so as it should not be deleted when clearing the project.
The name of the source files and the Java class have to be identical to the name of the port type. And the Java class has to be located in the Java package whos name is generated as `org.eclipse.titan.` + projectname + `.user_provided`.
17
Please note that the name mapping rules described in <<12-mapping_ttcn3_data_types_to_java_constructs.adoc#mapping-of-names-and-identifiers, Mapping of Names and Identifiers>> also apply to these class and file names.
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

During the compilation, when the Java compiler encounters the usage of a Test Port that does not yet has a user generated implementation, it will report an error in the generated code for missing its import. Also offering Quick Fixes either by simply bringing the mouse cursor over the error location, or by right clicking and selecting Quick Fix from the menu. Using the action that starts like `Create class 'MyMessagePort' in package ...` eclipse will automatically generate the class the user needs.
Once the class is create one should set its base class and right click in its body part selecting the `Source`/`Override\Implement Methods...` to automatically generate a skeleton for the needed functions.

If the list of message types/signatures of a TTCN-3 port type changes, the list of the Test Port class member functions also needs to change. Java will report build error like "The typeXY must implement the inherited abstract method...". In this case, the `Override\Implement Methods...` action should be invoked again, to create the skeletons of the newly required functions.

If you have defined a TTCN3 port type that you intend to use for internal communication only (that is, for sending and receiving messages between TTCN3 test components), you do not need to generate and compile an empty Test Port skeleton for that port type. Adding the attribute with `{extension "internal"}` to the port type definition in the TTCN3 module disables the generation and use of a Test Port for the port type.

In the following we introduce two port type definitions: one for a message based and another one for a procedure based port. In our further examples we will refer to the test port skeletons generated according to these definitions given within the project called `MyProject` and module called `MyModule`.

== Message-based Example

The definition of `MyMessagePort`:
[source]
----
type port MyMessagePort message
{
  in octetstring;
  out integer;
  inout charstring;
};
----
That is, the types integer and charstring can be sent, and octetstring and charstring can be received on port `MyMessagePort`.

The initial Test Port file (that is, `MyMessagePort.java`) will look as follows:
[source]
----
package org.eclipse.titan.MyProject.user_provided;

import org.eclipse.titan.MyProject.generated.MyModule.MyMessagePort_BASE;
import org.eclipse.titan.runtime.core.TitanCharString;
import org.eclipse.titan.runtime.core.TitanInteger;

public class MyMessagePort extends MyMessagePort_BASE {

	public MyMessagePort(final String name) {
		super(name);
	}

	@Override
	protected void outgoing_send(TitanInteger send_par) {
		// TODO Auto-generated method stub
	}

	@Override
	protected void outgoing_send(TitanCharString send_par) {
		// TODO Auto-generated method stub
	}
}
----

== Procedure-based Example

The definition of `MyProcedurePort` in module `MyModule`:
[source]
----
type port MyProcedurePort procedure
{
  in inProc;
  out outProc;
  inout inoutProc;
};
----

The signature definitions are imported from a module called `MyModule2`, `noblock` is not used and exceptions are used so that every member function of the port class is generated for this example. If the keyword `noblock` is used the compiler will optimize code generation by not generating outgoing reply, incoming reply member functions and their argument types. If the signature has no exception outgoing raise, incoming exception member functions and related types will not be generated.

The port type `MyProcedurePort` can handle `call`, `getreply` and `catch` operations referencing the signatures `outProc` and `inoutProc`, and it can handle `getcall`, `reply` and `raise` operations referencing the signatures `inProc` and `inoutProc`.

The initial Test Port file (that is, `MyProcedurePort.java`) will look as follows:

[source]
----
package org.eclipse.titan.MyProject.user_provided;

import org.eclipse.titan.MyProject.generated.MyModule.MyProcedurePort_BASE;
import org.eclipse.titan.MyProject.generated.MyModule2.inProc_reply;
import org.eclipse.titan.MyProject.generated.MyModule2.inoutProc_call;
import org.eclipse.titan.MyProject.generated.MyModule2.inoutProc_reply;
import org.eclipse.titan.MyProject.generated.MyModule2.outProc_call;

public class MyProcedurePort extends MyProcedurePort_BASE {

	public MyProcedurePort(final String name) {
		super(name);
	}

	@Override
	public void outgoing_call(outProc_call call_par) {
		// TODO Auto-generated method stub
	}

	@Override
	public void outgoing_call(inoutProc_call call_par) {
		// TODO Auto-generated method stub
	}

	@Override
	public void outgoing_reply(inProc_reply reply_par) {
		// TODO Auto-generated method stub
	}

	@Override
	public void outgoing_reply(inoutProc_reply reply_par) {
		// TODO Auto-generated method stub
	}
}
----


[[test-port-functions]]
== Test Port Functions

This section summarizes all possible member functions of the Test Port class. These functions have an empty implementation in the base class of the Test Port.

The identical functions of both port types are:

* the constructor

* the parameter setting function

* the map and unmap function

* the start and stop function

* descriptor event and timeout handler(s)

* some additional functions and attributes

The functions above will be described using an example of message based ports (`MyMessagePort`, also introducing the functions specific to message based port types). Using these functions is identical (or very similar) in procedure based Test Ports.

Functions specific to message based ports:

* send functions: outgoing send

* incoming functions: incoming message

* Functions specific to procedure based ports:

* outgoing functions: outgoing_call, outgoing_reply, outgoing_raise

* incoming functions: incoming_call, incoming_reply, incoming_exception

Both test port types can use the same logging and error handling mechanism, and the handling of incoming operations on port `MyProcedurePort` is similar to receiving messages on port `MyMessagePort` (regarding the event handler).

NOTE: The easiest way to discover what functions can be overwritten and to generate their skeleton is by using the earlier described `Override\Implement Methods...` functionality of eclipse. That functionality automatically list all functions from the class generated for the given testport and the its parent classes, that can be overwritten.

NOTE: Please note, that in Java functions by default inherit the documentation/comments from the function they overwrite. So while the functions just inserted to overwrite functions from the base class might not appear to have a comment, in eclipse moving the cursor over their name will reveal their actual comment.

=== Constructor

NOTE: On the Java side Test Ports do not have destructors.

The Test Port class belongs to a TTCN3 port type, and its instances implement the functions of the port instances. That is, each Test Port instance belongs to the port of a TTCN3 test component. The number of TTCN3 component types, port types and port instances is not limited; you may have several Test Port classes and several instances of a given Test Port class in one test suite.

The Test Port instances are global and static objects from the point of view of the Java code. This means, their constructor is called before the test execution (that is, before the main function starts). They are also stored as threadlocal to be only accessible by the thread (Parallel Test Component) they belong to. The name of a Test Port object is composed of the name of the corresponding component type and the name of the port instance within the component type.

In case of parallel test execution, each TTCN3 test component thread has its own Test Port instances. Of course, only the Test Ports of the active component type are used, the member functions of other inactive Test Port instances (except constructor) shall never be called. All Test Port instances should be handled as being static, their constructor is called only once, at the time their component is created. The test component threads (that is, the child threads of Host Controller) will have to create/initialize their own Test Port instances.

The Test Port class is derived from an abstract base class which can be found in the generated code. The base class implements, for instance, the queue of incoming messages.

The constructor takes one parameter containing the name of the port instance in a String. This string shall be passed further to the constructor of the base class as it can be found in the skeleton code. The default argument for the test port name is a null pointer, which is used when the test port object is a member of a port array.

WARNING: In case of port arrays the name of the test port is set after the constructor is completed. So the name of the test port should not be used in the constructor. The port name is always set correctly when any other member function is called.

=== Parameter Setting Function

Test Port parameters shall contain information which is independent from the TTCN-3 test suite. These values shall not be used in the test suite at all. You can define them as TTCN3 constants or module parameters, but these definitions are useless and redundant, and they must always be present when the Test Port is used.

For instance, using Test Port parameters can be used to convey configuration data (that is, some options or extra information that is necessary for correct operation) or lower protocol layer addresses (for example, IP addresses).

Adam Knapp's avatar
Adam Knapp committed
188
Test Port parameters shall be specified by the user of executable tests in the `[TESTPORT_PARAMETERS]` section of the run-time configuration file (see section `[TESTPORT_PARAMETERS]` in link:https://gitlab.eclipse.org/eclipse/titan/titan.core/tree/master/usrguide/referenceguide[Programmer's Technical Reference]). The parameters are maintained for each test port instance separately; wildcards can be used as well. In the latter case the parameter is passed to all Test Port matching the wildcard.
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307

Each Test Port parameter must have a name, which must be unique within the Test Port only. The name must be a valid identifier, that is, it must begin with a letter and must contain alphanumerical characters only.

All Test Port parameter values are interpreted by the test executor as character strings. Quotation marks must be used when specifying the parameter values in the configuration file. The interpretation of parameter values is up to you: you can use some of them as symbolic values, numbers, IP addresses or anything that you want.

Before the test execution begins, all parameters belonging to the Test Port are passed to the Test Port by the runtime environment of the test executor using the function `set_parameter`. The default implementation of this function does nothing and ignores all parameters.

Each parameter is passed to the Test Port one-by-one separatelyfootnote:[If the same parameter of the same port instance is specified several times in the configuration file, the function `set_parameter` will also be called several times.], the two arguments of `set_parameter` contain the name and value of the corresponding parameter, respectively, in Strings.

It is warmly recommended that the Test Port parameter handling functions be fool-proof. For instance, the Test Port should produce a proper error message (for example by calling `TtcnError`) if a mandatory parameter is missing instead of causing unreliable behavior later. Repeated setting of the same parameter should produce warnings for the user (for example by using the function `TtcnError.TtcnWarning`) and not memory leaks.

NOTE: On the MTC, in both single and parallel modes, the handling of Test Port parameters is a bit different from that on PTCs. The parameters are passed only to active ports, but the component type of MTC (thus the set of active ports) depends on the `runs on` clause of the test case that is currently being executed. It would be difficult for the runtime environment to check at the beginning of each test case whether the corresponding MTC component type has already been active during a previous test case run. Therefore all Test Port parameters belonging to the active ports of the MTC are passed to the `set_parameter` function at the beginning of every test case. The Test Ports of MTC shall be prepared to receive the same parameters several times (with the same values, of course) if more than one test case is being executed.

If system related Test Port parameters are used in the run-time configuration file (that is, the keyword `system` is used as component identifier), the parameters are passed to your Test Port during the execution of TTCN–3 `map` operations, but before calling your `user_map` function. Please note that in this case the port identifier of the configuration file refers to the port of the test system interface that your port is mapped to and not the name of your TTCN–3 port.

The name and exact meaning of all supported parameters must be specified in the user documentation of the Test Port.

=== Map and Unmap Functions

The run-time environment of the TTCN–3 executor knows nothing about the communication towards SUT, thus, it is the user’s responsibility to establish and terminate the connection with SUT. The TTCN–3 language uses two operations to control these connections, `map` and `unmap`.

For this purpose, the Test Port class provides two member functions, `user_map` and `user_unmap`. These functions are called by the test executor environment when performing TTCN–3 `map` and `unmap` operations, respectively.

The `map` and `unmap` operations take two pairs of component references and ports as arguments. These operations are correct only if one of the arguments refer to a port of a TTCN–3 test component while the other port corresponds to SUT. This aspect of correctness is verified by the run-time environment, but the existence of a system port is not checked.

The port names of the system are converted to Strings and passed to functions `user_map` and `user_unmap` as parameters. Unlike other identifiers, the underscore characters in these port names are not translated.

NOTE: in TTCN–3 it is not allowed to map a test component port to several system ports at the same time. The run-time environment, however, is not so strict and allows this to handle transient states during configuration changes. In this case messages can not be sent to SUT even with explicit addressing, but the reception of messages is permitted. When putting messages into the input queue of the port, it is not important for the test executor (even for the TTCN–3 language) which port of the system the message is received from.

The execution of TTCN–3 test component that requested the mapping or unmapping is suspended until your `user_map` or `user_unmap` functions finish. Therefore it is not allowed to block unnecessarily the test execution within these functions.

When the Test Port detects an error situation during the establishment or termination of the physical connection towards the SUT, the function `TTCN_error` shall be used to indicate the failure. If the error occurs within `user_map` the run-time environment will assume that the connection with SUT is not established thus it will not call `user_unmap` to destroy the mapping during the error recovery procedure. If `user_map` fails, it is the Test Port writer’s responsibility to release all allocated resources and bring the object variables into a stable state before calling `TtcnError`. Within `user_unmap` the errors should be handled in a more robust way. After a minor failure it is better to issue a warning and continue the connection termination instead of panicking. `TtcnError` shall be called only to indicate critical errors. If `user_unmap` is interrupted with an error the run-time environment assumes that the mapping has been terminated, that is, `user_unmap` will not be called again.

NOTE: if either `user_map` or `user_unmap` fails, the error is indicated on the initiator test component as well; that is, the respective map or `unmap` operation will also fail and error recovery procedure will start on that component.

==== Parameters of the Map and Unmap Functions

Parameters can be sent to the `user_map` and `user_unmap` functions from TTCN-3 code using the `param` clause of the `map` and `unmap` operations.

The `user_map` and `user_unmap` functions have a parameter of type `Map_Params`, which contains the string representations of the `in` and `inout` parameters of the `map`/`unmap` operation. The string representations of `out` parameters are empty strings (as these are considered as being `unbound` at the beginning of the `map`/`unmap` operation). After the `user_map` or `user_unmap` function ends and the mapping/unmapping is concluded, the final values (string representations) of `out` and `inout` parameters in the `Map_Params` object are sent back to the mapping/unmapping requestor.

The following member functions can be used to obtain or set data in the `Map_Params` object:

[source]
----
public int get_nof_params()
----
Returns the number of parameters in the object. This will either be zero (if the `map` or `unmap` operation had no `param` clause) or the number of parameters specified in the system port type definition's `map param` or `unmap param` clause.

[source]
----
public TitanCharString get_param(final int index)
----
Returns the string representation of the parameter at index `p_index`. This method shall be used to retrieve the values of `in` and `inout` parameters. The parameter indices start at 0. The order of the parameters is the same as their order of declaration. Default values of parameters are automatically set by the runtime environment before the `user_map`/`user_unmap` call. The string representations retrieved with this function can be converted back to the parameter's TTCN-3 type with the predefined function `string_to_ttcn`.

[source]
----
public void set_param(final int index, final TitanCharString param)
----
Sets the string representation of the parameter at index `p_index` to the string `p_param`. This method shall be used to set the final values of `out` and `inout` parameters. The string representation of a TTCN-3 value can be obtained using the predefined function `ttcn_to_string`. If the final value of an `out` or `inout` parameter is an empty string, then the variable used as parameter will remain unchanged. Otherwise its new value will be calculated by applying `string_to_ttcn` on the string value set in the `user_map` or `user_unmap` function (this could cause dynamic test case errors if the string representation is invalid).

Usage example:

Port type:
[source]
----
type port MyPort message {
  ...
  map param(in MyInParType in_par, inout MyInOutParType inout_par, out MyOutParType out_par)
}
----
`user_map` function in port implementation:
[source]
----
@Override
protected void user_map(final String system_port, final Map_Params params) {
  if (params.get_nof_params() == 0) {
    // there were no map parameters

    // do mapping
    ...
  } else {
    // there were map parameters

    // extract 'in' and 'out' parameters
    MyInParType in_par = new MyInParType();
    TitanCharString.string_to_ttcn(params.get_param(0), in_par);
    MyInOutParType inout_par = new MyInOutParType();
    TitanCharString.string_to_ttcn(params.get_param(1), inout_par);
    MyOutParType out_par = new MyOutParType(); // remains unbound

    // do mapping
    ...

    // update 'out' and 'inout' parameters
    params.set_param(1, TitanCharString.ttcn_to_string(inout_par));
    params.set_param(2, TitanCharString.ttcn_to_string(out_par));
  }
}
----

=== Start and Stop Functions

The Test Port class has two member functions: `user_start` and `user_stop`. These functions are called when executing `port start` and `port stop` operations, respectively. The functions have no parameters and return types.

These functions are called through a stub in the base class, which registers the current state of the port (whether it is started or not). So `user_start` will never be called twice without calling `user_stop` or vice versa.

All ports of test components are started implicitly immediately after creation. Operations put in a `user_start` function must not be blocking the execution for a longer period. This not only hangs the new PTC but the also component that performed the `create` operation (usually the MTC). All ports are stopped at the end of test cases or at PTC termination, even if `stop` statements are missing.

In functions `user_start` and `user_stop` the device should be initialized or shut down towards SUT (that is, the communications socket). Also the event handler should be installed or uninstalled (see later).

=== Outgoing Operations

Outgoing operations are `send` (specific to message based ports); `call`, `reply`, and `raise` (specific to procedure based ports).

==== Send Functions

The Test Port class has an overloaded function called `outgoing_send` for each outgoing message type. This function will be called when a message is sent on the port and it should be routed to the system (that is, SUT) according to the addressing semanticsfootnote:[That is, the port has exactly one mapping and either the port has no connections or the message is explicitly addressed by a `send (…) to system` statement.] of TTCN–3. The messages (implicitly or explicitly) addressed to other test components are handled inside the test executor; the Test Ports have nothing to do with them. The function `outgoing_send` will be also called if the port has neither connections nor mappings, but a message is sent on it.

308
The only parameter of `outgoing_send` contains a read-only reference to the message in the internal data representation format of the test executor. The access methods for internal data types are described in <<12-mapping_ttcn3_data_types_to_java_constructs.adoc#predefined-ttcn-3-data-types, Predefined TTCN–3 Data Types>>. The test port writer should encode and send the message towards SUT. For information on how to use the standard encoding functions like RAW, please consult the earlier chapters of this document. Sending a message on a not started port causes a dynamic test case error. In this case `outgoing_send` will not be called.
309
310
311
312
313

==== Call, Reply and Raise Functions

The procedure based Test Port class has overloaded functions called `outgoing_call`, `outgoing_reply` and `outgoing_raise` for each `call`, `reply` and `raise` operations, respectively. One of these functions will be called when a port-operation is addressing the system (that is, SUT using the to `system` statement).

314
The only parameter of these functions is an internal representation of the signature parameters (and possibly its return value) or the exceptions it may raise. The signature classes are described in <<12-mapping_ttcn3_data_types_to_java_constructs.adoc#using-the-signature-classes,Using the Signature Classes>>.
315
316
317

=== Incoming Operations

318
Incoming operations are `receive` for incoming messages (specific to message based ports); `call`, `reply` and `raise` for signatures (specific to procedure based ports).
319
320
321
322
323
324
325

==== Descriptor Event and Timeout Handlers

The handling of incoming messages (or operations) is more difficult than sending. The executable test program has two states. In the first state, it executes the operations one by one as specified in the test suite (for example, it evaluates expressions, calls functions, sends messages, etc.). In the other state it waits for the response from SUT or for a timer to expire. This happens when the execution reaches a blocking statement, that is, one of a stand-alone `receive`, `done`, `timeout` statements or an `alt` construct.

After reaching a blocking statement, the test executor evaluates the current snapshot of its timer and port queues and tries to match it with the reached statements and templates. If the matching fails, the executor sleeps until something happens to its timers or ports. After waking up, it re-evaluates its snapshot and tries to match it again. The last two steps are repeated until the executor finds the first matching statement. If the test executor realizes that its snapshot can never match the reached TTCN–3 statements, it causes a dynamic test case error. This mechanism prevents it from infinite blocking.

326
The test executor handles its timers itself, but it does not know anything about the communication with SUT. So each Test Port instance should inform the snapshot handler of the executor what kind of event the Test Port is waiting for. The event can be either the reception of data on one or more socket channels or a timeout (when polling is used) or both of them.
327
328
329

When the test executor reaches a blocking statement and any condition – for which the Test Port waits – is fulfilled, the event handler will be called. First one has to get the incoming message or operation from the operating system. After that, one has to decode it (and possibly decide its type). Finally, if the internal data structure is built, one has to put it into the queue of the port. This can be done using the member function `incoming_message` if it is a message, and using `incoming_call`, `incoming_reply` or `incoming_exception` if it is an operation.

330
The execution must not be blocked in event handler functions; these must return immediately when the message or operation processing is ready. In other words, always use non-blocking calls. In the case when the messages are fragmented (for instance, when testing TCP based application layer protocols, such as HTTP), intermediate buffering should be performed in the Test Port class.
331

332
===== Event and timeout handling interface
333

334
To be notified about available events the `Handle_Event` function has to be implemented.
335
[source, subs="+quotes"]
336
public void Handle_Event(final SelectableChannel channel, final boolean is_readable, final boolean is_writeable);
337

338
Using `Handle_Event` allows receiving all events of a descriptor in one function call.
339

340
The first parameter in all of these functions is the selectable channel. The second is true if the channel is readable. The third is true if it is writeable.
341

342
You can install or uninstall the event handler by calling the following inherited member functions:
343
[source, subs="+quotes"]
344
345
protected void Install_Handler(final Set<SelectableChannel> read_channels, final Set<SelectableChannel> write_channels, final double call_interval) throws IOException;
protected void Uninstall_Handler() throws IOException;
346

347
`Install_Handler` installs the event handler according to its parameters. It takes three arguments, two sets of SelectableChannels and a timeout value. Some of the parameters can be ignored, but ignoring all at the same time is not permitted.
348

349
350
`read_channels` is the set of SelectabeChannel to register the handler for reading. If null the handler is not registered for any channel to handle reading.
`write_channels` is the set of SelectabeChannel to register the handler for writing. If null the handler is not registered for any channel to handle writing.
351
352
353

The call interval value is measured in seconds. It means that the event handler function will be called when the time elapsed since its last call reaches the given value. This parameter is ignored when its value is set to zero or negative.

354
If you want to change your event handling parameters, you may simply call the function `Install_Handler` again (calling of `Uninstall_Handler` is not necessary).
355
356
357
358
359

`Uninstall_Handler` will uninstall your previously installed event handler. The `stop` port operation also uninstalls the event handler automatically. The event handler may be installed or uninstalled in any Test Port member function, even in the event handler itself.

==== Receiving messages

360
The member function `incoming_message` of message based ports can be used to put an incoming message in the queue of the port. There are different functions for each incoming message type. These functions are inherited from the base class. The received messages are logged when they are put into the queue and not when they are processed by the test suitefootnote:[Note that if the port has connections as well, the messages coming from other test components will also be inserted into the same queue independently from the event handler.].
361
362
363

In our example the class `MyMessagePort_BASE` has the following member functions:
[source, subs="+quotes"]
364
365
protected void incoming_message(final TitanOctetString incoming_par);
protected void incoming_message(final TitanCharString incoming_par);
366
367
368

==== Receiving calls, replies and exceptions

Kristof Szabados's avatar
Kristof Szabados committed
369
Receiving operations on procedure based ports is similar to receiving messages on message based ports. The difference is that there are different overloaded incoming functions for call, reply and raise operations called `incoming_call`, `incoming_reply` and `incoming_exception`, respectively. The event handler (when called) must recognize the type of operation on receiving and call one of these functions accordingly with one of the internal representations of the signature (see <<12-mapping_ttcn3_data_types_to_java_constructs.adoc #additional-non-standard-functions, Additional Non-Standard Functions>>).
370

371
In the examplefootnote:[In the example the signatures were defined in a different TTCN–3 module named MyModule2, as a consequence all types defined in that module must be prefixed with the Java name of that module and its class be imported.] the class `MyProcedurePort_BASE` has the following member functions for incoming operations:
372
373
[source]
----
374
375
376
377
378
379
protected void incoming_call(final MyModule2.inProc_call incoming_par);
protected void incoming_call(final MyModule2.inoutProc_call incoming_par);
protected void incoming_reply(final MyModule2.outProc_reply incoming_par);
protected void incoming_reply(final MyModule2.inoutProc_reply incoming_par);
protected void incoming_exception(final MyModule2.outProc_exception incoming_par);
protected void incoming_exception(final MyModule2.inoutProc_exception incoming_par);
380
381
382
383
384
385
386
387
388
----
For example, if the event handler receives a call operation that refers to the signature called `inoutProc`, it has to fill the parameters of an instance of the class `inoutProc_call` with the received data. Then it has to call the function `incoming_call` with this object to place the operation into the queue of the port.

The following table shows the relation between the direction of the message type or signature in the port type definition and the incoming/outgoing functions that can be used. `MyPort` in the table header refers to `MyMessagePort` or `MyProcedurePort` in the example depending on the type of the port (message based or procedure based).

.Outgoing and incoming operations

[cols=" ",options="header",]
|===
389
| | 4+^.^|`MyPort.outgoing_` 4+^.^| `MyPort_BASE.incoming_`
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
| | |send |call |reply |raise |message |call |reply |exception
.3+^.^|message type |in |○ |○ |○ |○ |● |○ |○ |○
|out |● |○ |○ |○ |○ |○ |○ |○
|inout |● |○ |○ |○ |● |○ |○ |○
.3+^.^|signature |in |○ |○ |● |● |○ |● |○ |○
|out |○ |● |○ |○ |○ |○ |● |●
|inout |○ |● |● |● |○ |● |● |●
|===

● supported

○ not supported

=== Additional Functions and Attributes

Kristof Szabados's avatar
updated    
Kristof Szabados committed
405
Any kind of attributes or member functions may be added to the Test Port. A selectable channel, which you communicate on, is almost always necessary. Names not interfering with the identifiers generated by the Java code generator can be used in the java file (for example, the names containing one underscore character). Avoid using static variables because it can be very confusing when more than one instances of the Test Port run simultaneously. Any kind of software libraries may be used in the Test Port as well.
406
407
408
409
410
411
412
413

In addition, the following `protected` attributes of ancestor classes are available:

.Protected attributes

[width="100%",cols="34%,33%,33%",options="header",]
|======================================================================================================
|Name ^.^|Type |Meaning
414
|`is_active` ^.^|boolean |Indicates whether the Test Port is active.
415
|`is_started` ^.^|boolean |Indicates whether the Test Port is started.
416
417
|`is_halted` ^.^|boolean |Indicates whether the Test Port is halted.
|`port_name` ^.^|String |Contains the name of the Test Port instance.
418
419
|======================================================================================================

420
421
Underscore characters are not duplicated in port_name. In case of port array member instances the name string looks like this: `"Myport_array[5]"`.

422
423
== Support of `address` Type

424
The special user-defined TTCN–3 type `address` can be used for addressing entities inside the SUT on ports mapped to the `system` component. Since the majority of Test Ports does not need TTCN–3 addressing and in order to keep the Test Port API backward compatible the support of `address` type is disabled by default. To enable addressing on a particular port type the extension attribute `"address"` must be added to the TTCN–3 port type definition. In addition to component references this extension will allow the usage of `address` values or variables in the `to` or `from` clauses and `sender` redirects of port operations.
425
426
427
428
429
430
431

In order to use addressing, a type named `address` shall be defined in the same TTCN–3 module as the corresponding port type. Address types defined in other modules of the test suite do not affect the operation of the port type. It is possible to link several Test Ports that use different types for addressing SUT into the same executable test suite.

Test Ports that support SUT addressing have a slightly different API, which is considered when generating Test Port skeleton. This section summarizes only the differences from the normal API.

In the communication operations the test port author is responsible for handling the address information associated with the message or the operation. In case of an incoming message or operation the value of the received address will be stored in the port queue together with the received message or operation.

432
The generated code for the port skeleton of message based ports will be the same, except `outgoing_send` member function, which has an extra parameter pointing to an `TitanAddress` value. With the example given in <<test-port-functions, Test Port Functions>>:
433
434
[source]
----
435
436
void outgoing_send(final TitanInteger send_par, final TitanAddress destination_address);
void outgoing_send(final TitanCharString send_par, final TitanAddress destination_address);
437
438
----

439
440
NOTE: when the type named `address` is defined as a synonym of an other type, these functions could also report that type to be the type of the `destination_address` formal parameter.

441
442
443
444
445
If an `address` value was specified in the `to` clause of the corresponding TTCN–3 `send` operation the second argument of `outgoing_send` points to that value. Otherwise it is set to the `NULL` pointer. The Test Port code shall be prepared to handle both cases.

The outgoing operations of procedure based ports are also generated in the same way if the `address` extension is specified. These functions will also have an extra parameter. Based on our example, these will have the following form:
[source]
----
446
447
448
449
450
451
452
453
454
void outgoing_call(final MyModule2.outProc_call call_par, final TitanAddress destination_address);
void outgoing_call(final MyModule2.inoutProc_call call_par, final TitanAddress destination_address);
void outgoing_reply(final MyModule2.inProc_reply reply_par, final TitanAddress destination_address);
void outgoing_reply(final MyModule2.inoutProc_reply reply_par, final TitanAddress destination_address);
void outgoing_raise(final MyModule2.inProc_exception raise_exception, final TitanAddress destination_address);
void outgoing_raise(final MyModule2.inoutProc_exception raise_exception, final TitanAddress destination_address);
----

The other difference is in the `incoming_message` member function of class `MyMessagePort_BASE`, and in the incoming member functions of class `MyProcedurePort_BASE`. These have an extra parameter, which is a pointer to an `TitanAddress` value. The version of the function that does not have this formal parameter, will call this function with a null value passed as the sender_address. In our example of `MyMessagePort_BASE`:
455
456
[source]
----
457
458
459
460
461
462
void incoming_call(final MyModule2.inProc_call incoming_par, final int sender_component, final TitanAddress sender_address);
void incoming_call(final MyModule2.inoutProc_call incoming_par, final int sender_component, final TitanAddress sender_address);
void incoming_reply(final MyModule2.outProc_reply incoming_par, final int sender_component, final TitanAddress sender_address)
void incoming_reply(final MyModule2.inoutProc_reply incoming_par, final int sender_component, final TitanAddress sender_address)
void incoming_exception(final MyModule2.outProc_exception incoming_par, final int sender_component, final TitanAddress sender_address)
void incoming_exception(final MyModule2.inoutProc_exception incoming_par, final int sender_component, final TitanAddress sender_address)
463
464
----

465
If the event handler of the Test Port can determine the source address where the message or the operation is coming from, it shall pass a pointer to the incoming function, which points to a variable that stores the `address` value. The given address value is not modified by the run-time environment and a copy of it is created when the message or the operation is appended to the port queue. If the event handler is unable to determine the sender address the default null value shall be passed as the argument.
466
467
468
469
470

The address value stored in the port queue is used in `receive`, `trigger`, `getcall`, `getreply`, `catch` and `check` port operations: it is matched with the `from` clause and/or stored into the variable given in the `sender` redirect. If the receiving operation wants to use the address information of the first element in the port queue, but the Test Port has not supplied it a dynamic testcase error will occur.

== Provider Port Types

Adam Knapp's avatar
Adam Knapp committed
471
Test Ports that belong to port types marked with `extension` attribute `"provider"` have a slightly different API. Such port types are used to realize dual-faced ports, the details of which can be found in section "Dual-faced ports" in the link:https://gitlab.eclipse.org/eclipse/titan/titan.core/tree/master/usrguide/referenceguide[Programmer's Technical Reference].
472
473
474

The purpose of this API is to allow the re-use of the Test Port class with other port types marked with attribute `user` or with ports with translation capability (link:https://www.etsi.org/deliver/etsi_es/202700_202799/202781/01.04.01_60/es_202781v010401p.pdf[Methods for Testing and Specification (MTS); The Testing and Test Control Notation version 3; TTCN-3 Language Extensions: Configuration and Deployment Support]). The user port types may have different lists of incoming and outgoing message types. The transformations between incoming and outgoing messages, which are specified entirely by the attribute of the user port type, are done independently of the Test Port. The Test Port needs to support the sending and reception of message types that are listed in the provider port type.

475
The provider port can be accessed through the port which maps to the port with provider attribute. The `get_provider_port()` is a member function of the TitanPort class:
476
[source, subs="+quotes"]
477
TitanPort get_provider_port();
478

479
This function is useful when a reference to the provider type is needed. It returns the provider port type for user ports and ports with translation capability. Otherwise returns null. The function causes dynamic testcase error when the port has more than one mapping, or the port has both mappings and connections. The functions return value must be manually cast to the correct provider port type.
480
481
482
483
484

This section summarizes only the differences from the normal Test Port API:

* The name of the Test Port class is suffixed with the string `_PROVIDER` (for example `MyMessagePort_PROVIDER` instead of `MyMessagePort`).

485
* The base class of the Test Port is class `TitanPort`, which is part of the Base Library. Please note that normal Test Ports are also derived from class TitanPort, but indirectly through an intermediate class with suffix `_BASE`.
486

487
* The member functions that handle incoming messages and procedure-based operations (that is `incoming_message`, `incoming_call`, `incoming_reply` and `incoming_exception`) must be defined as override-able functions. These functions will be implemented in various descendant classes differently.
488

489
* The member functions of the Test Port may refer to Java classes that are generated from user-defined message types and signatures.
490

491
The following example shows the skeleton of a provider port type Test Port.
492
493
494
495
496
497
498
499
500

Port type definition in TTCN3 :
[source]
----
type port MyProviderPort mixed {
  inout MyMessage, MySignature;
} with { extension "provider" }
----

501
Source file `MyProviderPort_PROVIDER.java`:
502
503
[source]
----
504
package org.eclipse.titan.MyProject.user_provided;
505

506
import java.nio.channels.SelectableChannel;
507

508
509
510
511
512
import org.eclipse.titan.MyProject.generated.MyModule.MyMessage;
import org.eclipse.titan.MyProject.generated.MyModule.MySignature_call;
import org.eclipse.titan.MyProject.generated.MyModule.MySignature_exception;
import org.eclipse.titan.MyProject.generated.MyModule.MySignature_reply;
import org.eclipse.titan.runtime.core.TitanPort;
513

514
public class MyProviderPort_PROVIDER extends TitanPort {
515

516
517
518
	public MyProviderPort_PROVIDER() {
		super();
	}
519

520
521
522
	public MyProviderPort_PROVIDER(final String name) {
		super(name);
	}
523

524
525
526
	@Override
	public void set_parameter(final String parameter_name, final String parameter_value) {
	}
527

528
529
530
531
	@Override
	public void Handle_Event(final SelectableChannel channel, final boolean is_readable,
			final boolean is_writeable) {
	}
532

533
534
535
	@Override
	protected void user_map(final String system_port, final Map_Params params) {
	}
536

537
538
539
	@Override
	protected void user_unmap(final String system_port, final Map_Params params) {
	}
540

541
542
543
	@Override
	protected void user_start() {
	}
544

545
546
547
	@Override
	protected void user_stop() {
	}
548

549
550
551
552
553
554
555
556
	public void outgoing_send(final MyMessage send_par) {
	}
	public void outgoing_call(final MySignature_call call_par) {
	}
	public void outgoing_reply(final MySignature_reply reply_par) {
	}
	public void outgoing_raise(final MySignature_exception raise_Exception) {
	}
557
558
559
560
561
562
563
564
565
566
567
}
----

== Tips and Tricks

The following sections deal with logging and error handling in Test Ports.

=== Logging

Test Ports may record important events in the Test Executor log during sending/receiving or encoding/decoding messages. Such log messages are also good for debugging fresh code.

568
569
The Test Port member functions may call the functions of class `TTCN_Logger`.
These functions are detailed in <<13-tips_&_troubleshooting.adoc#logging-in-test-ports-or-external-functions, Logging in Test Ports or External Functions>>.
570

571
If there are many points in the Test Port code that want to log something, it can be a good practice to write a common log function in the Test Port class. We show here an example where the calling of `log` uses Java's MessageFormat.format to create a custom message, and inside the `log` function TTCN_Logger.log_event demonstrates logging using the standard C function `printf` style and forwards the message to the Test Executor’s logger:
572
573
574

[source]
----
575
576
577
578
579
580
581
582
583
private void value_logging(final TitanInteger i) {
  log(MessageFormat.format("The value of i : {0}.", i.get_int()));
}

private void log(final String content) {
  TTCN_Logger.begin_event(Severity.DEBUG_USER);
  TTCN_Logger.log_event("Example Test Port (%s): ", get_name());
  TTCN_Logger.log_event_str(content);
  TTCN_Logger.end_event();
584
585
586
587
588
}
----

=== Error Handling

589
None of the Test Port member functions have return value like a status code. If a function returns normally, the run-time environment assumes that it has performed its task successfully. The handling of run-time errors is done using Java exceptions. This simplifies the program code because the return values do not have to be checked everywhere and dynamically created complex error messages can be used if necessary.
590

591
If any kind of fatal error is encountered anywhere in the Test Port, an exception of type `TtcnError` should be thrown:
592
[source, subs="+quotes"]
593
throw new TtcnError(errorMessage);
594

595
Its parameter should contain the description of the error in a String. The exception is usually caught at the end of the test case or PTC function that is being executed. In case of error, the verdict of the component is set to `error` and the execution of the test case or PTC function terminates immediately.
596

597
The error string is written into the log file by `TtcnError` immediately. Such type of exception should never be caught or thrown directly. If you want to implement your own error handling and error recovery routines you had better use your own classes as exceptions.
598

599
If you write your own error reporting function you can add automatically the name of the port instance to all of your error messages. This makes the fault analysis for the end-users easier. In the following example the error message will occupy two consecutive lines in the log since we can pass only one format string to `TtcnError`.
600
601
[source]
----
602
603
604
605
606
607
608
private void error(final String content) {
		TTCN_Logger.begin_event(Severity.ERROR_UNQUALIFIED);
		TTCN_Logger.log_event("Example Test Port (%s): ", get_name());
		TTCN_Logger.log_event_str(content);
		TTCN_Logger.end_event();
		throw new TtcnError(MessageFormat.format("Fatal error in Example Test Port {0} (see above).", get_name()));
	}
609
610
----

611
There is another function for denoting warnings (that is, events that are not so critical) with the same parameter list as TtcnError:
612
[source, subs="+quotes"]
613
void TtcnError.TtcnWarning(warningMessage);
614

615
This function puts an entry in the executor’s log with severity `TTCN_WARNING`. In contrast to `TtcnError`, after logging the given message `TtcnWarning` returns and your test port can continue running.
616
617
618

== Setting timestamps

619
In order to use the timestamp redirects (`-> timestamp`) described in chapter 5 of the TTCN-3 standard extension `TTCN-3 Performance and Real Time Testing` (ETSI ES 202 782 V1.3.1, <<14-references.adoc#_16, [16]>>) the test port writer needs to add extra code to set the timestamps for the incoming and outgoing port operations of each port with the `realtime` clause.
620
621
622
623
624

=== Incoming operations

The timestamps of incoming port operations (`receive`, `trigger`, `getcall`, `getreply`, `catch` and `check`) need to be set when the incoming message or procedure is added to the queue.

625
The member functions `incoming_message`, `incoming_call`, `incoming_reply` and `incoming_exception` (which add the message/procedure to the queue) have an optional `TitanFloat` parameter called `timestamp`, if the test port was declared with the `realtime` clause.
626
627
628

The value given to this parameter will be the one stored in the variable referenced in the timestamp redirect, if the operation has a timestamp redirect (otherwise the value is ignored).

629
It is recommended that this parameter be set to the current test system time, which can be queried with `TTCN_Runtime.now();`, or to a float variable that was set to the current test system time earlier in the function.
630
631
632
633

Examples:
[source]
----
634
incoming_message(my_message, TTCN_Runtime.now());
635
636
637
638
----

[source]
----
639
TitanFloat reply_time = TTCN_Runtime.now();
640
641
642
643
644
645
646
647
648
649

...

incoming_reply(my_reply, reply_time);
----

=== Outgoing operations

The timestamps of outgoing port operations (`send`, `call`, `reply`, `raise`) need to be set in the member functions `outgoing_send`, `outgoing_call`, `outgoing_reply` and `outgoing_raise`.

650
These functions have a `TitanFloat` pointer parameter called `timestamp_redirect`, if the test port was declared with the `realtime` clause.
651
652
653

The value pointed to by this parameter will be the one stored in the variable referenced in the timestamp redirect, if the operation has a timestamp redirect.

654
If it does not have a timestamp redirect, then this value parameter will be null. Because of this, the parameter must always have a null check before it is assigned a value.
655

656
It is recommended that the value pointed to by the parameter be set to the current test system time, which can be queried with `TTCN_Runtime.now()`.
657
658
659
660

Example:
[source]
----
661
662
if (timestamp_redirect != null) {
	timestamp_redirect.operator_assign(TTCN_Runtime.now());
663
664
665
}
----

balaskoa's avatar
Typo    
balaskoa committed
666
NOTE: Because of this extra parameter, adding or removing the `realtime` clause from a port will cause already-written Java code for the port to no longer compile. In these cases the new parameters must be manually added or removed from the mentioned functions.