yams/tests/unit/daemon/tune_advisor_threshold_test.cpp
trvon be24d07c27
fix(ci): stabilize sanitizer validation
Rebuild macOS TSan outputs so compiler instrumentation and compiler-rt come from one Apple toolchain generation. Preserve daemon fixture dependencies until ServiceManager teardown so sanitizer lanes do not exercise a test-only use-after-free.
2026-08-08 11:42:33 -04:00

1041 lines
40 KiB
C++

// TuneAdvisor model eviction threshold tests (Catch2)
// Tests profile defaults, env var overrides, programmatic setters, and validation
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include <yams/daemon/components/TuneAdvisor.h>
#include <yams/daemon/resource/gpu_info.h>
#include <array>
#include <cmath>
#include <cstdlib>
#include <limits>
#include <string>
#include "../../common/env_compat.h"
#include "../../common/test_helpers_catch2.h"
#include <yams/compat/unistd.h>
using namespace yams::daemon;
// =============================================================================
// Test Helpers
// =============================================================================
namespace {
/// RAII guard for TuneAdvisor profile restoration
class ProfileGuard {
TuneAdvisor::Profile prev_;
public:
explicit ProfileGuard(TuneAdvisor::Profile profile) : prev_(TuneAdvisor::tuningProfile()) {
TuneAdvisor::setTuningProfile(profile);
}
~ProfileGuard() { TuneAdvisor::setTuningProfile(prev_); }
ProfileGuard(const ProfileGuard&) = delete;
ProfileGuard& operator=(const ProfileGuard&) = delete;
};
using yams::test::ScopedEnvVar;
class PostIngestStageActivityGuard {
public:
PostIngestStageActivityGuard() {
try {
for (; acquired_ < kStages.size(); ++acquired_) {
tokens_[acquired_] =
TuneAdvisor::acquirePostIngestStageActivity(kStages[acquired_]);
}
} catch (...) {
releaseAcquired();
throw;
}
}
~PostIngestStageActivityGuard() { releaseAcquired(); }
PostIngestStageActivityGuard(const PostIngestStageActivityGuard&) = delete;
PostIngestStageActivityGuard& operator=(const PostIngestStageActivityGuard&) = delete;
private:
void releaseAcquired() noexcept {
while (acquired_ > 0) {
--acquired_;
TuneAdvisor::releasePostIngestStageActivity(kStages[acquired_], tokens_[acquired_]);
}
}
static constexpr std::array kStages{
TuneAdvisor::PostIngestStage::Extraction, TuneAdvisor::PostIngestStage::KnowledgeGraph,
TuneAdvisor::PostIngestStage::Symbol, TuneAdvisor::PostIngestStage::Entity,
TuneAdvisor::PostIngestStage::Title, TuneAdvisor::PostIngestStage::Embed,
};
std::array<TuneAdvisor::PostIngestStageActivityToken, kStages.size()> tokens_{};
std::size_t acquired_{0};
};
class HardwareGuard {
unsigned prev_;
public:
explicit HardwareGuard(unsigned hw) : prev_(TuneAdvisor::hardwareConcurrency()) {
TuneAdvisor::setHardwareConcurrencyForTests(hw);
}
~HardwareGuard() { TuneAdvisor::setHardwareConcurrencyForTests(prev_); }
HardwareGuard(const HardwareGuard&) = delete;
HardwareGuard& operator=(const HardwareGuard&) = delete;
};
/// Reset all eviction threshold overrides to default (unset)
void resetEvictionOverrides() {
TuneAdvisor::resetModelEvictThresholdOverrides();
}
} // namespace
// =============================================================================
// Profile Default Tests
// =============================================================================
TEST_CASE("Model eviction threshold profile defaults", "[daemon][tune][advisor][catch2]") {
resetEvictionOverrides();
SECTION("Efficient profile thresholds") {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.30));
CHECK(TuneAdvisor::modelEvictCriticalThreshold() == Catch::Approx(0.50));
CHECK(TuneAdvisor::modelEvictEmergencyThreshold() == Catch::Approx(0.70));
}
SECTION("Balanced profile thresholds") {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.60));
CHECK(TuneAdvisor::modelEvictCriticalThreshold() == Catch::Approx(0.75));
CHECK(TuneAdvisor::modelEvictEmergencyThreshold() == Catch::Approx(0.90));
}
SECTION("Aggressive profile thresholds") {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.75));
CHECK(TuneAdvisor::modelEvictCriticalThreshold() == Catch::Approx(0.85));
CHECK(TuneAdvisor::modelEvictEmergencyThreshold() == Catch::Approx(0.95));
}
}
// =============================================================================
// Environment Variable Override Tests
// =============================================================================
TEST_CASE("Model eviction threshold env var overrides", "[daemon][tune][advisor][catch2]") {
resetEvictionOverrides();
ProfileGuard profileGuard(TuneAdvisor::Profile::Balanced);
SECTION("Valid warning threshold env var overrides profile default") {
ScopedEnvVar envGuard("YAMS_MODEL_EVICT_WARNING_THRESHOLD", "0.45");
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.45));
}
SECTION("Valid critical threshold env var overrides profile default") {
ScopedEnvVar envGuard("YAMS_MODEL_EVICT_CRITICAL_THRESHOLD", "0.80");
CHECK(TuneAdvisor::modelEvictCriticalThreshold() == Catch::Approx(0.80));
}
SECTION("Valid emergency threshold env var overrides profile default") {
ScopedEnvVar envGuard("YAMS_MODEL_EVICT_EMERGENCY_THRESHOLD", "0.92");
CHECK(TuneAdvisor::modelEvictEmergencyThreshold() == Catch::Approx(0.92));
}
SECTION("Invalid env var (non-numeric) falls back to profile default") {
ScopedEnvVar envGuard("YAMS_MODEL_EVICT_WARNING_THRESHOLD", "invalid");
// Should return profile default (0.60 for Balanced), not crash
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.60));
}
SECTION("Env var exactly 1.0 is rejected (must be < 1.0)") {
ScopedEnvVar envGuard("YAMS_MODEL_EVICT_WARNING_THRESHOLD", "1.0");
// 1.0 should be rejected, falls back to profile default
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.60));
}
SECTION("Env var exactly 0.0 is rejected (must be > 0.0)") {
ScopedEnvVar envGuard("YAMS_MODEL_EVICT_WARNING_THRESHOLD", "0.0");
// 0.0 should be rejected, falls back to profile default
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.60));
}
SECTION("Negative env var is rejected") {
ScopedEnvVar envGuard("YAMS_MODEL_EVICT_WARNING_THRESHOLD", "-0.5");
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.60));
}
SECTION("Env var > 1.0 is rejected") {
ScopedEnvVar envGuard("YAMS_MODEL_EVICT_WARNING_THRESHOLD", "1.5");
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.60));
}
}
// =============================================================================
// Programmatic Setter Tests
// =============================================================================
TEST_CASE("Model eviction threshold setter validation", "[daemon][tune][advisor][catch2]") {
resetEvictionOverrides();
ProfileGuard profileGuard(TuneAdvisor::Profile::Balanced);
SECTION("Valid values are accepted") {
TuneAdvisor::setModelEvictWarningThreshold(0.5);
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.5));
TuneAdvisor::setModelEvictCriticalThreshold(0.7);
CHECK(TuneAdvisor::modelEvictCriticalThreshold() == Catch::Approx(0.7));
TuneAdvisor::setModelEvictEmergencyThreshold(0.85);
CHECK(TuneAdvisor::modelEvictEmergencyThreshold() == Catch::Approx(0.85));
}
SECTION("Boundary values near 0 and 1 are handled correctly") {
// Just above 0 should be accepted
TuneAdvisor::setModelEvictWarningThreshold(0.001);
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.001));
// Just below 1 should be accepted
TuneAdvisor::setModelEvictCriticalThreshold(0.999);
CHECK(TuneAdvisor::modelEvictCriticalThreshold() == Catch::Approx(0.999));
}
SECTION("Negative values are rejected") {
double prev = TuneAdvisor::modelEvictWarningThreshold();
TuneAdvisor::setModelEvictWarningThreshold(-0.5);
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(prev));
}
SECTION("Zero is rejected") {
double prev = TuneAdvisor::modelEvictCriticalThreshold();
TuneAdvisor::setModelEvictCriticalThreshold(0.0);
CHECK(TuneAdvisor::modelEvictCriticalThreshold() == Catch::Approx(prev));
}
SECTION("Values >= 1.0 are rejected") {
double prev = TuneAdvisor::modelEvictEmergencyThreshold();
TuneAdvisor::setModelEvictEmergencyThreshold(1.0);
CHECK(TuneAdvisor::modelEvictEmergencyThreshold() == Catch::Approx(prev));
TuneAdvisor::setModelEvictEmergencyThreshold(1.5);
CHECK(TuneAdvisor::modelEvictEmergencyThreshold() == Catch::Approx(prev));
TuneAdvisor::setModelEvictEmergencyThreshold(100.0);
CHECK(TuneAdvisor::modelEvictEmergencyThreshold() == Catch::Approx(prev));
}
SECTION("NaN is rejected") {
double prev = TuneAdvisor::modelEvictWarningThreshold();
TuneAdvisor::setModelEvictWarningThreshold(std::numeric_limits<double>::quiet_NaN());
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(prev));
}
SECTION("Positive infinity is rejected") {
double prev = TuneAdvisor::modelEvictCriticalThreshold();
TuneAdvisor::setModelEvictCriticalThreshold(std::numeric_limits<double>::infinity());
CHECK(TuneAdvisor::modelEvictCriticalThreshold() == Catch::Approx(prev));
}
SECTION("Negative infinity is rejected") {
double prev = TuneAdvisor::modelEvictEmergencyThreshold();
TuneAdvisor::setModelEvictEmergencyThreshold(-std::numeric_limits<double>::infinity());
CHECK(TuneAdvisor::modelEvictEmergencyThreshold() == Catch::Approx(prev));
}
}
// =============================================================================
// Override Precedence Tests
// =============================================================================
TEST_CASE("Model eviction threshold override precedence", "[daemon][tune][advisor][catch2]") {
resetEvictionOverrides();
SECTION("Programmatic setter takes precedence over env var") {
ProfileGuard profileGuard(TuneAdvisor::Profile::Balanced);
ScopedEnvVar envGuard("YAMS_MODEL_EVICT_WARNING_THRESHOLD", "0.45");
// Env var would give 0.45, but setter should override
TuneAdvisor::setModelEvictWarningThreshold(0.55);
CHECK(TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.55));
}
SECTION("Env var takes precedence over profile default") {
ProfileGuard profileGuard(TuneAdvisor::Profile::Efficient);
ScopedEnvVar envGuard("YAMS_MODEL_EVICT_CRITICAL_THRESHOLD", "0.65");
// Efficient profile default is 0.50, but env var overrides to 0.65
CHECK(TuneAdvisor::modelEvictCriticalThreshold() == Catch::Approx(0.65));
}
SECTION("Profile default is used when no overrides set") {
ProfileGuard profileGuard(TuneAdvisor::Profile::Aggressive);
CHECK(TuneAdvisor::modelEvictEmergencyThreshold() == Catch::Approx(0.95));
}
}
TEST_CASE("Governor warning scale percent tuning", "[daemon][tune][advisor][catch2]") {
TuneAdvisor::resetGovernorWarningScalePercentOverride();
SECTION("Default value is 85 percent") {
CHECK(TuneAdvisor::governorWarningScalePercent() == 85u);
}
SECTION("Valid env var overrides default") {
ScopedEnvVar envGuard("YAMS_GOV_WARNING_SCALE_PCT", "92");
CHECK(TuneAdvisor::governorWarningScalePercent() == 92u);
}
SECTION("Invalid env var falls back to default") {
ScopedEnvVar envGuard("YAMS_GOV_WARNING_SCALE_PCT", "invalid");
CHECK(TuneAdvisor::governorWarningScalePercent() == 85u);
}
SECTION("Out-of-range env var falls back to default") {
ScopedEnvVar envGuard("YAMS_GOV_WARNING_SCALE_PCT", "9");
CHECK(TuneAdvisor::governorWarningScalePercent() == 85u);
}
SECTION("Programmatic setter overrides env var") {
ScopedEnvVar envGuard("YAMS_GOV_WARNING_SCALE_PCT", "90");
TuneAdvisor::setGovernorWarningScalePercent(77u);
CHECK(TuneAdvisor::governorWarningScalePercent() == 77u);
}
SECTION("Setter clamps values into allowed range") {
TuneAdvisor::setGovernorWarningScalePercent(5u);
CHECK(TuneAdvisor::governorWarningScalePercent() == 10u);
TuneAdvisor::setGovernorWarningScalePercent(150u);
CHECK(TuneAdvisor::governorWarningScalePercent() == 100u);
}
SECTION("Setter zero resets override") {
TuneAdvisor::setGovernorWarningScalePercent(72u);
CHECK(TuneAdvisor::governorWarningScalePercent() == 72u);
TuneAdvisor::setGovernorWarningScalePercent(0u);
CHECK(TuneAdvisor::governorWarningScalePercent() == 85u);
}
TuneAdvisor::resetGovernorWarningScalePercentOverride();
}
TEST_CASE("Connection lifetime seconds env var", "[daemon][tune][advisor][catch2]") {
TuneAdvisor::resetConnectionLifetimeSecondsOverride();
SECTION("Default value is 300 seconds") {
CHECK(TuneAdvisor::connectionLifetimeSeconds() == 300);
}
SECTION("Valid env var overrides default") {
ScopedEnvVar envGuard("YAMS_CONNECTION_LIFETIME_S", "1800");
CHECK(TuneAdvisor::connectionLifetimeSeconds() == 1800);
}
SECTION("Zero env var disables lifetime enforcement") {
ScopedEnvVar envGuard("YAMS_CONNECTION_LIFETIME_S", "0");
CHECK(TuneAdvisor::connectionLifetimeSeconds() == 0);
}
SECTION("Out-of-range env var falls back to default") {
ScopedEnvVar envGuard("YAMS_CONNECTION_LIFETIME_S", "90000");
CHECK(TuneAdvisor::connectionLifetimeSeconds() == 300);
}
SECTION("Programmatic setter overrides env") {
ScopedEnvVar envGuard("YAMS_CONNECTION_LIFETIME_S", "1800");
TuneAdvisor::setConnectionLifetimeSeconds(42);
CHECK(TuneAdvisor::connectionLifetimeSeconds() == 42);
TuneAdvisor::resetConnectionLifetimeSecondsOverride();
}
TuneAdvisor::resetConnectionLifetimeSecondsOverride();
}
// =============================================================================
// Threshold Ordering Invariant Tests
// =============================================================================
TEST_CASE("Threshold ordering invariant: warning < critical < emergency",
"[daemon][tune][advisor][catch2]") {
resetEvictionOverrides();
SECTION("Efficient profile maintains ordering") {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
CHECK(TuneAdvisor::modelEvictWarningThreshold() <
TuneAdvisor::modelEvictCriticalThreshold());
CHECK(TuneAdvisor::modelEvictCriticalThreshold() <
TuneAdvisor::modelEvictEmergencyThreshold());
}
SECTION("Balanced profile maintains ordering") {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
CHECK(TuneAdvisor::modelEvictWarningThreshold() <
TuneAdvisor::modelEvictCriticalThreshold());
CHECK(TuneAdvisor::modelEvictCriticalThreshold() <
TuneAdvisor::modelEvictEmergencyThreshold());
}
SECTION("Aggressive profile maintains ordering") {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
CHECK(TuneAdvisor::modelEvictWarningThreshold() <
TuneAdvisor::modelEvictCriticalThreshold());
CHECK(TuneAdvisor::modelEvictCriticalThreshold() <
TuneAdvisor::modelEvictEmergencyThreshold());
}
}
// =============================================================================
// Repair Batch Size Profile Tests
// =============================================================================
TEST_CASE("Repair batch sizes are profile-aware", "[daemon][tune][advisor][catch2]") {
SECTION("Efficient profile uses smaller batch sizes") {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
// Efficient profile scale is 0.0
// repairMaxBatch: 8 + 24*0.0 = 8
// repairStartupBatchSize: 25 + 75*0.0 = 25
CHECK(TuneAdvisor::repairMaxBatch() == 8u);
CHECK(TuneAdvisor::repairStartupBatchSize() == 25u);
}
SECTION("Balanced profile uses default batch sizes") {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
// Balanced profile scale is 0.5
// repairMaxBatch: 8 + 24*0.5 = 20
// repairStartupBatchSize: 25 + 75*0.5 = 62
CHECK(TuneAdvisor::repairMaxBatch() == 20u);
CHECK(TuneAdvisor::repairStartupBatchSize() == 62u);
}
SECTION("Aggressive profile uses larger batch sizes") {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
// Aggressive profile scale is 1.0
// repairMaxBatch: 8 + 24*1.0 = 32
// repairStartupBatchSize: 25 + 75*1.0 = 100
CHECK(TuneAdvisor::repairMaxBatch() == 32u);
CHECK(TuneAdvisor::repairStartupBatchSize() == 100u);
}
}
TEST_CASE("Repair batch size env var overrides", "[daemon][tune][advisor][catch2]") {
ProfileGuard profileGuard(TuneAdvisor::Profile::Balanced);
SECTION("YAMS_REPAIR_MAX_BATCH env var overrides profile") {
ScopedEnvVar envGuard("YAMS_REPAIR_MAX_BATCH", "64");
CHECK(TuneAdvisor::repairMaxBatch() == 64u);
}
SECTION("YAMS_REPAIR_STARTUP_BATCH env var overrides profile") {
ScopedEnvVar envGuard("YAMS_REPAIR_STARTUP_BATCH", "200");
CHECK(TuneAdvisor::repairStartupBatchSize() == 200u);
}
SECTION("Invalid env var falls back to profile default") {
ScopedEnvVar envGuard("YAMS_REPAIR_MAX_BATCH", "invalid");
// Should fall back to profile default (20 for Balanced at 0.5 scale)
CHECK(TuneAdvisor::repairMaxBatch() == 20u);
}
SECTION("Zero env var falls back to profile default") {
ScopedEnvVar envGuard("YAMS_REPAIR_MAX_BATCH", "0");
// 0 is rejected, should fall back to profile default
CHECK(TuneAdvisor::repairMaxBatch() == 20u);
}
SECTION("Env var > 1000 falls back to profile default") {
ScopedEnvVar envGuard("YAMS_REPAIR_MAX_BATCH", "1001");
// > 1000 is rejected, should fall back to profile default
CHECK(TuneAdvisor::repairMaxBatch() == 20u);
}
}
TEST_CASE("Embed doc cap resolves to safe default when unset",
"[daemon][tune][advisor][embed][catch2]") {
struct EmbedCapGuard {
std::size_t prev{TuneAdvisor::getEmbedDocCap()};
~EmbedCapGuard() { TuneAdvisor::setEmbedDocCap(prev); }
} guard;
SECTION("Unset cap uses built-in default (no singleton fallback)") {
// Ensure no explicit override is active.
TuneAdvisor::setEmbedDocCap(0);
// Invalid env value should be ignored and still resolve to default.
ScopedEnvVar envGuard("YAMS_EMBED_DOC_CAP", "0");
CHECK(TuneAdvisor::getEmbedDocCap() == 0u);
CHECK(TuneAdvisor::resolvedEmbedDocCap() == TuneAdvisor::kDefaultEmbedDocCap);
CHECK(TuneAdvisor::resolvedEmbedDocCap() == 64u);
}
SECTION("Explicit cap overrides default") {
TuneAdvisor::setEmbedDocCap(128);
CHECK(TuneAdvisor::resolvedEmbedDocCap() == 128u);
}
}
// =============================================================================
// Worker poll cadence overrides
// =============================================================================
TEST_CASE("Worker poll cadence overrides", "[daemon][tune][advisor][catch2]") {
SECTION("Dynamic updates apply when not pinned") {
ScopedEnvVar envGuard("YAMS_WORKER_POLL_MS", "0");
TuneAdvisor::setWorkerPollMs(0);
TuneAdvisor::setWorkerPollMsDynamic(200);
CHECK(TuneAdvisor::workerPollMs() == 200u);
TuneAdvisor::setWorkerPollMs(0);
TuneAdvisor::setWorkerPollMsDynamic(120);
CHECK(TuneAdvisor::workerPollMs() == 120u);
}
SECTION("Dynamic updates ignored when pinned by setter") {
ScopedEnvVar envGuard("YAMS_WORKER_POLL_MS", "0");
TuneAdvisor::setWorkerPollMs(250);
CHECK(TuneAdvisor::workerPollMs() == 250u);
TuneAdvisor::setWorkerPollMsDynamic(100);
CHECK(TuneAdvisor::workerPollMs() == 250u);
TuneAdvisor::setWorkerPollMs(0);
}
SECTION("Dynamic updates ignored when env pins cadence") {
ScopedEnvVar envGuard("YAMS_WORKER_POLL_MS", "300");
TuneAdvisor::setWorkerPollMs(0);
TuneAdvisor::setWorkerPollMsDynamic(100);
CHECK(TuneAdvisor::workerPollMs() == 300u);
}
}
TEST_CASE("Model maintenance thresholds use bounded environment overrides",
"[daemon][tune][advisor][catch2]") {
ProfileGuard profile(TuneAdvisor::Profile::Balanced);
ScopedEnvVar connectionThreshold("YAMS_MODEL_MAINT_CONN_THRESHOLD", "17");
ScopedEnvVar searchThreshold("YAMS_MODEL_MAINT_SEARCH_THRESHOLD", "19");
ScopedEnvVar queueThreshold("YAMS_MODEL_MAINT_QUEUE_THRESHOLD", "23");
CHECK(TuneAdvisor::modelMaintenanceConnThreshold() == 17);
CHECK(TuneAdvisor::modelMaintenanceSearchThreshold() == 19);
CHECK(TuneAdvisor::modelMaintenanceQueueThreshold() == 23);
connectionThreshold.set("101");
searchThreshold.set("101");
queueThreshold.set("10001");
CHECK(TuneAdvisor::modelMaintenanceConnThreshold() == 1);
CHECK(TuneAdvisor::modelMaintenanceSearchThreshold() == 1);
CHECK(TuneAdvisor::modelMaintenanceQueueThreshold() == 10);
}
TEST_CASE("Default-enabled tuning flags use explicit boolean overrides",
"[daemon][tune][advisor][catch2]") {
ScopedEnvVar proactiveEviction("YAMS_PROACTIVE_EVICTION", "off");
ScopedEnvVar semanticBackfill("YAMS_ENABLE_SEMANTIC_NEIGHBOR_BACKFILL", "no");
ScopedEnvVar gradientLimiters("YAMS_ENABLE_GRADIENT_LIMITERS", "false");
CHECK_FALSE(TuneAdvisor::enableProactiveEviction());
CHECK_FALSE(TuneAdvisor::enableSemanticNeighborBackfill());
CHECK_FALSE(TuneAdvisor::enableGradientLimiters());
proactiveEviction.set("invalid");
semanticBackfill.set("invalid");
gradientLimiters.set("invalid");
CHECK(TuneAdvisor::enableProactiveEviction());
CHECK(TuneAdvisor::enableSemanticNeighborBackfill());
CHECK(TuneAdvisor::enableGradientLimiters());
}
// =============================================================================
// GPU Detection Tests (Apple Silicon sysctl path)
// =============================================================================
TEST_CASE("detectGpu returns consistent cached result", "[daemon][gpu][catch2]") {
using namespace yams::daemon::resource;
const auto& first = detectGpu();
const auto& second = detectGpu();
// std::call_once caching: same static object returned each time
CHECK(&first == &second);
// Basic structural sanity: fields are default-initialized or populated
CHECK(first.detected == second.detected);
CHECK(first.name == second.name);
CHECK(first.vramBytes == second.vramBytes);
CHECK(first.provider == second.provider);
CHECK(first.unifiedMemory == second.unifiedMemory);
}
TEST_CASE("effectiveGpuBatchBudgetBytes applies unified-memory safety defaults",
"[daemon][gpu][batch-budget][catch2]") {
using namespace yams::daemon::resource;
constexpr uint64_t GiB = 1024ULL * 1024ULL * 1024ULL;
SECTION("Dedicated memory uses full detected budget by default") {
ScopedEnvVar clear1("YAMS_GPU_BATCH_BUDGET_MB", "");
ScopedEnvVar clear2("YAMS_GPU_UNIFIED_BUDGET_MB", "");
ScopedEnvVar clear3("YAMS_GPU_UNIFIED_BUDGET_FRACTION", "");
GpuInfo gpu;
gpu.detected = true;
gpu.unifiedMemory = false;
gpu.provider = "migraphx";
gpu.vramBytes = 12ULL * GiB;
CHECK(effectiveGpuBatchBudgetBytes(gpu) == 12ULL * GiB);
}
SECTION("Unified memory defaults to conservative 4 GiB cap") {
ScopedEnvVar clear1("YAMS_GPU_BATCH_BUDGET_MB", "");
ScopedEnvVar clear2("YAMS_GPU_UNIFIED_BUDGET_MB", "");
ScopedEnvVar clear3("YAMS_GPU_UNIFIED_BUDGET_FRACTION", "");
GpuInfo gpu;
gpu.detected = true;
gpu.unifiedMemory = true;
gpu.provider = "coreml";
gpu.vramBytes = 24ULL * GiB;
CHECK(effectiveGpuBatchBudgetBytes(gpu) == 4ULL * GiB);
}
SECTION("Unified memory MB override is honored") {
ScopedEnvVar clear1("YAMS_GPU_BATCH_BUDGET_MB", "");
ScopedEnvVar envMb("YAMS_GPU_UNIFIED_BUDGET_MB", "6144");
ScopedEnvVar clear3("YAMS_GPU_UNIFIED_BUDGET_FRACTION", "");
GpuInfo gpu;
gpu.detected = true;
gpu.unifiedMemory = true;
gpu.provider = "coreml";
gpu.vramBytes = 24ULL * GiB;
CHECK(effectiveGpuBatchBudgetBytes(gpu) == 6ULL * GiB);
}
SECTION("Global budget override takes precedence") {
ScopedEnvVar envGlobal("YAMS_GPU_BATCH_BUDGET_MB", "2048");
ScopedEnvVar envMb("YAMS_GPU_UNIFIED_BUDGET_MB", "6144");
ScopedEnvVar envFrac("YAMS_GPU_UNIFIED_BUDGET_FRACTION", "0.5");
GpuInfo gpu;
gpu.detected = true;
gpu.unifiedMemory = true;
gpu.provider = "coreml";
gpu.vramBytes = 24ULL * GiB;
CHECK(effectiveGpuBatchBudgetBytes(gpu) == 2ULL * GiB);
}
}
#if defined(__APPLE__) && defined(__aarch64__)
TEST_CASE("detectAppleSiliconGpu populates all fields", "[daemon][gpu][catch2]") {
using namespace yams::daemon::resource;
GpuInfo info;
bool result = detail::detectAppleSiliconGpu(info);
CHECK(result == true);
CHECK(info.detected == true);
CHECK(info.provider == "coreml");
CHECK(info.unifiedMemory == true);
// Brand string should contain "Apple" (e.g. "Apple M3 Max")
CHECK(info.name.find("Apple") != std::string::npos);
// VRAM should be populated
CHECK(info.vramBytes > 0);
// Cross-check: read hw.memsize via sysctl, verify vramBytes ≈ 75% of memsize
uint64_t memsize = 0;
size_t len = sizeof(memsize);
REQUIRE(detail::sysctlbyname("hw.memsize", &memsize, &len, nullptr, 0) == 0);
REQUIRE(memsize > 0);
uint64_t expected = static_cast<uint64_t>(static_cast<double>(memsize) * 0.75);
double ratio = static_cast<double>(info.vramBytes) / static_cast<double>(expected);
CHECK(ratio == Catch::Approx(1.0).margin(0.01));
// Sanity bounds: > 1 GB and < 1 TB
constexpr uint64_t oneGB = 1024ULL * 1024ULL * 1024ULL;
constexpr uint64_t oneTB = 1024ULL * oneGB;
CHECK(info.vramBytes > oneGB);
CHECK(info.vramBytes < oneTB);
}
#endif // __APPLE__ && __aarch64__
#if defined(__APPLE__)
TEST_CASE("sysctlString returns values for valid keys", "[daemon][gpu][catch2]") {
using namespace yams::daemon::resource::detail;
CHECK_FALSE(sysctlString("hw.machine").empty());
CHECK_FALSE(sysctlString("machdep.cpu.brand_string").empty());
// Invalid key should return empty gracefully (no crash)
CHECK(sysctlString("totally.invalid.key.xyz").empty());
}
#endif // __APPLE__
#if defined(__APPLE__) && !defined(__aarch64__)
TEST_CASE("detectAppleSiliconGpu returns false on non-ARM64", "[daemon][gpu][catch2]") {
using namespace yams::daemon::resource;
GpuInfo info;
bool result = detail::detectAppleSiliconGpu(info);
CHECK(result == false);
CHECK(info.detected == false);
}
#endif // __APPLE__ && !__aarch64__
// =============================================================================
// PostIngestQueue Profile-Aware Concurrency Tests
// =============================================================================
TEST_CASE("PostIngestQueue methods are profile-aware", "[daemon][tune][advisor][catch2]") {
resetEvictionOverrides();
PostIngestStageActivityGuard activeStages;
auto setHardwareConcurrency = [](unsigned value) {
yams::daemon::TuneAdvisor::setHardwareConcurrencyForTests(value);
};
SECTION("Efficient profile uses 0.0 scaling") {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
ScopedEnvVar maxThreadsGuard("YAMS_MAX_THREADS", "0");
ScopedEnvVar postIngestGuard("YAMS_POST_INGEST_TOTAL_CONCURRENT", "0");
setHardwareConcurrency(8);
// With hw=8, all 6 stages active: totalBudget = max(formula, 6) = 6.
// Each stage gets exactly 1 slot (budget distributed evenly).
CHECK(TuneAdvisor::postExtractionConcurrent() == 1u);
CHECK(TuneAdvisor::postKgConcurrent() == 1u);
CHECK(TuneAdvisor::postSymbolConcurrent() == 1u);
CHECK(TuneAdvisor::postEntityConcurrent() == 1u);
CHECK(TuneAdvisor::postTitleConcurrent() == 1u);
CHECK(TuneAdvisor::postEmbedConcurrent() == 1u);
}
SECTION("Balanced profile uses 0.5 scaling") {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
ScopedEnvVar maxThreadsGuard("YAMS_MAX_THREADS", "0");
ScopedEnvVar postIngestGuard("YAMS_POST_INGEST_TOTAL_CONCURRENT", "0");
setHardwareConcurrency(8);
// With hw=8, all 6 stages active: totalBudget = max(3, 6) = 6.
// Each stage gets exactly 1 slot.
CHECK(TuneAdvisor::postExtractionConcurrent() == 1u);
CHECK(TuneAdvisor::postKgConcurrent() == 1u);
CHECK(TuneAdvisor::postSymbolConcurrent() == 1u);
CHECK(TuneAdvisor::postEntityConcurrent() == 1u);
CHECK(TuneAdvisor::postTitleConcurrent() == 1u);
CHECK(TuneAdvisor::postEmbedConcurrent() == 1u);
}
SECTION("Aggressive profile uses 1.0x (defaults)") {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
ScopedEnvVar maxThreadsGuard("YAMS_MAX_THREADS", "0");
ScopedEnvVar postIngestGuard("YAMS_POST_INGEST_TOTAL_CONCURRENT", "0");
setHardwareConcurrency(8);
// With hw=8, round-up division: base=max(2,2)=2, scaleRange=max(1,2)=2.
// total=2+2=4. But 6 active stages, total=max(4,6)=6. Each gets 1.
CHECK(TuneAdvisor::postExtractionConcurrent() == 1u);
CHECK(TuneAdvisor::postKgConcurrent() == 1u);
CHECK(TuneAdvisor::postSymbolConcurrent() == 1u);
CHECK(TuneAdvisor::postEntityConcurrent() == 1u);
CHECK(TuneAdvisor::postTitleConcurrent() == 1u);
CHECK(TuneAdvisor::postEmbedConcurrent() == 1u);
}
}
// =============================================================================
// statusTickMs Default and Override Tests
// =============================================================================
TEST_CASE("statusTickMs defaults to 5", "[daemon][governance][catch2]") {
CHECK(TuneAdvisor::statusTickMs() == 5);
}
TEST_CASE("statusTickMs remains fixed at 5ms", "[daemon][governance][catch2]") {
CHECK(TuneAdvisor::statusTickMs() == 5);
}
TEST_CASE("Thread reserve is profile-aware", "[daemon][governance][catch2]") {
for (unsigned hw : {8u, 16u}) {
const auto effReserve = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
return TuneAdvisor::testing_reservedHostThreads(hw);
}();
const auto balReserve = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
return TuneAdvisor::testing_reservedHostThreads(hw);
}();
const auto aggReserve = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
return TuneAdvisor::testing_reservedHostThreads(hw);
}();
INFO("hw=" << hw << " eff=" << effReserve << " bal=" << balReserve
<< " agg=" << aggReserve);
CHECK(effReserve >= balReserve);
CHECK(balReserve >= aggReserve);
CHECK(effReserve < hw);
CHECK(aggReserve < hw);
}
}
TEST_CASE("recommendedThreads remains profile-aware while leaving host headroom",
"[daemon][governance][catch2]") {
ScopedEnvVar maxThreadsGuard("YAMS_MAX_THREADS", "0");
HardwareGuard hwGuard(16);
const auto effThreads = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
return TuneAdvisor::recommendedThreads(1.0);
}();
const auto balThreads = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
return TuneAdvisor::recommendedThreads(1.0);
}();
const auto aggThreads = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
return TuneAdvisor::recommendedThreads(1.0);
}();
INFO("eff=" << effThreads << " bal=" << balThreads << " agg=" << aggThreads);
CHECK(effThreads <= balThreads);
CHECK(balThreads <= aggThreads);
CHECK(aggThreads < 16u);
CHECK(effThreads >= 1u);
}
TEST_CASE("Auto memory budget is profile-aware", "[daemon][governance][catch2]") {
constexpr uint64_t kSystemMem = 8ull * 1024ull * 1024ull * 1024ull;
const auto effBudget = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
return TuneAdvisor::testing_autoMemoryBudgetBytes(kSystemMem);
}();
const auto balBudget = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
return TuneAdvisor::testing_autoMemoryBudgetBytes(kSystemMem);
}();
const auto aggBudget = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
return TuneAdvisor::testing_autoMemoryBudgetBytes(kSystemMem);
}();
INFO("eff=" << effBudget << " bal=" << balBudget << " agg=" << aggBudget);
CHECK(effBudget < balBudget);
CHECK(balBudget < aggBudget);
CHECK(aggBudget < kSystemMem);
CHECK(effBudget == (kSystemMem * 45ull) / 100ull);
CHECK(balBudget == (kSystemMem * 60ull) / 100ull);
CHECK(aggBudget == (kSystemMem * 75ull) / 100ull);
}
TEST_CASE("WorkCoordinator defaults are io-biased", "[daemon][governance][catch2]") {
ScopedEnvVar workThreadsGuard("YAMS_WORK_COORDINATOR_THREADS", "0");
HardwareGuard hwGuard(16);
const auto efficientWorkers = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
return TuneAdvisor::workCoordinatorThreads();
}();
const auto balancedWorkers = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
return TuneAdvisor::workCoordinatorThreads();
}();
const auto aggressiveWorkers = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
return TuneAdvisor::workCoordinatorThreads();
}();
const auto efficientExpectedWorkers = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
return std::max(4u, TuneAdvisor::recommendedThreads(1.0));
}();
const auto balancedExpectedWorkers = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
return std::max(4u, TuneAdvisor::recommendedThreads(1.5));
}();
const auto aggressiveExpectedWorkers = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
return std::max(4u, TuneAdvisor::recommendedThreads(1.75));
}();
CHECK(efficientWorkers == efficientExpectedWorkers);
CHECK(balancedWorkers == balancedExpectedWorkers);
CHECK(aggressiveWorkers == aggressiveExpectedWorkers);
CHECK(efficientWorkers <= balancedWorkers);
CHECK(balancedWorkers <= aggressiveWorkers);
}
TEST_CASE("Search concurrency limit has a balanced floor", "[daemon][governance][catch2]") {
ScopedEnvVar searchOverrideGuard("YAMS_SEARCH_MAX_CONCURRENT", "0");
HardwareGuard hwGuard(10);
const auto efficientLimit = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
return TuneAdvisor::searchConcurrencyLimit();
}();
const auto balancedLimit = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
return TuneAdvisor::searchConcurrencyLimit();
}();
const auto aggressiveLimit = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
return TuneAdvisor::searchConcurrencyLimit();
}();
const auto efficientExpected = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
return std::max<uint32_t>(2, TuneAdvisor::recommendedThreads(0.5)) * 2;
}();
const auto balancedExpected = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
return std::max<uint32_t>(5u,
std::max<uint32_t>(2, TuneAdvisor::recommendedThreads(0.5)) * 2);
}();
const auto aggressiveExpected = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
return std::max<uint32_t>(6u,
std::max<uint32_t>(2, TuneAdvisor::recommendedThreads(0.5)) * 2);
}();
CHECK(efficientLimit == efficientExpected);
CHECK(balancedLimit == balancedExpected);
CHECK(aggressiveLimit == aggressiveExpected);
}
TEST_CASE("Read pool max connections follow centralized capacity model",
"[daemon][governance][catch2]") {
HardwareGuard hwGuard(16);
const auto efficientCap = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
return TuneAdvisor::readPoolMaxConnections(32);
}();
const auto balancedCap = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
return TuneAdvisor::readPoolMaxConnections(32);
}();
const auto aggressiveCap = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
return TuneAdvisor::readPoolMaxConnections(32);
}();
const auto efficientExpected = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
const auto derived = std::max<uint32_t>({4u, TuneAdvisor::workCoordinatorThreads(),
TuneAdvisor::searchConcurrencyLimit(),
TuneAdvisor::listInflightLimit()});
return std::min<uint32_t>(8u, derived);
}();
const auto balancedExpected = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
const auto derived = std::max<uint32_t>({4u, TuneAdvisor::workCoordinatorThreads(),
TuneAdvisor::searchConcurrencyLimit(),
TuneAdvisor::listInflightLimit()});
return std::min<uint32_t>(8u, derived);
}();
const auto aggressiveExpected = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
const auto derived = std::max<uint32_t>({4u, TuneAdvisor::workCoordinatorThreads(),
TuneAdvisor::searchConcurrencyLimit(),
TuneAdvisor::listInflightLimit()});
return std::min<uint32_t>(8u, derived);
}();
CHECK(efficientCap == efficientExpected);
CHECK(balancedCap == balancedExpected);
CHECK(aggressiveCap == aggressiveExpected);
const auto cappedConfiguredMax = [&]() {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
return TuneAdvisor::readPoolMaxConnections(6);
}();
CHECK(cappedConfiguredMax <= 6u);
CHECK(cappedConfiguredMax >= 1u);
}
// =============================================================================
// computeCpuThrottleDelayMs Tests
// =============================================================================
TEST_CASE("computeCpuThrottleDelayMs returns 0 below threshold", "[daemon][governance][catch2]") {
ProfileGuard guard(TuneAdvisor::Profile::Balanced);
// Balanced threshold = 50 + 0.5*35 = 67.5%
CHECK(TuneAdvisor::computeCpuThrottleDelayMs(60.0) == 0);
CHECK(TuneAdvisor::computeCpuThrottleDelayMs(0.0) == 0);
}
TEST_CASE("computeCpuThrottleDelayMs returns clamped delay above threshold",
"[daemon][governance][catch2]") {
ProfileGuard guard(TuneAdvisor::Profile::Aggressive);
// Aggressive threshold = 50 + 1.0*35 = 85%
double threshold = TuneAdvisor::cpuHighThresholdPercent();
int32_t delay = TuneAdvisor::computeCpuThrottleDelayMs(threshold + 10.0);
CHECK(delay >= 2);
CHECK(delay <= 25);
}
TEST_CASE("computeCpuThrottleDelayMs clamps to max 25ms", "[daemon][governance][catch2]") {
// Even at 100% CPU, delay should not exceed 25ms
int32_t delay = TuneAdvisor::computeCpuThrottleDelayMs(100.0);
CHECK(delay <= 25);
}
TEST_CASE("cpuHighThresholdPercent setter overrides profile and env",
"[daemon][governance][catch2]") {
TuneAdvisor::resetCpuHighThresholdPercentOverride();
SECTION("setter overrides profile default") {
ProfileGuard guard(TuneAdvisor::Profile::Efficient);
TuneAdvisor::setCpuHighThresholdPercent(72.0);
CHECK(TuneAdvisor::cpuHighThresholdPercent() == Catch::Approx(72.0));
TuneAdvisor::resetCpuHighThresholdPercentOverride();
}
SECTION("setter overrides env var") {
ScopedEnvVar envGuard("YAMS_CPU_HIGH_PCT", "55");
CHECK(TuneAdvisor::cpuHighThresholdPercent() == Catch::Approx(55.0));
TuneAdvisor::setCpuHighThresholdPercent(68.0);
CHECK(TuneAdvisor::cpuHighThresholdPercent() == Catch::Approx(68.0));
TuneAdvisor::resetCpuHighThresholdPercentOverride();
}
}