#include "TestMetrics.h" #include #include namespace grove { void TestMetrics::recordFPS(float fps) { fpsValues.push_back(fps); } void TestMetrics::recordMemoryUsage(size_t bytes) { if (!hasInitialMemory) { initialMemory = bytes; hasInitialMemory = true; } memoryValues.push_back(bytes); } void TestMetrics::recordReloadTime(float ms) { reloadTimes.push_back(ms); } void TestMetrics::recordCrash(const std::string& reason) { crashReasons.push_back(reason); } float TestMetrics::getFPSMin() const { if (fpsValues.empty()) return 0.0f; return *std::min_element(fpsValues.begin(), fpsValues.end()); } float TestMetrics::getFPSMax() const { if (fpsValues.empty()) return 0.0f; return *std::max_element(fpsValues.begin(), fpsValues.end()); } float TestMetrics::getFPSAvg() const { if (fpsValues.empty()) return 0.0f; return std::accumulate(fpsValues.begin(), fpsValues.end(), 0.0f) / fpsValues.size(); } float TestMetrics::getFPSStdDev() const { if (fpsValues.empty()) return 0.0f; float avg = getFPSAvg(); float variance = 0.0f; for (float fps : fpsValues) { variance += std::pow(fps - avg, 2); } return std::sqrt(variance / fpsValues.size()); } float TestMetrics::getFPSMinLast60s() const { if (fpsValues.empty()) return 0.0f; // Last 60 seconds = 3600 frames at 60 FPS size_t startIdx = fpsValues.size() > 3600 ? fpsValues.size() - 3600 : 0; auto it = std::min_element(fpsValues.begin() + startIdx, fpsValues.end()); return *it; } float TestMetrics::getFPSAvgLast60s() const { if (fpsValues.empty()) return 0.0f; size_t startIdx = fpsValues.size() > 3600 ? fpsValues.size() - 3600 : 0; float sum = std::accumulate(fpsValues.begin() + startIdx, fpsValues.end(), 0.0f); return sum / (fpsValues.size() - startIdx); } size_t TestMetrics::getMemoryInitial() const { return initialMemory; } size_t TestMetrics::getMemoryFinal() const { if (memoryValues.empty()) return 0; return memoryValues.back(); } size_t TestMetrics::getMemoryGrowth() const { if (memoryValues.empty()) return 0; return memoryValues.back() - initialMemory; } size_t TestMetrics::getMemoryPeak() const { if (memoryValues.empty()) return 0; return *std::max_element(memoryValues.begin(), memoryValues.end()); } float TestMetrics::getReloadTimeAvg() const { if (reloadTimes.empty()) return 0.0f; return std::accumulate(reloadTimes.begin(), reloadTimes.end(), 0.0f) / reloadTimes.size(); } float TestMetrics::getReloadTimeMin() const { if (reloadTimes.empty()) return 0.0f; return *std::min_element(reloadTimes.begin(), reloadTimes.end()); } float TestMetrics::getReloadTimeMax() const { if (reloadTimes.empty()) return 0.0f; return *std::max_element(reloadTimes.begin(), reloadTimes.end()); } float TestMetrics::getReloadTimeP99() const { if (reloadTimes.empty()) return 0.0f; auto sorted = reloadTimes; std::sort(sorted.begin(), sorted.end()); size_t p99Index = static_cast(sorted.size() * 0.99); if (p99Index >= sorted.size()) p99Index = sorted.size() - 1; return sorted[p99Index]; } int TestMetrics::getReloadCount() const { return static_cast(reloadTimes.size()); } int TestMetrics::getCrashCount() const { return static_cast(crashReasons.size()); } const std::vector& TestMetrics::getCrashReasons() const { return crashReasons; } void TestMetrics::printReport() const { std::cout << "╔══════════════════════════════════════════════════════════════\n"; std::cout << "║ METRICS REPORT\n"; std::cout << "╠══════════════════════════════════════════════════════════════\n"; if (!fpsValues.empty()) { std::cout << "║ FPS:\n"; std::cout << "║ Min: " << std::setw(8) << std::fixed << std::setprecision(2) << getFPSMin() << "\n"; std::cout << "║ Avg: " << std::setw(8) << std::fixed << std::setprecision(2) << getFPSAvg() << "\n"; std::cout << "║ Max: " << std::setw(8) << std::fixed << std::setprecision(2) << getFPSMax() << "\n"; std::cout << "║ Std Dev: " << std::setw(8) << std::fixed << std::setprecision(2) << getFPSStdDev() << "\n"; } if (!memoryValues.empty()) { std::cout << "║ Memory:\n"; std::cout << "║ Initial: " << std::setw(8) << std::fixed << std::setprecision(2) << (initialMemory / 1024.0f / 1024.0f) << " MB\n"; std::cout << "║ Final: " << std::setw(8) << std::fixed << std::setprecision(2) << (memoryValues.back() / 1024.0f / 1024.0f) << " MB\n"; std::cout << "║ Peak: " << std::setw(8) << std::fixed << std::setprecision(2) << (getMemoryPeak() / 1024.0f / 1024.0f) << " MB\n"; std::cout << "║ Growth: " << std::setw(8) << std::fixed << std::setprecision(2) << (getMemoryGrowth() / 1024.0f / 1024.0f) << " MB\n"; } if (!reloadTimes.empty()) { std::cout << "║ Reload Times:\n"; std::cout << "║ Count: " << std::setw(8) << reloadTimes.size() << "\n"; std::cout << "║ Avg: " << std::setw(8) << std::fixed << std::setprecision(2) << getReloadTimeAvg() << " ms\n"; std::cout << "║ Min: " << std::setw(8) << std::fixed << std::setprecision(2) << getReloadTimeMin() << " ms\n"; std::cout << "║ Max: " << std::setw(8) << std::fixed << std::setprecision(2) << getReloadTimeMax() << " ms\n"; std::cout << "║ P99: " << std::setw(8) << std::fixed << std::setprecision(2) << getReloadTimeP99() << " ms\n"; } if (!crashReasons.empty()) { std::cout << "║ Crashes: " << crashReasons.size() << "\n"; for (const auto& reason : crashReasons) { std::cout << "║ - " << reason << "\n"; } } std::cout << "╚══════════════════════════════════════════════════════════════\n"; } } // namespace grove