GroveEngine/tests/CMakeLists.txt
StillHammer 360f39325b feat: Add Memory Leak Hunter test & fix critical ModuleLoader leaks
**Test Suite Completion - Scenario 5**

Add comprehensive memory leak detection test for hot-reload system with 200 reload cycles.

**New Test: test_05_memory_leak**
- 200 hot-reload cycles without recompilation
- Memory monitoring every 5 seconds (RSS, temp files, .so handles)
- Multi-threaded: Engine (60 FPS) + ReloadScheduler + MemoryMonitor
- Strict validation: <10 MB growth, <50 KB/reload, ≤2 temp files

**New Module: LeakTestModule**
- Controlled memory allocations (1 MB work buffer)
- Large state serialization (100 KB blob)
- Simulates real-world module behavior

**Critical Fix: ModuleLoader Memory Leaks** (src/ModuleLoader.cpp:34-39)
- Auto-unload previous library before loading new one
- Prevents library handle leaks (+200 .so mappings eliminated)
- Prevents temp file accumulation (778 files → 1-2 files)
- Memory leak reduced by 97%: 36.5 MB → 1.9 MB

**Test Results - Before Fix:**
- Memory growth: 36.5 MB 
- Per reload: 187.1 KB 
- Temp files: 778 
- Mapped .so: +200 

**Test Results - After Fix:**
- Memory growth: 1.9 MB 
- Per reload: 9.7 KB 
- Temp files: 1-2 
- Mapped .so: stable 
- 200/200 reloads successful (100%)

**Enhanced SystemUtils helpers:**
- countTempFiles(): Count temp module files
- getMappedLibraryCount(): Track .so handle leaks via /proc/self/maps

**Test Lifecycle Improvements:**
- test_04 & test_05: Destroy old module before reload to prevent use-after-free
- Proper state/config preservation across reload boundary

**Files Modified:**
- src/ModuleLoader.cpp: Auto-unload on load()
- tests/integration/test_05_memory_leak.cpp: NEW - 200 cycle leak detector
- tests/modules/LeakTestModule.cpp: NEW - Test module with allocations
- tests/helpers/SystemUtils.{h,cpp}: Memory monitoring functions
- tests/integration/test_04_race_condition.cpp: Fixed module lifecycle
- tests/CMakeLists.txt: Added test_05 and LeakTestModule

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 10:06:18 +08:00

209 lines
5.4 KiB
CMake

