Description
Hi, I have a question regarding branch coverage. I have turned on the branch coverage in the config file `/etc/lcovrc'
# Specify if branch coverage data should be collected and processed.
lcov_branch_coverage = 1
The sample code I'm testing does not have any if statement, so I was expecting the branch coverage to be 100%. But, the result is not what I expected.
For some reason, some lines that do not belong to the branches are treated as branch statements.
Here's the minimum working example.
Testing environment: Ubuntu 20.04.4 LTS, LCOV 1.14, and GCOV 9.04.
The sample_pkg_node
subscribes to topic received_topic
and publishes to topic sending_topic
, both of them having std_msg::String
type.
sample_pkg/src/sample_pkg_node.cpp
#include <ros/ros.h>
#include <std_msgs/String.h>
class SamplePkg
{
public:
SamplePkg(ros::NodeHandle& node) {
subscriber_ = node.subscribe("received_topic", 100, &SamplePkg::sub_callback, this);
publisher_ = node.advertise<std_msgs::String>("sending_topic", 100, true);
};
~SamplePkg() = default;
private:
ros::Subscriber subscriber_;
ros::Publisher publisher_;
void sub_callback(const std_msgs::String::ConstPtr& input_msg) {
std_msgs::String output_msg;
output_msg.data = input_msg->data + " -- output";
ROS_INFO_STREAM("Publish: " << output_msg.data);
publisher_.publish(output_msg);
}
};
int main(int argc, char** argv)
{
ros::init(argc, argv, "sample_pkg_node");
ros::NodeHandle node;
SamplePkg sample_pkg(node);
ros::spin();
return 0;
}
The sample_pkg_test_node
is the reverse of the sample_pkg_node
, which subscribes to sending_topic
and publishes to received_topic
.
sample_pkg/test/sample_pkg_test.cpp
#include <ros/ros.h>
#include <gtest/gtest.h>
#include <std_msgs/String.h>
class SamplePkgTest: public ::testing::Test
{
public:
SamplePkgTest()
: node_handle_(),
publisher_(node_handle_.advertise<std_msgs::String>("received_topic", 5, true)),
subscriber_(node_handle_.subscribe("sending_topic", 5, &SamplePkgTest::Callback, this)),
message_received_(false)
{
}
/*
* This is necessary because it takes a while for the node under
* test to start up.
*/
void SetUp() override {
while (!IsNodeReady()) {
ros::spinOnce();
}
}
void TearDown() override {
}
void Publish(std_msgs::String& msg) {
publisher_.publish(msg);
}
/*
* This is necessary because it takes time for messages from the
* node under test to reach this node.
*/
boost::shared_ptr<const std_msgs::String> WaitForMessage() {
// The second parameter is a timeout duration.
return ros::topic::waitForMessage<std_msgs::String>(
subscriber_.getTopic(), node_handle_, ros::Duration(1));
}
private:
bool message_received_;
ros::NodeHandle node_handle_;
ros::Publisher publisher_;
ros::Subscriber subscriber_;
/*
* This callback is a no-op because we get the messages from the
* node under test using WaitForMessage().
*/
void Callback(const std_msgs::String& event) {
message_received_ = true;
}
/*
* See SetUp method.
*/
bool IsNodeReady() {
return (publisher_.getNumSubscribers() > 0) && (subscriber_.getNumPublishers() > 0);
}
};
TEST_F(SamplePkgTest, SampleTest) {
std_msgs::String msg;
msg.data = "Test";
Publish(msg);
auto output = WaitForMessage();
ASSERT_TRUE(output != NULL);
EXPECT_EQ("Test -- output", output->data);
}
sample_pkg/test/main_test.cpp
#include <ros/ros.h>
#include <gtest/gtest.h>
int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
ros::init(argc, argv, "sample_pkg_test_node");
return RUN_ALL_TESTS();
}
sample_pkg/test/sample_pkg_test.test
<?xml version="1.0"?>
<launch>
<node name="sample_pkg_node" pkg="sample_pkg" type="sample_pkg_node"/>
<test test-name="sample_pkg_test_node" pkg="sample_pkg" type="sample_pkg_test_node"/>
</launch>
I have followed the README to setup the CMakeLists.txt as well as the package.xml.
sample_pkg/CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2)
project(sample_pkg)
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
)
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES sample_pkg
CATKIN_DEPENDS roscpp std_msgs
# DEPENDS system_lib
)
if(CATKIN_ENABLE_TESTING AND ENABLE_COVERAGE_TESTING)
find_package(code_coverage REQUIRED)
# Add compiler flags for coverage instrumentation before defining any targets
APPEND_COVERAGE_COMPILER_FLAGS()
endif()
include_directories(
#include
${catkin_INCLUDE_DIRS}
)
add_executable(${PROJECT_NAME}_node
src/sample_pkg_node.cpp
)
target_link_libraries(${PROJECT_NAME}_node
${catkin_LIBRARIES}
)
if(CATKIN_ENABLE_TESTING)
find_package(rostest REQUIRED)
add_rostest_gtest(sample_pkg_test_node
test/sample_pkg_test.test
test/sample_pkg_test.cpp
test/main_test.cpp
)
target_link_libraries(sample_pkg_test_node
${catkin_LIBRARIES}
)
# Create a target ${PROJECT_NAME}_coverage_report
if(ENABLE_COVERAGE_TESTING)
set(COVERAGE_EXCLUDES "*/${PROJECT_NAME}/test*" "*/${PROJECT_NAME}/other_dir_i_dont_care_about*")
add_code_coverage(
NAME ${PROJECT_NAME}_coverage_report
DEPENDENCIES tests
)
endif()
endif()
sample_pkg/package.xml
<?xml version="1.0"?>
<package format="2">
<name>sample_pkg</name>
<version>0.0.0</version>
<description>The sample_pkg package</description>
<maintainer email="[email protected]">yi-chen</maintainer>
<license>TODO</license>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>std_msgs</build_depend>
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>std_msgs</exec_depend>
<test_depend>rostest</test_depend>
<test_depend>code_coverage</test_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->
</export>
</package>
Then, the package is built by the following commands:
catkin config --cmake-args -DENABLE_COVERAGE_TESTING=ON -DCMAKE_BUILD_TYPE=Debug
catkin build sample_pkg
catkin build sample_pkg -v --no-deps --catkin-make-args sample_pkg_coverage_report
I don't know which part I was doing it wrong. Any insight would be appreciated. Thank you.