Unit testing is an important aspect of a quantifiable quality assurance methodology. Although unit testing requires some extra overhead (the writing of the unit tests) during the development process it can provide enormous benefits during the project life cycle.
One benefit of writing unit tests is that it gets the developer using the interface to the unit they designed. This forces the developer to think about, and hopefully design for, usability of the interface early in the development cycle.
Another major benefit to unit testing is that it provides a documented and verifiable level of correctness for the designed unit. This allows the developer to refactor the code more aggressively and to quickly verify its correctness. Unit tests can also be used to perform regression testing when adding new features.
Despite the benefits of unit testing, unit tests are often omitted because of the initial overhead of writing the tests and the complexity of testing frameworks. Legato's Unit Test Framework is simple to use and very flexible and lightweight consisting of some handy macros.
The Legato Test Framework can run in one of two modes: pass through or exit on failure. In pass through mode all tests are run even if some of the tests fail. Failed tests are counted and reported on completion. Exit on failure mode also runs all tests but exits right away if any tests fail.
Mode selection is done through a command line argument. If the test program is run with the command line argument '-p' or '–pass-through' then pass through mode is selected. If the neither the '-p' or '–pass-through' arguments are present then exit on failure mode is selected.
To setup the Legato Test Framework, call the LE_TEST_INIT
macro, once before any tests are started.
To perform tests, call the LE_TEST
macro and pass in your test function to LE_TEST as a parameter. The test function must have a bool return type which indicates that the test passed (true) or failed (false).
For example:
When a test program is finished executing tests and needs to exit, it should always exit using the LE_TEST_EXIT macro.
It's also okay to exit using LE_FATAL(), LE_FATAL_IF() or LE_ASSERT(), if the test must be halted immeditately due to some failure that cannot be recovered from.
The LE_TEST_EXIT macro will cause the process to exit with the number of failed tests as the exit code.
Also, LE_TEST will log an error message if the test fails and will log an info message if the test passes.
If the unit test in the example above was run in "pass-through mode" (continue even when a test fails) and Test1 failed and Test2 passed, the logs will contain the messages:
=ERR= | Unit Test Failed: 'Test1()' INFO | Unit Test Passed: 'Test2()'
And the return code would be 1.
For unit tests that contain multiple threads run the various tests, the normal testing procedure will work because all the macros in this test framework are thread safe.
For unit tests that require the use of multiple concurrent processes, a single process can fork the other processes using LE_TEST_FORK() and then wait for them to terminate using LE_TEST_JOIN().
When a child that is being waited for terminates, LE_TEST_JOIN() will look at the child's termination status and add the results to the running test summary.
If the child process exits normally with a non-negative exit code, that exit code will be considered a count of the number of test failures that occurred in that child process.
If the child exits normally with a negative exit code or if the child is terminated due to a signal (which can be caused by a segmentation fault, etc.), LE_TEST_JOIN() will count one test failure for that child process.
Copyright (C) Sierra Wireless Inc. Use of this work is subject to license.