Skip to content

Commit 9df8e13

Browse files
committed
add tests for subscriptions in the execution order
Signed-off-by: William Woodall <[email protected]>
1 parent 82ce8fb commit 9df8e13

File tree

1 file changed

+106
-31
lines changed

1 file changed

+106
-31
lines changed

rclcpp/test/rclcpp/executors/test_executors.cpp

Lines changed: 106 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -897,11 +897,11 @@ TYPED_TEST(TestExecutors, deterministic_execution_order_ub)
897897
{
898898
using ExecutorType = TypeParam;
899899

900-
// number of waitable to test
901-
constexpr size_t number_of_waitables = 10;
902-
std::vector<size_t> forward(number_of_waitables);
900+
// number of each entity to test
901+
constexpr size_t number_of_entities = 20;
902+
std::vector<size_t> forward(number_of_entities);
903903
std::iota(std::begin(forward), std::end(forward), 0);
904-
std::vector<size_t> reverse(number_of_waitables);
904+
std::vector<size_t> reverse(number_of_entities);
905905
std::reverse_copy(std::begin(forward), std::end(forward), std::begin(reverse));
906906

907907
struct test_case
@@ -948,42 +948,117 @@ TYPED_TEST(TestExecutors, deterministic_execution_order_ub)
948948
rclcpp::CallbackGroupType::MutuallyExclusive,
949949
automatically_add_to_executor_with_node);
950950

