Back to TTCN-3 Refactoring Catalog
Replace Altstep with Default
When subsequent alt statements have an equal branch at the end that is calling an altstep, the altstep can be activated as default and hence reduce code clutter.
Motivation
Subsequent alt statements often contain an duplicate branch at the end that is calling an altstep. Such a branch is typically used for error handling (e.g. handling of unexpected messages or timeouts). Such error handling is a typical cross-cutting concern in TTCN-3 and can be further simplified by using default altsteps. By using a default altstep, the equal alternative branches can be removed, but need to be activated prior to the first alt statement and deactivated after the last. While it may simplify the alt statements, it can also make code hard to understand when used too much as tracking of the activated altsteps can be hard. Activating and deactivating altsteps at different nesting levels is strongly discouraged as it makes the code very hard to understand. Also note that semantic preservation can only be guaranteed when the equal branches calling an altstep are at the end of the alt statement since default altsteps are automatically attached to these positions. Reordering of branches may be possible to achieve equal ends, but this depends on the semantics of the alt statement.
Mechanics
- Find the first and the last source alt statement referencing a duplicate tail altstep. If the alt statement tails have more than one altstep call in common and they all should be activated, it is easier to apply the following steps at once.
- If the source altstep references are parameterized, check whether the parameter refers to the same element in all source altstep references. If not, this refactoring is not applicable.
- Activate the target altstep before the first source alt statement and store the returned reference in a newly declared variable of type default. Typically, this variable is declared in the local scope of the test case or function. If the source altstep references are parameterized, you have to use the actual parameter during the activation of the default.
- If there are other default altstep activations before this first source alt statement, place the new target default altstep activation before the first existing default altstep activation (TTCN-3 default altsteps are executed in the reverse order of their activation, i.e. the first altstep activated is executed last).
- Deactivate the target altstep using the stored reference after the last source alt statement. If the last source alt statement is nested one or more levels below the activation level, deactivation should be delayed until returned to the activation level.
- Remove the last branch of each source alt statement.
- Compile and validate.
Example
In the listing below, there are two subsequent alt statements with equal branches at their end. These branches are calling the same altstep alt_timeGuard which makes sure that the local test case verdict of tc_exampleTestCase fails when no expected message is received in time. The Replace Altstep with Default refactoring can be applied on these alt statements.
testcase tc_exampleTestCase() runs on ExampleComponent {
timer t_guard;
// [...]
t_guard.start ( 10.0 );
alt {
[] // [...]
[] alt_timeGuard( t_guard ) { }
}
// [...]
t_guard.start ( 10.0 );
alt {
[] // [...]
[] alt_timeGuard( t_guard ) { }
}
}
altstep alt_timeGuard( inout timer p_t ) {
[] p_t.timeout {
setverdict( fail );
}
}
After the refactoring is applied, the alt_timeGuard altstep is activated before the first alt statement and deactivated after the last. As a timer was started immediately before the first alt statement, the altstep activation is placed before the timer launch.
testcase tc_exampleTestCase() runs on ExampleComponent {
timer t_guard;
// [...]
var default v_defaultRef;
v_defaultRef := activate( alt_timeGuard( t_guard ) );
t_guard.start ( 10.0 );
alt {
[] // [...]
}
// [...]
t_guard.start ( 10.0 );
alt {
[] // [...]
}
deactivate( v_defaultRef );
}
altstep alt_timeGuard( inout timer p_t ) {
[] p_t.timeout {
setverdict( fail );
}
}
