-
-
Notifications
You must be signed in to change notification settings - Fork 1
Emit speed_changed signal; add tests #542
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
cc33c56
Emit speed_changed signal; add tests
ikostan bd9f03c
Update player.gd
ikostan 1590492
Test name and assertions are inconsistent (max_and_min but only max i…
ikostan d857fe3
Update player.gd
ikostan e0715d7
Update player.gd
ikostan aa9fdc8
Update test_player_movement_signals.gd
ikostan a861547
Add HUD, wire it up & refactor player/settings
ikostan f7d0a6c
Update game_settings_resource.gd
ikostan 56a18a8
Update main_scene.gd
ikostan 3237fd1
Refactor tests to use Globals settings & HUD
ikostan ce6864d
Update tests for Globals/HUD refactor
ikostan 7b8127c
Update test_helpers.gd
ikostan 5459961
Update player.gd
ikostan 625942a
Update test_player_movement_signals.gd
ikostan e765c1f
Extract GUT mock builder to helper
ikostan bdd2377
Update scripts/game_settings_resource.gd
ikostan 9944fd7
Update game_settings_resource.gd
ikostan 4553ccd
Update game_settings_resource.gd
ikostan a358b22
Update scripts/hud.gd
ikostan 4d2c69d
Use GameSettingsResource for the speed thresholds instead of duplicat…
ikostan c957693
Update hud.gd
ikostan afdb474
Fix HUD logging and connection guards; add tests
ikostan 2122a7a
Update globals.gd
ikostan 6195b8c
Update hud.gd
ikostan 7557229
Update test_player.gd
ikostan 5d8f016
Update scripts/game_settings_resource.gd
ikostan 4e289dc
Update game_settings_resource.gd
ikostan 0746137
Update hud.gd
ikostan 6cc9a94
issue (bug_risk): high_yellow_fraction and low_yellow_fraction can be…
ikostan 3ee148e
issue (bug_risk): setup_hud assumes the player_node has a speed_chang…
ikostan 910d718
Release simulated actions before erasing them.
ikostan 6d0f630
This flameout assertion is currently ambiguous.
ikostan efecc65
suggestion (bug_risk): HUD does not react to speed-related setting ch…
ikostan c69e1fc
Expose HUD accessors and update tests
ikostan d6f09f5
Update hud.gd
ikostan 27dff10
Update hud.gd
ikostan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,207 @@ | ||
| ## Copyright (C) 2026 Egor Kostan | ||
| ## SPDX-License-Identifier: GPL-3.0-or-later | ||
| ## test_player_fuel_logic.gd | ||
| ## GUT unit tests for Player fuel consumption, engine states, and UI Reactivity. | ||
| extends "res://addons/gut/test.gd" | ||
|
|
||
| const PLAYER_SCRIPT_PATH: String = "res://scripts/player.gd" | ||
|
|
||
| var _mock_root: Node | ||
| var _player: Variant # CHANGED: Use Variant to allow dynamic property access to player.gd variables | ||
| var _original_settings: GameSettingsResource | ||
| var _added_actions: Array[String] = [] | ||
|
|
||
| ## Per-test setup. | ||
| ## :rtype: void | ||
| func before_each() -> void: | ||
| _original_settings = Globals.settings | ||
| Globals.settings = GameSettingsResource.new() | ||
| Globals.settings.current_log_level = Globals.LogLevel.NONE | ||
|
|
||
| for action: String in ["speed_up", "speed_down", "move_left", "move_right"]: | ||
| if not InputMap.has_action(action): | ||
| InputMap.add_action(action) | ||
| _added_actions.append(action) | ||
|
|
||
| _mock_root = _build_mock_player_scene() | ||
| add_child_autoqfree(_mock_root) | ||
| _player = _mock_root.get_node("Player") | ||
|
|
||
| ## Per-test cleanup. | ||
| ## :rtype: void | ||
| func after_each() -> void: | ||
| Globals.settings = _original_settings | ||
| for action: String in _added_actions: | ||
| InputMap.erase_action(action) | ||
| _added_actions.clear() | ||
| Input.action_release("move_left") | ||
|
|
||
| ## test_ui_updates_automatically_on_resource_change | Observer Pattern | ||
| ## :rtype: void | ||
| func test_ui_updates_automatically_on_resource_change() -> void: | ||
| gut.p("Testing: Player UI responds seamlessly to external fuel updates.") | ||
|
|
||
| var fuel_bar: ProgressBar = _player.fuel_bar | ||
|
|
||
| Globals.settings.max_fuel = 200.0 | ||
| # Because of the resource setter, current_fuel modification fires 'setting_changed' automatically | ||
| Globals.settings.current_fuel = 150.0 | ||
|
|
||
| assert_eq(fuel_bar.max_value, 200.0, "Fuel Bar max_value must sync with Resource max.") | ||
| assert_eq(fuel_bar.value, 150.0, "Fuel Bar value must sync automatically.") | ||
|
|
||
| ## test_engine_stops_on_zero_fuel | Component State | ||
| ## :rtype: void | ||
| func test_engine_stops_on_zero_fuel() -> void: | ||
| gut.p("Testing: Zero fuel stops timers and rotor animations immediately.") | ||
|
|
||
| _player.fuel_timer.start() | ||
| var anim_r: AnimatedSprite2D = _player.rotor_right.get_node("AnimatedSprite2D") | ||
| anim_r.play("default") | ||
|
|
||
| _player._on_player_out_of_fuel() | ||
|
|
||
| assert_true(_player.fuel_timer.is_stopped(), "Fuel timer must stop running on flameout.") | ||
| assert_false(anim_r.is_playing(), "Rotors must stop animating when fuel is empty.") | ||
|
|
||
| ## test_engine_reignites_on_refuel | Component State | ||
| ## :rtype: void | ||
| func test_engine_reignites_on_refuel() -> void: | ||
| gut.p("Testing: Refueling from an empty tank restarts rotors and timers.") | ||
|
|
||
| # Simulate dead engine | ||
| _player.fuel_timer.stop() | ||
| var anim_l: AnimatedSprite2D = _player.rotor_left.get_node("AnimatedSprite2D") | ||
| anim_l.stop() | ||
|
|
||
| # Trigger the global setting change to simulate refuel logic | ||
| Globals.settings.current_fuel = 50.0 | ||
|
|
||
| assert_false(_player.fuel_timer.is_stopped(), "Fuel timer must reignite on refuel.") | ||
| assert_true(anim_l.is_playing(), "Rotors must automatically resume spinning.") | ||
|
|
||
| ## test_lateral_movement_blocked_without_fuel | Movement Constraints | ||
| ## :rtype: void | ||
| func test_lateral_movement_blocked_without_fuel() -> void: | ||
| gut.p("Testing: Lateral turning is disabled if fuel is completely empty.") | ||
|
|
||
| Globals.settings.current_fuel = 0.0 | ||
| _player.speed["speed"] = 150.0 | ||
| _player.player.velocity.x = 0.0 | ||
|
|
||
| Input.action_press("move_left") | ||
| _player._physics_process(0.1) | ||
|
|
||
| assert_eq(float(_player.player.velocity.x), 0.0, "Plane must not turn without fuel, ignoring inputs.") | ||
|
|
||
| # ========================================== | ||
| # MOCK BUILDER HELPER | ||
| # ========================================== | ||
| # Note: You can optionally extract this into a shared res://tests/test_helpers.gd base class later! | ||
| ## Dynamically constructs the node hierarchy required by player.gd. | ||
| ## :rtype: Node | ||
| func _build_mock_player_scene() -> Node: | ||
| var root: Node = Node.new() | ||
| root.name = "MockLevel" | ||
|
|
||
| var panel: Panel = Panel.new() | ||
| panel.name = "PlayerStatsPanel" | ||
| var stats: Control = Control.new() | ||
| stats.name = "Stats" | ||
|
|
||
| var fuel: Control = Control.new() | ||
| fuel.name = "Fuel" | ||
| var fuel_bar: ProgressBar = ProgressBar.new() | ||
| fuel_bar.name = "FuelBar" | ||
| var fuel_label: Label = Label.new() | ||
| fuel_label.name = "FuelLabel" | ||
| var f_timer: Timer = Timer.new() | ||
| f_timer.name = "BlinkTimer" | ||
| fuel_label.add_child(f_timer) | ||
| fuel.add_child(fuel_bar) | ||
| fuel.add_child(fuel_label) | ||
|
|
||
| var speed: Control = Control.new() | ||
| speed.name = "Speed" | ||
| var speed_bar: ProgressBar = ProgressBar.new() | ||
| speed_bar.name = "SpeedBar" | ||
| var speed_label: Label = Label.new() | ||
| speed_label.name = "SpeedLabel" | ||
| var s_timer: Timer = Timer.new() | ||
| s_timer.name = "BlinkTimer" | ||
| speed_label.add_child(s_timer) | ||
| speed.add_child(speed_bar) | ||
| speed.add_child(speed_label) | ||
|
|
||
| stats.add_child(fuel) | ||
| stats.add_child(speed) | ||
| panel.add_child(stats) | ||
| root.add_child(panel) | ||
|
|
||
| var PlayerScript := load(PLAYER_SCRIPT_PATH) | ||
| var p_node: Variant = PlayerScript.new() | ||
| p_node.name = "Player" | ||
|
|
||
| var cb2d: CharacterBody2D = CharacterBody2D.new() | ||
| cb2d.name = "CharacterBody2D" | ||
|
|
||
| for rotor_name: String in ["RotorRight", "RotorLeft"]: | ||
| var rotor: Node2D = Node2D.new() | ||
| rotor.name = rotor_name | ||
| var sfx: AudioStreamPlayer2D = AudioStreamPlayer2D.new() | ||
| sfx.name = "AudioStreamPlayer2D" | ||
| var anim: AnimatedSprite2D = AnimatedSprite2D.new() | ||
| anim.name = "AnimatedSprite2D" | ||
| # var frames: SpriteFrames = SpriteFrames.new() | ||
| # frames.add_animation("default") | ||
| # anim.sprite_frames = frames | ||
|
|
||
| var frames: SpriteFrames = SpriteFrames.new() | ||
| frames.add_animation("default") | ||
| # Add a dummy frame so play() actually engages and is_playing() returns true | ||
| var dummy_tex: PlaceholderTexture2D = PlaceholderTexture2D.new() | ||
| frames.add_frame("default", dummy_tex) | ||
| anim.sprite_frames = frames | ||
|
|
||
| rotor.add_child(anim) | ||
| rotor.add_child(sfx) | ||
| cb2d.add_child(rotor) | ||
|
|
||
| var sprite: Sprite2D = Sprite2D.new() | ||
| sprite.name = "Sprite2D" | ||
| var coll: CollisionPolygon2D = CollisionPolygon2D.new() | ||
| coll.name = "CollisionPolygon2D" | ||
| #var weapon: Node2D = Node2D.new() | ||
| #weapon.name = "Weapon" | ||
|
|
||
| var weapon: Node2D = Node2D.new() | ||
| weapon.name = "Weapon" | ||
|
|
||
| # Create a dummy script so player.gd's _ready() and _input() don't crash | ||
| var mock_weapon_script: GDScript = GDScript.new() | ||
| mock_weapon_script.source_code = """ | ||
| extends Node2D | ||
| var weapon_types: Array = [] | ||
| var current_index: int = 0 | ||
| func fire() -> void: | ||
| pass | ||
| func get_num_weapons() -> int: | ||
| return 1 | ||
| func switch_to(idx: int) -> void: | ||
| pass | ||
| """ | ||
| mock_weapon_script.reload() | ||
| weapon.set_script(mock_weapon_script) | ||
|
|
||
| cb2d.add_child(sprite) | ||
| cb2d.add_child(coll) | ||
| cb2d.add_child(weapon) | ||
|
|
||
| var fuel_timer: Timer = Timer.new() | ||
| fuel_timer.name = "FuelTimer" | ||
|
|
||
| p_node.add_child(cb2d) | ||
| p_node.add_child(fuel_timer) | ||
| root.add_child(p_node) | ||
|
|
||
| return root |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| uid://b0q7q3g1c058o |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.