Skip to content

Commit 35d29c4

Browse files
authored
Fix Reactive Sequence async control node validation (#1183)
The XML check that rejects more than one asynchronous control node under a ReactiveSequence (they livelock: the reactive parent restarts the first one every tick and halts the second) recognized AsyncSequence but not AsyncFallback. Add it, and drop ThreadedAction / StatefulActionNode / CoroActionNode from the list: they are base classes, never registered as CONTROL nodes, so those names could not match. Async *actions* are still not detected by this check (#1184). Behavior change: a ReactiveSequence with two or more direct children among AsyncSequence / AsyncFallback, at least one of them an AsyncFallback, now fails to load, like two AsyncSequence already did.
1 parent af878ef commit 35d29c4

2 files changed

Lines changed: 81 additions & 3 deletions

File tree

‎src/xml_parsing.cpp‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -679,10 +679,11 @@ void VerifyXML(const std::string& xml_text,
679679
std::string("Unknown node type: ") + child_name);
680680
}
681681
const auto child_type = child_search->second;
682+
// Only the asynchronous CONTROL nodes can be detected here: the
683+
// manifest does not record whether an ACTION is asynchronous, so
684+
// async leaf actions are not caught.
682685
if(child_type == NodeType::CONTROL &&
683-
((child_name == "ThreadedAction") ||
684-
(child_name == "StatefulActionNode") ||
685-
(child_name == "CoroActionNode") || (child_name == "AsyncSequence")))
686+
((child_name == "AsyncSequence") || (child_name == "AsyncFallback")))
686687
{
687688
++async_count;
688689
if(async_count > 1)

‎tests/gtest_reactive.cpp‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,83 @@ TEST(Reactive, TwoAsyncNodesInReactiveSequence)
187187
EXPECT_ANY_THROW(auto tree = factory.createTreeFromText(reactive_xml_text));
188188
}
189189

190+
TEST(Reactive, TwoAsyncFallbacksInReactiveSequence)
191+
{
192+
static const char* reactive_xml_text = R"(
193+
<root BTCPP_format="4" >
194+
<BehaviorTree ID="MainTree">
195+
<ReactiveSequence>
196+
<AsyncFallback name="first">
197+
<TestA/>
198+
<TestB/>
199+
<TestC/>
200+
</AsyncFallback>
201+
<AsyncFallback name="second">
202+
<TestD/>
203+
<TestE/>
204+
<TestF/>
205+
</AsyncFallback>
206+
</ReactiveSequence>
207+
</BehaviorTree>
208+
</root>
209+
)";
210+
211+
BT::BehaviorTreeFactory factory;
212+
std::array<int, 6> counters{};
213+
RegisterTestTick(factory, "Test", counters);
214+
215+
EXPECT_ANY_THROW(auto tree = factory.createTreeFromText(reactive_xml_text));
216+
}
217+
218+
TEST(Reactive, AsyncSequenceAndAsyncFallbackInReactiveSequence)
219+
{
220+
static const char* reactive_xml_text = R"(
221+
<root BTCPP_format="4" >
222+
<BehaviorTree ID="MainTree">
223+
<ReactiveSequence>
224+
<AsyncSequence name="first">
225+
<TestA/>
226+
<TestB/>
227+
</AsyncSequence>
228+
<AsyncFallback name="second">
229+
<TestC/>
230+
<TestD/>
231+
</AsyncFallback>
232+
</ReactiveSequence>
233+
</BehaviorTree>
234+
</root>
235+
)";
236+
237+
BT::BehaviorTreeFactory factory;
238+
std::array<int, 6> counters{};
239+
RegisterTestTick(factory, "Test", counters);
240+
241+
EXPECT_ANY_THROW(auto tree = factory.createTreeFromText(reactive_xml_text));
242+
}
243+
244+
TEST(Reactive, SingleAsyncChildInReactiveSequenceIsAllowed)
245+
{
246+
static const char* reactive_xml_text = R"(
247+
<root BTCPP_format="4" >
248+
<BehaviorTree ID="MainTree">
249+
<ReactiveSequence>
250+
<TestA/>
251+
<AsyncFallback name="only_async">
252+
<TestB/>
253+
<TestC/>
254+
</AsyncFallback>
255+
</ReactiveSequence>
256+
</BehaviorTree>
257+
</root>
258+
)";
259+
260+
BT::BehaviorTreeFactory factory;
261+
std::array<int, 6> counters{};
262+
RegisterTestTick(factory, "Test", counters);
263+
264+
EXPECT_NO_THROW(auto tree = factory.createTreeFromText(reactive_xml_text));
265+
}
266+
190267
// ============ Phase 4: Additional Reactive Tests ============
191268

192269
TEST(Reactive, ReactiveSequence_FirstChildFails)

0 commit comments

Comments
 (0)