From 8153ec07e72d8c355234e859b31e3baad363edcd Mon Sep 17 00:00:00 2001 From: Minimata Date: Sun, 8 Mar 2026 09:44:02 +0100 Subject: [PATCH] Revert "removing tests because they might break the solution" This reverts commit 3a21f00528c5ceec337b67fea9c0e04f159683d3. --- Movement tests.csproj | 1 + tests/components/DamageComponentUnitTest.cs | 101 ++++++++++++ .../components/DamageComponentUnitTest.cs.uid | 1 + tests/components/HealthComponentUnitTest.cs | 55 +++++++ .../components/HealthComponentUnitTest.cs.uid | 1 + .../components/KnockbackComponentUnitTest.cs | 32 ++++ .../KnockbackComponentUnitTest.cs.uid | 1 + tests/components/MovementSystemUnitTest.cs | 32 ++++ .../components/MovementSystemUnitTest.cs.uid | 1 + tests/enemies/EnemyUnitTest.cs | 30 ++++ tests/enemies/EnemyUnitTest.cs.uid | 1 + tests/player/DashSystemUnitTest.cs | 55 +++++++ tests/player/DashSystemUnitTest.cs.uid | 1 + tests/player/HeadSystemUnitTest.cs | 91 +++++++++++ tests/player/HeadSystemUnitTest.cs.uid | 1 + tests/player/PlayerControllerUnitTest.cs | 149 ++++++++++++++++++ tests/player/PlayerControllerUnitTest.cs.uid | 1 + tests/player/WeaponSystemUnitTest.cs | 51 ++++++ tests/player/WeaponSystemUnitTest.cs.uid | 1 + .../interactions/PlayerInteractionsTest.cs | 50 ++++++ .../PlayerInteractionsTest.cs.uid | 1 + .../player_interactions_scene.tscn | 35 ++++ tests/player/movement/PlayerMovementTest.cs | 109 +++++++++++++ .../player/movement/PlayerMovementTest.cs.uid | 1 + .../movement/player_movement_scene.tscn | 35 ++++ 25 files changed, 837 insertions(+) create mode 100644 tests/components/DamageComponentUnitTest.cs create mode 100644 tests/components/DamageComponentUnitTest.cs.uid create mode 100644 tests/components/HealthComponentUnitTest.cs create mode 100644 tests/components/HealthComponentUnitTest.cs.uid create mode 100644 tests/components/KnockbackComponentUnitTest.cs create mode 100644 tests/components/KnockbackComponentUnitTest.cs.uid create mode 100644 tests/components/MovementSystemUnitTest.cs create mode 100644 tests/components/MovementSystemUnitTest.cs.uid create mode 100644 tests/enemies/EnemyUnitTest.cs create mode 100644 tests/enemies/EnemyUnitTest.cs.uid create mode 100644 tests/player/DashSystemUnitTest.cs create mode 100644 tests/player/DashSystemUnitTest.cs.uid create mode 100644 tests/player/HeadSystemUnitTest.cs create mode 100644 tests/player/HeadSystemUnitTest.cs.uid create mode 100644 tests/player/PlayerControllerUnitTest.cs create mode 100644 tests/player/PlayerControllerUnitTest.cs.uid create mode 100644 tests/player/WeaponSystemUnitTest.cs create mode 100644 tests/player/WeaponSystemUnitTest.cs.uid create mode 100644 tests/player/interactions/PlayerInteractionsTest.cs create mode 100644 tests/player/interactions/PlayerInteractionsTest.cs.uid create mode 100644 tests/player/interactions/player_interactions_scene.tscn create mode 100644 tests/player/movement/PlayerMovementTest.cs create mode 100644 tests/player/movement/PlayerMovementTest.cs.uid create mode 100644 tests/player/movement/player_movement_scene.tscn diff --git a/Movement tests.csproj b/Movement tests.csproj index 11a9afa1..6d6d122a 100644 --- a/Movement tests.csproj +++ b/Movement tests.csproj @@ -125,6 +125,7 @@ + diff --git a/tests/components/DamageComponentUnitTest.cs b/tests/components/DamageComponentUnitTest.cs new file mode 100644 index 00000000..984a78c0 --- /dev/null +++ b/tests/components/DamageComponentUnitTest.cs @@ -0,0 +1,101 @@ +using Godot; +using GdUnit4; +using static GdUnit4.Assertions; +using Movementtests.interfaces; +using Movementtests.systems.damage; + +namespace Movementtests.tests; + +[TestSuite, RequireGodotRuntime] +public class DamageComponentUnitTest +{ + [TestCase] + public void DamageModifierAppliesWhenTypeMatches() + { + var input = new DamageRecord(Vector3.Zero, new RDamage(10.0f, EDamageTypes.Normal)); + var modifier = new RDamageModifier(EDamageTypes.Normal, 2.0f); + var signalTriggered = false; + modifier.DamageTaken += (_, _) => signalTriggered = true; + + var result = modifier.TakeDamage(input); + + AssertFloat(result.Damage.DamageDealt).IsEqual(20.0f); + AssertBool(signalTriggered).IsTrue(); + } + + [TestCase] + public void DamageModifierIgnoresWhenTypeDifferent() + { + var input = new DamageRecord(Vector3.Zero, new RDamage(10.0f, EDamageTypes.Normal)); + var modifier = new RDamageModifier(EDamageTypes.Fire, 3.0f); + var signalTriggered = false; + modifier.DamageTaken += (_, _) => signalTriggered = true; + + var result = modifier.TakeDamage(input); + + AssertFloat(result.Damage.DamageDealt).IsEqual(0.0f); + AssertBool(signalTriggered).IsFalse(); // No damage actually taken + } + + [TestCase] + public void DamageableSumsAllModifiers() + { + var mod1 = new RDamageModifier(EDamageTypes.Normal, 1.0f); + var mod2 = new RDamageModifier(EDamageTypes.Normal, 0.5f); + + var damageable = new CDamageable(); + damageable.DamageModifiers = new[] { mod1, mod2 }; + var signalTriggered = false; + damageable.DamageTaken += (_, _) => signalTriggered = true; + + var input = new DamageRecord(Vector3.Zero, new RDamage(10.0f, EDamageTypes.Normal)); + var result = damageable.TakeDamage(input); + + AssertFloat(result.Damage.DamageDealt).IsEqual(15.0f); + AssertBool(signalTriggered).IsTrue(); + } + + [TestCase] + public void ComputeDamageModifierAppliesWhenTypeMatches() + { + var input = new DamageRecord(Vector3.Zero, new RDamage(10.0f, EDamageTypes.Normal)); + var modifier = new RDamageModifier(EDamageTypes.Normal, 2.0f); + var signalTriggered = false; + modifier.DamageTaken += (_, _) => signalTriggered = true; + + var result = modifier.ComputeDamage(input); + + AssertFloat(result.Damage.DamageDealt).IsEqual(20.0f); + AssertBool(signalTriggered).IsFalse(); + } + [TestCase] + public void ComputeDamageModifierIgnoresWhenTypeDifferent() + { + var input = new DamageRecord(Vector3.Zero, new RDamage(10.0f, EDamageTypes.Normal)); + var modifier = new RDamageModifier(EDamageTypes.Fire, 3.0f); + var signalTriggered = false; + modifier.DamageTaken += (_, _) => signalTriggered = true; + + var result = modifier.ComputeDamage(input); + + AssertFloat(result.Damage.DamageDealt).IsEqual(0.0f); + AssertBool(signalTriggered).IsFalse(); + } + [TestCase] + public void ComputeDamageableSumsAllModifiers() + { + var mod1 = new RDamageModifier(EDamageTypes.Normal, 1.0f); + var mod2 = new RDamageModifier(EDamageTypes.Normal, 0.5f); + + var cDamageable = new CDamageable(); + cDamageable.DamageModifiers = new[] { mod1, mod2 }; + var signalTriggered = false; + cDamageable.DamageTaken += (_, _) => signalTriggered = true; + + var input = new DamageRecord(Vector3.Zero, new RDamage(10.0f, EDamageTypes.Normal)); + var result = cDamageable.ComputeDamage(input); + + AssertFloat(result.Damage.DamageDealt).IsEqual(15.0f); + AssertBool(signalTriggered).IsFalse(); + } +} diff --git a/tests/components/DamageComponentUnitTest.cs.uid b/tests/components/DamageComponentUnitTest.cs.uid new file mode 100644 index 00000000..1351834c --- /dev/null +++ b/tests/components/DamageComponentUnitTest.cs.uid @@ -0,0 +1 @@ +uid://db6rva7uccppc diff --git a/tests/components/HealthComponentUnitTest.cs b/tests/components/HealthComponentUnitTest.cs new file mode 100644 index 00000000..63d1cb0e --- /dev/null +++ b/tests/components/HealthComponentUnitTest.cs @@ -0,0 +1,55 @@ +using Godot; +using GdUnit4; +using static GdUnit4.Assertions; +using Movementtests.interfaces; +using Movementtests.systems.damage; + +namespace Movementtests.tests; + +[TestSuite, RequireGodotRuntime] +public class HealthComponentUnitTest +{ + [TestCase] + public void ReadyInitializesCurrentHealth() + { + var cHealth = new CHealth(); + cHealth.RHealth = new RHealth(150.0f); + cHealth._Ready(); + AssertFloat(cHealth.CurrentHealth).IsEqual(150.0f); + } + + [TestCase] + public void ReduceHealthDecreasesAndDoesNotDeplete() + { + var cHealth = new CHealth(); + cHealth.RHealth = new RHealth(100.0f); + cHealth.CurrentHealth = 100.0f; + + var damage = new DamageRecord(Vector3.Zero, new RDamage(25.0f, EDamageTypes.Normal)); + var record = cHealth.ReduceHealth(source: null!, damageRecord: damage); + + AssertFloat(cHealth.CurrentHealth).IsEqual(75.0f); + AssertFloat(record.CurrentHealth).IsEqual(75.0f); + AssertFloat(record.PreviousHealth).IsEqual(100.0f); + AssertFloat(record.MaxHealth).IsEqual(100.0f); + } + + [TestCase] + public void ReduceHealthTriggersDepletionToZero() + { + var cHealth = new CHealth(); + cHealth.RHealth = new RHealth(50.0f); + cHealth.CurrentHealth = 50.0f; + + bool depleted = false; + cHealth.HealthDepleted += _ => depleted = true; + + var damage = new DamageRecord(Vector3.Zero, new RDamage(100.0f, EDamageTypes.Normal)); + var record = cHealth.ReduceHealth(source: null!, damageRecord: damage); + + AssertBool(depleted).IsTrue(); + AssertFloat(cHealth.CurrentHealth).IsEqual(0.0f); + AssertFloat(record.CurrentHealth).IsEqual(-50.0f); + AssertFloat(record.MaxHealth).IsEqual(50.0f); + } +} diff --git a/tests/components/HealthComponentUnitTest.cs.uid b/tests/components/HealthComponentUnitTest.cs.uid new file mode 100644 index 00000000..00cee7a1 --- /dev/null +++ b/tests/components/HealthComponentUnitTest.cs.uid @@ -0,0 +1 @@ +uid://bd52i51hncgmf diff --git a/tests/components/KnockbackComponentUnitTest.cs b/tests/components/KnockbackComponentUnitTest.cs new file mode 100644 index 00000000..605d7f75 --- /dev/null +++ b/tests/components/KnockbackComponentUnitTest.cs @@ -0,0 +1,32 @@ +using Godot; +using GdUnit4; +using static GdUnit4.Assertions; +using Movementtests.interfaces; +using Movementtests.systems.damage; + +namespace Movementtests.tests; + +[TestSuite, RequireGodotRuntime] +public class KnockbackComponentUnitTest +{ + [TestCase] + public void RegisterAndComputeKnockback() + { + var cKnock = new CKnockback(); + cKnock.RKnockback = new RKnockback(2.0f); + cKnock.GlobalPosition = Vector3.Zero; + + var damage = new DamageRecord(new Vector3(10, 0, 0), new RDamage(0, EDamageTypes.Normal)); + var record = new KnockbackRecord(damage, 1.5f); + + cKnock.RegisterKnockback(record); + var force = cKnock.ComputeKnockback(); + + // Direction from source(10,0,0) to target(0,0,0) is (-1,0,0), scaled by modifier(2) and multiplier(1.5) => (-3,0,0) + AssertVector(force).IsEqual(new Vector3(-3, 0, 0)); + + // Second call returns zero since internal state resets + var second = cKnock.ComputeKnockback(); + AssertVector(second).IsEqual(Vector3.Zero); + } +} diff --git a/tests/components/KnockbackComponentUnitTest.cs.uid b/tests/components/KnockbackComponentUnitTest.cs.uid new file mode 100644 index 00000000..c5795539 --- /dev/null +++ b/tests/components/KnockbackComponentUnitTest.cs.uid @@ -0,0 +1 @@ +uid://bv0eionbgbig5 diff --git a/tests/components/MovementSystemUnitTest.cs b/tests/components/MovementSystemUnitTest.cs new file mode 100644 index 00000000..4e075eef --- /dev/null +++ b/tests/components/MovementSystemUnitTest.cs @@ -0,0 +1,32 @@ +using Godot; +using GdUnit4; +using static GdUnit4.Assertions; +using Movementtests.interfaces; +using Movementtests.scenes.movement; + +namespace Movementtests.tests; + +[TestSuite, RequireGodotRuntime] +public class MovementSystemUnitTest +{ + [TestCase] + public void GroundedMovementAcceleratesAndAppliesGravity() + { + var move = new CGroundedMovement(); + move.RMovement = new RMovement(speed: 10.0f, acceleration: 1.0f, gravityModifier: 0.5f, targetHeight: 0.0f); + move.WallInFrontRayCast = new RayCast3D(); + //move.GlobalPosition = Vector3.Zero; + + var inputs = new MovementInputs( + Velocity: Vector3.Zero, + TargetLocation: new Vector3(10, 0, 0), + isOnFloor: false, + gravity: Vector3.Down * 9.8f, + delta: 1.0 + ); + + var v = move.ComputeVelocity(inputs); + + AssertVector(v).IsEqualApprox(new Vector3(10, -4.9f, 0), new Vector3(0.001f, 0.001f, 0.001f)); + } +} diff --git a/tests/components/MovementSystemUnitTest.cs.uid b/tests/components/MovementSystemUnitTest.cs.uid new file mode 100644 index 00000000..83493b96 --- /dev/null +++ b/tests/components/MovementSystemUnitTest.cs.uid @@ -0,0 +1 @@ +uid://cofj5s4x74ay diff --git a/tests/enemies/EnemyUnitTest.cs b/tests/enemies/EnemyUnitTest.cs new file mode 100644 index 00000000..cbd73410 --- /dev/null +++ b/tests/enemies/EnemyUnitTest.cs @@ -0,0 +1,30 @@ +using Godot; +using GdUnit4; +using static GdUnit4.Assertions; +using Movementtests.interfaces; +using Movementtests.systems.damage; + +namespace Movementtests.tests; + +[TestSuite, RequireGodotRuntime] +public class EnemyUnitTest +{ + [TestCase] + public void ComputeDamageNoComponent() + { + var enemy = new Enemy(); + var input = new DamageRecord(Vector3.Zero, new RDamage(10.0f, EDamageTypes.Normal)); + + var result = enemy.ComputeDamage(input); + AssertFloat(result.Damage.DamageDealt).IsEqual(0.0f); + } + + [TestCase] + public void Unstun() + { + var enemy = new Enemy(); + enemy.IsStunned = true; + enemy.Unstun(); + AssertBool(enemy.IsStunned).IsFalse(); + } +} diff --git a/tests/enemies/EnemyUnitTest.cs.uid b/tests/enemies/EnemyUnitTest.cs.uid new file mode 100644 index 00000000..1c82d5ec --- /dev/null +++ b/tests/enemies/EnemyUnitTest.cs.uid @@ -0,0 +1 @@ +uid://cojxgcs6xqqoq diff --git a/tests/player/DashSystemUnitTest.cs b/tests/player/DashSystemUnitTest.cs new file mode 100644 index 00000000..1da71c91 --- /dev/null +++ b/tests/player/DashSystemUnitTest.cs @@ -0,0 +1,55 @@ +using Godot; +using GdUnit4; +using static GdUnit4.Assertions; +using Movementtests.systems; + +namespace Movementtests.tests; + +[TestSuite, RequireGodotRuntime] +public class DashSystemUnitTest +{ + private DashSystem _dashSystem; + + [BeforeTest] + public void SetupTest() + { + _dashSystem = new DashSystem(); + + _dashSystem.DashCast3D = new ShapeCast3D(); + _dashSystem.AddChild(_dashSystem.DashCast3D); + + _dashSystem.DashCastDrop = new ShapeCast3D(); + _dashSystem.AddChild(_dashSystem.DashCastDrop); + + _dashSystem.DashTarget = new MeshInstance3D(); + _dashSystem.AddChild(_dashSystem.DashTarget); + + _dashSystem.DashDropIndicator = new MeshInstance3D(); + _dashSystem.AddChild(_dashSystem.DashDropIndicator); + + _dashSystem.DashDropLocationIndicator = new MeshInstance3D(); + _dashSystem.AddChild(_dashSystem.DashDropLocationIndicator); + } + + [AfterTest] + public void CleanupTest() + { + _dashSystem?.Free(); + } + + [TestCase] + public void TestStopPreparingDash() + { + _dashSystem.CanDashThroughTarget = true; + _dashSystem.DashTarget.Visible = true; + _dashSystem.DashDropIndicator.Visible = true; + _dashSystem.DashDropLocationIndicator.Visible = true; + + _dashSystem.StopPreparingDash(); + + AssertBool(_dashSystem.CanDashThroughTarget).IsFalse(); + AssertBool(_dashSystem.DashTarget.Visible).IsFalse(); + AssertBool(_dashSystem.DashDropIndicator.Visible).IsFalse(); + AssertBool(_dashSystem.DashDropLocationIndicator.Visible).IsFalse(); + } +} diff --git a/tests/player/DashSystemUnitTest.cs.uid b/tests/player/DashSystemUnitTest.cs.uid new file mode 100644 index 00000000..545620a4 --- /dev/null +++ b/tests/player/DashSystemUnitTest.cs.uid @@ -0,0 +1 @@ +uid://pv570go4cxws diff --git a/tests/player/HeadSystemUnitTest.cs b/tests/player/HeadSystemUnitTest.cs new file mode 100644 index 00000000..07d06790 --- /dev/null +++ b/tests/player/HeadSystemUnitTest.cs @@ -0,0 +1,91 @@ +using Godot; +using GdUnit4; +using static GdUnit4.Assertions; +using Movementtests.systems; + +namespace Movementtests.tests; + +[TestSuite, RequireGodotRuntime] +public class HeadSystemUnitTest +{ + private HeadSystem _head; + + [BeforeTest] + public void SetupTest() + { + _head = new HeadSystem(); + _head._camera = new Camera3D(); + _head.AddChild(_head._camera); + + _head._cameraAnchor = new Marker3D(); + _head.AddChild(_head._cameraAnchor); + + _head._fpRig = new Node3D(); + _head.AddChild(_head._fpRig); + + _head._fpDisplacedRig = new Node3D(); + _head.AddChild(_head._fpDisplacedRig); + } + + [AfterTest] + public void CleanupTest() + { + _head?.Free(); + } + + [TestCase] + public void TestResetHeadBobbing() + { + _head._bobbingAccumulator = 10.0f; + _head.ResetHeadBobbing(); + AssertFloat(_head._bobbingAccumulator).IsEqual(0.0f); + } + + [TestCase] + public void TestComputeHowMuchInputForward() + { + Vector3 forwardInput = new Vector3(0, 0, -1); + AssertFloat(_head.ComputeHowMuchInputForward(forwardInput)).IsEqual(1.0f); + + Vector3 backwardInput = new Vector3(0, 0, 1); + AssertFloat(_head.ComputeHowMuchInputForward(backwardInput)).IsEqual(-1.0f); + } + + [TestCase] + public void TestComputeHowMuchInputSideways() + { + Vector3 rightInput = new Vector3(1, 0, 0); + AssertFloat(_head.ComputeHowMuchInputSideways(rightInput)).IsEqual(1.0f); + + Vector3 leftInput = new Vector3(-1, 0, 0); + AssertFloat(_head.ComputeHowMuchInputSideways(leftInput)).IsEqual(-1.0f); + } + + [TestCase] + public void TestGetForwardHorizontalVector() + { + Vector3 forward = _head.GetForwardHorizontalVector(); + AssertVector(forward).IsEqualApprox(Vector3.Back, new Vector3(0.001f, 0.001f, 0.001f)); + } + + [TestCase] + public void TestLookAroundRotation() + { + var inputs = new HeadSystem.CameraParameters( + Delta: 0.016, + LookDir: new Vector2(1, 0), + PlayerInput: Vector3.Zero, + PlayerVelocity: Vector3.Zero, + WallContactPoint: Vector3.Zero, + SensitivitMultiplier: 1.0f, + WithCameraJitter: false, + WithCameraBobbing: false, + BobbingMultiplier: 1.0f, + FovMultiplier: 1.0f + ); + + float initialY = _head.Rotation.Y; + _head.LookAround(inputs); + AssertFloat(_head.Rotation.Y).IsEqual(initialY + 1.0f); + } +} diff --git a/tests/player/HeadSystemUnitTest.cs.uid b/tests/player/HeadSystemUnitTest.cs.uid new file mode 100644 index 00000000..110c87c4 --- /dev/null +++ b/tests/player/HeadSystemUnitTest.cs.uid @@ -0,0 +1 @@ +uid://bp0xn8k3dmfkg diff --git a/tests/player/PlayerControllerUnitTest.cs b/tests/player/PlayerControllerUnitTest.cs new file mode 100644 index 00000000..c41fe8fc --- /dev/null +++ b/tests/player/PlayerControllerUnitTest.cs @@ -0,0 +1,149 @@ +using Godot; +using GdUnit4; +using static GdUnit4.Assertions; +using Movementtests.interfaces; +using Movementtests.systems.damage; + +namespace Movementtests.tests; + +[TestSuite, RequireGodotRuntime] +public class PlayerControllerUnitTest +{ + private PlayerController _player; + + [BeforeTest] + public void SetupTest() + { + _player = new PlayerController(); + _player.TargetSpeed = 7.0f; + _player.Gravity = 9.8f; + + var rHealth = new RHealth(100.0f); + _player.RHealth = rHealth; + _player.CHealth = new CHealth { RHealth = rHealth, CurrentHealth = 100.0f }; + } + + [AfterTest] + public void CleanupTest() + { + _player?.Free(); + } + + [TestCase] + public void TestCalculateGravityForce() + { + _player.Weight = 3.0f; + // gravity is 9.8f + AssertFloat(_player.CalculateGravityForce()).IsEqualApprox(29.4f, 0.001f); + } + + [TestCase] + public void TestIsPlayerInputtingForward() + { + // Test Keyboard Input + _player.InputDeviceChanged(false); + _player.OnInputMoveKeyboard(Vector3.Forward); + AssertBool(_player.IsPlayerInputtingForward()).IsTrue(); + + _player.OnInputMoveKeyboard(Vector3.Back); + AssertBool(_player.IsPlayerInputtingForward()).IsFalse(); + + // Test Gamepad Input + _player.InputDeviceChanged(true); + _player.OnInputMove(new Vector3(0, 0, -1)); + AssertBool(_player.IsPlayerInputtingForward()).IsTrue(); + } + + [TestCase] + public void TestSetVerticalVelocity() + { + _player.Velocity = new Vector3(1, 0, 2); + _player.SetVerticalVelocity(5.0f); + AssertVector(_player.Velocity).IsEqual(new Vector3(1, 5, 2)); + } + + [TestCase] + public void TestComputeHVelocityGround() + { + _player.Velocity = Vector3.Zero; + _player.AccelerationFloor = 10.0f; + + float delta = 0.1f; + Vector3 newVelocity = _player.ComputeHVelocity(delta, _player.AccelerationFloor, _player.DecelerationFloor, Vector3.Forward); + AssertVector(newVelocity).IsEqual(new Vector3(0, 0, -7.0f)); + } + + [TestCase] + public void TestComputeHVelocityAir() + { + _player.Velocity = new Vector3(5, 0, 0); + _player.AccelerationAir = 2.0f; + _player.DecelerationAir = 2.0f; + + float delta = 0.5f; + Vector3 newVelocity = _player.ComputeHVelocity(delta, _player.AccelerationAir, _player.DecelerationAir, Vector3.Zero); + + AssertVector(newVelocity).IsEqual(Vector3.Zero); + } + + [TestCase] + public void TestReduceHealth() + { + var damageRecord = new DamageRecord(Vector3.Zero, new RDamage(25.0f, EDamageTypes.Normal)); + _player.ReduceHealth(_player, damageRecord); + AssertFloat(_player.CHealth.CurrentHealth).IsEqual(75.0f); + } + + [TestCase] + public void TestEmpoweredActionsLeft() + { + var mockUi = new PlayerUi(); + var dashIcons = new TextureRect[3] { new TextureRect(), new TextureRect(), new TextureRect() }; + mockUi._dashIcons = dashIcons; + + _player.PlayerUi = mockUi; + + _player.EmpoweredActionsLeft = 2; + AssertInt(_player.EmpoweredActionsLeft).IsEqual(2); + AssertBool(dashIcons[0].Visible).IsTrue(); + AssertBool(dashIcons[1].Visible).IsTrue(); + AssertBool(dashIcons[2].Visible).IsFalse(); + } + + [TestCase] + public void TestDashCooldownTimeout() + { + _player.CanDash = false; + _player.DashCooldownTimeout(); + AssertBool(_player.CanDash).IsTrue(); + } + + [TestCase] + public void TestGetInputLocalHDirection() + { + _player.InputDeviceChanged(false); + _player.OnInputMoveKeyboard(new Vector3(1, 0, 1)); + + Vector3 expected = new Vector3(1, 0, 1).Normalized(); + AssertVector(_player.GetInputLocalHDirection()).IsEqualApprox(expected, new Vector3(0.001f, 0.001f, 0.001f)); + } + + [TestCase] + public void TestComputeKnockback() + { + var cKnockback = new CKnockback(); + cKnockback.RKnockback = new RKnockback(10.0f); + _player.CKnockback = cKnockback; + + var damageRecord = new DamageRecord(new Vector3(10, 0, 0), new RDamage(0, EDamageTypes.Normal)); + var knockbackRecord = new KnockbackRecord(damageRecord, 1.0f); + + _player.GlobalPosition = Vector3.Zero; + cKnockback.GlobalPosition = Vector3.Zero; + + _player.RegisterKnockback(knockbackRecord); + + Vector3 knockback = cKnockback.ComputeKnockback(); + AssertVector(knockback).IsEqual(new Vector3(-10, 0, 0)); + } +} diff --git a/tests/player/PlayerControllerUnitTest.cs.uid b/tests/player/PlayerControllerUnitTest.cs.uid new file mode 100644 index 00000000..16a906a1 --- /dev/null +++ b/tests/player/PlayerControllerUnitTest.cs.uid @@ -0,0 +1 @@ +uid://kmphtu0ovixi diff --git a/tests/player/WeaponSystemUnitTest.cs b/tests/player/WeaponSystemUnitTest.cs new file mode 100644 index 00000000..cd575190 --- /dev/null +++ b/tests/player/WeaponSystemUnitTest.cs @@ -0,0 +1,51 @@ +using Godot; +using GdUnit4; +using static GdUnit4.Assertions; +using Movementtests.systems; +using Movementtests.systems.damage; + +namespace Movementtests.tests; + +[TestSuite, RequireGodotRuntime] +public class WeaponSystemUnitTest +{ + private WeaponSystem _weapon; + + [BeforeTest] + public void SetupTest() + { + _weapon = new WeaponSystem(); + _weapon.RDamage = new RDamage(5.0f, EDamageTypes.Normal); + + _weapon.WeaponMesh = new MeshInstance3D(); + _weapon.AddChild(_weapon.WeaponMesh); + _weapon.WeaponLocationIndicator = new MeshInstance3D(); + _weapon.AddChild(_weapon.WeaponLocationIndicator); + } + + [AfterTest] + public void CleanupTest() + { + _weapon?.Free(); + } + + [TestCase] + public void TestWeaponLeftAndBackVisibility() + { + _weapon.Visible = false; + + _weapon.WeaponLeft(); + AssertBool(_weapon.Visible).IsTrue(); + + _weapon.WeaponBack(); + AssertBool(_weapon.Visible).IsFalse(); + } + + [TestCase] + public void TestThrowWeaponOnCurveSetsUnfrozen() + { + _weapon.Freeze = true; + _weapon.ThrowWeaponOnCurve(); + AssertBool(_weapon.Freeze).IsFalse(); + } +} diff --git a/tests/player/WeaponSystemUnitTest.cs.uid b/tests/player/WeaponSystemUnitTest.cs.uid new file mode 100644 index 00000000..487b0dea --- /dev/null +++ b/tests/player/WeaponSystemUnitTest.cs.uid @@ -0,0 +1 @@ +uid://vkv8aderakcb diff --git a/tests/player/interactions/PlayerInteractionsTest.cs b/tests/player/interactions/PlayerInteractionsTest.cs new file mode 100644 index 00000000..fcd83f87 --- /dev/null +++ b/tests/player/interactions/PlayerInteractionsTest.cs @@ -0,0 +1,50 @@ +using System.Threading.Tasks; +using Godot; +using GodotStateCharts; + +namespace Movementtests.tests; + +using GdUnit4; +using static GdUnit4.Assertions; + +[TestSuite, RequireGodotRuntime] +public class PlayerInteractionsTest +{ + private ISceneRunner _runner; + private Node _scene; + private PlayerController _player; + + private readonly float _tolerance = 0.01f; + private readonly Vector3 _vectorTolerance = new Vector3(0.01f, 0.01f, 0.01f); + + [BeforeTest] + public void SetupTest() + { + _runner = ISceneRunner.Load("res://tests/player/interactions/player_interactions_scene.tscn"); + + _scene = _runner.Scene()!; + var player = _scene.FindChild("Player") as PlayerController; + _player = player!; + } + [AfterTest] + public void CleanupTest() {} + + [TestCase("BaseLocation")] + public async Task PlayerMoveForward(string markerName) + { + var marker = _scene.FindChild(markerName) as Marker3D; + AssertObject(marker).IsNotNull(); + _player.GlobalPosition = marker!.GlobalPosition; + await _runner.AwaitIdleFrame(); + + var startPos = _player.GlobalPosition; + + _runner.SimulateKeyPress(Key.W); + await _runner.AwaitMillis(300); + _runner.SimulateKeyRelease(Key.W); + + var endPos = _player.GlobalPosition; + var direction = startPos.DirectionTo(endPos); + AssertVector(direction).IsEqualApprox(Vector3.Forward, _vectorTolerance); + } +} \ No newline at end of file diff --git a/tests/player/interactions/PlayerInteractionsTest.cs.uid b/tests/player/interactions/PlayerInteractionsTest.cs.uid new file mode 100644 index 00000000..75e43ca5 --- /dev/null +++ b/tests/player/interactions/PlayerInteractionsTest.cs.uid @@ -0,0 +1 @@ +uid://denedm5b8rmhh diff --git a/tests/player/interactions/player_interactions_scene.tscn b/tests/player/interactions/player_interactions_scene.tscn new file mode 100644 index 00000000..0a7887de --- /dev/null +++ b/tests/player/interactions/player_interactions_scene.tscn @@ -0,0 +1,35 @@ +[gd_scene format=3 uid="uid://l0lflvsjbyvs"] + +[ext_resource type="Material" uid="uid://31aulub2nqov" path="res://assets/materials/greybox/m_greybox.tres" id="1_dv0re"] +[ext_resource type="PackedScene" uid="uid://bei4nhkf8lwdo" path="res://scenes/player_controller/PlayerController.tscn" id="2_52d52"] + +[node name="PlayerMovementScene" type="Node3D" unique_id=231040688] + +[node name="CSGCombiner3D" type="CSGCombiner3D" parent="." unique_id=241909240] +use_collision = true +collision_layer = 256 +collision_mask = 65553 + +[node name="Ground" type="CSGBox3D" parent="CSGCombiner3D" unique_id=432200143] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, -0.5, -3.25) +use_collision = true +collision_layer = 256 +collision_mask = 65553 +size = Vector3(1000, 1, 1000) +material = ExtResource("1_dv0re") + +[node name="Ground2" type="CSGBox3D" parent="CSGCombiner3D" unique_id=854660236] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.75, 0.5, -1.75) +use_collision = true +collision_layer = 256 +collision_mask = 65553 +size = Vector3(1.5, 1, 1.5) +material = ExtResource("1_dv0re") + +[node name="Player" parent="." unique_id=709076448 instance=ExtResource("2_52d52")] +TutorialDone = true + +[node name="BaseLocation" type="Marker3D" parent="." unique_id=1793710692] + +[node name="MantleLocation1" type="Marker3D" parent="." unique_id=550080845] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.75, 0, 0) diff --git a/tests/player/movement/PlayerMovementTest.cs b/tests/player/movement/PlayerMovementTest.cs new file mode 100644 index 00000000..1ffbe76d --- /dev/null +++ b/tests/player/movement/PlayerMovementTest.cs @@ -0,0 +1,109 @@ +using System.Threading.Tasks; +using Godot; +using GodotStateCharts; + +namespace Movementtests.tests; + +using GdUnit4; +using static GdUnit4.Assertions; + +[TestSuite, RequireGodotRuntime] +public class PlayerMovementTest +{ + private ISceneRunner _runner; + private Node _scene; + private PlayerController _player; + + private readonly float _tolerance = 0.01f; + private readonly Vector3 _vectorTolerance = new Vector3(0.01f, 0.01f, 0.01f); + + [Before] + public void Setup() {} + [After] + public void Cleanup() {} + + [BeforeTest] + public void SetupTest() + { + _runner = ISceneRunner.Load("res://tests/player/movement/player_movement_scene.tscn"); + + _scene = _runner.Scene()!; + var player = _scene.FindChild("Player") as PlayerController; + _player = player!; + } + [AfterTest] + public void CleanupTest() {} + + [TestCase("BaseLocation")] + public async Task PlayerMoveForward(string markerName) + { + var marker = _scene.FindChild(markerName) as Marker3D; + AssertObject(marker).IsNotNull(); + _player.GlobalPosition = marker!.GlobalPosition; + await _runner.AwaitIdleFrame(); + + var startPos = _player.GlobalPosition; + + _runner.SimulateKeyPress(Key.W); + await _runner.AwaitMillis(100); + _runner.SimulateKeyRelease(Key.W); + + var endPos = _player.GlobalPosition; + var direction = startPos.DirectionTo(endPos); + AssertVector(direction).IsEqualApprox(Vector3.Forward, _vectorTolerance); + } + + [TestCase("BaseLocation")] + public async Task PlayerJump(string markerName) + { + var marker = _scene.FindChild(markerName) as Marker3D; + AssertObject(marker).IsNotNull(); + _player.GlobalPosition = marker!.GlobalPosition; + await _runner.AwaitIdleFrame(); + + var startPos = _player.GlobalPosition; + + _runner.SimulateKeyPress(Key.Space); + await _runner.AwaitIdleFrame(); + + var jumping = StateChartState.Of(_player.GetNode("StateChart/Root/Movement/Jump")); + AssertBool(jumping.Active).IsTrue(); + + _runner.SimulateKeyRelease(Key.Space); + await _runner.AwaitIdleFrame(); + var endPos = _player.GlobalPosition; + var direction = startPos.DirectionTo(endPos); + AssertVector(direction).IsEqualApprox(Vector3.Up, _vectorTolerance); + AssertVector(_player.Velocity.Normalized()).IsEqualApprox(Vector3.Up, _vectorTolerance); + + await _runner.AwaitMillis(600); + endPos = _player.GlobalPosition; + AssertVector(endPos - startPos).IsEqualApprox(Vector3.Zero, _vectorTolerance); + + var grounded = StateChartState.Of(_player.GetNode("StateChart/Root/Movement/Grounded")); + AssertBool(grounded.Active).IsTrue(); + } + + [TestCase("MantleLocation1")] + public async Task PlayerMantle(string markerName) + { + var marker = _scene.FindChild(markerName) as Marker3D; + AssertObject(marker).IsNotNull(); + _player.GlobalPosition = marker!.GlobalPosition; + await _runner.AwaitMillis(100); + + var startPos = _player.GlobalPosition; + + _runner.SimulateKeyPress(Key.Space); + await _runner.AwaitMillis(100); + var mantling = StateChartState.Of(_player.GetNode("StateChart/Root/Movement/Mantling")); + AssertBool(mantling.Active).IsTrue(); + + _runner.SimulateKeyRelease(Key.Space); + await _runner.AwaitMillis(500); + + var endPos = _player.GlobalPosition; + AssertFloat((endPos - startPos).Length()).IsGreater(_tolerance); + AssertFloat(endPos.Y).IsEqualApprox(1.0f, _tolerance); + } +} \ No newline at end of file diff --git a/tests/player/movement/PlayerMovementTest.cs.uid b/tests/player/movement/PlayerMovementTest.cs.uid new file mode 100644 index 00000000..a5061437 --- /dev/null +++ b/tests/player/movement/PlayerMovementTest.cs.uid @@ -0,0 +1 @@ +uid://x5pj2ymam2gg diff --git a/tests/player/movement/player_movement_scene.tscn b/tests/player/movement/player_movement_scene.tscn new file mode 100644 index 00000000..e40a34fc --- /dev/null +++ b/tests/player/movement/player_movement_scene.tscn @@ -0,0 +1,35 @@ +[gd_scene format=3 uid="uid://i8kb38q7bdfk"] + +[ext_resource type="Material" uid="uid://31aulub2nqov" path="res://assets/materials/greybox/m_greybox.tres" id="1_bdfhg"] +[ext_resource type="PackedScene" uid="uid://bei4nhkf8lwdo" path="res://scenes/player_controller/PlayerController.tscn" id="1_hg1sy"] + +[node name="PlayerMovementScene" type="Node3D" unique_id=231040688] + +[node name="CSGCombiner3D" type="CSGCombiner3D" parent="." unique_id=241909240] +use_collision = true +collision_layer = 256 +collision_mask = 65553 + +[node name="Ground" type="CSGBox3D" parent="CSGCombiner3D" unique_id=432200143] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, -0.5, -3.25) +use_collision = true +collision_layer = 256 +collision_mask = 65553 +size = Vector3(1000, 1, 1000) +material = ExtResource("1_bdfhg") + +[node name="Ground2" type="CSGBox3D" parent="CSGCombiner3D" unique_id=854660236] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.75, 0.5, -1.75) +use_collision = true +collision_layer = 256 +collision_mask = 65553 +size = Vector3(1.5, 1, 1.5) +material = ExtResource("1_bdfhg") + +[node name="Player" parent="." unique_id=709076448 instance=ExtResource("1_hg1sy")] +TutorialDone = true + +[node name="BaseLocation" type="Marker3D" parent="." unique_id=1793710692] + +[node name="MantleLocation1" type="Marker3D" parent="." unique_id=550080845] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.75, 0, 0)