Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use gcovr to enforce code coverage everywhere #437

Merged
merged 4 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/ci_pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ jobs:
- name: Install debian packages
run: >-
sudo apt-get update && sudo apt-get install -y \
gcovr \
git \
lcov \
python3-colcon-common-extensions \
Expand Down Expand Up @@ -101,6 +102,10 @@ jobs:
working-directory: ${{ github.workspace }}
run: ./src/beluga/tools/run-clang-tidy.sh

- name: Enforce code coverage
working-directory: ${{ github.workspace }}
run: ./src/beluga/tools/check-code-coverage.sh

- name: Upload code coverage report
uses: codecov/codecov-action@v3
with:
Expand All @@ -109,7 +114,7 @@ jobs:
name: codecov-umbrella
fail_ci_if_error: true
verbose: true
if: ${{ matrix.upload_artifacts }}
if: ${{ matrix.upload_artifacts && !github.event.pull_request.head.repo.fork }}

build-docs:
needs: build-test
Expand Down
3 changes: 3 additions & 0 deletions beluga/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
-Wextra
-Wpedantic)
endif()
if(CMAKE_BUILD_TYPE MATCHES "Debug")
target_compile_options(beluga_compile_options INTERFACE -fno-inline)
endif()

find_package(Eigen3 REQUIRED NO_MODULE)
find_package(
Expand Down
3 changes: 3 additions & 0 deletions beluga_amcl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
-Werror
-Wpedantic)
endif()
if(CMAKE_BUILD_TYPE MATCHES "Debug")
add_compile_options(-fno-inline)
endif()

set(ROS_VERSION $ENV{ROS_VERSION})
set(ROS_VERSION $ENV{ROS_VERSION})
Expand Down
43 changes: 40 additions & 3 deletions beluga_amcl/test/test_amcl_nodelet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ class Tester {
laser_scan_publisher_ = nh_.advertise<sensor_msgs::LaserScan>("scan", 1);

global_localization_client_ = nh_.serviceClient<std_srvs::Empty>("global_localization");

nomotion_update_client_ = nh_.serviceClient<std_srvs::Empty>("request_nomotion_update");
}

