Back to TTCN-3 Refactoring Catalog
Extract Altstep
Move alternative branches of an alt statement into an altstep when used more than once.
Motivation
Quite often, identical alternative branches in an alt statement are used more than once in a test suite. To avoid code duplication, reduce maintenance load and improve readability, identical alternative branches should be extracted into their own altstep. An altstep invocation inside an alt statement can provide code as "tail" for the altstep. Hence, the identical branches may even differ in their end-piece which would then be left in the calling alt statement. Note that altsteps need to be properly named to be self-explanatory. Depending on the content of the extracted alternative branches, it may be necessary to add parameters to the altstep. A refactoring that may be used after extracting altsteps is the Replace Altstep with Default refactoring.
Mechanics
- Create a new target altstep and name it by what the alternative branch to be extracted does. If the alternative is so simple that it is hard to come up with a good name, usage of this refactoring should possibly be reconsidered.
- Copy the source branches to be extracted from the alt statement into the target altstep.
- Check the extracted alternative branch for references to any variables, constants and timers that were local to the scope of the source alt statement or its surrounding compound respectively, i.e. either locally defined temporary elements or elements passed as parameters to the surrounding compound. Ports cannot be locally defined, but passed as parameters; in this case, they have also local scope.
- Locally defined temporary elements which are only used in the extracted branches are declared in the new target altstep again as temporary elements.
- All other local-scope elements must be passed in or out of the altstep as follows:
- Timers and ports are always passed as reference. i.e. as inout.
- Elements which are only read but not modified by the extracted branches or read and modified by the extracted branches but not read afterwards at the source location are passed as in parameter.
- Elements that are modified but not read before modification in the extracted branches and read afterwards at the source location must be passed as out parameter.
- Elements that are both read and modified in the extracted code and also read afterwards need to be passed as inout parameters.
- Check if any elements declared in the component on which the alt statement runs on are used in the extracted branches. If so, the target altstep must have a runs on specifying this component as well. Afterwards, Generalize Runs On may be appropriate or the runs on specification may even be removed if Move Component Variable/Constant/Timer to Local Scope is applicable.
- Compile.
- Replace the extracted code in the source alt statement with a call to the new target altstep and provide any required parameters.
- If any declarations of local temporary elements have been copied to the target altstep, remove their declaration from the source compound.
- If the extracted branches are duplicated at other locations, repeat the previous step for those locations as well. If the additional duplicated branches to be replaced are located in different modules than the target altstep, a corresponding import statement must be added or adjusted in the module of the additional duplicated branches to import the altstep.
- Compile and validate.
Example
alt statements can easily block when no alternative matches as expected. Therefore, it is a good idea to use a timer that can stop the alt statement when it takes unexpectedly long on the one hand and on the other hand unexpected messages should be handled as well. Such error handling distracts from the core behavior, is very generic and can therefore be reused.
testcase tc_exampleTestCase() runs on ExampleComponent {
timer t_guard;
// [...]
t_guard.start ( 10.0 );
alt {
[] pt.receive( a_MessageOne ) {
pt.send( a_MessageTwo );
}
[] any port.receive {
setverdict( fail );
stop;
}
[] t_guard.timeout {
setverdict( fail );
stop;
}
}
}
By applying the Extract Altstep refactoring, the identified branches are moved from the alt statement into the new altstep alt_otherwiseFail as shown in the following listing. Because the local timer t_guard needs to be initialized before the alt statement, it cannot be moved to the altstep and cannot be declared as temporary variable. Instead, it is passed as parameter. Since the altstep uses ports from the ExampleComponent?, the runs on specification is required as well.
testcase tc_exampleTestCase() runs on ExampleComponent {
timer t_guard;
// [...]
t_guard.start ( 10.0 );
alt {
[] pt.receive( a_MessageOne ) {
pt.send( a_MessageTwo );
}
[] alt_otherwiseFail( t_guard ) { }
}
}
altstep alt_otherwiseFail( inout timer p_t )
runs on ExampleComponent {
[] any port.receive {
setverdict( fail );
stop;
}
[] p_t.timeout {
setverdict( fail );
stop;
}
}
