Write some unit tests for the input buffer

This commit is contained in:
Kevin Poretti 2023-11-25 21:03:31 -05:00
parent ee8dff88ea
commit 3d4c56fcd9
4 changed files with 159 additions and 3 deletions

View File

@ -18,14 +18,14 @@ bool UFFInputBufferComponent::CheckInputSequence(const FFFInputSequence& InputSe
{ {
if(InputSequence.Sequence.IsEmpty()) if(InputSequence.Sequence.IsEmpty())
{ {
UE_LOG(LogTemp, Error, UE_LOG(LogTemp, Warning,
TEXT("FFInputBufferComponent :: CheckInputSequence - tried to check input sequence but it was empty")); TEXT("FFInputBufferComponent :: CheckInputSequence - tried to check input sequence but it was empty"));
return false; return false;
} }
int CondIdx = InputSequence.Sequence.Num() - 1; int CondIdx = InputSequence.Sequence.Num() - 1;
int ElapsedFrames = 0; int ElapsedFrames = 0;
for(int InpIdx = InputBuffer.Num() - 1; InpIdx > 1; InpIdx--) for(int InpIdx = InputBuffer.Num() - 1; InpIdx > 0; InpIdx--)
{ {
int32 RequiredButtons = InputSequence.Sequence[CondIdx].RequiredButtons; int32 RequiredButtons = InputSequence.Sequence[CondIdx].RequiredButtons;
EFFButtonState RequiredButtonState = InputSequence.Sequence[CondIdx].RequiredButtonState; EFFButtonState RequiredButtonState = InputSequence.Sequence[CondIdx].RequiredButtonState;
@ -100,3 +100,8 @@ void UFFInputBufferComponent::DisableMostRecentInput()
InputBuffer[InputBuffer.Num() - 1].DisabledButtons |= InputBuffer[InputBuffer.Num() - 1].Buttons; InputBuffer[InputBuffer.Num() - 1].DisabledButtons |= InputBuffer[InputBuffer.Num() - 1].Buttons;
InputBuffer[InputBuffer.Num() - 2].DisabledButtons |= InputBuffer[InputBuffer.Num() - 2].Buttons; InputBuffer[InputBuffer.Num() - 2].DisabledButtons |= InputBuffer[InputBuffer.Num() - 2].Buttons;
} }
void UFFInputBufferComponent::FlushBuffer()
{
InputBuffer.Flush();
}

View File

@ -101,6 +101,8 @@ public:
void DisableMostRecentInput(); void DisableMostRecentInput();
void FlushBuffer();
protected: protected:
/** The underlying buffer data structure for holding past input states */ /** The underlying buffer data structure for holding past input states */
TCircleBuffer<FFFInputState, 120> InputBuffer; TCircleBuffer<FFFInputState, 120> InputBuffer;

View File

@ -0,0 +1,139 @@
// Project Sword & Gun Copyright Kevin Poretti
// FF includes
#include "Input/FFInputBufferComponent.h"
// UE includes
#include "CoreMinimal.h"
#include "Misc/AutomationTest.h"
#if WITH_DEV_AUTOMATION_TESTS
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FInputBufferTest, "FF.Input.InputBuffer",
EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter)
bool FInputBufferTest::RunTest(const FString& Parameters)
{
TStrongObjectPtr<UWorld> World = TStrongObjectPtr(UWorld::CreateWorld(EWorldType::Game, false));
// Create a dummy actor
TStrongObjectPtr<AActor> InputBufferOwner = TStrongObjectPtr<AActor>(World->SpawnActor<AActor>());
TestTrue(TEXT("Create InputBufferOwner"), InputBufferOwner.IsValid());
UFFInputBufferComponent* InputBuffer =
NewObject<UFFInputBufferComponent>(InputBufferOwner.Get(),
UFFInputBufferComponent::StaticClass(), TEXT("InputBuffer"));
InputBuffer->RegisterComponent();
// InputBuffer must be able to be created as a subobject of an actor
{
TestNotNull(TEXT("Create new InputBuffer component"), InputBuffer);
}
// InputBuffer should not attempt to evaluate an input sequence if there is only one input in
// the buffer and should return false
{
FFFInputState InputDown;
InputDown.Buttons = 0x00000001;
InputBuffer->AddInput(InputDown);
FFFInputCondition PressedCondition;
PressedCondition.RequiredButtons = 0x00000001;
PressedCondition.RequiredButtonState = EFFButtonState::BTNS_Pressed;
PressedCondition.TimeoutDuration = 0;
FFFInputSequence PressedSequence;
PressedSequence.Sequence.Add(PressedCondition);
TestFalse(TEXT("Don't try to detect a sequence if only one input is present in the buffer"),
InputBuffer->CheckInputSequence(PressedSequence));
}
InputBuffer->FlushBuffer();
// InputBuffer should not attempt to evaluate an input sequence if the sequence has no input conditions
{
FFFInputState InputUp;
InputUp.Buttons = 0x00000000;
FFFInputState InputDown;
InputDown.Buttons = 0x00000001;
InputBuffer->AddInput(InputUp);
InputBuffer->AddInput(InputDown);
FFFInputSequence EmptySequence;
TestFalse(TEXT("Don't try to detect a sequence if the sequence has no input conditions"),
InputBuffer->CheckInputSequence(EmptySequence));
}
InputBuffer->FlushBuffer();
// InputBuffer must detect simple button pressed condition
{
FFFInputState InputUp;
InputUp.Buttons = 0x00000000;
FFFInputState InputDown;
InputDown.Buttons = 0x00000001;
InputBuffer->AddInput(InputUp);
InputBuffer->AddInput(InputDown);
FFFInputCondition PressedCondition;
PressedCondition.RequiredButtons = 0x00000001;
PressedCondition.RequiredButtonState = EFFButtonState::BTNS_Pressed;
PressedCondition.TimeoutDuration = 0;
FFFInputSequence PressedSequence;
PressedSequence.Sequence.Add(PressedCondition);
TestTrue(TEXT("Detect a single button press in the buffer"),
InputBuffer->CheckInputSequence(PressedSequence));
}
InputBuffer->FlushBuffer();
// InputBuffer must detect simple button released condition
{
FFFInputState InputUp;
InputUp.Buttons = 0x0000000;
FFFInputState InputDown;
InputDown.Buttons = 0x00000001;
InputBuffer->AddInput(InputDown);
InputBuffer->AddInput(InputUp);
FFFInputCondition ReleasedCondition;
ReleasedCondition.RequiredButtons = 0x00000001;
ReleasedCondition.RequiredButtonState = EFFButtonState::BTNS_Released;
ReleasedCondition.TimeoutDuration = 0;
FFFInputSequence ReleasedSequence;
ReleasedSequence.Sequence.Add(ReleasedCondition);
TestTrue(TEXT("Detect a single button release in the buffer"),
InputBuffer->CheckInputSequence(ReleasedSequence));
}
InputBuffer->FlushBuffer();
// InputBuffer must detect simple button down condition
{
FFFInputState InputUp;
InputUp.Buttons = 0x0000000;
FFFInputState InputDown;
InputDown.Buttons = 0x00000001;
InputBuffer->AddInput(InputUp);
InputBuffer->AddInput(InputDown);
FFFInputCondition DownCondition;
DownCondition.RequiredButtons = 0x00000001;
DownCondition.RequiredButtonState = EFFButtonState::BTNS_Down;
DownCondition.TimeoutDuration = 0;
FFFInputSequence DownSequence;
DownSequence.Sequence.Add(DownCondition);
TestTrue(TEXT("Detect a single button press in the buffer"),
InputBuffer->CheckInputSequence(DownSequence));
}
World->DestroyWorld(false);
return true;
}
#endif //WITH_DEV_AUTOMATION_TESTS

View File

@ -108,6 +108,16 @@ public:
return true; return true;
} }
/**
* @brief Flushes the buffer of all contents
*/
void Flush()
{
WriteIdx = 0;
ReadIdx = 0;
_Num = 0;
}
/** /**
* @brief Returns the element at an index supplied to the function. * @brief Returns the element at an index supplied to the function.
* This function will account for write index offset and wraparound of the supplied index. * This function will account for write index offset and wraparound of the supplied index.
@ -119,7 +129,7 @@ public:
{ {
return Buffer[(ReadIdx + Index) % L]; return Buffer[(ReadIdx + Index) % L];
} }
FORCEINLINE bool IsEmpty() const FORCEINLINE bool IsEmpty() const
{ {
return _Num == 0; return _Num == 0;