// Copyright Epic Games, Inc. All Rights Reserved. #include "CQTestInputTestHelper.h" #include "Engine/World.h" #include "InputCoreTypes.h" #include "InputMappingContext.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(CQTestInputTestHelper) void UTestEnhancedInputSubsystem::Init(APlayerController* InPlayerController) { check(InPlayerController); PlayerController = InPlayerController; InputComponent = CastChecked(PlayerController->InputComponent); PlayerInput = CastChecked(PlayerController->PlayerInput); InitalizeUserSettings(); SetupTestBindings(); } void UTestEnhancedInputSubsystem::MappingListener(const FInputActionInstance& Instance) { const UInputAction* SourceAction = Instance.GetSourceAction(); check(SourceAction); ETriggerEvent* TriggerEvent = InputActionTriggerEventMap.Find(SourceAction->GetName()); if (TriggerEvent) { *TriggerEvent = Instance.GetTriggerEvent(); } else { InputActionTriggerEventMap.Add(SourceAction->GetName(), Instance.GetTriggerEvent()); } } ETriggerEvent* UTestEnhancedInputSubsystem::GetEventForAction(const FString& InputActionName) { return InputActionTriggerEventMap.Find(InputActionName); } void UTestEnhancedInputSubsystem::SetupTestBindings() { InputMappingContext = NewObject(PlayerController, "TestContext"); AddMappingContext(InputMappingContext, 0); BindInputAction(FCQTestInputSubsystemHelper::TestButtonActionName, EInputActionValueType::Boolean, { EKeys::A }); BindInputAction(FCQTestInputSubsystemHelper::TestAxisActionName, EInputActionValueType::Axis1D, { EKeys::Gamepad_LeftTriggerAxis, EKeys::MouseX }); // Generate a live mapping on the player FModifyContextOptions Options; Options.bForceImmediately = true; RequestRebuildControlMappings(Options); } void UTestEnhancedInputSubsystem::BindInputAction(const FString& InputActionName, EInputActionValueType InputActionValueType, const TArray& Keys) { UInputAction* Action = NewObject(PlayerController, *InputActionName); Action->ValueType = InputActionValueType; // Bind action to the binding targets so we can check if they were called correctly, but only the first time we bind the action! InputComponent->BindAction(Action, ETriggerEvent::Started, this, &UTestEnhancedInputSubsystem::MappingListener); InputComponent->BindAction(Action, ETriggerEvent::Ongoing, this, &UTestEnhancedInputSubsystem::MappingListener); InputComponent->BindAction(Action, ETriggerEvent::Canceled, this, &UTestEnhancedInputSubsystem::MappingListener); InputComponent->BindAction(Action, ETriggerEvent::Completed, this, &UTestEnhancedInputSubsystem::MappingListener); InputComponent->BindAction(Action, ETriggerEvent::Triggered, this, &UTestEnhancedInputSubsystem::MappingListener); // Initialise input action mapping in context to be used for testing for (const FKey& Key : Keys) { InputMappingContext->MapKey(Action, Key); } } bool FCQTestInputSubsystemHelper::ActionExpectedEvent(const FString& InputActionName, ETriggerEvent ExpectedTriggerEvent) { check(InputSubsystem); ETriggerEvent* TriggerEvent = InputSubsystem->GetEventForAction(InputActionName); if (!TriggerEvent) { return false; } return (*TriggerEvent) == ExpectedTriggerEvent; } void FCQTestInputSubsystemHelper::InitializePlayerControllerInput() { check(Pawn); if (!Pawn->IsPlayerControlled()) { UWorld* CurrentWorld = Pawn->GetWorld(); check(CurrentWorld); PlayerController = NewObject(CurrentWorld); PlayerController->Possess(Pawn); Pawn->PossessedBy(PlayerController); } else { PlayerController = CastChecked(Pawn->GetController()); check(PlayerController); } PlayerController->InputComponent = NewObject(PlayerController); PlayerController->PlayerInput = NewObject(PlayerController); PlayerController->InitInputSystem(); // Keep a strong reference to the newly created input system during the test execution InputSubsystem = TStrongObjectPtr(NewObject()); InputSubsystem->Init(PlayerController); } void FCQTestPawnTestActions::PressButton(const FString& ButtonActionName) { PerformAction(FPressButtonAction{ ButtonActionName }); } void FCQTestPawnTestActions::HoldAxis(const FString& ExampleAxisActionName, const FInputActionValue& ActionValue, FTimespan&& Duration) { PerformAction(FHoldAxisAction{ ExampleAxisActionName, ActionValue }, [this, Duration = MoveTemp(Duration)]() -> bool { if (StartTime.GetTicks() == 0) { StartTime = FDateTime::UtcNow(); } FTimespan Elapsed = FDateTime::UtcNow() - StartTime; return Elapsed >= Duration; }); } bool FCQTestPawnTestActions::IsTriggered(const FString& InputActionName) { return InputSubsystemHelper->ActionExpectedEvent(InputActionName, ETriggerEvent::Triggered); } bool FCQTestPawnTestActions::IsCompleted(const FString& InputActionName) { return InputSubsystemHelper->ActionExpectedEvent(InputActionName, ETriggerEvent::Completed); }