Skip to content

Commit 5090086

Browse files
committed
Improve test coverage for polymorphic port casting
1 parent 42b1d80 commit 5090086

File tree

1 file changed

+109
-19
lines changed

1 file changed

+109
-19
lines changed

tests/gtest_blackboard.cpp

Lines changed: 109 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,6 @@ TEST(BlackboardTest, SetBlackboard_WithPortRemapping)
856856
ASSERT_NO_THROW(tree.tickWhileRunning(););
857857
}
858858

859-
860859
class CreateHelloGreeter : public SyncActionNode
861860
{
862861
public:
@@ -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

Comments
 (0)