@@ -929,22 +929,69 @@ class ShowGreetMessage : public SyncActionNode
929929 }
930930};
931931
932- TEST (BlackboardTest, Upcasting_Issue943)
932+ class ShowFancyGreetMessage : public SyncActionNode
933933{
934- auto bb = BT::Blackboard::create ();
934+ public:
935+ ShowFancyGreetMessage (const std::string& name, const NodeConfig& config)
936+ : SyncActionNode(name, config)
937+ {}
935938
936- auto hello_greeter = std::make_shared<HelloGreeter>();
937- bb->set (" hello_greeter" , hello_greeter);
939+ NodeStatus tick () override
940+ {
941+ FancyHelloGreeter::Ptr greeter{};
938942
939- std::shared_ptr<Greeter> g{} ;
940- ASSERT_TRUE (bb-> get ( " hello_greeter " , g));
941- ASSERT_STREQ ( " hello " , g-> show_msg (). c_str ()) ;
943+ getInput ( " in_invalid_derived " , greeter) ;
944+ if (!greeter)
945+ return NodeStatus::FAILURE ;
942946
943- std::shared_ptr<HelloGreeter> hg{};
944- ASSERT_TRUE (bb->get (" hello_greeter" , hg));
945- ASSERT_STREQ (" hello" , hg->show_msg ().c_str ());
947+ greeter->show_msg ();
948+
949+ return NodeStatus::SUCCESS;
950+ }
946951
947- std::string xml_txt = R"(
952+ static PortsList providedPorts ()
953+ {
954+ return { BT::InputPort<FancyHelloGreeter::Ptr>(" in_invalid_derived" ) };
955+ }
956+ };
957+
958+
959+ TEST (BlackboardTest, Upcasting_Issue943)
960+ {
961+ {
962+ auto bb = BT::Blackboard::create ();
963+
964+ // set hello_greeter
965+ auto hello_greeter = std::make_shared<HelloGreeter>();
966+ bb->set (" hello_greeter" , hello_greeter);
967+
968+ // retrieve as base class -> OK
969+ std::shared_ptr<Greeter> g{};
970+ ASSERT_TRUE (bb->get (" hello_greeter" , g));
971+ ASSERT_STREQ (" hello" , g->show_msg ().c_str ());
972+
973+ // retrieve as derived class -> OK
974+ std::shared_ptr<HelloGreeter> hg{};
975+ ASSERT_TRUE (bb->get (" hello_greeter" , hg));
976+ ASSERT_STREQ (" hello" , hg->show_msg ().c_str ());
977+
978+ // retrieve as most-derived class -> should throw (type mismatch)
979+ std::shared_ptr<FancyHelloGreeter> fhg{};
980+ std::cout << " D" << std::endl;
981+ ASSERT_ANY_THROW (auto rc = bb->get (" hello_greeter" , fhg));
982+
983+ // overwrite hello_greeter bb key
984+ ASSERT_ANY_THROW (bb->set (" hello_greeter" , g));
985+ ASSERT_NO_THROW (bb->set (" hello_greeter" , hg));
986+ ASSERT_ANY_THROW (bb->set (" hello_greeter" , fhg));
987+ }
988+
989+ // This test verifies that polymorphic upcasting works correctly during tree creation.
990+ // The port "hello_greeter" is produced as HelloGreeter and later consumed as both
991+ // HelloGreeter and its base type Greeter. The tree should execute successfully,
992+ // confirming safe polymorphic compatibility through the base_chain mechanism.
993+ {
994+ std::string xml_txt = R"(
948995 <root BTCPP_format="4" >
949996 <BehaviorTree ID="Main">
950997 <Sequence>
@@ -955,14 +1002,59 @@ TEST(BlackboardTest, Upcasting_Issue943)
9551002 </BehaviorTree>
9561003 </root>)" ;
9571004
958- BehaviorTreeFactory factory;
959- factory.registerNodeType <CreateHelloGreeter>(" CreateHelloGreeter" );
960- factory.registerNodeType <SetDerivedParameter>(" SetDerivedParameter" );
961- factory.registerNodeType <ShowGreetMessage>(" ShowGreetMessage" );
1005+ BehaviorTreeFactory factory;
1006+ factory.registerNodeType <CreateHelloGreeter>(" CreateHelloGreeter" );
1007+ factory.registerNodeType <SetDerivedParameter>(" SetDerivedParameter" );
1008+ factory.registerNodeType <ShowGreetMessage>(" ShowGreetMessage" );
9621009
963- auto tree = factory.createTreeFromText (xml_txt);
1010+ auto tree = factory.createTreeFromText (xml_txt);
9641011
965- NodeStatus status = tree.tickWhileRunning ();
1012+ NodeStatus status = tree.tickWhileRunning ();
9661013
967- ASSERT_EQ (status, NodeStatus::SUCCESS);
1014+ ASSERT_EQ (status, NodeStatus::SUCCESS);
1015+ }
1016+
1017+ // This test ensures that an invalid polymorphic downcast is correctly detected
1018+ // during tree creation. The port "hello_greeter" is first created with HelloGreeter,
1019+ // then later expected as FancyHelloGreeter (a more derived type), which fails.
1020+ {
1021+ std::string xml_txt = R"(
1022+ <root BTCPP_format="4" >
1023+ <BehaviorTree ID="Main">
1024+ <Sequence>
1025+ <Script code="test := false"/>
1026+ <CreateHelloGreeter out_derived="{hello_greeter}" />
1027+ <SetDerivedParameter in_derived="{hello_greeter}" n="2" />
1028+ <ShowGreetMessage in_base="{hello_greeter}" />
1029+ <ShowFancyGreetMessage in_invalid_derived="{hello_greeter}" />
1030+ </Sequence>
1031+ </BehaviorTree>
1032+ </root>)" ;
1033+
1034+ BehaviorTreeFactory factory;
1035+ factory.registerNodeType <CreateHelloGreeter>(" CreateHelloGreeter" );
1036+ factory.registerNodeType <SetDerivedParameter>(" SetDerivedParameter" );
1037+ factory.registerNodeType <ShowGreetMessage>(" ShowGreetMessage" );
1038+ factory.registerNodeType <ShowFancyGreetMessage>(" ShowFancyGreetMessage" );
1039+
1040+ try
1041+ {
1042+ auto tree = factory.createTreeFromText (xml_txt);
1043+ FAIL () << " Expected BT::RuntimeError to be thrown" ;
1044+ }
1045+ catch (const BT::RuntimeError& e)
1046+ {
1047+ std::string expected_msg = " The creation of the tree failed because the port "
1048+ " [hello_greeter] was initially "
1049+ " created with type [std::shared_ptr<HelloGreeter>] and, "
1050+ " later type "
1051+ " [std::shared_ptr<FancyHelloGreeter>] was used "
1052+ " somewhere else." ;
1053+ ASSERT_EQ (e.what (), expected_msg);
1054+ }
1055+ catch (...)
1056+ {
1057+ FAIL () << " Expected BT::RuntimeError but caught a different exception" ;
1058+ }
1059+ }
9681060}
0 commit comments