# Hot-reload test suite
# Test module as shared library (.so) for hot-reload
add_library(TestModule SHARED
modules/TestModule.cpp
)
target_link_libraries(TestModule PRIVATE
GroveEngine::core
GroveEngine::impl # For JsonDataNode implementation
)
# Don't add "lib" prefix on Linux (we want TestModule.so, not libTestModule.so)
set_target_properties(TestModule PROPERTIES PREFIX "lib")
set_target_properties(TestModule PROPERTIES OUTPUT_NAME "TestModule")
# Basic hot-reload test executable (manual dlopen/dlclose)
add_executable(test_hotreload
hotreload/test_hotreload.cpp
)
target_link_libraries(test_hotreload PRIVATE
GroveEngine::core
GroveEngine::impl # For JsonDataNode implementation
${CMAKE_DL_LIBS} # For dlopen/dlclose
)
# Make sure test module is built before test executable
add_dependencies(test_hotreload TestModule)
# Copy test module to test executable directory after build
add_custom_command(TARGET test_hotreload POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:TestModule>
$<TARGET_FILE_DIR:test_hotreload>/
COMMENT "Copying TestModule.so to test directory"
)
# Engine hot-reload test (uses DebugEngine + SequentialModuleSystem + FileWatcher)
add_executable(test_engine_hotreload
hotreload/test_engine_hotreload.cpp
)
target_link_libraries(test_engine_hotreload PRIVATE
GroveEngine::core
GroveEngine::impl
${CMAKE_DL_LIBS}
)
add_dependencies(test_engine_hotreload TestModule)
add_custom_command(TARGET test_engine_hotreload POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:TestModule>
$<TARGET_FILE_DIR:test_engine_hotreload>/
COMMENT "Copying TestModule.so to engine test directory"
)
# ================================================================================
# Integration Tests
# ================================================================================
# Helpers library (partagée par tous les tests)
add_library(test_helpers STATIC
helpers/TestMetrics.cpp
helpers/TestReporter.cpp
helpers/SystemUtils.cpp
helpers/AutoCompiler.cpp
)
target_include_directories(test_helpers PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(test_helpers PUBLIC
GroveEngine::core
spdlog::spdlog
)
# Set PIC for static library
set_target_properties(test_helpers PROPERTIES POSITION_INDEPENDENT_CODE ON)
# TankModule pour tests d'intégration
add_library(TankModule SHARED
modules/TankModule.cpp
)
target_link_libraries(TankModule PRIVATE
GroveEngine::core
GroveEngine::impl
spdlog::spdlog
)
# Ensure spdlog is compiled with PIC
set_target_properties(spdlog PROPERTIES POSITION_INDEPENDENT_CODE ON)
# Test 01: Production Hot-Reload
add_executable(test_01_production_hotreload
integration/test_01_production_hotreload.cpp
)
target_link_libraries(test_01_production_hotreload PRIVATE
test_helpers
GroveEngine::core
GroveEngine::impl
)
add_dependencies(test_01_production_hotreload TankModule)
# CTest integration
add_test(NAME ProductionHotReload COMMAND test_01_production_hotreload)
# ChaosModule pour tests de robustesse
add_library(ChaosModule SHARED
modules/ChaosModule.cpp
)
target_link_libraries(ChaosModule PRIVATE
GroveEngine::core
GroveEngine::impl
spdlog::spdlog
)
# Test 02: Chaos Monkey
add_executable(test_02_chaos_monkey
integration/test_02_chaos_monkey.cpp
)
target_link_libraries(test_02_chaos_monkey PRIVATE
test_helpers
GroveEngine::core
GroveEngine::impl
)
add_dependencies(test_02_chaos_monkey ChaosModule)
# CTest integration
add_test(NAME ChaosMonkey COMMAND test_02_chaos_monkey)
# StressModule pour tests de stabilité long-terme
add_library(StressModule SHARED
modules/StressModule.cpp
)
target_link_libraries(StressModule PRIVATE
GroveEngine::core
GroveEngine::impl
spdlog::spdlog
)
# Test 03: Stress Test - 10 minutes stability
add_executable(test_03_stress_test
integration/test_03_stress_test.cpp
)
target_link_libraries(test_03_stress_test PRIVATE
test_helpers
GroveEngine::core
GroveEngine::impl
)
add_dependencies(test_03_stress_test StressModule)
# CTest integration
add_test(NAME StressTest COMMAND test_03_stress_test)
# Test 04: Race Condition Hunter - Concurrent compilation & reload
add_executable(test_04_race_condition
integration/test_04_race_condition.cpp
)
target_link_libraries(test_04_race_condition PRIVATE
test_helpers
GroveEngine::core
GroveEngine::impl
)
# This test uses TestModule (not TankModule)
add_dependencies(test_04_race_condition TestModule)
# CTest integration
add_test(NAME RaceConditionHunter COMMAND test_04_race_condition)
# LeakTestModule pour memory leak detection
add_library(LeakTestModule SHARED
modules/LeakTestModule.cpp
)
target_link_libraries(LeakTestModule PRIVATE
GroveEngine::core
GroveEngine::impl
)
# Test 05: Memory Leak Hunter - 200 reload cycles
add_executable(test_05_memory_leak
integration/test_05_memory_leak.cpp
)
target_link_libraries(test_05_memory_leak PRIVATE
test_helpers
GroveEngine::core
GroveEngine::impl
)
add_dependencies(test_05_memory_leak LeakTestModule)
# CTest integration
add_test(NAME MemoryLeakHunter COMMAND test_05_memory_leak)