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>
78 lines
3.0 KiB
C++
78 lines
3.0 KiB
C++
#pragma once
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
#include <cmath>
|
|
|
|
// Couleurs pour output
|
|
#define COLOR_RED "\033[31m"
|
|
#define COLOR_GREEN "\033[32m"
|
|
#define COLOR_RESET "\033[0m"
|
|
|
|
#define ASSERT_TRUE(condition, message) \
|
|
do { \
|
|
if (!(condition)) { \
|
|
std::cerr << COLOR_RED << "❌ ASSERTION FAILED: " << message << COLOR_RESET << "\n"; \
|
|
std::cerr << " At: " << __FILE__ << ":" << __LINE__ << "\n"; \
|
|
std::exit(1); \
|
|
} \
|
|
} while(0)
|
|
|
|
#define ASSERT_FALSE(condition, message) \
|
|
ASSERT_TRUE(!(condition), message)
|
|
|
|
#define ASSERT_EQ(actual, expected, message) \
|
|
do { \
|
|
if ((actual) != (expected)) { \
|
|
std::cerr << COLOR_RED << "❌ ASSERTION FAILED: " << message << COLOR_RESET << "\n"; \
|
|
std::cerr << " Expected: " << (expected) << "\n"; \
|
|
std::cerr << " Actual: " << (actual) << "\n"; \
|
|
std::cerr << " At: " << __FILE__ << ":" << __LINE__ << "\n"; \
|
|
std::exit(1); \
|
|
} \
|
|
} while(0)
|
|
|
|
#define ASSERT_NE(actual, expected, message) \
|
|
do { \
|
|
if ((actual) == (expected)) { \
|
|
std::cerr << COLOR_RED << "❌ ASSERTION FAILED: " << message << COLOR_RESET << "\n"; \
|
|
std::cerr << " Should not equal: " << (expected) << "\n"; \
|
|
std::cerr << " But got: " << (actual) << "\n"; \
|
|
std::cerr << " At: " << __FILE__ << ":" << __LINE__ << "\n"; \
|
|
std::exit(1); \
|
|
} \
|
|
} while(0)
|
|
|
|
#define ASSERT_LT(value, max, message) \
|
|
do { \
|
|
if ((value) >= (max)) { \
|
|
std::cerr << COLOR_RED << "❌ ASSERTION FAILED: " << message << COLOR_RESET << "\n"; \
|
|
std::cerr << " Expected: < " << (max) << "\n"; \
|
|
std::cerr << " Actual: " << (value) << "\n"; \
|
|
std::cerr << " At: " << __FILE__ << ":" << __LINE__ << "\n"; \
|
|
std::exit(1); \
|
|
} \
|
|
} while(0)
|
|
|
|
#define ASSERT_GT(value, min, message) \
|
|
do { \
|
|
if ((value) <= (min)) { \
|
|
std::cerr << COLOR_RED << "❌ ASSERTION FAILED: " << message << COLOR_RESET << "\n"; \
|
|
std::cerr << " Expected: > " << (min) << "\n"; \
|
|
std::cerr << " Actual: " << (value) << "\n"; \
|
|
std::cerr << " At: " << __FILE__ << ":" << __LINE__ << "\n"; \
|
|
std::exit(1); \
|
|
} \
|
|
} while(0)
|
|
|
|
#define ASSERT_WITHIN(actual, expected, tolerance, message) \
|
|
do { \
|
|
auto diff = std::abs((actual) - (expected)); \
|
|
if (diff > (tolerance)) { \
|
|
std::cerr << COLOR_RED << "❌ ASSERTION FAILED: " << message << COLOR_RESET << "\n"; \
|
|
std::cerr << " Expected: " << (expected) << " ± " << (tolerance) << "\n"; \
|
|
std::cerr << " Actual: " << (actual) << " (diff: " << diff << ")\n"; \
|
|
std::cerr << " At: " << __FILE__ << ":" << __LINE__ << "\n"; \
|
|
std::exit(1); \
|
|
} \
|
|
} while(0)
|