Add comprehensive concurrent compilation and hot-reload testing infrastructure
to validate thread safety and file stability during race conditions.
## New Components
### AutoCompiler Helper (tests/helpers/AutoCompiler.{h,cpp})
- Automatically modifies source files to bump version numbers
- Compiles modules repeatedly on separate thread (15 iterations @ 1s interval)
- Tracks compilation success/failure rates with atomic counters
- Thread-safe compilation statistics
### Race Condition Test (tests/integration/test_04_race_condition.cpp)
- **3 concurrent threads:**
- Compiler: Recompiles TestModule.so every 1 second
- FileWatcher: Detects .so changes and triggers hot-reload with mutex protection
- Engine: Runs at 60 FPS with try_lock to skip frames during reload
- Validates module integrity (health status, version, configuration)
- Tracks metrics: compilation rate, reload success, corrupted loads, crashes
- 90-second timeout with progress monitoring
### TestModule Enhancements (tests/modules/TestModule.cpp)
- Added global moduleVersion variable for AutoCompiler modification
- Version bumping support for reload validation
## Test Results (Initial Implementation)
```
Duration: 88s
Compilations: 15/15 (100%) ✅
Reloads: ~30 (100% success) ✅
Corrupted: 0 ✅
Crashes: 0 ✅
File Stability: 328ms avg (proves >100ms wait) ✅
```
## Known Issue (To Fix in Next Commit)
- Module versions not actually changing during reload
- setConfiguration() overwrites compiled version
- Reload mechanism validated but version bumping needs fix
## Files Modified
- tests/CMakeLists.txt: Add AutoCompiler to helpers, add test_04
- tests/modules/TestModule.cpp: Add version bumping support
- .gitignore: Add build/ and logs/
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
113 lines
3.0 KiB
C++
113 lines
3.0 KiB
C++
#include "AutoCompiler.h"
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <regex>
|
|
#include <filesystem>
|
|
#include <chrono>
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
|
|
namespace TestHelpers {
|
|
|
|
AutoCompiler::AutoCompiler(const std::string& moduleName,
|
|
const std::string& buildDir,
|
|
const std::string& sourcePath)
|
|
: moduleName_(moduleName)
|
|
, buildDir_(buildDir)
|
|
, sourcePath_(sourcePath)
|
|
{
|
|
}
|
|
|
|
AutoCompiler::~AutoCompiler() {
|
|
stop();
|
|
}
|
|
|
|
void AutoCompiler::start(int iterations, int intervalMs) {
|
|
if (running_.load()) {
|
|
return; // Already running
|
|
}
|
|
|
|
running_ = true;
|
|
compilationThread_ = std::thread(&AutoCompiler::compilationLoop, this, iterations, intervalMs);
|
|
}
|
|
|
|
void AutoCompiler::stop() {
|
|
running_ = false;
|
|
if (compilationThread_.joinable()) {
|
|
compilationThread_.join();
|
|
}
|
|
}
|
|
|
|
void AutoCompiler::waitForCompletion() {
|
|
if (compilationThread_.joinable()) {
|
|
compilationThread_.join();
|
|
}
|
|
}
|
|
|
|
void AutoCompiler::modifySourceVersion(int iteration) {
|
|
// Read entire file
|
|
std::ifstream inFile(sourcePath_);
|
|
if (!inFile.is_open()) {
|
|
std::cerr << "[AutoCompiler] Failed to open source file: " << sourcePath_ << std::endl;
|
|
return;
|
|
}
|
|
|
|
std::stringstream buffer;
|
|
buffer << inFile.rdbuf();
|
|
inFile.close();
|
|
|
|
std::string content = buffer.str();
|
|
|
|
// Replace version string: moduleVersion = "vX" → moduleVersion = "vITERATION"
|
|
std::regex versionRegex(R"(std::string\s+moduleVersion\s*=\s*"v\d+")");
|
|
std::string newVersion = "std::string moduleVersion = \"v" + std::to_string(iteration) + "\"";
|
|
content = std::regex_replace(content, versionRegex, newVersion);
|
|
|
|
// Write back to file
|
|
std::ofstream outFile(sourcePath_);
|
|
if (!outFile.is_open()) {
|
|
std::cerr << "[AutoCompiler] Failed to write source file: " << sourcePath_ << std::endl;
|
|
return;
|
|
}
|
|
|
|
outFile << content;
|
|
outFile.close();
|
|
}
|
|
|
|
bool AutoCompiler::compile(int iteration) {
|
|
// Modify source version before compiling
|
|
modifySourceVersion(iteration);
|
|
|
|
// Small delay to ensure file is written
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
// Build the module using CMake
|
|
std::string command = "cmake --build " + buildDir_ + " --target " + moduleName_ + " 2>&1 > /dev/null";
|
|
int result = std::system(command.c_str());
|
|
|
|
return (result == 0);
|
|
}
|
|
|
|
void AutoCompiler::compilationLoop(int iterations, int intervalMs) {
|
|
for (int i = 1; i <= iterations && running_.load(); ++i) {
|
|
currentIteration_ = i;
|
|
|
|
// Compile
|
|
bool success = compile(i);
|
|
if (success) {
|
|
successCount_++;
|
|
} else {
|
|
failureCount_++;
|
|
}
|
|
|
|
// Wait for next iteration
|
|
if (i < iterations) {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));
|
|
}
|
|
}
|
|
|
|
running_ = false;
|
|
}
|
|
|
|
} // namespace TestHelpers
|