From 8efeaf07d3163d57bc30422dea9ed5bf4ae2d6df Mon Sep 17 00:00:00 2001 From: Max Schmeller <6088931+mojomex@users.noreply.github.com> Date: Tue, 18 Feb 2025 17:40:47 +0900 Subject: [PATCH] test(rate_checker): add unit tests (#269) * test(rate_checker): add unit tests Signed-off-by: Max SCHMELLER * chore: add license to unit test Co-authored-by: Kenzo Lobos Tsunekawa --------- Signed-off-by: Max SCHMELLER Co-authored-by: Kenzo Lobos Tsunekawa --- nebula_common/CMakeLists.txt | 8 +++ .../nebula_common/util/rate_checker.hpp | 9 ++- .../nebula_common/util/ring_buffer.hpp | 12 +++- nebula_common/test/test_rate_checker.cpp | 63 +++++++++++++++++++ 4 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 nebula_common/test/test_rate_checker.cpp diff --git a/nebula_common/CMakeLists.txt b/nebula_common/CMakeLists.txt index 9a17fb217..48407515c 100644 --- a/nebula_common/CMakeLists.txt +++ b/nebula_common/CMakeLists.txt @@ -21,7 +21,15 @@ endif(NOT ${YAML_CPP_VERSION} VERSION_LESS "0.5") if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) + find_package(ament_cmake_gtest REQUIRED) ament_lint_auto_find_test_dependencies() + + ament_add_gtest(test_rate_checker + test/test_rate_checker.cpp + ) + target_link_libraries(test_rate_checker + nebula_common + ) endif() include_directories( diff --git a/nebula_common/include/nebula_common/util/rate_checker.hpp b/nebula_common/include/nebula_common/util/rate_checker.hpp index 3ebee7982..2efc50ec0 100644 --- a/nebula_common/include/nebula_common/util/rate_checker.hpp +++ b/nebula_common/include/nebula_common/util/rate_checker.hpp @@ -16,6 +16,8 @@ #include "nebula_common/util/ring_buffer.hpp" +#include + namespace nebula::util { @@ -27,7 +29,7 @@ class RateChecker { } - bool is_full() const { return ring_buffer_.is_full(); } + [[nodiscard]] bool is_full() const { return ring_buffer_.is_full(); } void update(double stamp) { @@ -38,13 +40,14 @@ class RateChecker last_stamp_ = stamp; } - bool is_valid() const + [[nodiscard]] bool is_valid() const { + if (ring_buffer_.size() == 0) return false; double average = get_average(); return average >= min_rate_hz_ && average <= max_rate_hz_; } - double get_average() const { return 1.0 / ring_buffer_.get_average(); } + [[nodiscard]] double get_average() const { return 1.0 / ring_buffer_.get_average(); } private: std::optional last_stamp_; diff --git a/nebula_common/include/nebula_common/util/ring_buffer.hpp b/nebula_common/include/nebula_common/util/ring_buffer.hpp index 5b26ed4fd..55ccf2669 100644 --- a/nebula_common/include/nebula_common/util/ring_buffer.hpp +++ b/nebula_common/include/nebula_common/util/ring_buffer.hpp @@ -40,11 +40,17 @@ class RingBuffer index_ = (index_ + 1) % buffer_.capacity(); } - std::size_t size() const { return buffer_.size(); } + [[nodiscard]] std::size_t size() const { return buffer_.size(); } - bool is_full() const { return buffer_.size() == buffer_.capacity(); } + [[nodiscard]] bool is_full() const { return buffer_.size() == buffer_.capacity(); } - T get_average() const { return sum_ / buffer_.size(); } + [[nodiscard]] T get_average() const + { + if (buffer_.size() == 0) { + throw std::runtime_error("Buffer is empty"); + } + return sum_ / buffer_.size(); + } private: T sum_{}; diff --git a/nebula_common/test/test_rate_checker.cpp b/nebula_common/test/test_rate_checker.cpp new file mode 100644 index 000000000..f0d814f1b --- /dev/null +++ b/nebula_common/test/test_rate_checker.cpp @@ -0,0 +1,63 @@ +// Copyright 2025 TIER IV, 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. + +#include "nebula_common/util/rate_checker.hpp" + +#include + +static void feed_at_rate(nebula::util::RateChecker & checker, double rate_hz, uint32_t num_updates) +{ + double interval = 1.0 / rate_hz; + for (uint32_t i = 0; i < num_updates; ++i) { + checker.update(i * interval); + } +} + +TEST(RateCheckerTest, BasicFunctionality) +{ + nebula::util::RateChecker checker(8.0, 12.0, 5); + + EXPECT_FALSE(checker.is_full()); + EXPECT_FALSE(checker.is_valid()); + EXPECT_THROW(static_cast(checker.get_average()), std::runtime_error); + + feed_at_rate(checker, 10.0, 6); + + EXPECT_TRUE(checker.is_full()); + EXPECT_NO_THROW(static_cast(checker.get_average())); + EXPECT_NEAR(checker.get_average(), 10.0, 0.1); + EXPECT_TRUE(checker.is_valid()); +} + +TEST(RateCheckerTest, InvalidRate) +{ + nebula::util::RateChecker checker(8.0, 12.0, 3); + + feed_at_rate(checker, 5.0, 4); + + EXPECT_NEAR(checker.get_average(), 5.0, 0.1); + EXPECT_FALSE(checker.is_valid()); + + checker = nebula::util::RateChecker(8.0, 12.0, 3); + feed_at_rate(checker, 15.0, 4); + + EXPECT_NEAR(checker.get_average(), 15.0, 0.1); + EXPECT_FALSE(checker.is_valid()); +} + +int main(int argc, char ** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}