Skip to content

Commit 03929e7

Browse files
mjcarrollJanosch Machowinski
andcommitted
Add test for cbg getting reset
Signed-off-by: Michael Carroll <[email protected]> Co-authored-by: Janosch Machowinski <[email protected]>
1 parent f9c4894 commit 03929e7

File tree

2 files changed

+148
-0
lines changed

2 files changed

+148
-0
lines changed

rclcpp/test/rclcpp/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,15 @@ if(TARGET test_executors)
473473
target_link_libraries(test_executors_timer_cancel_behavior ${PROJECT_NAME} ${rosgraph_msgs_TARGETS})
474474
endif()
475475

476+
ament_add_gtest(
477+
test_executors_callback_group_behavior
478+
executors/test_executors_callback_group_behavior.cpp
479+
APPEND_LIBRARY_DIRS "${append_library_dirs}"
480+
TIMEOUT 180)
481+
if(TARGET test_executors)
482+
target_link_libraries(test_executors_callback_group_behavior ${PROJECT_NAME})
483+
endif()
484+
476485
ament_add_gtest(
477486
test_executors_intraprocess
478487
executors/test_executors_intraprocess.cpp
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright 2024 Open Source Robotics Foundation, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/**
16+
* This test checks that when callback groups go out of scope, that their associated executable
17+
* entities should not be returned as valid executables.
18+
*
19+
* The test makes use of a bit of executor internals, but is meant to prevent regressions of behavior.
20+
* Ref: https://github.com/ros2/rclcpp/issues/2474
21+
*/
22+
23+
#include <gtest/gtest.h>
24+
25+
#include <chrono>
26+
#include <rclcpp/callback_group.hpp>
27+
#include <rclcpp/executor.hpp>
28+
#include <rclcpp/node.hpp>
29+
30+
class CustomExecutor: public rclcpp::Executor
31+
{
32+
public:
33+
explicit CustomExecutor(const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions())
34+
: rclcpp::Executor(options)
35+
{
36+
}
37+
38+
~CustomExecutor() override = default;
39+
40+
void spin() override {};
41+
42+
void collect() {
43+
this->collect_entities();
44+
}
45+
46+
void wait() {
47+
this->wait_for_work(std::chrono::milliseconds(10));
48+
}
49+
50+
size_t collected_timers() const {
51+
return this->current_collection_.timers.size();
52+
}
53+
54+
rclcpp::AnyExecutable next() {
55+
rclcpp::AnyExecutable ret;
56+
this->get_next_ready_executable(ret);
57+
return ret;
58+
}
59+
};
60+
61+
62+
TEST(TestCallbackGroup, ValidCbg)
63+
{
64+
rclcpp::init(0, nullptr);
65+
66+
// Create a timer associated with a callback group
67+
auto node = std::make_shared<rclcpp::Node>("node");
68+
69+
// Add the callback group to the executor
70+
auto executor = CustomExecutor();
71+
executor.add_node(node);
72+
executor.spin_all(std::chrono::milliseconds(10));
73+
74+
auto cbg = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive, true);
75+
auto timer = node->create_wall_timer(std::chrono::milliseconds(1), [](){}, cbg);
76+
executor.add_callback_group(cbg, node->get_node_base_interface());
77+
78+
executor.spin_all(std::chrono::milliseconds(10));
79+
80+
// Collect the entities
81+
executor.collect();
82+
EXPECT_EQ(1u, executor.collected_timers());
83+
84+
executor.wait();
85+
auto next_executable = executor.next();
86+
EXPECT_EQ(timer, next_executable.timer);
87+
EXPECT_EQ(cbg, next_executable.callback_group);
88+
89+
EXPECT_EQ(nullptr, next_executable.client);
90+
EXPECT_EQ(nullptr, next_executable.service);
91+
EXPECT_EQ(nullptr, next_executable.subscription);
92+
EXPECT_EQ(nullptr, next_executable.waitable);
93+
EXPECT_EQ(nullptr, next_executable.data);
94+
95+
rclcpp::shutdown();
96+
}
97+
98+
TEST(TestCallbackGroup, InvalidCbg)
99+
{
100+
rclcpp::init(0, nullptr);
101+
102+
// Create a timer associated with a callback group
103+
auto node = std::make_shared<rclcpp::Node>("node");
104+
105+
// Add the callback group to the executor
106+
auto executor = CustomExecutor();
107+
executor.add_node(node);
108+
executor.spin_all(std::chrono::milliseconds(10));
109+
110+
auto cbg = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive, false);
111+
auto timer = node->create_wall_timer(std::chrono::milliseconds(1), [](){}, cbg);
112+
executor.add_callback_group(cbg, nullptr);
113+
114+
executor.spin_all(std::chrono::milliseconds(10));
115+
116+
// Collect the entities
117+
executor.collect();
118+
EXPECT_EQ(1u, executor.collected_timers());
119+
120+
executor.wait();
121+
122+
cbg.reset();
123+
124+
// Since the callback group has been reset, this should not be allowed to
125+
// be a valid executable (timer and cbg should both be nullptr).
126+
// In the regression, timer == next_executable.timer whil
127+
// next_executable.callback_group == nullptr, which was incorrect.
128+
auto next_executable = executor.next();
129+
EXPECT_EQ(nullptr, next_executable.timer);
130+
EXPECT_EQ(nullptr, next_executable.callback_group);
131+
132+
EXPECT_EQ(nullptr, next_executable.client);
133+
EXPECT_EQ(nullptr, next_executable.service);
134+
EXPECT_EQ(nullptr, next_executable.subscription);
135+
EXPECT_EQ(nullptr, next_executable.waitable);
136+
EXPECT_EQ(nullptr, next_executable.data);
137+
138+
rclcpp::shutdown();
139+
}

0 commit comments

Comments
 (0)