#![cfg(test)]
use super::{scan_results_response, RequestIdAllocator, RequestProfileCapture};
use crate::daemon::protocol::{response_kind, ProfileStageMeasurement, RequestProfile, Response};
use std::collections::{BTreeMap, HashSet};
use std::sync::Barrier;
use std::time::Instant;
fn stage_calls(profile: &RequestProfile) -> BTreeMap<&str, u64> {
profile
.stages
.iter()
.map(|stage| (stage.stage.as_str(), stage.calls))
.collect()
}
fn busy_work() {
for value in 0..10_000_u64 {
std::hint::black_box(value);
}
}
#[test]
fn request_ids_are_unique_across_threads_and_carry_the_daemon_generation() {
let allocator = std::sync::Arc::new(RequestIdAllocator::new("gen-test".to_string()));
let mut handles = Vec::new();
for _ in 0..8 {
let allocator = allocator.clone();
handles.push(std::thread::spawn(move || {
(0..500).map(|_| allocator.next()).collect::<Vec<_>>()
}));
}
let mut ids = HashSet::new();
for handle in handles {
for id in handle.join().expect("allocator thread") {
assert!(
id.starts_with("gen-test-"),
"request id must carry the daemon generation: {id}"
);
assert!(ids.insert(id.clone()), "duplicate request id: {id}");
}
}
assert_eq!(ids.len(), 8 * 500, "every allocated id must be unique");
}
#[test]
fn profile_capture_reports_exact_stage_calls_and_zero_loss() {
let capture = RequestProfileCapture::new("gen-one-0000000000000000".to_string());
let started = Instant::now();
{
let _guard = capture.enter();
for _ in 0..3 {
let _span = keyhog_profile::span(keyhog_profile::Stage::Phase1Triggers);
busy_work();
}
let _span = keyhog_profile::span(keyhog_profile::Stage::Entropy);
busy_work();
drop(_span);
let profile = capture.finish(started);
assert_eq!(profile.request_id, "gen-one-0000000000000000");
let mut expected: BTreeMap<&str, u64> = BTreeMap::new();
expected.insert(keyhog_profile::Stage::Phase1Triggers.as_str(), 3);
expected.insert(keyhog_profile::Stage::Entropy.as_str(), 1);
assert_eq!(stage_calls(&profile), expected);
assert!(
profile.wall_time_ns > 0,
"wall time must cover the recorded work"
);
assert!(
profile
.stages
.iter()
.all(|stage| stage.elapsed_ns <= profile.wall_time_ns),
"per-stage elapsed time cannot exceed the request wall time: {:?}",
profile.stages
);
assert_eq!(profile.dropped_span_events, 0);
assert_eq!(profile.dropped_point_events, 0);
assert_eq!(profile.dropped_annotations, 0);
assert_eq!(profile.sampled_out_events, 0);
}
}
#[test]
fn concurrent_profiled_requests_stay_isolated_per_request_id() {
let overlap = std::sync::Arc::new(Barrier::new(2));
let capture_a = RequestProfileCapture::new("gen-a-0000000000000001".to_string());
let capture_b = RequestProfileCapture::new("gen-b-0000000000000002".to_string());
let runtime_a = capture_a.runtime.clone();
let worker = std::thread::spawn(move || {
let _guard = runtime_a.enter();
for _ in 0..2 {
let _span = keyhog_profile::span(keyhog_profile::Stage::BackendDispatch);
busy_work();
}
});
let (profile_a, profile_b) = std::thread::scope(|scope| {
let overlap_a = overlap.clone();
let a = scope.spawn(move || {
let _guard = capture_a.enter();
let started = Instant::now();
overlap_a.wait();
for _ in 0..4 {
let _span = keyhog_profile::span(keyhog_profile::Stage::Phase1Triggers);
busy_work();
}
overlap_a.wait();
worker.join().expect("runtime A worker");
capture_a.finish(started)
});
let b = scope.spawn(move || {
let _guard = capture_b.enter();
let started = Instant::now();
overlap.wait();
for _ in 0..7 {
let _span = keyhog_profile::span(keyhog_profile::Stage::HotPatterns);
busy_work();
}
overlap.wait();
capture_b.finish(started)
});
(a.join().expect("request A"), b.join().expect("request B"))
});
assert_eq!(profile_a.request_id, "gen-a-0000000000000001");
assert_eq!(profile_b.request_id, "gen-b-0000000000000002");
assert_ne!(
profile_a.request_id, profile_b.request_id,
"concurrent requests must receive distinct identities"
);
let mut expected_a: BTreeMap<&str, u64> = BTreeMap::new();
expected_a.insert(keyhog_profile::Stage::Phase1Triggers.as_str(), 4);
expected_a.insert(keyhog_profile::Stage::BackendDispatch.as_str(), 2);
assert_eq!(
stage_calls(&profile_a),
expected_a,
"request A must own exactly its main-thread and propagated-worker calls"
);
let mut expected_b: BTreeMap<&str, u64> = BTreeMap::new();
expected_b.insert(keyhog_profile::Stage::HotPatterns.as_str(), 7);
assert_eq!(
stage_calls(&profile_b),
expected_b,
"request B must own exactly its own calls and none of request A's"
);
for stage in &profile_b.stages {
assert_ne!(
stage.stage,
keyhog_profile::Stage::Phase1Triggers.as_str(),
"request B must not attribute request A stages"
);
assert_ne!(
stage.stage,
keyhog_profile::Stage::BackendDispatch.as_str(),
"request B must not attribute request A worker stages"
);
}
}
#[test]
fn scan_results_carries_profile_only_when_requested() {
let telemetry = keyhog_scanner::telemetry::ScanTelemetry::new().drain();
let unprofiled =
scan_results_response(None, Vec::new(), telemetry, Default::default(), None, None);
match unprofiled {
Response::ScanResults { profile, .. } => {
assert!(
profile.is_none(),
"profile=false must not attach a request profile"
);
}
other => panic!("expected ScanResults, got {}", response_kind(&other)),
}
let payload = RequestProfile {
request_id: "gen-c-0000000000000003".to_string(),
wall_time_ns: 42_000,
stages: vec![ProfileStageMeasurement {
stage: keyhog_profile::Stage::Entropy.as_str().to_string(),
calls: 2,
elapsed_ns: 30_000,
}],
dropped_span_events: 1,
dropped_point_events: 2,
dropped_annotations: 3,
sampled_out_events: 4,
};
let telemetry = keyhog_scanner::telemetry::ScanTelemetry::new().drain();
let profiled = scan_results_response(
None,
Vec::new(),
telemetry,
Default::default(),
None,
Some(payload.clone()),
);
match profiled {
Response::ScanResults { profile, .. } => {
assert_eq!(
profile.expect("profiled response must carry the payload"),
payload
);
}
other => panic!("expected ScanResults, got {}", response_kind(&other)),
}
}
#[test]
fn unprofiled_request_builds_no_capture() {
let capture = false.then(|| RequestProfileCapture::new("unused".to_string()));
assert!(capture.is_none());
let capture = true.then(|| RequestProfileCapture::new("used".to_string()));
assert!(capture.is_some());
}