diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp
index e37836d0f..a658cb405 100644
--- a/src/xml_parsing.cpp
+++ b/src/xml_parsing.cpp
@@ -1081,10 +1081,20 @@ void BT::XMLParser::PImpl::recursivelyCreateSubtree(
// common case: iterate through all children
if(node->type() != NodeType::SUBTREE)
{
+ // Include the current node in the prefix passed to children so that
+ // TreeNode::fullPath() identifies the node's complete hierarchy.
+ // Previously every sibling was created with the same prefix, causing
+ // fullPath() to contain only the node name (issue #1114).
+ std::string child_prefix = prefix;
+ if(!node->name().empty())
+ {
+ child_prefix += node->name();
+ child_prefix += "/";
+ }
for(auto child_element = element->FirstChildElement(); child_element != nullptr;
child_element = child_element->NextSiblingElement())
{
- recursiveStep(node, subtree, prefix, child_element, depth + 1);
+ recursiveStep(node, subtree, child_prefix, child_element, depth + 1);
}
}
else // special case: SubTreeNode
diff --git a/tests/gtest_subtree.cpp b/tests/gtest_subtree.cpp
index cda5d68a8..bf08f033f 100644
--- a/tests/gtest_subtree.cpp
+++ b/tests/gtest_subtree.cpp
@@ -914,6 +914,34 @@ TEST(SubTree, UniqueSubTreeNames_WorksCorrectly)
ASSERT_EQ(status, NodeStatus::SUCCESS);
}
+// Regression test for issue #1114: fullPath() must include the hierarchy of
+// control/decorator nodes, not just the leaf node's name.
+TEST(SubTree, FullPathIncludesNodeHierarchy_Issue1114)
+{
+ static const char* xml_text = R"(
+
+
+
+
+
+
+
+
+
+ )";
+
+ BehaviorTreeFactory factory;
+ Tree tree = factory.createTreeFromText(xml_text);
+
+ std::vector paths;
+ tree.applyVisitor([&](TreeNode* node) { paths.push_back(node->fullPath()); });
+
+ ASSERT_EQ(paths.size(), 3);
+ EXPECT_EQ(paths[0], "outer_sequence");
+ EXPECT_EQ(paths[1], "outer_sequence/fallback");
+ EXPECT_EQ(paths[2], "outer_sequence/fallback/leaf_action");
+}
+
// Test that omitting name attribute auto-generates unique paths
TEST(SubTree, NoNameAttribute_AutoGeneratesUniquePaths)
{