This interpreter can process annotations in RPG code to be used to define assertions for testing purposes. These annotations are called Mute for historical reasons.
The standard notation for a MUTE file follows the structure MUTEnn_mmk
where:
nn
is a two-digit number that identifies the test domain (e.g. API, Element, etc)mm
is a two-digit sequential number, used to enumerate the tests of a given domain.k
is a letter and indicates a MUTE subtest that is called by the main MUTE test (with identical name but withoutk
).
A list of the meanings of the MUTE code-names (note that the prefix represent the domain, nn
values, while the suffix represent the domain tests, mm
value):
CODE | DOMAIN TESTS |
---|---|
MUTE01 | Element |
MUTE02 | List |
MUTE03 | DS |
MUTE05 | Expressions |
MUTE06 | Data Access |
MUTE07 | Opcodes |
MUTE08 | Application functions |
MUTE09 | Validation Functions |
MUTE10 | Performance Functions |
MUTE11 | Plugin gateway and tests |
MUTE12 | Data type package |
MUTE13 | BIF and Opcodes |
MUTE14 | /COPY |
MUTE15 | Procedures |
MUTE16 | Reload |
MUTE18 | /API directive |
The Mute annotations that compare two values looks like this:
MU* VAL1(B) VAL2(8) COMP(EQ)
In this case we are stating that we expect the value B to be equal to 8.
The operators supported are:
- EQ equal
- NE not equal
- GT greater than
- GE greater than or equal
- LT lower than
- LE lower than or equal
The Mute annotations are verified after executing the statement they annotate. Typically the annotate the statement immediately following them. Note that multiple annotations can be applied to the same statement:
MU* VAL1(A) VAL2(5) COMP(EQ)
MU* VAL1(B) VAL2(8) COMP(EQ)
MU* VAL1(RESULT) VAL2(13) COMP(EQ)
C EVAL RESULT = A + B
For testing of performances, there are MUTE annotations that introduce a timeout in the execution:
MU* TIMEOUT(123)
This instruction tells the MUTE running engine to fail if the current program has an execution time that exceeds 123 milliseconds. Note that the assertion is verified at the end of the program, so it doesn't stop long running executions, but it just signals the timeout condition at the end of the interpretation.
You can add many TIMEOUT
assertion in the same program, but, obviously, just the one with the shortest timeout is considered.
There isn't a mandatory position to insert the TIMEOUT
annotation line, that, as other MUTE
annotations, are just considered as comments by the standard interpreter engine.
Like in other frameworks for unit testing, there is an assertion that always fails:
MU* FAIL('Something went wrong')
An expression can be used for the failing message:
D MSG S 15 INZ('Failure message')
MU* FAIL(MSG)
This kind of assertion is still an annotation, so it's tied with the instruction it annotates.
For example, this is a wrong way of using a FAIL
assertion, since it results to annotate the DSPLY
instruction.
C IF 1=2
MU* FAIL('This code should not be executed')
C ENDIF
C '' DSPLY
The correct way of doing this should be:
C IF 1=2
MU* FAIL('This code should not be executed')
C '' DSPLY
C ENDIF
The execution of the program continues even after the failing of the assertion, but an error is reported at the end of the MUTE tests.
The Mute annotations can be verified using an utility contained in the class com.smeup.rpgparser.mute.MuterunnerKt
.
For your convenience you can produce a JAR with all the dependencies by running:
./gradlew fatJar
And then run such class with this command:
java -cp rpgJavaInterpreter-core/build/libs/rpgJavaInterpreter-core-all.jar com.smeup.rpgparser.mute.MuterunnerKt
You may want to create an alias for convenience.
This program accepts the flags -verbose
(default) and -silent
.
All the other arguments are treated as paths to examine. If no paths are specified
the current directory is used.
Example of output produced:
MUTE Runner
(running in verbose mode)
paths to process: [../mutes_for_ci]
../mutes_for_ci/SIMPLE_MUTE.rpgle
Mute annotation at line 5 attached to statement 8
Mute annotation at line 6 attached to statement 8
Mute annotation at line 7 attached to statement 8
Total annotation: 3, executed: 3, failed: 0
Total files: 1, resolved: 3, executed: 3, failed:0
SUCCESS
It is also possible to package the MUTE engine as a single jar that can be run independently. To create such jar, yoi can run:
./gradlew fatMuteJar
This command creates the JAR rpgJavaInterpreter-core/build/libs/rpgJavaInterpreter-core-mute-all.jar
.
Then run the mute test with:
java -jar rpgJavaInterpreter-core/build/libs/rpgJavaInterpreter-core-mute-all.jar MY_TEST.rpgle
You can run all the mute tests (i.e. files with suffix rpgle) contained in a directory with:
java -jar rpgJavaInterpreter-core-mute-all.jar /path/to/my/dir
You can also collect all timeouts exception in a .csv file with
java -DexportCsvFile=/path/to/timeouts.csv -jar rpgJavaInterpreter-core-mute-all.jar /path/to/my/dir
The utility to verify Mute annotation is run as part of the CI configuration. All Mute files contained in the directory mutes_for_ci are automatically verified when running ./gradlew check
.
You can run mute tests in the directory mutes_for_ci using this gradle task:
./gradlew runMutes
If you want to run tests in a different directory, use the muteDir
property. For example:
./gradlew runMutes -PmuteDir="../mute_tests"
To be more specific, you can follow these steps to run your own MUTE tests:
- Create a new folder to contain your Mutes:
/my/dir/my_mutes
- Copy in this folder the a config fil like
mute_tests/mute_logging.config
ormutes_for_ci/mute_logging.config
- Customize this file with your logging preferences
- Create your MUTE file in this folder.
- From the root directory of the smeup-rpg project, run
./gradlew runMutes -PmuteDir="/my/dir/my_mutes"
With this technique, you can have a (sort of) debug view of the execution of your program.
You can run MUTE tests in Visual Studio Code: see the documentation
Here you can also see how to execute the FizzBuzz TDD Kata in RPGLE with MUTEs. You can see the source of the tests and of the progam