You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#jira UE-143918 #rb marc.audy #preflight 62265f5d123eef55e6f6bdff [CL 19290708 by ben hoffman in ue5-main branch]
208 lines
7.5 KiB
C++
208 lines
7.5 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "EnhancedActionKeyMapping.h"
|
|
#include "EnhancedPlayerInput.h"
|
|
#include "InputModifiers.h"
|
|
#include "InputTestFramework.h"
|
|
#include "Misc/AutomationTest.h"
|
|
|
|
// Tests focused on individual modifiers
|
|
|
|
|
|
// THEN step wrappers to give human readable test failure output.
|
|
bool TestActionIsActuated(UControllablePlayer& Data, FName ActionName)
|
|
{
|
|
return FInputTestHelper::GetActionData(Data, ActionName).GetValue().Get<bool>();
|
|
}
|
|
|
|
bool TestActionIsNotActuated(UControllablePlayer& Data, FName ActionName)
|
|
{
|
|
return !FInputTestHelper::GetActionData(Data, ActionName).GetValue().Get<bool>();
|
|
}
|
|
|
|
float GetActionValue(UControllablePlayer& Data, FName ActionName)
|
|
{
|
|
return FInputTestHelper::GetActionData(Data, ActionName).GetValue().Get<float>();
|
|
}
|
|
|
|
constexpr auto BasicModifierTestFlags = EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter; // TODO: Run as Smoke/Client? No world on RunSmokeTests startup...
|
|
|
|
UControllablePlayer& ABasicModifierTest(FAutomationTestBase* Test, EInputActionValueType ForValueType)
|
|
{
|
|
UWorld* World =
|
|
GIVEN(AnEmptyWorld());
|
|
|
|
UControllablePlayer& Data =
|
|
AND(AControllablePlayer(World));
|
|
Test->TestTrue(TEXT("Controllable Player is valid"), Data.IsValid()); // TODO: Can we early out on a failed Test?
|
|
AND(AnInputContextIsAppliedToAPlayer(Data, TestContext, 0));
|
|
UInputAction* Action =
|
|
AND(AnInputAction(Data, TestAction, ForValueType));
|
|
|
|
return Data;
|
|
}
|
|
|
|
|
|
// ******************************
|
|
// Value modification tests
|
|
// ******************************
|
|
|
|
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FInputModifieNegateTest, "Input.Modifiers.Negate", BasicModifierTestFlags)
|
|
|
|
bool FInputModifieNegateTest::RunTest(const FString& Parameters)
|
|
{
|
|
UControllablePlayer& Data =
|
|
GIVEN(ABasicModifierTest(this, EInputActionValueType::Axis1D));
|
|
|
|
AND(AnActionIsMappedToAKey(Data, TestContext, TestAction, TestKey));
|
|
AND(AModifierIsAppliedToAnActionMapping(Data, NewObject<UInputModifierNegate>(), TestContext, TestAction, TestKey));
|
|
|
|
// Bool key tests. Negating a false/true bool should give -0/-1, not true/false, allowing driving a negative axis movement.
|
|
// An unactuated bool input should always return false.
|
|
|
|
// Test 1 - By default, output is false (0) (unactuated bools always return false)
|
|
WHEN(InputIsTicked(Data));
|
|
THEN(TestActionIsNotActuated(Data, TestAction));
|
|
|
|
// Test 2 - Key press/hold/release
|
|
|
|
// Actuated output is true (-1) when key is down
|
|
WHEN(AKeyIsActuated(Data, TestKey));
|
|
AND(InputIsTicked(Data));
|
|
THEN(TestActionIsActuated(Data, TestAction));
|
|
AND(TestEqual(TEXT("Key actuated press value"), GetActionValue(Data, TestAction), -1.f));
|
|
|
|
// Actuated output remains true (-1) next tick
|
|
WHEN(InputIsTicked(Data));
|
|
THEN(TestActionIsActuated(Data, TestAction));
|
|
AND(TestEqual(TEXT("Key actuated hold value"), GetActionValue(Data, TestAction), -1.f));
|
|
|
|
// Releasing the key reverts to false (0)
|
|
WHEN(AKeyIsReleased(Data, TestKey));
|
|
AND(InputIsTicked(Data));
|
|
THEN(TestActionIsNotActuated(Data, TestAction));
|
|
|
|
InputIsTicked(Data);// Clear state
|
|
|
|
// Test 3 - Axis press/hold/release
|
|
WHEN(AnActionIsMappedToAKey(Data, TestContext, TestAction, TestAxis));
|
|
AND(AModifierIsAppliedToAnActionMapping(Data, NewObject<UInputModifierNegate>(), TestContext, TestAction, TestAxis));
|
|
AND(AKeyIsActuated(Data, TestAxis, 0.5f));
|
|
AND(InputIsTicked(Data));
|
|
THEN(TestActionIsActuated(Data, TestAction));
|
|
AND(TestEqual(TEXT("Axis inverted press value"), GetActionValue(Data, TestAction), -0.5f));
|
|
|
|
// Actuated output remains constant next tick
|
|
WHEN(InputIsTicked(Data));
|
|
AND(TestEqual(TEXT("Axis inverted hold value"), GetActionValue(Data, TestAction), -0.5f));
|
|
|
|
// Actuation stops when axis is released
|
|
WHEN(AKeyIsReleased(Data, TestAxis));
|
|
AND(InputIsTicked(Data));
|
|
THEN(TestActionIsNotActuated(Data, TestAction));
|
|
|
|
return true;
|
|
}
|
|
|
|
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FInputModifierScalarTest, "Input.Modifiers.Scalar", BasicModifierTestFlags)
|
|
|
|
bool FInputModifierScalarTest::RunTest(const FString& Parameters)
|
|
{
|
|
GIVEN(UControllablePlayer& Data = ABasicModifierTest(this, EInputActionValueType::Axis1D));
|
|
AND(AnActionIsMappedToAKey(Data, TestContext, TestAction, TestAxis));
|
|
AND(UInputModifierScalar* Scalar = Cast<UInputModifierScalar>(AModifierIsAppliedToAnActionMapping(Data, NewObject<UInputModifierScalar>(), TestContext, TestAction, TestAxis)));
|
|
check(Scalar);
|
|
|
|
// Test 1 - By default (no input), output is 0
|
|
WHEN(InputIsTicked(Data));
|
|
TestEqual(TEXT("No input value"), GetActionValue(Data, TestAction), 0.f);
|
|
|
|
// Test 2 - By default scale is 1. When actuated output == actuated value
|
|
WHEN(AKeyIsActuated(Data, TestAxis, 0.5f));
|
|
AND(InputIsTicked(Data));
|
|
TestEqual(TEXT("Input value"), GetActionValue(Data, TestAction), 0.5f);
|
|
|
|
// Multi value tests
|
|
TArray<float> TestValues = { 0.5f, -0.5f, 2.f, -1000.f, 0.f };
|
|
for(float TestValue : TestValues)
|
|
{
|
|
WHEN(AKeyIsActuated(Data, TestAxis, TestValue));
|
|
Scalar->Scalar = FVector::OneVector * 1.f;
|
|
AND(InputIsTicked(Data));
|
|
TestEqual(TEXT("Input value (new)"), GetActionValue(Data, TestAction), TestValue * (float)Scalar->Scalar.X);
|
|
|
|
// Test 3 - Modify scalar on the fly.
|
|
Scalar->Scalar = FVector::OneVector * 0.5f;
|
|
WHEN(InputIsTicked(Data));
|
|
TestEqual(TEXT("Input value (modify)"), GetActionValue(Data, TestAction), TestValue * (float)Scalar->Scalar.X);
|
|
|
|
// Test 4 - negate
|
|
Scalar->Scalar = FVector::OneVector * -2.f;
|
|
WHEN(InputIsTicked(Data));
|
|
TestEqual(TEXT("Input value (negate)"), GetActionValue(Data, TestAction), TestValue * (float)Scalar->Scalar.X);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
IMPLEMENT_SIMPLE_AUTOMATION_TEST(FInputModifierDeadzoneTest, "Input.Modifiers.DeadZone", BasicModifierTestFlags)
|
|
|
|
bool FInputModifierDeadzoneTest::RunTest(const FString& Parameters)
|
|
{
|
|
UControllablePlayer& Data =
|
|
GIVEN(ABasicModifierTest(this, EInputActionValueType::Axis1D));
|
|
AND(AnActionIsMappedToAKey(Data, TestContext, TestAction, TestAxis));
|
|
|
|
UInputModifierDeadZone* DeadZone =
|
|
AND(Cast<UInputModifierDeadZone>(AModifierIsAppliedToAnAction(Data, NewObject<UInputModifierDeadZone>(), TestAction)));
|
|
DeadZone->LowerThreshold = 0.1f;
|
|
|
|
// Provide initial valid input
|
|
WHEN(AKeyIsActuated(Data, TestAxis, 1.f));
|
|
AND(InputIsTicked(Data));
|
|
THEN(PressingKeyTriggersAction(Data, TestAction));
|
|
|
|
// Drop below deadzone
|
|
WHEN(AKeyIsActuated(Data, TestAxis, DeadZone->LowerThreshold * 0.5f));
|
|
AND(InputIsTicked(Data));
|
|
THEN(HoldingKeyTriggersCompleted(Data, TestAction));
|
|
|
|
// Jitter samples below deadzone lower threshold
|
|
TArray<float> Sample = { 0.01f, 0.0f, 0.02f, 0.07f, 0.01f };
|
|
const int NumSamples = 50;
|
|
for (int32 i = 0; i < NumSamples; ++i)
|
|
{
|
|
WHEN(AKeyIsActuated(Data, TestAxis, Sample[i % Sample.Num()]));
|
|
AND(InputIsTicked(Data));
|
|
THEN(HoldingKeyDoesNotTrigger(Data, TestAction));
|
|
}
|
|
|
|
// No noise on release
|
|
WHEN(AKeyIsReleased(Data, TestAxis));
|
|
AND(InputIsTicked(Data));
|
|
THEN(ReleasingKeyDoesNotTrigger(Data, TestAction));
|
|
|
|
|
|
// Upper threshold testing
|
|
DeadZone->UpperThreshold = 0.9f;
|
|
WHEN(AKeyIsActuated(Data, TestAxis, 0.5f));
|
|
AND(InputIsTicked(Data));
|
|
THEN(PressingKeyTriggersAction(Data, TestAction));
|
|
|
|
// At threshold response is 1
|
|
WHEN(AKeyIsActuated(Data, TestAxis, 0.9f));
|
|
AND(InputIsTicked(Data));
|
|
THEN(HoldingKeyTriggersAction(Data, TestAction));
|
|
AND(TestEqual(TEXT("Upper threshold value at threshold"), GetActionValue(Data, TestAction), 1.f));
|
|
|
|
// Past threshold response is clamped to 1
|
|
WHEN(AKeyIsActuated(Data, TestAxis, 0.99f));
|
|
AND(InputIsTicked(Data));
|
|
THEN(HoldingKeyTriggersAction(Data, TestAction));
|
|
AND(TestEqual(TEXT("Upper threshold value beyond threshold"), GetActionValue(Data, TestAction), 1.f));
|
|
|
|
return true;
|
|
}
|