951-
auto waitable_interfaces = this->node->get_node_waitables_interface();
951+
// perform each of the test cases for waitables
952+
{
953+
auto waitable_interfaces = this->node->get_node_waitables_interface();
952954

953-
std::vector<std::shared_ptr<TestWaitable>> waitables;
954-
for (size_t i = 0; i < number_of_waitables; ++i) {
955-
auto my_waitable = std::make_shared<TestWaitable>();
956-
waitable_interfaces->add_waitable(my_waitable, isolated_callback_group);
957-
waitables.push_back(my_waitable);
958-
}
955+
std::vector<std::shared_ptr<TestWaitable>> waitables;
956+
for (size_t i = 0; i < number_of_entities; ++i) {
957+
auto my_waitable = std::make_shared<TestWaitable>();
958+
waitable_interfaces->add_waitable(my_waitable, isolated_callback_group);
959+
waitables.push_back(my_waitable);
960+
}
959961

960-
// perform each of the test cases
961-
for (const auto & test_case_pair : test_cases) {
962-
const std::string & test_case_name = test_case_pair.first;
963-
const auto & test_case = test_case_pair.second;
962+
for (const auto & test_case_pair : test_cases) {
963+
const std::string & test_case_name = test_case_pair.first;
964+
const auto & test_case = test_case_pair.second;
964965

965-
ExecutorType executor;
966-
executor.add_callback_group(isolated_callback_group, this->node->get_node_base_interface());
966+
ExecutorType executor;
967+
executor.add_callback_group(isolated_callback_group, this->node->get_node_base_interface());
967968

968-
RCPPUTILS_SCOPE_EXIT({
969-
for (size_t i = 0; i < number_of_waitables; ++i) {
970-
waitables[i]->set_on_execute_callback(nullptr);
969+
RCPPUTILS_SCOPE_EXIT({
970+
for (size_t i = 0; i < number_of_entities; ++i) {
971+
waitables[i]->set_on_execute_callback(nullptr);
972+
}
973+
});
974+
975+
std::vector<size_t> actual_order;
976+
for (size_t i : test_case.call_order) {
977+
waitables[i]->set_on_execute_callback([&actual_order, i]() {actual_order.push_back(i);});
978+
waitables[i]->trigger();
971979
}
972-
});
973980

974-
std::vector<size_t> actual_order;
975-
for (size_t i : test_case.call_order) {
976-
waitables[i]->set_on_execute_callback([&actual_order, i]() {actual_order.push_back(i);});
977-
waitables[i]->trigger();
978-
}
981+
while (actual_order.size() < number_of_entities && rclcpp::ok()) {
982+
executor.spin_once(10s); // large timeout because it should normally exit quickly
983+
}
979984

980-
while (actual_order.size() < number_of_waitables) {
981-
executor.spin_once(10s); // large timeout because it should normally exit quickly
985+
EXPECT_EQ(actual_order, test_case.expected_execution_order)
986+
<< "callback call order in test case '" << test_case_name << "' different than expected, "
987+
<< "this may be a false positive, see test description";
982988
}
989+
}
990+
991+
const std::string test_topic_name = "/deterministic_execution_order_ub";
992+
std::map<rclcpp::SubscriptionBase *, std::function<void()>> on_sub_data_callbacks;
993+
std::vector<rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr> subscriptions;
994+
rclcpp::SubscriptionOptions so;
995+
so.callback_group = isolated_callback_group;
996+
for (size_t i = 0; i < number_of_entities; ++i) {
997+
size_t next_sub_index = subscriptions.size();
998+
auto sub = this->node->template create_subscription<test_msgs::msg::Empty>(
999+
test_topic_name,
1000+
10,
1001+
[&on_sub_data_callbacks, &subscriptions, next_sub_index](const test_msgs::msg::Empty &) {
1002+
auto this_sub_pointer = subscriptions[next_sub_index].get();
1003+
auto callback_for_sub_it = on_sub_data_callbacks.find(this_sub_pointer);
1004+
ASSERT_NE(callback_for_sub_it, on_sub_data_callbacks.end());
1005+
auto on_sub_data_callback = callback_for_sub_it->second;
1006+
if (on_sub_data_callback) {
1007+
on_sub_data_callback();
1008+
}
1009+
},
1010+
so);
1011+
subscriptions.push_back(sub);
1012+
}
1013+
1014+
// perform each of the test cases for subscriptions
1015+
if (
1016+
// TODO(wjwwood): the order of subscriptions in the EventsExecutor does not
1017+
// follow call order or the insertion order, though it seems to be
1018+
// consistently the same order (from what I can tell in testing).
1019+
// I don't know why that is, but it means that this part of the test
1020+
// will not pass for the EventsExecutor, so skip it.
1021+
!std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>())
1022+
{
1023+
for (const auto & test_case_pair : test_cases) {
1024+
const std::string & test_case_name = test_case_pair.first;
1025+
const auto & test_case = test_case_pair.second;
1026+
1027+
ExecutorType executor;
1028+
executor.add_callback_group(isolated_callback_group, this->node->get_node_base_interface());
1029+
1030+
RCPPUTILS_SCOPE_EXIT({
1031+
for (auto & sub_pair : on_sub_data_callbacks) {
1032+
sub_pair.second = nullptr;
1033+
}
1034+
});
9831035

984-
EXPECT_EQ(actual_order, test_case.expected_execution_order)
985-
<< "callback call order in test case '" << test_case_name << "' different than expected, "
986-
<< "this may be a false positive, see test description";
1036+
std::vector<size_t> actual_order;
1037+
for (size_t i = 0; i < number_of_entities; ++i) {
1038+
auto sub = subscriptions[i];
1039+
on_sub_data_callbacks[sub.get()] = [&actual_order, i]() {
1040+
actual_order.push_back(i);
1041+
};
1042+
}
1043+
1044+
// create publisher and wait for all of the subscriptions to match
1045+
auto pub = this->node->template create_publisher<test_msgs::msg::Empty>(test_topic_name, 10);
1046+
size_t number_of_matches = pub->get_subscription_count();
1047+
while (number_of_matches < number_of_entities && rclcpp::ok()) {
1048+
executor.spin_once(10s); // large timeout because it should normally exit quickly
1049+
number_of_matches = pub->get_subscription_count();
1050+
}
1051+
1052+
// publish once and wait for all subscriptions to be handled
1053+
pub->publish(test_msgs::msg::Empty());
1054+
while (actual_order.size() < number_of_entities && rclcpp::ok()) {
1055+
executor.spin_once(10s); // large timeout because it should normally exit quickly
1056+
}
1057+
1058+
EXPECT_EQ(actual_order, test_case.expected_execution_order)
1059+
<< "callback call order in test case '" << test_case_name << "' different than expected, "
1060+
<< "this may be a false positive, see test description";
1061+
}
9871062
}
9881063
}
9891064

0 commit comments

Comments
 (0)