From 811d23dbca415bc22d47937e96931f9b99924aa7 Mon Sep 17 00:00:00 2001 From: nan Date: Fri, 4 Sep 2026 17:13:39 +0800 Subject: [PATCH] Fix TreeNode fullPath hierarchy for nested nodes --- src/xml_parsing.cpp | 12 +++++++++++- tests/gtest_subtree.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) 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) {