test: cover testBehavior
This commit is contained in:
1 parent
a53c700c17
commit
8103be3f96
1 file changed
+157
@@ -156,3 +156,160 @@ VF_TEST(aRunningNodeResumesNextTickInsteadOfRestartingTheSequence) {
|
|||||||
int slowRuns = 0;
|
int slowRuns = 0;
|
||||||
int slowProgress = 0;
|
int slowProgress = 0;
|
||||||
|
|
||||||
|
auto root = std::unique_ptr<SequenceNode>(new SequenceNode("sequence"));
|
||||||
|
root->add(constantAction("first", BehaviorStatus::Success, &firstRuns));
|
||||||
|
root->add(slowAction("slow", 2, &slowRuns, &slowProgress));
|
||||||
|
|
||||||
|
BehaviorTree tree(std::move(root));
|
||||||
|
BehaviorContext ctx;
|
||||||
|
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Running);
|
||||||
|
CHECK_EQ(firstRuns, 1);
|
||||||
|
CHECK_EQ(slowRuns, 1);
|
||||||
|
|
||||||
|
// The whole point: the first child must not run again while the second is
|
||||||
|
// still Running.
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Running);
|
||||||
|
CHECK_EQ(firstRuns, 1);
|
||||||
|
CHECK_EQ(slowRuns, 2);
|
||||||
|
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Success);
|
||||||
|
CHECK_EQ(firstRuns, 1);
|
||||||
|
CHECK_EQ(slowRuns, 3);
|
||||||
|
|
||||||
|
// Once settled the sequence starts over from the top.
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Running);
|
||||||
|
CHECK_EQ(firstRuns, 2);
|
||||||
|
CHECK_EQ(slowRuns, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(selectorResumesTheRunningBranchWithoutRecheckingEarlierOnes) {
|
||||||
|
int guardChecks = 0;
|
||||||
|
int slowRuns = 0;
|
||||||
|
int slowProgress = 0;
|
||||||
|
|
||||||
|
auto root = std::unique_ptr<SelectorNode>(new SelectorNode("selector"));
|
||||||
|
root->add(std::unique_ptr<BehaviorNode>(
|
||||||
|
new ConditionNode("guard", [&guardChecks](BehaviorContext&) {
|
||||||
|
++guardChecks;
|
||||||
|
return false;
|
||||||
|
})));
|
||||||
|
root->add(slowAction("slow", 3, &slowRuns, &slowProgress));
|
||||||
|
|
||||||
|
BehaviorTree tree(std::move(root));
|
||||||
|
BehaviorContext ctx;
|
||||||
|
|
||||||
|
tree.tick(ctx);
|
||||||
|
CHECK_EQ(guardChecks, 1);
|
||||||
|
tree.tick(ctx);
|
||||||
|
tree.tick(ctx);
|
||||||
|
CHECK_EQ(guardChecks, 1);
|
||||||
|
CHECK_EQ(slowRuns, 3);
|
||||||
|
CHECK_EQ(tree.root()->childAt(1)->name(), std::string("slow"));
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(cooldownBlocksForExactlyTheRequestedNumberOfTicks) {
|
||||||
|
int runs = 0;
|
||||||
|
auto gated = std::unique_ptr<BehaviorNode>(
|
||||||
|
new CooldownNode("cooldown", constantAction("fire", BehaviorStatus::Success, &runs), 3));
|
||||||
|
BehaviorTree tree(std::move(gated));
|
||||||
|
BehaviorContext ctx;
|
||||||
|
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Success);
|
||||||
|
CHECK_EQ(runs, 1);
|
||||||
|
|
||||||
|
for (int blocked = 0; blocked < 3; ++blocked) {
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Failure);
|
||||||
|
CHECK_EQ(runs, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Success);
|
||||||
|
CHECK_EQ(runs, 2);
|
||||||
|
|
||||||
|
// And the block starts again immediately after the second firing.
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Failure);
|
||||||
|
CHECK_EQ(runs, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(resettingTheTreeClearsALiveCooldown) {
|
||||||
|
int runs = 0;
|
||||||
|
auto gated = std::unique_ptr<BehaviorNode>(
|
||||||
|
new CooldownNode("cooldown", constantAction("fire", BehaviorStatus::Success, &runs), 5));
|
||||||
|
BehaviorTree tree(std::move(gated));
|
||||||
|
BehaviorContext ctx;
|
||||||
|
|
||||||
|
tree.tick(ctx);
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Failure);
|
||||||
|
tree.reset();
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Success);
|
||||||
|
CHECK_EQ(runs, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(inverterFlipsSuccessAndFailureButLetsRunningThrough) {
|
||||||
|
int progress = 0;
|
||||||
|
int runs = 0;
|
||||||
|
|
||||||
|
BehaviorContext ctx;
|
||||||
|
BehaviorTree yes(std::unique_ptr<BehaviorNode>(new InverterNode(
|
||||||
|
"invert", constantAction("succeeds", BehaviorStatus::Success, nullptr))));
|
||||||
|
CHECK(yes.tick(ctx) == BehaviorStatus::Failure);
|
||||||
|
|
||||||
|
BehaviorTree no(std::unique_ptr<BehaviorNode>(
|
||||||
|
new InverterNode("invert", constantAction("fails", BehaviorStatus::Failure, nullptr))));
|
||||||
|
CHECK(no.tick(ctx) == BehaviorStatus::Success);
|
||||||
|
|
||||||
|
BehaviorTree waiting(std::unique_ptr<BehaviorNode>(
|
||||||
|
new InverterNode("invert", slowAction("slow", 2, &runs, &progress))));
|
||||||
|
CHECK(waiting.tick(ctx) == BehaviorStatus::Running);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(repeaterSucceedsOnlyAfterTheRequestedNumberOfCompletions) {
|
||||||
|
int runs = 0;
|
||||||
|
auto repeat = std::unique_ptr<BehaviorNode>(new RepeaterNode(
|
||||||
|
"repeat", constantAction("beat", BehaviorStatus::Success, &runs), 3));
|
||||||
|
BehaviorTree tree(std::move(repeat));
|
||||||
|
BehaviorContext ctx;
|
||||||
|
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Running);
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Running);
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Success);
|
||||||
|
CHECK_EQ(runs, 3);
|
||||||
|
|
||||||
|
int failingRuns = 0;
|
||||||
|
auto stopping = std::unique_ptr<BehaviorNode>(new RepeaterNode(
|
||||||
|
"repeat", constantAction("beat", BehaviorStatus::Failure, &failingRuns), 3, true));
|
||||||
|
BehaviorTree strict(std::move(stopping));
|
||||||
|
CHECK(strict.tick(ctx) == BehaviorStatus::Failure);
|
||||||
|
CHECK_EQ(failingRuns, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(retryUntilSuccessGivesUpAfterTheAttemptBudget) {
|
||||||
|
int runs = 0;
|
||||||
|
auto retry = std::unique_ptr<BehaviorNode>(new RetryUntilSuccessNode(
|
||||||
|
"retry", constantAction("attempt", BehaviorStatus::Failure, &runs), 3));
|
||||||
|
BehaviorTree tree(std::move(retry));
|
||||||
|
BehaviorContext ctx;
|
||||||
|
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Running);
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Running);
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Failure);
|
||||||
|
CHECK_EQ(runs, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(timeLimitCutsOffAChildThatNeverSettles) {
|
||||||
|
int runs = 0;
|
||||||
|
auto limited = std::unique_ptr<BehaviorNode>(new TimeLimitNode(
|
||||||
|
"window", constantAction("stall", BehaviorStatus::Running, &runs), 3));
|
||||||
|
BehaviorTree tree(std::move(limited));
|
||||||
|
BehaviorContext ctx;
|
||||||
|
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Running);
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Running);
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Failure);
|
||||||
|
CHECK_EQ(runs, 3);
|
||||||
|
|
||||||
|
// The budget is refilled once the decorator gave its verdict.
|
||||||
|
CHECK(tree.tick(ctx) == BehaviorStatus::Running);
|
||||||
|
CHECK_EQ(runs, 4);
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user