void create_pose_subscriber() {
Expand All @@ -120,7 +122,7 @@ class Tester {

void pose_callback(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr& message) { latest_pose_ = *message; }

const auto& latest_pose() const { return latest_pose_; }
auto& latest_pose() { return latest_pose_; }

void create_particle_cloud_subscriber() {
particle_cloud_subscriber_ = nh_.subscribe<geometry_msgs::PoseArray>(
Expand Down Expand Up @@ -241,6 +243,16 @@ class Tester {
return global_localization_client_.call(srv);
}

template <class Rep, class Period>
bool wait_for_nomotion_update_service(const std::chrono::duration<Rep, Period>& timeout) {
return nomotion_update_client_.waitForExistence(ros::Duration(std::chrono::duration<double>(timeout).count()));
}

bool request_nomotion_update() {
std_srvs::Empty srv;
return nomotion_update_client_.call(srv);
}

private:
static bool static_map_callback(nav_msgs::GetMap::Request&, nav_msgs::GetMap::Response& response) {
response.map = make_dummy_map();
Expand All @@ -267,6 +279,7 @@ class Tester {
tf2_ros::TransformListener tf_listener_;

ros::ServiceClient global_localization_client_;
ros::ServiceClient nomotion_update_client_;
};

/// Base node fixture class with common utilities.
Expand All @@ -279,13 +292,12 @@ class BaseTestFixture : public T {
tester_ = std::make_shared<Tester>();
}

void TearDown() override {}

bool wait_for_initialization() {
return spin_until([this] { return amcl_nodelet_->is_initialized(); }, 1000ms);
}

bool wait_for_pose_estimate() {
tester_->latest_pose().reset();
return spin_until([this] { return tester_->latest_pose().has_value(); }, 1000ms);
}

Expand All @@ -301,6 +313,13 @@ class BaseTestFixture : public T {
return tester_->request_global_localization();
}

bool request_nomotion_update() {
if (!tester_->wait_for_nomotion_update_service(500ms)) {
return false;
}
return tester_->request_nomotion_update();
}

protected:
std::shared_ptr<AmclNodeletUnderTest> amcl_nodelet_;
std::shared_ptr<Tester> tester_;
Expand Down Expand Up @@ -551,6 +570,24 @@ TEST_F(TestFixture, FirstMapOnly) {
}
}

TEST_F(TestFixture, CanForcePoseEstimate) {
beluga_amcl::AmclConfig config;
EXPECT_TRUE(amcl_nodelet_->default_config(config));
config.set_initial_pose = false;
EXPECT_TRUE(amcl_nodelet_->set(config));
tester_->publish_map();
EXPECT_TRUE(request_global_localization());
EXPECT_TRUE(wait_for_initialization());
tester_->create_pose_subscriber();
tester_->publish_laser_scan();
EXPECT_TRUE(wait_for_pose_estimate());
EXPECT_TRUE(tester_->can_transform("map", "odom"));
EXPECT_TRUE(request_nomotion_update());
tester_->publish_laser_scan();
EXPECT_TRUE(wait_for_pose_estimate());
EXPECT_TRUE(tester_->can_transform("map", "odom"));
}

TEST_F(TestFixture, KeepCurrentEstimate) {
beluga_amcl::AmclConfig config;
ASSERT_TRUE(amcl_nodelet_->default_config(config));
Expand Down
3 changes: 3 additions & 0 deletions beluga_ros/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
-Werror
-Wpedantic)
endif()
if(CMAKE_BUILD_TYPE MATCHES "Debug")
add_compile_options(-fno-inline)
endif()

set(ROS_VERSION $ENV{ROS_VERSION})
if(NOT ROS_VERSION)
Expand Down
11 changes: 11 additions & 0 deletions beluga_ros/test/test_amcl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,17 @@ TEST(TestAmcl, UpdateWithParticles) {
ASSERT_TRUE(estimate.has_value());
}

TEST(TestAmcl, UpdateWithParticlesWithMotion) {
auto amcl = make_amcl();
ASSERT_EQ(amcl.particles().size(), 0);
amcl.initialize_from_map();
ASSERT_EQ(amcl.particles().size(), 50UL);
auto estimate = amcl.update(Sophus::SE2d{}, make_dummy_laser_scan());
ASSERT_TRUE(estimate.has_value());
estimate = amcl.update(Sophus::SE2d{0.0, {1.0, 0.0}}, make_dummy_laser_scan());
ASSERT_TRUE(estimate.has_value());
}

TEST(TestAmcl, UpdateWithParticlesNoMotion) {
auto amcl = make_amcl();
ASSERT_EQ(amcl.particles().size(), 0);
Expand Down
1 change: 1 addition & 0 deletions docker/images/humble/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ RUN apt-get update \
&& apt-get install --no-install-recommends -y \
ccache \
curl \
gcovr \
gdb \
git \
lcov \
Expand Down
1 change: 1 addition & 0 deletions docker/images/iron/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ RUN apt-get update \
&& apt-get install --no-install-recommends -y \
ccache \
curl \
gcovr \
gdb \
git \
lcov \
Expand Down
1 change: 1 addition & 0 deletions docker/images/jazzy/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ RUN apt-get update \
&& apt-get install --no-install-recommends -y \
ccache \
curl \
gcovr \
gdb \
git \
lcov \
Expand Down
1 change: 1 addition & 0 deletions docker/images/noetic/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ RUN apt-get update \
&& apt-get install --no-install-recommends -y \
ccache \
curl \
gcovr \
gdb \
git \
lcov \
Expand Down
1 change: 1 addition & 0 deletions docker/images/rolling/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ RUN apt-get update \
&& apt-get install --no-install-recommends -y \
ccache \
curl \
gcovr \
gdb \
git \
lcov \
Expand Down
15 changes: 8 additions & 7 deletions tools/build-and-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -60,24 +60,25 @@ colcon build \
--cmake-force-configure
echo ::endgroup::

LCOV_CONFIG_PATH=${SCRIPT_PATH}/../.lcovrc

echo ::group::Test
colcon lcov-result --initial
colcon lcov-result \
--initial \
--lcov-config-file ${LCOV_CONFIG_PATH} \
--packages-select ${ROS_PACKAGES}
colcon test \
--packages-select ${ROS_PACKAGES} \
--event-handlers console_cohesion+ \
--return-code-on-test-failure \
--mixin coverage-pytest
echo ::endgroup::

LCOV_CONFIG_PATH=${SCRIPT_PATH}/../.lcovrc

echo ::group::Generate code coverage results
colcon lcov-result \
--packages-select ${ROS_PACKAGES} \
--lcov-config-file ${LCOV_CONFIG_PATH} \
--verbose
--lcov-config-file ${LCOV_CONFIG_PATH}
colcon coveragepy-result \
--packages-select ${ROS_PACKAGES} \
--coverage-report-args -m \
--verbose
--coverage-report-args -m
echo ::endgroup::
25 changes: 25 additions & 0 deletions tools/check-code-coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

# Copyright 2024 Ekumen, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Enforce code coverage limits

[[ -z "${WITHIN_DEV}" ]] && echo -e "\033[1;33mWARNING: Try running this script inside the development container if you experience any issues.\033[0m"

set -o errexit

gcovr --fail-under-line 95 -j -u -f 'src/beluga/.*' -e '.*/test/.*cpp' build/
# NOTE: Enable Python code coverage checks when attained
# (cd coveragepy && python3 -m coverage report --fail-under=95)
Loading