// Copyright Sam Bonifacio. All Rights Reserved. #include "CoreTypes.h" #include "InputMappingManager.h" #include "PlayerInputMappings.h" #include "Misc/AutomationTest.h" #include "Misc/AutoSettingsInputConfig.h" #if WITH_DEV_AUTOMATION_TESTS /** * Check that when null base preset is specified, the resulting layout is empty */ IMPLEMENT_SIMPLE_AUTOMATION_TEST(FEmptyPresetTest, "AutoSettings.Input.EmptyPreset", EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::SmokeFilter) bool FEmptyPresetTest::RunTest(const FString& Parameters) { UAutoSettingsInputConfig* Config = NewObject(); const FPlayerInputMappings PlayerInputMappings = FPlayerInputMappings(true, Config); // Check layout has nothing bound const FInputMappingLayout MergedLayout = PlayerInputMappings.BuildMergedMappingLayout(); TestEqual(TEXT("Layout must have no actions bound"), MergedLayout.GetActions().Num(), 0); TestEqual(TEXT("Layout must have no axes bound"), MergedLayout.GetAxes().Num(), 0); return true; } /** * Check that when an empty base preset tag is specified, the resulting layout is built on a default preset from the config */ IMPLEMENT_SIMPLE_AUTOMATION_TEST(FDefaultPresetTest, "AutoSettings.Input.DefaultPreset", EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::SmokeFilter) bool FDefaultPresetTest::RunTest(const FString& Parameters) { UAutoSettingsInputConfig* Config = NewObject(); // Add a preset to config Config->InputPresets.Add(FInputMappingPreset(FGameplayTag::EmptyTag, false, Config)); FInputMappingPreset& Preset = Config->InputPresets.Last(); Preset.InputLayout.MappingGroups.Add(FInputMappingGroup(Config)); FInputMappingGroup& MappingGroup = Preset.InputLayout.MappingGroups.Last(); MappingGroup.ActionMappings.Add(FConfigActionKeyMapping(FInputActionKeyMapping(TEXT("Jump"), EKeys::SpaceBar))); // Null base preset false, because we want to test with config preset const FPlayerInputMappings PlayerInputMappings = FPlayerInputMappings(false, Config); // Check second action is now bound const FInputActionKeyMapping JumpAction = PlayerInputMappings.BuildMergedMappingLayout().GetAction(0, FName(TEXT("Jump"))); TestEqual(TEXT("Must inherit from default preset"), JumpAction.Key, EKeys::SpaceBar); return true; } /** * Check that the config object is registered at different levels of the input structure */ IMPLEMENT_SIMPLE_AUTOMATION_TEST(FConfigRegisteredTest, "AutoSettings.Input.ConfigRegistered", EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::SmokeFilter) bool FConfigRegisteredTest::RunTest(const FString& Parameters) { UAutoSettingsInputConfig* Config = NewObject(); FPlayerInputMappings PlayerInputMappings = FPlayerInputMappings(true, Config); PlayerInputMappings.AddAxisOverride(FInputAxisKeyMapping(FName(TEXT("MoveForward")), EKeys::I), 0, false); TestEqual(TEXT("MappingOverrides must have the created config"), PlayerInputMappings.MappingOverrides.Config->_getUObject(), Cast(Config)); TestEqual(TEXT("MappingGroup must have the created config"), PlayerInputMappings.MappingOverrides.MappingGroups[0].Config->_getUObject(), Cast(Config)); return true; } /** * Check that when a button is bound to an axis, the binding is applied correctly */ IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMapPreviousAxisButtonTest, "AutoSettings.Input.BindPreviousAxisButton", EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::SmokeFilter) bool FMapPreviousAxisButtonTest::RunTest(const FString& Parameters) { UAutoSettingsInputConfig* Config = NewObject(); FPlayerInputMappings PlayerInputMappings = FPlayerInputMappings(true, Config); PlayerInputMappings.AddAxisOverride(FInputAxisKeyMapping(FName(TEXT("MoveForward")), EKeys::MouseY), 0, false); PlayerInputMappings.AddAxisOverride(FInputAxisKeyMapping(FName(TEXT("MoveForward")), EKeys::W), 0, false); const FInputAxisKeyMapping Axis = PlayerInputMappings.BuildMergedMappingLayout().GetAxis(0, FName(TEXT("MoveForward")), 1.f); TestEqual(TEXT("Latest override must be current"), Axis.Key, EKeys::W); return true; } /** * Check that Multiple Bindings Per Key set to false correctly unbinds previous mappings */ IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMultipleBindingsPerKeyDisallowTest, "AutoSettings.Input.MultipleBindingsPerKey.Disallow", EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::SmokeFilter) bool FMultipleBindingsPerKeyDisallowTest::RunTest(const FString& Parameters) { UAutoSettingsInputConfig* Config = NewObject(); ensure(Config->AllowMultipleBindingsPerKey == false); FPlayerInputMappings PlayerInputMappings = FPlayerInputMappings(true, Config); // Bind two actions to the same key PlayerInputMappings.AddActionOverride(FInputActionKeyMapping(FName(TEXT("FireWeapon")), EKeys::LeftMouseButton), 0, false); PlayerInputMappings.AddActionOverride(FInputActionKeyMapping(FName(TEXT("Jump")), EKeys::LeftMouseButton), 0, false); // Check second action is now bound const FInputActionKeyMapping JumpAction = PlayerInputMappings.BuildMergedMappingLayout().GetAction(0, FName(TEXT("Jump"))); TestEqual(TEXT("Second action must be bound"), JumpAction.Key, EKeys::LeftMouseButton); // Check first action is now unbound const FInputActionKeyMapping FireWeaponAction = PlayerInputMappings.BuildMergedMappingLayout().GetAction(0, FName(TEXT("FireWeapon"))); TestEqual(TEXT("First action must be unbound by the second"), FireWeaponAction.Key, EKeys::Invalid); return true; } /** * Check that Multiple Bindings Per Key set to true correctly preserves previous mappings */ IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMultipleBindingsPerKeyAllowTest, "AutoSettings.Input.MultipleBindingsPerKey.Allow", EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::SmokeFilter) bool FMultipleBindingsPerKeyAllowTest::RunTest(const FString& Parameters) { UAutoSettingsInputConfig* Config = NewObject(); Config->AllowMultipleBindingsPerKey = true; FPlayerInputMappings PlayerInputMappings = FPlayerInputMappings(true, Config); // Bind two actions to the same key PlayerInputMappings.AddActionOverride(FInputActionKeyMapping(FName(TEXT("FireWeapon")), EKeys::LeftMouseButton), 0, false); PlayerInputMappings.AddActionOverride(FInputActionKeyMapping(FName(TEXT("Jump")), EKeys::LeftMouseButton), 0, false); // Check second action is now bound const FInputActionKeyMapping JumpAction = PlayerInputMappings.BuildMergedMappingLayout().GetAction(0, FName(TEXT("Jump"))); TestEqual(TEXT("Second action must be bound"), JumpAction.Key, EKeys::LeftMouseButton); // Check first action is still bound const FInputActionKeyMapping FireWeaponAction = PlayerInputMappings.BuildMergedMappingLayout().GetAction(0, FName(TEXT("FireWeapon"))); TestEqual(TEXT("First action must still be bound"), FireWeaponAction.Key, EKeys::LeftMouseButton); return true; } /** * Check that linked mapping groups functions correctly by unbinding mappings on linked groups, but not on other groups */ IMPLEMENT_SIMPLE_AUTOMATION_TEST(FLinkedMappingGroupsTest, "AutoSettings.Input.LinkedMappingGroups", EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::SmokeFilter) bool FLinkedMappingGroupsTest::RunTest(const FString& Parameters) { UAutoSettingsInputConfig* Config = NewObject(); Config->AllowMultipleBindingsPerKey = true; Config->MappingGroupLinks.Add(FMappingGroupLink({ 0, 1 })); FPlayerInputMappings PlayerInputMappings = FPlayerInputMappings(true, Config); // Bind two actions on linked groups to the same key PlayerInputMappings.AddActionOverride(FInputActionKeyMapping(FName(TEXT("FireWeapon")), EKeys::LeftMouseButton), 0, false); PlayerInputMappings.AddActionOverride(FInputActionKeyMapping(FName(TEXT("Jump")), EKeys::LeftMouseButton), 1, false); // Bind another action on an unlinked group PlayerInputMappings.AddActionOverride(FInputActionKeyMapping(FName(TEXT("Crouch")), EKeys::LeftMouseButton), 2, false); // Check second action is now bound const FInputActionKeyMapping JumpAction = PlayerInputMappings.BuildMergedMappingLayout().GetAction(1, FName(TEXT("Jump"))); TestEqual(TEXT("Second action must be bound"), JumpAction.Key, EKeys::LeftMouseButton); // Check first action is unbound const FInputActionKeyMapping FireWeaponAction = PlayerInputMappings.BuildMergedMappingLayout().GetAction(0, FName(TEXT("FireWeapon"))); TestEqual(TEXT("First action must be unbound by the second"), FireWeaponAction.Key, EKeys::Invalid); // Check third action is bound const FInputActionKeyMapping CrouchAction = PlayerInputMappings.BuildMergedMappingLayout().GetAction(2, FName(TEXT("Crouch"))); TestEqual(TEXT("Third action must be bound"), CrouchAction.Key, EKeys::LeftMouseButton); return true; } /** * Check that deprecated mapping properties can be migrated */ IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMigrateDeprecatedPropertiesTest, "AutoSettings.Input.MigrateDeprecatedProperties", EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::SmokeFilter) bool FMigrateDeprecatedPropertiesTest::RunTest(const FString& Parameters) { UAutoSettingsInputConfig* Config = NewObject(); // Add a preset to config Config->InputPresets.Add(FInputMappingPreset(FGameplayTag::EmptyTag, false, Config)); FInputMappingPreset& Preset = Config->InputPresets.Last(); Preset.InputLayout.MappingGroups.Add(FInputMappingGroup(Config)); FInputMappingGroup& MappingGroup = Preset.InputLayout.MappingGroups.Last(); // Add a preset mapping MappingGroup.ActionMappings.Add(FConfigActionKeyMapping(FInputActionKeyMapping(TEXT("Jump"), EKeys::SpaceBar))); // Create raw PlayerInputMappings as if it were an old version loaded from config FPlayerInputMappings PlayerInputMappings = FPlayerInputMappings(); PlayerInputMappings.PlayerIndex_DEPRECATED = 1; PlayerInputMappings.BasePresetTag = FGameplayTag::EmptyTag; PlayerInputMappings.Preset_DEPRECATED.MappingGroups_DEPRECATED.AddDefaulted(); // Add some mappings PlayerInputMappings.Preset_DEPRECATED.MappingGroups_DEPRECATED[0].ActionMappings.Add(FConfigActionKeyMapping(FInputActionKeyMapping(TEXT("Jump"), EKeys::SpaceBar))); PlayerInputMappings.Preset_DEPRECATED.MappingGroups_DEPRECATED[0].ActionMappings.Add(FConfigActionKeyMapping(FInputActionKeyMapping(TEXT("FireWeapon"), EKeys::LeftMouseButton))); // Migrate PlayerInputMappings.SetConfig(Config); PlayerInputMappings.MigrateDeprecatedProperties(); // Jump override should now be invalid, because it already exists on the preset FInputActionKeyMapping JumpAction = PlayerInputMappings.MappingOverrides.GetAction(0, TEXT("Jump")); TestEqual(TEXT("Jump key"), JumpAction.Key, EKeys::Invalid); // Fire weapon should be bound to left mouse button FInputActionKeyMapping FireWeaponAction = PlayerInputMappings.MappingOverrides.GetAction(0, TEXT("FireWeapon")); TestEqual(TEXT("Fire weapon key"), FireWeaponAction.Key, EKeys::LeftMouseButton); return true; } /** * Check preserved actions are respected */ IMPLEMENT_SIMPLE_AUTOMATION_TEST(FPreservedActionsTest, "AutoSettings.Input.PreservedActions", EAutomationTestFlags_ApplicationContextMask | EAutomationTestFlags::SmokeFilter) bool FPreservedActionsTest::RunTest(const FString& Parameters) { UAutoSettingsInputConfig* Config = NewObject(); // Add a preset to config Config->InputPresets.Add(FInputMappingPreset(FGameplayTag::EmptyTag, false, Config)); FInputMappingPreset& Preset = Config->InputPresets.Last(); Preset.InputLayout.MappingGroups.Add(FInputMappingGroup(Config)); FInputMappingGroup& MappingGroup = Preset.InputLayout.MappingGroups.Last(); // Add a preset mapping MappingGroup.ActionMappings.Add(FConfigActionKeyMapping(FInputActionKeyMapping(TEXT("UIAction"), EKeys::Escape))); MappingGroup.ActionMappings.Add(FConfigActionKeyMapping(FInputActionKeyMapping(TEXT("Jump"), EKeys::SpaceBar))); // Add the UI action as a preserved action Config->PreservedActions.Add(TEXT("UIAction")); FPlayerInputMappings PlayerInputMappings = FPlayerInputMappings(false, Config); // Test UIAction is initially bound to escape const FInputActionKeyMapping UIActionBefore = PlayerInputMappings.BuildMergedMappingLayout().GetAction(0, FName(TEXT("UIAction"))); TestEqual(TEXT("UI action must be bound before rebinding"), UIActionBefore.Key, EKeys::Escape); // Rebind Jump to escape PlayerInputMappings.AddActionOverride(FInputActionKeyMapping(FName(TEXT("Jump")), EKeys::Escape), 0, false); // Test UIAction is still bound to escape const FInputActionKeyMapping UIActionAfter = PlayerInputMappings.BuildMergedMappingLayout().GetAction(0, FName(TEXT("UIAction"))); TestEqual(TEXT("UI action must be bound after rebinding"), UIActionAfter.Key, EKeys::Escape); return true; } #endif