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