Boost::Test is used for writing unit tests in the ArmarX Framework.
Running Tests
To run all tests go to the project build directory and run:
Both test binaries and test reports can be found in:
How to Write Tests
Writing tests involves editing at least two files. First, you need to add the test to the CMakeLists.txt
of your component. You want to add your tests here using the following line:
armarx_add_test(TestName test/TestName.cpp "${DEPENDENT_LIBRARY_NAMES}")
Second, you need a C++ file test/TestName.cpp
containing your tests. This should look as follows:
#define BOOST_TEST_MODULE ArmarX::Some::Unique::Identifier
#define ARMARX_BOOST_TEST
#include <ArmarXCore/Test.h>
#include <path/to/component_headerfile>
{
int i = 0;
BOOST_CHECK_EQUAL(i, 0);
}
{
}
The most important things are the defines and the includes at the top of the file. Here, the unique name of the current test module must be defined which is then used in the automatically generated <ProjectName/Test.h>
file which does all the necessary setup for creating testsuites and log output. The Test.h
file is automatically generated in build/testing/<ProjectInstallName>/
and is unique for every ArmarX Package.
Afterwards all the tests are added using BOOST_AUTO_TEST_CASE(name) {}
macros.
A list of the most commonly used test macros can be found here: http://www.boost.org/doc/libs/1_48_0/libs/test/doc/html/utf/testing-tools/reference.html
To run the complete testsuite the command 'make test' must be executed. The output of the tests is stored in XML files located in ${PROJECT_DIR}/build/bin
where they can be found and evaluated by the continuous integration pipeline.
To get more information about occurred test failures the respective tests must be run manually by executing their test binaries. The console output will contain more detailed descriptions about unmatched conditions, wrong parameters or locations in the source code.
Testing in ArmarX Environment
The component you want to test may depend on the availability of other ArmarX components or you may wish to test the interaction with your component through Ice. In order to test your component within a running ArmarX context you need to create a test environment that looks similar to the following:
#include <path/to/your/component.h>
#include <ArmarXCore/core/test/IceTestHelper.h>
class YourComponentTestEnvironment
{
public:
YourComponentTestEnvironment(const std::string& testName, int registryPort = 11220, bool addObjects = true)
{
_iceTestHelper = new IceTestHelper(registryPort, registryPort + 1);
_iceTestHelper->startEnvironment();
_manager = new TestArmarXManager(testName, _iceTestHelper->getCommunicator(), properties);
if (addObjects)
{
_yourComponent = _manager->createComponentAndRun<YourComponent, YourComponentInterfacePrx>("ArmarX", "YourComponent");
_anotherComponent = _manager->createComponentAndRun<AnotherComponent, AnotherComponentInterfacePrx>("ArmarX", "AnotherComponent");
}
}
~YourComponentTestEnvironment()
{
_manager->shutdown();
}
YourComponentInterfacePrx _yourComponent;
AnotherComponentInterfacePrx _anotherComponent;
TestArmarXManagerPtr _manager;
IceTestHelperPtr _iceTestHelper;
};
using YourComponentTestEnvironmentPtr = std::shared_ptr<YourComponentTestEnvironment>;
In your test code all you need to do is import your test environment, create an instance of it and access your component via Ice through the proxy stored in you environment. See
armarx/ArmarXCore/source/ArmarXCore/statechart/test/StatechartIceTest.cpp
armarx/MemoryX/source/MemoryX/libraries/memorytypes/entity/test/KBMTest.cpp
armarx/RobotAPI/source/RobotAPI/components/RobotIK/test/RobotIKTest.cpp
for examples.
Testing Statechart Scenarios
It is possible to write tests for testing whole statechart-scenarios for correctness of their output-values. This requires following steps:
- Adding output parameters to your statechart
- Creating unit tests that can test your statechart
For adding output parameters to a statechart using the StatechartEditor, you can refer to Basic Statechart Editor Tutorial, Extended Statechart Editor Tutorial and Advanced Statechart Editor Tutorial. In some cases this might not be enough, so you have to modify the cpp files of your states directly. The following example can be found in armarx/Tutorials/WavingTutorial/source/WavingTutorial/statecharts/WavingTutorialGroup/MoveJoints.cpp
.
void MoveJoints::run()
{
std::map<std::string, float> jointVelocityMap = in.getJointVelocity();
NameControlModeMap velocityControlModeMap;
for (const auto & jointVelocity : jointVelocityMap)
{
velocityControlModeMap[jointVelocity.first] = eVelocityControl;
}
KinematicUnitInterfacePrx kinUnit = getKinematicUnit();
kinUnit->switchControlMode(velocityControlModeMap);
kinUnit->setJointVelocities(jointVelocityMap);
std::map<std::string, float> jointResultMap;
jointResultMap.insert(std::pair<std::string, float>("Elbow L", robot->getRobotNode("Elbow L")->getJointValue()));
out.setJointValuesResult(jointResultMap);
}
After you set up the output parameters of your statechart, you can write unit test, just like in How to Write Tests.
- Note
- These tests will take a lot of time and need a lot of ressources, because every test is started in its own new environment, which contains several started components.
Here you can see a detailed example of how to write a statechart-scenario-test (armarx/Tutorials/WavingTutorial/source/WavingTutorial/statecharts/WavingTutorialGroup/test/WavingTutorialScenarioTestExample.cpp
):
#define BOOST_TEST_MODULE ArmarX::Tutorials::WavingTutorialScenarioTestExample
#define ARMARX_BOOST_TEST
#include <WavingTutorial/Test.h>
#include <ArmarXCore/statechart/test/StatechartScenarioTestEnv.h>
{
StatechartScenarioTestEnvironment env("WavingTutorialScenarioTestExample");
env.startScenario("WavingTutorial", "StartStateChart");
env.watcher->waitForStateChartFinished(90000);
StringVariantContainerBaseMap statechartOutput = env.watcher->getStateChartOutput();
VariantContainerBasePtr counterResult = statechartOutput.find("counterResult")->second;
VariantPtr counterResultVariant = (SingleVariantPtr::dynamicCast(counterResult))->get();
int counterResultValue = counterResultVariant->getInt();
VariantContainerBasePtr finalJointValues = statechartOutput.find("FinalJointValues")->second;
VariantPtr elbowLVariant = (StringValueMapPtr::dynamicCast(finalJointValues))->getVariant(
"Elbow L");
float elbowLValue = elbowLVariant->getFloat();
BOOST_CHECK_EQUAL(counterResultValue, 5);
float expextedElbowLValue = -0.8000000119209290f;
float eps = 7.0f;
BOOST_CHECK_CLOSE(elbowLValue, expextedElbowLValue, eps);
}
If you want to inspect the memories after the statechart has finished and use their content for your tests, you have to use a different environment, which is shown in the example below (armarx/Tutorials/pick_and_place_tutorial/source/pick_and_place_tutorial/statecharts/PickAndPlaceTutorialGroup/test/PickAndPlaceTutorialScenarioTestExample.cpp
).
- Note
- This example does not work reliably, because the Advanced Statechart Editor Tutorial does not finish always with the same results. They depend on the calculation of the physics of the added object and these might vary.
#define BOOST_TEST_MODULE ArmarX::Tutorials::PickAndPlaceTutorialScenarioTestExample
#define ARMARX_BOOST_TEST
#include <pick_and_place_tutorial/Test.h>
#include <MemoryX/core/test/StatechartScenarioMemoryTestEnv.h>
{
StatechartScenarioMemoryTestEnvironment env("PickAndPlaceTutorialScenarioTestExample", "ArmarXDB", "ArmarXDB/dbexport/memdb");
env.startScenario("ArmarXSimulation", "Armar3Simulation", "Simulator", 5000);
env.startScenario("pick_and_place_tutorial", "PickAndPlaceScenario");
env.watcher->waitForStateChartFinished(90000);
StringVariantContainerBaseMap statechartOutput = env.watcher->getStateChartOutput();
try
{
ObjectInstanceMemorySegmentBasePrx objectInstances = env.memoryAccess->workingMemory->getObjectInstancesSegment();
ObjectInstancePtr vitaliscereal = ObjectInstancePtr::dynamicCast(objectInstances->getObjectInstanceByName(
"vitaliscereal"));
BOOST_CHECK_EQUAL(vitaliscereal, !false);
float eps = 1.0f;
int expectedXPosition = 2089;
int expectedYPosition = 5221;
int expectedZPosition = 788;
BOOST_CHECK_CLOSE(vitaliscereal->getPosition()->x, expectedXPosition, eps);
BOOST_CHECK_CLOSE(vitaliscereal->getPosition()->y, expectedYPosition, eps);
BOOST_CHECK_CLOSE(vitaliscereal->getPosition()->z, expectedZPosition, eps);
}
catch (...)
{
}
}
Code Coverage Reports
Generating Code Coverage reports takes quite some time and is therefore disabled by default. To enable it the following steps need to be performed:
- install the "lcov" and "Boost unit_test_framework" package
cd ${Package_DIR}/build
cmake -DARMARX_BUILD_TESTS=TRUE -DARMARX_ENABLE_COVERAGE=TRUE ..
make clean all
make coverage
Afterwards, the coverage report in HTML format can be found in:
${Package_DIR}/build/coverage/
index.html