gtest_factory.cpp
Go to the documentation of this file.
1 #include <gtest/gtest.h>
2 #include <filesystem>
3 #include <string>
4 #include <utility>
5 #include <vector>
7 #include "../sample_nodes/crossdoor_nodes.h"
8 #include "../sample_nodes/dummy_nodes.h"
9 
10 using namespace BT;
11 
12 // clang-format off
13 
14 static const char* xml_text = R"(
15 
16 <root BTCPP_format="4" >
17 
18  <BehaviorTree ID="MainTree">
19  <Fallback name="root_selector">
20 
21  <Sequence name="door_open_sequence">
22  <Action ID="IsDoorOpen" />
23  <Action ID="PassThroughDoor" />
24  </Sequence>
25 
26  <Sequence name="door_closed_sequence">
27  <Decorator ID="Inverter">
28  <Action ID="IsDoorOpen" />
29  </Decorator>
30  <Action ID="OpenDoor" />
31  <Action ID="PassThroughDoor" />
32  <Action ID="CloseDoor" />
33  </Sequence>
34 
35  <Action ID="PassThroughWindow" />
36 
37  </Fallback>
38  </BehaviorTree>
39 
40  <!-- TreeNodesModel is used only by the Graphic interface -->
41  <TreeNodesModel>
42  <Action ID="IsDoorOpen" />
43  <Action ID="PassThroughDoor" />
44  <Action ID="CloseDoor" />
45  <Action ID="OpenDoor" />
46  <Action ID="PassThroughWindow" />
47  </TreeNodesModel>
48 </root>
49  )";
50 
51 static const char* xml_text_subtree = R"(
52 
53 <root BTCPP_format="4" main_tree_to_execute="MainTree" >
54 
55  <BehaviorTree ID="MainTree">
56  <Sequence>
57  <Fallback>
58  <Inverter>
59  <IsDoorClosed/>
60  </Inverter>
61  <SubTree ID="DoorClosedSubtree"/>
62  </Fallback>
63  <PassThroughDoor/>
64  </Sequence>
65  </BehaviorTree>
66 
67  <BehaviorTree ID="DoorClosedSubtree">
68  <Fallback>
69  <OpenDoor/>
70  <RetryUntilSuccessful num_attempts="5">
71  <PickLock/>
72  </RetryUntilSuccessful>
73  <SmashDoor/>
74  </Fallback>
75  </BehaviorTree>
76 
77 </root> )";
78 
79 static const char* xml_text_subtree_part1 = R"(
80 
81 <root BTCPP_format="4">
82  <BehaviorTree ID="MainTree">
83  <Fallback name="root_selector">
84  <SubTree ID="DoorClosedSubtree" />
85  <Action ID="PassThroughWindow" />
86  </Fallback>
87  </BehaviorTree>
88 </root> )";
89 
90 static const char* xml_text_subtree_part2 = R"(
91 
92 <root BTCPP_format="4">
93  <BehaviorTree ID="DoorClosedSubtree">
94  <Sequence name="door_sequence">
95  <Decorator ID="Inverter">
96  <Action ID="IsDoorLocked" />
97  </Decorator>
98  <Action ID="OpenDoor" />
99  <Action ID="PassThroughDoor" />
100  <Action ID="CloseDoor" />
101  </Sequence>
102  </BehaviorTree>
103 </root> )";
104 
105 // clang-format on
106 
107 TEST(BehaviorTreeFactory, XMLParsingOrder)
108 {
109  BehaviorTreeFactory factory;
110  CrossDoor cross_door;
111  cross_door.registerNodes(factory);
112 
113  {
114  XMLParser parser(factory);
116  auto trees = parser.registeredBehaviorTrees();
117  ASSERT_EQ(trees[0], "DoorClosedSubtree");
118  ASSERT_EQ(trees[1], "MainTree");
119  }
120  {
121  XMLParser parser(factory);
124  auto trees = parser.registeredBehaviorTrees();
125  ASSERT_EQ(trees[0], "DoorClosedSubtree");
126  ASSERT_EQ(trees[1], "MainTree");
127  }
128  {
129  XMLParser parser(factory);
132  auto trees = parser.registeredBehaviorTrees();
133  ASSERT_EQ(trees[0], "DoorClosedSubtree");
134  ASSERT_EQ(trees[1], "MainTree");
135  }
136 }
137 
139 {
140  BehaviorTreeFactory factory;
141  CrossDoor cross_door;
142  cross_door.registerNodes(factory);
143 
144  Tree tree = factory.createTreeFromText(xml_text_subtree);
145 
147 
148  ASSERT_EQ(tree.subtrees.size(), 2);
149 
150  auto const& main_tree = tree.subtrees[0];
151  auto const& subtree = tree.subtrees[1];
152 
153  ASSERT_EQ(main_tree->nodes.size(), 6);
154  ASSERT_EQ(main_tree->nodes[0]->name(), "Sequence");
155  ASSERT_EQ(main_tree->nodes[1]->name(), "Fallback");
156  ASSERT_EQ(main_tree->nodes[2]->name(), "Inverter");
157  ASSERT_EQ(main_tree->nodes[3]->name(), "IsDoorClosed");
158  ASSERT_EQ(main_tree->nodes[4]->type(), NodeType::SUBTREE);
159  ASSERT_EQ(main_tree->nodes[5]->name(), "PassThroughDoor");
160 
161  ASSERT_EQ(subtree->nodes.size(), 5);
162  ASSERT_EQ(subtree->nodes[0]->name(), "Fallback");
163  ASSERT_EQ(subtree->nodes[1]->name(), "OpenDoor");
164  ASSERT_EQ(subtree->nodes[2]->name(), "RetryUntilSuccessful");
165  ASSERT_EQ(subtree->nodes[3]->name(), "PickLock");
166  ASSERT_EQ(subtree->nodes[4]->name(), "SmashDoor");
167 }
168 
170 {
171  const std::string xml_text_issue = R"(
172 <root BTCPP_format="4">
173  <BehaviorTree ID="ReceiveGuest">
174  </BehaviorTree>
175 </root> )";
176 
177  BehaviorTreeFactory factory;
178  XMLParser parser(factory);
179 
180  EXPECT_THROW(parser.loadFromText(xml_text_issue), RuntimeError);
181 }
182 
183 // clang-format off
184 
185 static const char* xml_ports_subtree = R"(
186 
187 <root BTCPP_format="4" main_tree_to_execute="MainTree">
188 
189  <BehaviorTree ID="TalkToMe">
190  <Sequence>
191  <SaySomething message="{hello_msg}" />
192  <SaySomething message="{bye_msg}" />
193  <Script code=" output:='done!' " />
194  </Sequence>
195  </BehaviorTree>
196 
197  <BehaviorTree ID="MainTree">
198  <Sequence>
199  <Script code = " talk_hello:='hello' " />
200  <Script code = " talk_bye:='bye bye' " />
201  <SubTree ID="TalkToMe" hello_msg="{talk_hello}"
202  bye_msg="{talk_bye}"
203  output="{talk_out}" />
204  <SaySomething message="{talk_out}" />
205  </Sequence>
206  </BehaviorTree>
207 
208 </root> )";
209 
210 // clang-format on
211 
212 TEST(BehaviorTreeFactory, SubTreeWithRemapping)
213 {
214  BehaviorTreeFactory factory;
215  factory.registerNodeType<DummyNodes::SaySomething>("SaySomething");
216 
217  Tree tree = factory.createTreeFromText(xml_ports_subtree);
218 
219  auto main_bb = tree.subtrees.at(0)->blackboard;
220  auto talk_bb = tree.subtrees.at(1)->blackboard;
221 
222  std::cout << "\n --------------------------------- \n" << std::endl;
223  main_bb->debugMessage();
224  std::cout << "\n ----- \n" << std::endl;
225  talk_bb->debugMessage();
226  std::cout << "\n --------------------------------- \n" << std::endl;
227 
228  // Should not throw
229  tree.tickWhileRunning();
230 
231  ASSERT_EQ(main_bb->entryInfo("talk_hello")->type(), typeid(std::string));
232  ASSERT_EQ(main_bb->entryInfo("talk_bye")->type(), typeid(std::string));
233  ASSERT_EQ(main_bb->entryInfo("talk_out")->type(), typeid(std::string));
234 
235  ASSERT_EQ(talk_bb->entryInfo("bye_msg")->type(), typeid(std::string));
236  ASSERT_EQ(talk_bb->entryInfo("hello_msg")->type(), typeid(std::string));
237 
238  std::cout << "\n --------------------------------- \n" << std::endl;
239  main_bb->debugMessage();
240  std::cout << "\n ----- \n" << std::endl;
241  talk_bb->debugMessage();
242  std::cout << "\n --------------------------------- \n" << std::endl;
243 
244  ASSERT_EQ(main_bb->entryInfo("talk_hello")->type(), typeid(std::string));
245  ASSERT_EQ(main_bb->entryInfo("talk_bye")->type(), typeid(std::string));
246  ASSERT_EQ(main_bb->entryInfo("talk_out")->type(), typeid(std::string));
247 
248  ASSERT_EQ(talk_bb->entryInfo("bye_msg")->type(), typeid(std::string));
249  ASSERT_EQ(talk_bb->entryInfo("hello_msg")->type(), typeid(std::string));
250  ASSERT_EQ(talk_bb->entryInfo("output")->type(), typeid(std::string));
251 
252  ASSERT_EQ(main_bb->get<std::string>("talk_hello"), "hello");
253  ASSERT_EQ(main_bb->get<std::string>("talk_bye"), "bye bye");
254  ASSERT_EQ(main_bb->get<std::string>("talk_out"), "done!");
255 
256  // these ports should not be present in the subtree TalkToMe
257  ASSERT_FALSE(talk_bb->getAnyLocked("talk_hello"));
258  ASSERT_FALSE(talk_bb->getAnyLocked("talk_bye"));
259  ASSERT_FALSE(talk_bb->getAnyLocked("talk_out"));
260 }
261 
262 std::string FilePath(const std::filesystem::path& relative_path)
263 {
264  // clang-format off
265  static const std::filesystem::path search_paths[] = {
266  BT_TEST_FOLDER,
267  std::filesystem::current_path() / "tests"};
268  // clang-format on
269 
270  for(auto const& path : search_paths)
271  {
272  if(std::filesystem::exists(path / relative_path))
273  {
274  return (path / relative_path).string();
275  }
276  }
277  return {};
278 }
279 
280 TEST(BehaviorTreeFactory, CreateTreeFromFile)
281 {
282  BehaviorTreeFactory factory;
283 
284  // should not throw
285  std::string path = FilePath("trees/parent_no_include.xml");
286  Tree tree = factory.createTreeFromFile(path);
287  ASSERT_EQ(NodeStatus::SUCCESS, tree.tickWhileRunning());
288 }
289 
290 TEST(BehaviorTreeFactory, CreateTreeFromFileWhichIncludesFileFromSameDirectory)
291 {
292  BehaviorTreeFactory factory;
293 
294  // should not throw
295  std::string path = FilePath("trees/child/child_include_sibling.xml");
296  Tree tree = factory.createTreeFromFile(path);
297  ASSERT_EQ(NodeStatus::SUCCESS, tree.tickWhileRunning());
298 }
299 
300 TEST(BehaviorTreeFactory, CreateTreeFromFileWhichIncludesFileFromChildDirectory)
301 {
302  BehaviorTreeFactory factory;
303 
304  // should not throw
305  std::string path = FilePath("trees/parent_include_child.xml");
306  Tree tree = factory.createTreeFromFile(path);
307  ASSERT_EQ(NodeStatus::SUCCESS, tree.tickWhileRunning());
308 }
309 
312  CreateTreeFromFileWhichIncludesFileFromChildDirectoryWhichIncludesFileFromSameDirectory)
313 {
314  BehaviorTreeFactory factory;
315 
316  // should not throw
317  std::string path = FilePath("trees/parent_include_child_include_sibling.xml");
318  Tree tree = factory.createTreeFromFile(path);
319  ASSERT_EQ(NodeStatus::SUCCESS, tree.tickWhileRunning());
320 }
321 
324  CreateTreeFromFileWhichIncludesFileFromChildDirectoryWhichIncludesFileFromChildDirectory)
325 {
326  BehaviorTreeFactory factory;
327 
328  // should not throw
329  std::string path = FilePath("trees/parent_include_child_include_child.xml");
330  Tree tree = factory.createTreeFromFile(path);
331  ASSERT_EQ(NodeStatus::SUCCESS, tree.tickWhileRunning());
332 }
333 
336  CreateTreeFromFileWhichIncludesFileFromChildDirectoryWhichIncludesFileFromParentDirectory)
337 {
338  BehaviorTreeFactory factory;
339 
340  // should not throw
341  std::string path = FilePath("trees/parent_include_child_include_parent.xml");
342  Tree tree = factory.createTreeFromFile(path);
343  ASSERT_EQ(NodeStatus::SUCCESS, tree.tickWhileRunning());
344 }
345 
346 TEST(BehaviorTreeFactory, WrongTreeName)
347 {
348  const char* xmlA = R"(
349  <root BTCPP_format="4" >
350  <BehaviorTree ID="MainTree">
351  <AlwaysSuccess/>
352  </BehaviorTree>
353  </root> )";
354 
355  BehaviorTreeFactory factory;
356 
357  factory.registerBehaviorTreeFromText(xmlA);
358  EXPECT_ANY_THROW(auto tree = factory.createTree("Wrong Name"));
359 }
360 
361 TEST(BehaviorTreeReload, ReloadSameTree)
362 {
363  const char* xmlA = R"(
364 <root BTCPP_format="4" >
365  <BehaviorTree ID="MainTree">
366  <AlwaysSuccess/>
367  </BehaviorTree>
368 </root> )";
369 
370  const char* xmlB = R"(
371 <root BTCPP_format="4" >
372  <BehaviorTree ID="MainTree">
373  <AlwaysFailure/>
374  </BehaviorTree>
375 </root> )";
376 
377  BehaviorTreeFactory factory;
378 
379  factory.registerBehaviorTreeFromText(xmlA);
380  {
381  auto tree = factory.createTree("MainTree");
382  ASSERT_EQ(NodeStatus::SUCCESS, tree.tickWhileRunning());
383  }
384 
385  factory.registerBehaviorTreeFromText(xmlB);
386  {
387  auto tree = factory.createTree("MainTree");
388  ASSERT_EQ(NodeStatus::FAILURE, tree.tickWhileRunning());
389  }
390 }
391 
393 {
394  return {
395  std::make_pair<std::string, std::string>("foo", "hello"),
396  std::make_pair<std::string, std::string>("bar", "42"),
397  };
398 }
399 
401 {
402 public:
403  ActionWithMetadata(const std::string& name, const NodeConfig& config)
404  : SyncActionNode(name, config)
405  {}
406 
407  BT::NodeStatus tick() override
408  {
409  return NodeStatus::SUCCESS;
410  }
411 
413  {
414  return {};
415  }
416 
418  {
419  return makeTestMetadata();
420  }
421 };
422 
423 TEST(BehaviorTreeFactory, ManifestMethod)
424 {
425  const char* expectedXML = R"(
426  <Action ID="ActionWithMetadata">
427  <MetadataFields>
428  <Metadata foo="hello"/>
429  <Metadata bar="42"/>
430  </MetadataFields>
431  </Action>)";
432 
433  BehaviorTreeFactory factory;
434  factory.registerNodeType<ActionWithMetadata>("ActionWithMetadata");
435  const auto& manifest = factory.manifests().at("ActionWithMetadata");
436  EXPECT_EQ(manifest.metadata, makeTestMetadata());
437 
438  auto xml = writeTreeNodesModelXML(factory, false);
439  std::cout << xml << std::endl;
440 
441  EXPECT_NE(xml.find(expectedXML), std::string::npos);
442 }
443 
444 TEST(BehaviorTreeFactory, addMetadataToManifest)
445 {
446  BehaviorTreeFactory factory;
447  factory.registerNodeType<DummyNodes::SaySomething>("SaySomething");
448  const auto& initial_manifest = factory.manifests().at("SaySomething");
449  EXPECT_TRUE(initial_manifest.metadata.empty());
450  factory.addMetadataToManifest("SaySomething", makeTestMetadata());
451  const auto& modified_manifest = factory.manifests().at("SaySomething");
452  EXPECT_EQ(modified_manifest.metadata, makeTestMetadata());
453 }
BT
Definition: ex01_wrap_legacy.cpp:29
xml_text_subtree
static const char * xml_text_subtree
Definition: gtest_factory.cpp:51
BT::XMLParser::loadFromText
void loadFromText(const std::string &xml_text, bool add_includes=true) override
Definition: xml_parsing.cpp:164
BT::BehaviorTreeFactory::createTree
Tree createTree(const std::string &tree_name, Blackboard::Ptr blackboard=Blackboard::create())
Definition: bt_factory.cpp:432
TEST
TEST(BehaviorTreeFactory, XMLParsingOrder)
Definition: gtest_factory.cpp:107
makeTestMetadata
KeyValueVector makeTestMetadata()
Definition: gtest_factory.cpp:392
BT::XMLParser::registeredBehaviorTrees
std::vector< std::string > registeredBehaviorTrees() const override
Definition: xml_parsing.cpp:174
BT::Tree
Struct used to store a tree. If this object goes out of scope, the tree is destroyed.
Definition: bt_factory.h:91
BT::NodeType::SUBTREE
@ SUBTREE
BT::BehaviorTreeFactory::addMetadataToManifest
void addMetadataToManifest(const std::string &node_id, const KeyValueVector &metadata)
Definition: bt_factory.cpp:440
xml_text_subtree_part2
static const char * xml_text_subtree_part2
Definition: gtest_factory.cpp:90
manifest
string manifest
xml_text_subtree_part1
static const char * xml_text_subtree_part1
Definition: gtest_factory.cpp:79
BT::BehaviorTreeFactory::manifests
const std::unordered_map< std::string, TreeNodeManifest > & manifests() const
Manifests of all the registered TreeNodes.
Definition: bt_factory.cpp:385
BT::Tree::tickWhileRunning
NodeStatus tickWhileRunning(std::chrono::milliseconds sleep_time=std::chrono::milliseconds(10))
Definition: bt_factory.cpp:609
BT::PortsList
std::unordered_map< std::string, PortInfo > PortsList
Definition: basic_types.h:585
BT::NodeStatus::FAILURE
@ FAILURE
BT::Tree::rootNode
TreeNode * rootNode() const
Definition: bt_factory.cpp:579
CrossDoor::registerNodes
void registerNodes(BT::BehaviorTreeFactory &factory)
Definition: crossdoor_nodes.cpp:56
BT::BehaviorTreeFactory::registerNodeType
void registerNodeType(const std::string &ID, const PortsList &ports, ExtraArgs... args)
Definition: bt_factory.h:322
BT::BehaviorTreeFactory::createTreeFromText
Tree createTreeFromText(const std::string &text, Blackboard::Ptr blackboard=Blackboard::create())
createTreeFromText will parse the XML directly from string. The XML needs to contain either a single ...
Definition: bt_factory.cpp:395
xml_ports_subtree
static const char * xml_ports_subtree
Definition: gtest_factory.cpp:185
ActionWithMetadata::ActionWithMetadata
ActionWithMetadata(const std::string &name, const NodeConfig &config)
Definition: gtest_factory.cpp:403
BT::RuntimeError
Definition: exceptions.h:58
xml_parsing.h
DummyNodes::SaySomething
Definition: dummy_nodes.h:47
BT::BehaviorTreeFactory
The BehaviorTreeFactory is used to create instances of a TreeNode at run-time.
Definition: bt_factory.h:205
BT::KeyValueVector
std::vector< std::pair< std::string, std::string > > KeyValueVector
Definition: basic_types.h:66
BT::BehaviorTreeFactory::createTreeFromFile
Tree createTreeFromFile(const std::filesystem::path &file_path, Blackboard::Ptr blackboard=Blackboard::create())
createTreeFromFile will parse the XML from a given file. The XML needs to contain either a single <Be...
Definition: bt_factory.cpp:413
BT::NodeStatus::SUCCESS
@ SUCCESS
FilePath
std::string FilePath(const std::filesystem::path &relative_path)
Definition: gtest_factory.cpp:262
BT::Tree::subtrees
std::vector< Subtree::Ptr > subtrees
Definition: bt_factory.h:104
BT::printTreeRecursively
void printTreeRecursively(const TreeNode *root_node, std::ostream &stream=std::cout)
Definition: behavior_tree.cpp:66
ActionWithMetadata::tick
BT::NodeStatus tick() override
Method to be implemented by the user.
Definition: gtest_factory.cpp:407
xml_text
static const char * xml_text
Definition: gtest_factory.cpp:14
CrossDoor
Definition: crossdoor_nodes.h:5
BT::BehaviorTreeFactory::registerBehaviorTreeFromText
void registerBehaviorTreeFromText(const std::string &xml_text)
Definition: bt_factory.cpp:277
ActionWithMetadata::providedPorts
static PortsList providedPorts()
Definition: gtest_factory.cpp:412
ActionWithMetadata::metadata
static KeyValueVector metadata()
Definition: gtest_factory.cpp:417
BT::XMLParser
The XMLParser is a class used to read the model of a BehaviorTree from file or text and instantiate t...
Definition: xml_parsing.h:16
BT::NodeConfig
Definition: tree_node.h:73
ActionWithMetadata
Definition: gtest_factory.cpp:400
BT::SyncActionNode
The SyncActionNode is an ActionNode that explicitly prevents the status RUNNING and doesn't require a...
Definition: action_node.h:52
BT::NodeStatus
NodeStatus
Definition: basic_types.h:33
BT::writeTreeNodesModelXML
std::string writeTreeNodesModelXML(const BehaviorTreeFactory &factory, bool include_builtin=false)
writeTreeNodesModelXML generates an XMl that contains the manifests in the <TreeNodesModel>
Definition: xml_parsing.cpp:1178


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Jun 28 2024 02:20:07