Skip to content

Commit e14e74f

Browse files
committed
Improve test coverage for polymorphic port casting
1 parent 3a4d990 commit e14e74f

File tree

1 file changed

+110
-18
lines changed

1 file changed

+110
-18
lines changed

tests/gtest_blackboard.cpp

Lines changed: 110 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)