From 83cc433c2f00f5aa61b1c8ac75c9a64528dd8881 Mon Sep 17 00:00:00 2001 From: Vladimir nett00n Budylnikov Date: Wed, 24 Sep 2025 11:04:16 +0400 Subject: [PATCH] add logging --- docs/CLAUDE.md | 27 ++- docs/CODE_OF_CONDUCT.md | 48 ++++-- docs/MAP.md | 77 ++++++++- docs/TESTING.md | 156 ++++++++++++++++++ scenes/game/gameplays/clickomania_gameplay.gd | 2 +- .../game/gameplays/clickomania_gameplay.tscn | 2 +- scenes/ui/DebugToggle.gd | 2 +- scenes/ui/DebugToggle.tscn | 2 +- src/autoloads/AudioManager.gd | 2 +- src/autoloads/DebugManager.gd | 84 +++++++++- src/autoloads/GameManager.gd | 11 +- src/autoloads/SettingsManager.gd | 16 +- tests/README.md | 22 +++ tests/test_logging.gd | 106 ++++++++++++ 14 files changed, 515 insertions(+), 42 deletions(-) create mode 100644 docs/TESTING.md create mode 100644 tests/README.md create mode 100644 tests/test_logging.gd diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md index d45d273..1fed281 100644 --- a/docs/CLAUDE.md +++ b/docs/CLAUDE.md @@ -21,6 +21,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - Match-3 debug controls include gem count adjustment and board reroll - Difficulty presets: Easy (3 gems), Normal (5 gems), Hard (8 gems) - Gameplay mode switching: Space+Enter in game scene switches between match-3 and clickomania modes +- Test scripts located in `tests/` directory for system validation +- Use `test_logging.gd` to validate the logging system functionality ### Audio Configuration - Music: Located in `assets/audio/music/` directory with loop configuration in AudioManager @@ -51,11 +53,19 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - Use F12 key for global debug toggle - Remove debug prints before committing unless permanently useful +### Logging System Usage +- **ALWAYS** use `DebugManager` logging functions instead of `print()`, `push_error()`, etc. +- Use appropriate log levels: INFO for general messages, WARN for issues, ERROR for failures +- Include meaningful categories to organize log output: `"GameManager"`, `"Match3"`, `"Settings"` +- Leverage structured logging for better debugging and production monitoring +- Use `DebugManager.set_log_level()` to control verbosity during development and testing + ## Important File References ### Documentation Structure - **`docs/MAP.md`** - Complete project architecture and structure - **`docs/CODE_OF_CONDUCT.md`** - Coding standards and best practices +- **`docs/TESTING.md`** - Testing guidelines and conventions - **This file** - Claude Code specific development guidelines ### Key Scripts to Understand @@ -78,12 +88,27 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - Test debug UI with F12 toggle - Verify scene transitions work correctly - Check mobile compatibility if UI changes made +- Use relevant test scripts from `tests/` directory to validate system functionality +- Run `test_logging.gd` after making changes to the logging system ### Common Implementation Patterns - Scene transitions: Use `GameManager.start_game_with_mode()` and related methods - Debug integration: Connect to `DebugManager` signals and initialize debug state +- Logging: Use `DebugManager.log_*()` functions with appropriate levels and categories - Gameplay modes: Implement in `scenes/game/gameplays/` directory following modular pattern - Scoring system: Connect `score_changed` signal from gameplay to main game scene - Settings: Use `SettingsManager` for persistent configuration - Audio: Use `AudioManager` for music and sound effects -- Localization: Use `LocalizationManager` for language switching \ No newline at end of file +- Localization: Use `LocalizationManager` for language switching + +### Logging Best Practices +```gdscript +# ✅ Good logging practices +DebugManager.log_info("Scene transition completed", "GameManager") +DebugManager.log_warn("Settings file not found, using defaults", "Settings") +DebugManager.log_error("Failed to load audio resource: " + audio_path, "AudioManager") + +# ❌ Avoid these patterns +print("debug") # Use structured logging instead +push_error("error") # Use DebugManager.log_error() with category +``` diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md index 275fa74..c4db700 100644 --- a/docs/CODE_OF_CONDUCT.md +++ b/docs/CODE_OF_CONDUCT.md @@ -123,8 +123,8 @@ func some_deep_function(): ### Debug System Integration - Always connect to DebugManager signals for debug UI -- Use debug prints with clear prefixes -- Remove debug prints before committing (unless permanently useful) +- Use structured logging instead of plain print statements +- Remove temporary debug logs before committing (unless permanently useful) ```gdscript # ✅ Correct debug integration @@ -132,27 +132,47 @@ func _connect_to_global_debug() -> void: DebugManager.debug_ui_toggled.connect(_on_debug_ui_toggled) debug_ui.visible = DebugManager.is_debug_ui_visible() -# ✅ Good debug prints -print("Match3: Debug UI toggled to: ", visible) -print("TileSystem: Initialized ", tiles.size(), " tiles") +# ✅ Good structured logging +DebugManager.log_debug("Debug UI toggled to: " + str(visible), "Match3") +DebugManager.log_info("Initialized " + str(tiles.size()) + " tiles", "TileSystem") -# ❌ Bad debug prints -print("test") # Not descriptive -print(some_variable) # No context +# ❌ Bad logging practices +print("test") # Not descriptive, use structured logging +print(some_variable) # No context, use proper log level +``` + +### Logging Standards +- **ALWAYS** use `DebugManager.log_*()` functions instead of `print()` or `push_error()` +- Choose appropriate log levels based on message importance and audience +- Include meaningful categories to organize log output by system/component +- Format messages with clear, descriptive text and relevant context + +```gdscript +# ✅ Correct logging usage +DebugManager.log_info("Game scene loaded successfully", "SceneManager") +DebugManager.log_warn("Audio file not found, using default", "AudioManager") +DebugManager.log_error("Failed to save settings: " + error_message, "Settings") + +# ❌ Wrong approaches +print("loaded") # Use DebugManager.log_info() with category +push_error("error") # Use DebugManager.log_error() with context +if debug_mode: print("debug info") # Use DebugManager.log_debug() ``` ### Error Handling - Always check if resources loaded successfully -- Use `push_error()` for critical failures +- Use `DebugManager.log_error()` for critical failures - Provide fallback behavior when possible +- Include meaningful context in error messages ```gdscript -# ✅ Correct error handling +# ✅ Correct error handling with structured logging func load_scene(path: String) -> void: var packed_scene := load(path) if not packed_scene or not packed_scene is PackedScene: - push_error("Failed to load scene at: %s" % path) + DebugManager.log_error("Failed to load scene at: %s" % path, "SceneLoader") return + DebugManager.log_info("Successfully loaded scene: %s" % path, "SceneLoader") get_tree().change_scene_to_packed(packed_scene) ``` @@ -253,10 +273,12 @@ func _process(delta): ```gdscript # ❌ Don't create separate debug systems var my_debug_enabled = false +print("debug: " + some_info) # Don't use plain print() -# ✅ Use the global debug system +# ✅ Use the global debug and logging systems if DebugManager.is_debug_enabled(): show_debug_info() +DebugManager.log_debug("Debug information: " + some_info, "MyComponent") ``` ## Learning Resources @@ -294,4 +316,4 @@ This code of conduct emphasizes: - **Integration**: Use the project's systems properly - **Learning**: Continuously improve your skills -Remember: It's better to ask questions and write clear, simple code than to guess and create complex, hard-to-maintain solutions. The goal is to contribute effectively to a codebase that will grow and evolve over time. \ No newline at end of file +Remember: It's better to ask questions and write clear, simple code than to guess and create complex, hard-to-maintain solutions. The goal is to contribute effectively to a codebase that will grow and evolve over time. diff --git a/docs/MAP.md b/docs/MAP.md index 9131099..42d7968 100644 --- a/docs/MAP.md +++ b/docs/MAP.md @@ -15,8 +15,9 @@ skelly/ ├── localization/ # Internationalization files ├── scenes/ # Godot scenes (.tscn) and scripts (.gd) ├── src/ # Source code organization -├── project.godot # Main Godot project configuration -└── icon.svg # Project icon +├── tests/ # Test scripts and validation utilities +├── project.godot # Main Godot project configuration +└── icon.svg # Project icon ``` ## Core Architecture @@ -48,10 +49,13 @@ Located in `src/autoloads/`, these scripts are automatically loaded when the gam - Uses translation files in `localization/` 5. **DebugManager** (`src/autoloads/DebugManager.gd`) - - Global debug state management + - Global debug state management and centralized logging system - Debug UI visibility control - F12 toggle functionality - Signal-based debug system + - Structured logging with configurable log levels (TRACE, DEBUG, INFO, WARN, ERROR, FATAL) + - Timestamp-based log formatting with category support + - Runtime log level filtering for development and production builds ## Scene Hierarchy & Flow @@ -175,11 +179,23 @@ assets/ ### Game Data (`data/`) - `default_bus_layout.tres` - Audio bus configuration for Godot +### Documentation (`docs/`) +- `MAP.md` - Project architecture and structure overview +- `CLAUDE.md` - Claude Code development guidelines +- `CODE_OF_CONDUCT.md` - Coding standards and best practices +- `TESTING.md` - Testing guidelines and conventions + ### Localization (`localization/`) - `languages.json` - Available language definitions - `MainStrings.en.translation` - English translations - `MainStrings.ru.translation` - Russian translations +### Testing & Validation (`tests/`) +- `test_logging.gd` - DebugManager logging system validation +- `README.md` - Brief directory overview (see docs/TESTING.md for full guidelines) +- Future test scripts for individual components and integration testing +- Temporary test utilities for development and debugging + ### Project Configuration - `project.godot` - Main Godot project settings - Autoload definitions @@ -215,6 +231,58 @@ SettingsManager --> localization/languages.json AudioManager --> data/default_bus_layout.tres ``` +## Logging System + +The project uses a centralized logging system implemented in DebugManager for consistent, structured logging throughout the application. + +### Log Levels +``` +TRACE (0) - Detailed execution tracing (debug mode only) +DEBUG (1) - Development debugging information (debug mode only) +INFO (2) - General application information (always visible) +WARN (3) - Warning messages that don't break functionality +ERROR (4) - Error conditions that may affect functionality +FATAL (5) - Critical errors that may cause application failure +``` + +### Usage Examples +```gdscript +# Basic logging with automatic categorization +DebugManager.log_info("Game started successfully") +DebugManager.log_warn("Settings file not found, using defaults") +DebugManager.log_error("Failed to load audio resource") + +# Logging with custom categories for better organization +DebugManager.log_debug("Grid regenerated with 64 tiles", "Match3") +DebugManager.log_info("Language changed to: en", "SettingsManager") +DebugManager.log_error("Invalid scene path provided", "GameManager") +``` + +### Log Format +``` +[timestamp] LEVEL [category]: message +Example: [2025-09-24T10:48:08] INFO [GameManager]: Loading main scene +``` + +### Runtime Configuration +```gdscript +# Set minimum log level (filters out lower priority messages) +DebugManager.set_log_level(DebugManager.LogLevel.WARN) + +# Get current log level +var current_level = DebugManager.get_log_level() + +# Check if a specific level would be logged +if DebugManager._should_log(DebugManager.LogLevel.DEBUG): + # Expensive debug calculation here +``` + +### Integration with Godot Systems +- **WARN/ERROR/FATAL** levels automatically call `push_warning()` and `push_error()` +- **TRACE/DEBUG** levels only display when debug mode is enabled +- **INFO** and higher levels always display regardless of debug mode +- All levels respect the configured minimum log level threshold + ## Development Notes ### Current Implementation Status @@ -233,5 +301,6 @@ AudioManager --> data/default_bus_layout.tres 5. **Data-Driven Configuration** - JSON for settings and translations 6. **Component Architecture** - Reusable UI and game components 7. **Centralized Scoring System** - Global score management across gameplay modes +8. **Structured Logging System** - Centralized logging with level-based filtering and formatted output -This structure provides a clean separation of concerns, making the codebase maintainable and extensible for future features. \ No newline at end of file +This structure provides a clean separation of concerns, making the codebase maintainable and extensible for future features. diff --git a/docs/TESTING.md b/docs/TESTING.md new file mode 100644 index 0000000..7d6aef0 --- /dev/null +++ b/docs/TESTING.md @@ -0,0 +1,156 @@ +# Tests Directory + +This directory contains test scripts and utilities for validating various systems and components in the Skelly project. + +## Overview + +The `tests/` directory is designed to house: +- System validation scripts +- Component testing utilities +- Integration tests +- Performance benchmarks +- Debugging tools + +## Current Test Files + +### `test_logging.gd` +Comprehensive test script for the DebugManager logging system. + +**Features:** +- Tests all log levels (TRACE, DEBUG, INFO, WARN, ERROR, FATAL) +- Validates log level filtering functionality +- Tests category-based logging organization +- Verifies debug mode integration +- Demonstrates proper logging usage patterns + +**Usage:** +```gdscript +# Option 1: Add as temporary autoload +# In project.godot, add: tests/test_logging.gd + +# Option 2: Instantiate in a scene +var test_script = preload("res://tests/test_logging.gd").new() +add_child(test_script) + +# Option 3: Run directly from editor +# Open the script and run the scene containing it +``` + +**Expected Output:** +The script will output formatted log messages demonstrating: +- Proper timestamp formatting +- Log level filtering behavior +- Category organization +- Debug mode dependency for TRACE/DEBUG levels + +## Adding New Tests + +When creating new test files, follow these conventions: + +### File Naming +- Use descriptive names starting with `test_` +- Example: `test_audio_manager.gd`, `test_scene_transitions.gd` + +### File Structure +```gdscript +extends Node + +# Brief description of what this test validates + +func _ready(): + # Wait for system initialization if needed + await get_tree().process_frame + run_tests() + +func run_tests(): + print("=== Starting [System Name] Tests ===") + + # Individual test functions + test_basic_functionality() + test_edge_cases() + test_error_conditions() + + print("=== [System Name] Tests Complete ===") + +func test_basic_functionality(): + print("\\n--- Test: Basic Functionality ---") + # Test implementation + +func test_edge_cases(): + print("\\n--- Test: Edge Cases ---") + # Edge case testing + +func test_error_conditions(): + print("\\n--- Test: Error Conditions ---") + # Error condition testing +``` + +### Testing Guidelines + +1. **Independence**: Each test should be self-contained and not depend on other tests +2. **Cleanup**: Restore original state after testing (settings, debug modes, etc.) +3. **Clear Output**: Use descriptive print statements to show test progress +4. **Error Handling**: Test both success and failure conditions +5. **Documentation**: Include comments explaining complex test scenarios + +### Integration with Main Project + +- **Temporary Usage**: Test files are meant to be added temporarily during development +- **Not in Production**: These files should not be included in release builds +- **Autoload Testing**: Add to autoloads temporarily for automatic execution +- **Manual Testing**: Run individually when testing specific components + +## Test Categories + +### System Tests +Test core autoload managers and global systems: +- `test_logging.gd` - DebugManager logging system +- Future: `test_settings.gd` - SettingsManager functionality +- Future: `test_audio.gd` - AudioManager functionality +- Future: `test_scene_management.gd` - GameManager transitions + +### Component Tests +Test individual game components: +- Future: `test_match3.gd` - Match-3 gameplay mechanics +- Future: `test_tile_system.gd` - Tile behavior and interactions +- Future: `test_ui_components.gd` - Menu and UI functionality + +### Integration Tests +Test system interactions and workflows: +- Future: `test_game_flow.gd` - Complete game session flow +- Future: `test_debug_system.gd` - Debug UI integration +- Future: `test_localization.gd` - Language switching and translations + +## Running Tests + +### During Development +1. Copy or symlink the test file to your scene +2. Add as a child node or autoload temporarily +3. Run the project and observe console output +4. Remove from project when testing is complete + +### Automated Testing +While Godot doesn't have built-in unit testing, these scripts provide: +- Consistent validation approach +- Repeatable test scenarios +- Clear pass/fail output +- System behavior documentation + +## Best Practices + +1. **Document Expected Behavior**: Include comments about what should happen +2. **Test Boundary Conditions**: Include edge cases and error conditions +3. **Measure Performance**: Add timing for performance-critical components +4. **Visual Validation**: For UI components, include visual checks +5. **Cleanup After Tests**: Restore initial state to avoid side effects + +## Contributing + +When adding new test files: +1. Follow the naming and structure conventions +2. Update this README with new test descriptions +3. Ensure tests are self-contained and documented +4. Test both success and failure scenarios +5. Include performance considerations where relevant + +This testing approach helps maintain code quality and provides validation tools for system changes and refactoring. diff --git a/scenes/game/gameplays/clickomania_gameplay.gd b/scenes/game/gameplays/clickomania_gameplay.gd index 30add9c..a909dad 100644 --- a/scenes/game/gameplays/clickomania_gameplay.gd +++ b/scenes/game/gameplays/clickomania_gameplay.gd @@ -7,4 +7,4 @@ func _ready(): # Example: Add some score after a few seconds to test the system await get_tree().create_timer(2.0).timeout score_changed.emit(100) - print("Clickomania awarded 100 points") \ No newline at end of file + print("Clickomania awarded 100 points") diff --git a/scenes/game/gameplays/clickomania_gameplay.tscn b/scenes/game/gameplays/clickomania_gameplay.tscn index 3cc5220..d24cc4f 100644 --- a/scenes/game/gameplays/clickomania_gameplay.tscn +++ b/scenes/game/gameplays/clickomania_gameplay.tscn @@ -18,4 +18,4 @@ offset_bottom = 12.0 grow_horizontal = 2 grow_vertical = 2 text = "Clickomania Gameplay (Demo)" -horizontal_alignment = 1 \ No newline at end of file +horizontal_alignment = 1 diff --git a/scenes/ui/DebugToggle.gd b/scenes/ui/DebugToggle.gd index 8f1f54a..2d225cd 100644 --- a/scenes/ui/DebugToggle.gd +++ b/scenes/ui/DebugToggle.gd @@ -12,4 +12,4 @@ func _on_pressed(): DebugManager.toggle_debug() func _on_debug_toggled(enabled: bool): - text = "Debug: " + ("ON" if enabled else "OFF") \ No newline at end of file + text = "Debug: " + ("ON" if enabled else "OFF") diff --git a/scenes/ui/DebugToggle.tscn b/scenes/ui/DebugToggle.tscn index 8aa5059..c9332d7 100644 --- a/scenes/ui/DebugToggle.tscn +++ b/scenes/ui/DebugToggle.tscn @@ -15,4 +15,4 @@ offset_bottom = -10.0 grow_horizontal = 0 grow_vertical = 0 text = "Debug: OFF" -script = ExtResource("1_gn2ol") \ No newline at end of file +script = ExtResource("1_gn2ol") diff --git a/src/autoloads/AudioManager.gd b/src/autoloads/AudioManager.gd index f839dc3..f281f7d 100644 --- a/src/autoloads/AudioManager.gd +++ b/src/autoloads/AudioManager.gd @@ -19,7 +19,7 @@ func _ready(): var orig_stream = _load_stream() if not orig_stream: - push_error("Failed to load music stream: %s" % MUSIC_PATH) + DebugManager.log_error("Failed to load music stream: %s" % MUSIC_PATH, "AudioManager") return var stream = orig_stream.duplicate(true) as AudioStream diff --git a/src/autoloads/DebugManager.gd b/src/autoloads/DebugManager.gd index 8669935..6de48c8 100644 --- a/src/autoloads/DebugManager.gd +++ b/src/autoloads/DebugManager.gd @@ -2,16 +2,26 @@ extends Node signal debug_toggled(enabled: bool) +enum LogLevel { + TRACE = 0, + DEBUG = 1, + INFO = 2, + WARN = 3, + ERROR = 4, + FATAL = 5 +} + var debug_enabled: bool = false var debug_overlay_visible: bool = false +var current_log_level: LogLevel = LogLevel.INFO func _ready(): - print("DebugManager loaded") + log_info("DebugManager loaded") func toggle_debug(): debug_enabled = !debug_enabled debug_toggled.emit(debug_enabled) - print("Debug mode: ", "ON" if debug_enabled else "OFF") + log_info("Debug mode: " + ("ON" if debug_enabled else "OFF")) func set_debug_enabled(enabled: bool): if debug_enabled != enabled: @@ -30,6 +40,70 @@ func set_overlay_visible(visible: bool): func is_overlay_visible() -> bool: return debug_overlay_visible -func log_debug(message: String, category: String = "DEBUG"): - if debug_enabled: - print("[", category, "] ", message) +func set_log_level(level: LogLevel): + current_log_level = level + log_info("Log level set to: " + _log_level_to_string(level)) + +func get_log_level() -> LogLevel: + return current_log_level + +func _should_log(level: LogLevel) -> bool: + return level >= current_log_level + +func _log_level_to_string(level: LogLevel) -> String: + match level: + LogLevel.TRACE: + return "TRACE" + LogLevel.DEBUG: + return "DEBUG" + LogLevel.INFO: + return "INFO" + LogLevel.WARN: + return "WARN" + LogLevel.ERROR: + return "ERROR" + LogLevel.FATAL: + return "FATAL" + _: + return "UNKNOWN" + +func _format_log_message(level: LogLevel, message: String, category: String = "") -> String: + var timestamp = Time.get_datetime_string_from_system() + var level_str = _log_level_to_string(level) + var category_str = (" [" + category + "]") if category != "" else "" + return "[%s] %s%s: %s" % [timestamp, level_str, category_str, message] + +func log_trace(message: String, category: String = ""): + if _should_log(LogLevel.TRACE): + var formatted = _format_log_message(LogLevel.TRACE, message, category) + if debug_enabled: + print(formatted) + +func log_debug(message: String, category: String = ""): + if _should_log(LogLevel.DEBUG): + var formatted = _format_log_message(LogLevel.DEBUG, message, category) + if debug_enabled: + print(formatted) + +func log_info(message: String, category: String = ""): + if _should_log(LogLevel.INFO): + var formatted = _format_log_message(LogLevel.INFO, message, category) + print(formatted) + +func log_warn(message: String, category: String = ""): + if _should_log(LogLevel.WARN): + var formatted = _format_log_message(LogLevel.WARN, message, category) + print(formatted) + push_warning(formatted) + +func log_error(message: String, category: String = ""): + if _should_log(LogLevel.ERROR): + var formatted = _format_log_message(LogLevel.ERROR, message, category) + print(formatted) + push_error(formatted) + +func log_fatal(message: String, category: String = ""): + if _should_log(LogLevel.FATAL): + var formatted = _format_log_message(LogLevel.FATAL, message, category) + print(formatted) + push_error(formatted) diff --git a/src/autoloads/GameManager.gd b/src/autoloads/GameManager.gd index 8313b4a..cf29761 100644 --- a/src/autoloads/GameManager.gd +++ b/src/autoloads/GameManager.gd @@ -18,7 +18,7 @@ func start_game_with_mode(gameplay_mode: String) -> void: pending_gameplay_mode = gameplay_mode var packed_scene := load(GAME_SCENE_PATH) if not packed_scene or not packed_scene is PackedScene: - push_error("Failed to load Game scene at: %s" % GAME_SCENE_PATH) + DebugManager.log_error("Failed to load Game scene at: %s" % GAME_SCENE_PATH, "GameManager") return get_tree().change_scene_to_packed(packed_scene) # Wait one frame for the scene to be ready, then set gameplay mode @@ -27,14 +27,13 @@ func start_game_with_mode(gameplay_mode: String) -> void: get_tree().current_scene.set_gameplay_mode(pending_gameplay_mode) func save_game() -> void: - print("Game saved (mock)") + DebugManager.log_info("Game saved (mock)", "GameManager") func exit_to_main_menu() -> void: - print("GameManager: Attempting to exit to main menu") + DebugManager.log_info("Attempting to exit to main menu", "GameManager") var packed_scene := load(MAIN_SCENE_PATH) if not packed_scene or not packed_scene is PackedScene: - push_error("Failed to load Main scene at: %s" % MAIN_SCENE_PATH) + DebugManager.log_error("Failed to load Main scene at: %s" % MAIN_SCENE_PATH, "GameManager") return - print("GameManager: Loading main scene") + DebugManager.log_info("Loading main scene", "GameManager") get_tree().change_scene_to_packed(packed_scene) - diff --git a/src/autoloads/SettingsManager.gd b/src/autoloads/SettingsManager.gd index ab9cf92..ac61caa 100644 --- a/src/autoloads/SettingsManager.gd +++ b/src/autoloads/SettingsManager.gd @@ -18,7 +18,7 @@ var default_settings = { var languages_data = {} func _ready(): - print("SettingsManager ready") + DebugManager.log_info("SettingsManager ready", "SettingsManager") load_languages() load_settings() @@ -27,10 +27,10 @@ func load_settings(): if config.load(SETTINGS_FILE) == OK: for key in default_settings.keys(): settings[key] = config.get_value("settings", key, default_settings[key]) - print("Settings loaded: ", settings) + DebugManager.log_info("Settings loaded: " + str(settings), "SettingsManager") else: - print("No settings file found, using defaults") - print("Language is set to: ", settings["language"]) + DebugManager.log_warn("No settings file found, using defaults", "SettingsManager") + DebugManager.log_info("Language is set to: " + str(settings["language"]), "SettingsManager") TranslationServer.set_locale(settings["language"]) AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), linear_to_db(settings["master_volume"])) AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Music"), linear_to_db(settings["music_volume"])) @@ -42,7 +42,7 @@ func save_settings(): for key in settings.keys(): config.set_value("settings", key, settings[key]) config.save(SETTINGS_FILE) - print("Settings saved: ", settings) + DebugManager.log_info("Settings saved: " + str(settings), "SettingsManager") func get_setting(key: String): return settings.get(key) @@ -65,7 +65,7 @@ func _apply_setting_side_effect(key: String, value) -> void: func load_languages(): var file = FileAccess.open(LANGUAGES_JSON_PATH, FileAccess.READ) if not file: - print("Could not open languages.json") + DebugManager.log_error("Could not open languages.json", "SettingsManager") return var json_string = file.get_as_text() @@ -73,12 +73,12 @@ func load_languages(): var json = JSON.new() if json.parse(json_string) != OK: - print("Error parsing languages.json") + DebugManager.log_error("Error parsing languages.json", "SettingsManager") return languages_data = json.data if languages_data.has("languages"): - print("Languages loaded: ", languages_data.languages.keys()) + DebugManager.log_info("Languages loaded: " + str(languages_data.languages.keys()), "SettingsManager") func get_languages_data(): return languages_data diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..0f1a547 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,22 @@ +# Tests Directory + +This directory contains test scripts and validation utilities for the Skelly project. + +## Documentation + +For complete testing guidelines, conventions, and usage instructions, see: +**[docs/TESTING.md](../docs/TESTING.md)** + +## Current Files + +- `test_logging.gd` - Comprehensive logging system validation script + +## Quick Usage + +```gdscript +# Add as temporary autoload or run in scene +var test_script = preload("res://tests/test_logging.gd").new() +add_child(test_script) +``` + +See [docs/TESTING.md](../docs/TESTING.md) for detailed usage instructions and conventions. diff --git a/tests/test_logging.gd b/tests/test_logging.gd new file mode 100644 index 0000000..df6f5c8 --- /dev/null +++ b/tests/test_logging.gd @@ -0,0 +1,106 @@ +extends Node + +# Test script for the DebugManager logging system +# This script validates all log levels, filtering, and formatting functionality +# Usage: Add to scene or autoload temporarily to run tests + +func _ready(): + # Wait a frame for DebugManager to initialize + await get_tree().process_frame + test_logging_system() + +func test_logging_system(): + print("=== Starting Logging System Tests ===") + + # Test 1: Basic log level functionality + test_basic_logging() + + # Test 2: Log level filtering + test_log_level_filtering() + + # Test 3: Category functionality + test_category_logging() + + # Test 4: Debug mode integration + test_debug_mode_integration() + + print("=== Logging System Tests Complete ===") + +func test_basic_logging(): + print("\n--- Test 1: Basic Log Level Functionality ---") + + # Reset to INFO level for consistent testing + DebugManager.set_log_level(DebugManager.LogLevel.INFO) + + DebugManager.log_trace("TRACE: This should not appear (below INFO level)") + DebugManager.log_debug("DEBUG: This should not appear (below INFO level)") + DebugManager.log_info("INFO: This message should appear") + DebugManager.log_warn("WARN: This warning should appear") + DebugManager.log_error("ERROR: This error should appear") + DebugManager.log_fatal("FATAL: This fatal error should appear") + +func test_log_level_filtering(): + print("\n--- Test 2: Log Level Filtering ---") + + # Test DEBUG level + print("Setting log level to DEBUG...") + DebugManager.set_log_level(DebugManager.LogLevel.DEBUG) + DebugManager.log_trace("TRACE: Should not appear (below DEBUG)") + DebugManager.log_debug("DEBUG: Should appear with debug enabled") + DebugManager.log_info("INFO: Should appear") + + # Test ERROR level (very restrictive) + print("Setting log level to ERROR...") + DebugManager.set_log_level(DebugManager.LogLevel.ERROR) + DebugManager.log_debug("DEBUG: Should not appear (below ERROR)") + DebugManager.log_warn("WARN: Should not appear (below ERROR)") + DebugManager.log_error("ERROR: Should appear") + DebugManager.log_fatal("FATAL: Should appear") + + # Reset to INFO for remaining tests + DebugManager.set_log_level(DebugManager.LogLevel.INFO) + +func test_category_logging(): + print("\n--- Test 3: Category Functionality ---") + + DebugManager.log_info("Message without category") + DebugManager.log_info("Message with TEST category", "TEST") + DebugManager.log_info("Message with LOGGING category", "LOGGING") + DebugManager.log_warn("Warning with VALIDATION category", "VALIDATION") + DebugManager.log_error("Error with SYSTEM category", "SYSTEM") + +func test_debug_mode_integration(): + print("\n--- Test 4: Debug Mode Integration ---") + + # Set to TRACE level to test debug mode dependency + DebugManager.set_log_level(DebugManager.LogLevel.TRACE) + + var original_debug_state = DebugManager.is_debug_enabled() + + # Test with debug mode OFF + DebugManager.set_debug_enabled(false) + print("Debug mode OFF - TRACE and DEBUG should not appear:") + DebugManager.log_trace("TRACE: Should NOT appear (debug mode OFF)") + DebugManager.log_debug("DEBUG: Should NOT appear (debug mode OFF)") + DebugManager.log_info("INFO: Should appear regardless of debug mode") + + # Test with debug mode ON + DebugManager.set_debug_enabled(true) + print("Debug mode ON - TRACE and DEBUG should appear:") + DebugManager.log_trace("TRACE: Should appear (debug mode ON)") + DebugManager.log_debug("DEBUG: Should appear (debug mode ON)") + DebugManager.log_info("INFO: Should still appear") + + # Restore original debug state + DebugManager.set_debug_enabled(original_debug_state) + DebugManager.set_log_level(DebugManager.LogLevel.INFO) + +# Helper function to validate log level enum values +func test_log_level_enum(): + print("\n--- Log Level Enum Values ---") + print("TRACE: ", DebugManager.LogLevel.TRACE) + print("DEBUG: ", DebugManager.LogLevel.DEBUG) + print("INFO: ", DebugManager.LogLevel.INFO) + print("WARN: ", DebugManager.LogLevel.WARN) + print("ERROR: ", DebugManager.LogLevel.ERROR) + print("FATAL: ", DebugManager.LogLevel.FATAL)