Skip to content

Commit a548f70

Browse files
committed
Added selector conditional abort tests
Added several tests to protect against unexpected selector conditional abort behavior
1 parent f50ea64 commit a548f70

File tree

2 files changed

+255
-3
lines changed

2 files changed

+255
-3
lines changed

Assets/FluidBehaviorTree/Editor/Testing/ParentTasks/SelectorTest.cs

Lines changed: 254 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,8 +308,260 @@ public void Keeps_conditional_aborts_before_the_revaluated_abort () {
308308
}
309309
}
310310

311-
public class AbortTypeSelector {
312-
// @TODO Add composite abort type tests
311+
public class AbortTypeSelector : UpdateMethod {
312+
public class WhenAbortingFromFailureToSuccess : UpdateMethod {
313+
private Selector selectorSub;
314+
private ITask conditionAbort;
315+
316+
[SetUp]
317+
public void Build_tree () {
318+
selectorSub = new Selector();
319+
selectorSub.AbortType = AbortType.LowerPriority;
320+
321+
conditionAbort = A.TaskStub()
322+
.WithAbortConditionSelf(true)
323+
.WithUpdateStatus(TaskStatus.Failure)
324+
.Build();
325+
326+
var actionContinue = A.TaskStub()
327+
.WithUpdateStatus(TaskStatus.Continue)
328+
.Build();
329+
330+
_selector
331+
.AddChild(selectorSub
332+
.AddChild(conditionAbort))
333+
.AddChild(actionContinue);
334+
}
335+
336+
[Test]
337+
public void First_call_should_return_continue () {
338+
Assert.AreEqual(TaskStatus.Continue, _selector.Update());
339+
}
340+
341+
[Test]
342+
public void Revaluates_when_going_from_failure_to_success () {
343+
_selector.Update();
344+
conditionAbort.Update().Returns(TaskStatus.Success);
345+
_selector.Update();
346+
347+
CheckUpdateCalls(_selector, new List<int> { -1, 1 });
348+
CheckUpdateCalls(selectorSub, new List<int> { 3 });
349+
}
350+
351+
[Test]
352+
public void Returns_success_when_going_from_failure_to_success () {
353+
_selector.Update();
354+
conditionAbort.Update().Returns(TaskStatus.Success);
355+
Assert.AreEqual(TaskStatus.Success, _selector.Update());
356+
}
357+
}
358+
359+
public class WhenAbortingWithMultipleSelectorConditions : UpdateMethod {
360+
private Selector selectorSub;
361+
private ITask conditionAbortA;
362+
private ITask conditionAbortB;
363+
364+
[SetUp]
365+
public void Build_tree () {
366+
selectorSub = new Selector();
367+
selectorSub.AbortType = AbortType.LowerPriority;
368+
369+
conditionAbortA = A.TaskStub()
370+
.WithAbortConditionSelf(true)
371+
.WithUpdateStatus(TaskStatus.Failure)
372+
.Build();
373+
374+
conditionAbortB = A.TaskStub()
375+
.WithAbortConditionSelf(true)
376+
.WithUpdateStatus(TaskStatus.Failure)
377+
.Build();
378+
379+
var actionContinue = A.TaskStub()
380+
.WithUpdateStatus(TaskStatus.Continue)
381+
.Build();
382+
383+
_selector
384+
.AddChild(selectorSub
385+
.AddChild(conditionAbortA)
386+
.AddChild(conditionAbortB))
387+
.AddChild(actionContinue);
388+
}
389+
390+
[Test]
391+
public void First_call_should_return_continue () {
392+
Assert.AreEqual(TaskStatus.Continue, _selector.Update());
393+
}
394+
395+
[Test]
396+
public void Revaluates_when_going_from_failure_to_success () {
397+
_selector.Update();
398+
conditionAbortA.Update().Returns(TaskStatus.Success);
399+
_selector.Update();
400+
401+
CheckUpdateCalls(_selector, new List<int> { -1, 1 });
402+
CheckUpdateCalls(selectorSub, new List<int> { 3, 1 });
403+
}
404+
405+
[Test]
406+
public void Revaluates_both_conditions_when_going_from_failure_to_success () {
407+
_selector.Update();
408+
conditionAbortB.Update().Returns(TaskStatus.Success);
409+
_selector.Update();
410+
411+
// @NOTE Must re-run the entire selector since it only detects that something changed in it (could be more optimized)
412+
CheckUpdateCalls(_selector, new List<int> { -1, 1 });
413+
CheckUpdateCalls(selectorSub, new List<int> { 3, 3 });
414+
}
415+
416+
[Test]
417+
public void Returns_success_when_going_from_failure_to_success () {
418+
_selector.Update();
419+
conditionAbortA.Update().Returns(TaskStatus.Success);
420+
421+
Assert.AreEqual(TaskStatus.Success, _selector.Update());
422+
}
423+
424+
[Test]
425+
public void Returns_success_when_last_abort_goes_from_failure_to_success () {
426+
_selector.Update();
427+
conditionAbortB.Update().Returns(TaskStatus.Success);
428+
429+
Assert.AreEqual(TaskStatus.Success, _selector.Update());
430+
}
431+
}
432+
433+
public class WhenAbortsHaveSiblings : UpdateMethod {
434+
private Selector selectorSub;
435+
private Sequence selectorSequenceSub;
436+
private ITask conditionAbortA;
437+
private ITask conditionAbortASibling;
438+
private ITask conditionAbortB;
439+
440+
[SetUp]
441+
public void Build_tree () {
442+
selectorSub = new Selector();
443+
selectorSub.AbortType = AbortType.LowerPriority;
444+
445+
selectorSequenceSub = new Sequence();
446+
447+
conditionAbortA = A.TaskStub()
448+
.WithAbortConditionSelf(true)
449+
.WithUpdateStatus(TaskStatus.Failure)
450+
.Build();
451+
452+
conditionAbortB = A.TaskStub()
453+
.WithAbortConditionSelf(true)
454+
.WithUpdateStatus(TaskStatus.Failure)
455+
.Build();
456+
457+
conditionAbortASibling = A.TaskStub().WithUpdateStatus(TaskStatus.Failure).Build();
458+
459+
var actionContinue = A.TaskStub()
460+
.WithUpdateStatus(TaskStatus.Continue)
461+
.Build();
462+
463+
_selector
464+
.AddChild(selectorSub
465+
.AddChild(selectorSequenceSub
466+
.AddChild(conditionAbortA)
467+
.AddChild(conditionAbortASibling))
468+
.AddChild(conditionAbortB))
469+
.AddChild(actionContinue);
470+
}
471+
472+
[Test]
473+
public void First_call_should_return_continue () {
474+
Assert.AreEqual(TaskStatus.Continue, _selector.Update());
475+
}
476+
477+
[Test]
478+
public void Returns_continue_when_conditional_is_activated_but_sibling_action_returns_failure () {
479+
_selector.Update();
480+
conditionAbortA.Update().Returns(TaskStatus.Success);
481+
Assert.AreEqual(TaskStatus.Continue, _selector.Update());
482+
}
483+
484+
[Test]
485+
public void Revaluates_when_conditional_is_activated_but_sibling_action_returns_failure () {
486+
_selector.Update();
487+
conditionAbortA.Update().Returns(TaskStatus.Success);
488+
_selector.Update();
489+
490+
CheckUpdateCalls(_selector, new List<int> { -1, 2 });
491+
CheckUpdateCalls(selectorSub, new List<int> { -1, 2 });
492+
CheckUpdateCalls(selectorSequenceSub, new List<int> { 3, 1 });
493+
}
494+
495+
[Test]
496+
public void Returns_success_when_conditional_is_activated_but_sibling_action_returns_success () {
497+
conditionAbortASibling.Update().Returns(TaskStatus.Success);
498+
499+
_selector.Update();
500+
conditionAbortA.Update().Returns(TaskStatus.Success);
501+
Assert.AreEqual(TaskStatus.Success, _selector.Update());
502+
}
503+
504+
[Test]
505+
public void Revaluates_when_conditional_is_activated_but_sibling_action_returns_success () {
506+
conditionAbortASibling.Update().Returns(TaskStatus.Success);
507+
508+
_selector.Update();
509+
conditionAbortA.Update().Returns(TaskStatus.Success);
510+
_selector.Update();
511+
512+
CheckUpdateCalls(_selector, new List<int> { -1, 1 });
513+
CheckUpdateCalls(selectorSub, new List<int> { -1, 1 });
514+
CheckUpdateCalls(selectorSequenceSub, new List<int> { 3, 1 });
515+
}
516+
}
517+
518+
public class WhenAbortIsNotThe1stNode : UpdateMethod {
519+
private Selector selectorSub;
520+
private ITask conditionAbort;
521+
522+
[SetUp]
523+
public void Build_tree () {
524+
selectorSub = new Selector();
525+
selectorSub.AbortType = AbortType.LowerPriority;
526+
527+
conditionAbort = A.TaskStub()
528+
.WithAbortConditionSelf(true)
529+
.WithUpdateStatus(TaskStatus.Failure)
530+
.Build();
531+
532+
var actionContinue = A.TaskStub()
533+
.WithUpdateStatus(TaskStatus.Continue)
534+
.Build();
535+
536+
_selector
537+
.AddChild(A.TaskStub().WithUpdateStatus(TaskStatus.Failure).Build())
538+
.AddChild(selectorSub
539+
.AddChild(conditionAbort))
540+
.AddChild(actionContinue);
541+
}
542+
543+
[Test]
544+
public void First_call_should_return_continue () {
545+
Assert.AreEqual(TaskStatus.Continue, _selector.Update());
546+
}
547+
548+
[Test]
549+
public void Revaluates_when_going_from_failure_to_success () {
550+
_selector.Update();
551+
conditionAbort.Update().Returns(TaskStatus.Success);
552+
_selector.Update();
553+
554+
CheckUpdateCalls(_selector, new List<int> { 1, -1, 1 });
555+
CheckUpdateCalls(selectorSub, new List<int> { 3 });
556+
}
557+
558+
[Test]
559+
public void Returns_success_when_going_from_failure_to_success () {
560+
_selector.Update();
561+
conditionAbort.Update().Returns(TaskStatus.Success);
562+
Assert.AreEqual(TaskStatus.Success, _selector.Update());
563+
}
564+
}
313565
}
314566
}
315567
}

Assets/FluidBehaviorTree/Scripts/TaskParents/Composites/Selector.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ public class Selector : CompositeBase {
66
List<int> _lowerPriorityIndices = new List<int>();
77

88
protected override TaskStatus OnUpdate () {
9-
// @TODO Consider abort self being able to revaluate any immediate child condition that has already been run
9+
// @NOTE Consider abort self being able to revaluate any immediate child condition that has already been run
1010
if (AbortSelf(TaskStatus.Success)) {
1111
return Update();
1212
}

0 commit comments

Comments
 (0)