diff --git a/src/bt_factory.cpp b/src/bt_factory.cpp index a3ef80def..9040e80f1 100644 --- a/src/bt_factory.cpp +++ b/src/bt_factory.cpp @@ -773,7 +773,12 @@ NodeStatus Tree::tickRoot(TickOption opt, std::chrono::milliseconds sleep_time) void BlackboardRestore(const std::vector& backup, Tree& tree) { - assert(backup.size() == tree.subtrees.size()); + if(backup.size() != tree.subtrees.size()) + { + throw RuntimeError("BlackboardRestore: the backup contains ", + std::to_string(backup.size()), " blackboards, but the tree has ", + std::to_string(tree.subtrees.size()), " subtrees"); + } for(size_t i = 0; i < tree.subtrees.size(); i++) { backup[i]->cloneInto(*(tree.subtrees[i]->blackboard)); diff --git a/tests/gtest_blackboard.cpp b/tests/gtest_blackboard.cpp index f4ce45689..857388e45 100644 --- a/tests/gtest_blackboard.cpp +++ b/tests/gtest_blackboard.cpp @@ -542,6 +542,47 @@ TEST(BlackboardTest, BlackboardBackup) ASSERT_EQ(status, BT::NodeStatus::SUCCESS); } +TEST(BlackboardTest, BlackboardRestoreSizeMismatch) +{ + BT::BehaviorTreeFactory factory; + + const std::string one_subtree = R"( + + + + + )"; + + const std::string four_subtrees = R"( + + + + + + + + + + + + )"; + + auto small_tree = factory.createTreeFromText(one_subtree); + const auto bb_backup = BlackboardBackup(small_tree); + ASSERT_EQ(bb_backup.size(), 1u); + + auto big_tree = factory.createTreeFromText(four_subtrees); + ASSERT_EQ(big_tree.subtrees.size(), 4u); + + // The backup is shorter than the number of subtrees: reject it instead of + // indexing past the end of the vector. + ASSERT_THROW(BlackboardRestore(bb_backup, big_tree), BT::RuntimeError); + + // A backup taken from the same tree must still be accepted. + const auto big_backup = BlackboardBackup(big_tree); + ASSERT_NO_THROW(BlackboardRestore(big_backup, big_tree)); +} + TEST(BlackboardTest, RootBlackboard) { BT::BehaviorTreeFactory factory;