This commit implements a complete test infrastructure for validating hot-reload stability and robustness across multiple scenarios. ## New Test Infrastructure ### Test Helpers (tests/helpers/) - TestMetrics: FPS, memory, reload time tracking with statistics - TestReporter: Assertion tracking and formatted test reports - SystemUtils: Memory usage monitoring via /proc/self/status - TestAssertions: Macro-based assertion framework ### Test Modules - TankModule: Realistic module with 50 tanks for production testing - ChaosModule: Crash-injection module for robustness validation - StressModule: Lightweight module for long-duration stability tests ## Integration Test Scenarios ### Scenario 1: Production Hot-Reload (test_01_production_hotreload.cpp) ✅ PASSED - End-to-end hot-reload validation - 30 seconds simulation (1800 frames @ 60 FPS) - TankModule with 50 tanks, realistic state - Source modification (v1.0 → v2.0), recompilation, reload - State preservation: positions, velocities, frameCount - Metrics: ~163ms reload time, 0.88MB memory growth ### Scenario 2: Chaos Monkey (test_02_chaos_monkey.cpp) ✅ PASSED - Extreme robustness testing - 150+ random crashes per run (5% crash probability per frame) - 5 crash types: runtime_error, logic_error, out_of_range, domain_error, state corruption - 100% recovery rate via automatic hot-reload - Corrupted state detection and rejection - Random seed for unpredictable crash patterns - Proof of real reload: temporary files in /tmp/grove_module_*.so ### Scenario 3: Stress Test (test_03_stress_test.cpp) ✅ PASSED - Long-duration stability validation - 10 minutes simulation (36000 frames @ 60 FPS) - 120 hot-reloads (every 5 seconds) - 100% reload success rate (120/120) - Memory growth: 2 MB (threshold: 50 MB) - Avg reload time: 160ms (threshold: 500ms) - No memory leaks, no file descriptor leaks ## Core Engine Enhancements ### ModuleLoader (src/ModuleLoader.cpp) - Temporary file copy to /tmp/ for Linux dlopen cache bypass - Robust reload() method: getState() → unload() → load() → setState() - Automatic cleanup of temporary files - Comprehensive error handling and logging ### DebugEngine (src/DebugEngine.cpp) - Automatic recovery in processModuleSystems() - Exception catching → logging → module reload → continue - Module state dump utilities for debugging ### SequentialModuleSystem (src/SequentialModuleSystem.cpp) - extractModule() for safe module extraction - registerModule() for module re-registration - Enhanced processModules() with error handling ## Build System - CMake configuration for test infrastructure - Shared library compilation for test modules (.so) - CTest integration for all scenarios - PIC flag management for spdlog compatibility ## Documentation (planTI/) - Complete test architecture documentation - Detailed scenario specifications with success criteria - Global test plan and validation thresholds ## Validation Results All 3 integration scenarios pass successfully: - Production hot-reload: State preservation validated - Chaos Monkey: 100% recovery from 150+ crashes - Stress Test: Stable over 120 reloads, minimal memory growth 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
131 lines
4.7 KiB
C++
131 lines
4.7 KiB
C++
#pragma once
|
|
|
|
#include <string>
|
|
#include <memory>
|
|
#include "IDataNode.h"
|
|
#include "ITaskScheduler.h"
|
|
|
|
// Forward declarations
|
|
namespace grove {
|
|
class IIO;
|
|
}
|
|
|
|
namespace grove {
|
|
|
|
|
|
|
|
/**
|
|
* @brief Pure business logic interface - optimized for Claude Code development
|
|
*
|
|
* This interface defines the contract for all game modules. Each module contains
|
|
* 200-300 lines of pure game logic with zero infrastructure code.
|
|
*
|
|
* Key design principles:
|
|
* - PURE FUNCTION: process() method has minimal side effects
|
|
* - CONFIG VIA IDATANODE: Configuration via immutable IDataNode references
|
|
* - IDATANODE COMMUNICATION: All data via IDataNode abstraction (backend agnostic)
|
|
* - IIO FOR PERSISTENCE: Save requests via IIO publish (Engine handles persistence)
|
|
* - NO INFRASTRUCTURE: No threading, networking, or framework dependencies
|
|
* - HOT-RELOAD READY: State serialization for seamless module replacement
|
|
* - CLAUDE OPTIMIZED: Micro-context size for AI development efficiency
|
|
*
|
|
* DATA FLOW:
|
|
* - Configuration: Read-only via setConfiguration(const IDataNode&)
|
|
* - Input: Read-only via process(const IDataNode&)
|
|
* - Save: Publish via IIO: m_io->publish("save:module:state", data)
|
|
* - State: Serialized via getState() for hot-reload
|
|
*
|
|
* Module constraint: Maximum 300 lines per module (Exception: ProductionModule 500-800 lines)
|
|
*/
|
|
class IModule {
|
|
public:
|
|
virtual ~IModule() = default;
|
|
|
|
/**
|
|
* @brief Process game logic
|
|
* @param input Data input from other modules or the module system
|
|
*
|
|
* This is the core method where all module logic is implemented.
|
|
* Modules communicate via IIO pub/sub and can delegate tasks via ITaskScheduler.
|
|
* Must handle state properly through getState/setState for hot-reload.
|
|
*/
|
|
virtual void process(const IDataNode& input) = 0;
|
|
|
|
/**
|
|
* @brief Set module configuration (replaces initialize)
|
|
* @param configNode Configuration node (immutable reference)
|
|
* @param io Pub/sub communication interface for messaging
|
|
* @param scheduler Task scheduling interface for delegating work
|
|
*
|
|
* Called when the module is loaded or configuration changes.
|
|
* Should setup internal state, validate configuration, and store service references.
|
|
*/
|
|
virtual void setConfiguration(const IDataNode& configNode, IIO* io, ITaskScheduler* scheduler) = 0;
|
|
|
|
/**
|
|
* @brief Get current module configuration
|
|
* @return Configuration node reference
|
|
*/
|
|
virtual const IDataNode& getConfiguration() = 0;
|
|
|
|
/**
|
|
* @brief Get detailed health status of the module
|
|
* @return Health report with status, metrics, and diagnostics
|
|
*/
|
|
virtual std::unique_ptr<IDataNode> getHealthStatus() = 0;
|
|
|
|
/**
|
|
* @brief Cleanup and shutdown the module
|
|
*
|
|
* Called when the module is being unloaded. Should clean up any
|
|
* resources and prepare for safe destruction.
|
|
*/
|
|
virtual void shutdown() = 0;
|
|
|
|
/**
|
|
* @brief Get current module state for hot-reload support
|
|
* @return Data representation of all module state
|
|
*
|
|
* Critical for hot-reload functionality. Must serialize all internal
|
|
* state that needs to be preserved when the module is replaced.
|
|
* The returned data should be sufficient to restore the module to
|
|
* its current state via setState().
|
|
*/
|
|
virtual std::unique_ptr<IDataNode> getState() = 0;
|
|
|
|
/**
|
|
* @brief Restore module state after hot-reload
|
|
* @param state State previously returned by getState()
|
|
*
|
|
* Called after module replacement to restore the previous state.
|
|
* Must be able to reconstruct all internal state from the data
|
|
* to ensure seamless hot-reload without game disruption.
|
|
*/
|
|
virtual void setState(const IDataNode& state) = 0;
|
|
|
|
/**
|
|
* @brief Get module type identifier
|
|
* @return Module type as string (e.g., "tank", "economy", "production")
|
|
*/
|
|
virtual std::string getType() const = 0;
|
|
|
|
/**
|
|
* @brief Check if module is idle (no processing in progress)
|
|
* @return True if module has no active processing and can be safely hot-reloaded
|
|
*
|
|
* Used by hot-reload system to ensure safe reload timing.
|
|
* A module is considered idle when:
|
|
* - No synchronous processing in progress
|
|
* - Not waiting for critical state updates
|
|
* - Safe to extract state via getState()
|
|
*
|
|
* Note: Async tasks scheduled via ITaskScheduler are tracked separately
|
|
* by the module system and don't affect idle status.
|
|
*
|
|
* Default implementation should return true unless module explicitly
|
|
* tracks long-running synchronous operations.
|
|
*/
|
|
virtual bool isIdle() const = 0;
|
|
};
|
|
|
|
} // namespace grove
|