#![cfg(unix)]
use std::{
path::PathBuf,
time::{Duration, Instant},
};
use cli::client::local_daemon::{
LocalDaemonStatus, connect_local_daemon_channel, detect_local_daemon, probe,
};
use repo::Repository;
use daemon::local_daemon::{LocalDaemonConfig, serve};
use tempfile::TempDir;
use tokio::sync::oneshot;
async fn spawn_local_daemon() -> (
TempDir,
PathBuf,
oneshot::Sender<()>,
tokio::task::JoinHandle<()>,
) {
let temp = TempDir::new().expect("temp repo dir");
let repo = Repository::init_default(temp.path()).expect("init heddle repo");
let heddle_dir = repo.heddle_dir().to_path_buf();
let config = LocalDaemonConfig::from_repo(&repo);
let (tx, rx) = oneshot::channel::<()>();
let task = tokio::spawn(async move {
let shutdown = async move {
let _ = rx.await;
};
if let Err(err) = serve(repo, config, shutdown).await {
eprintln!("serve_local test daemon exited: {err}");
}
});
let socket = heddle_dir.join("sockets").join("grpc.sock");
let deadline = Instant::now() + Duration::from_secs(5);
loop {
if socket.exists() && matches!(probe(&heddle_dir).status, LocalDaemonStatus::Running { .. })
{
break;
}
if Instant::now() > deadline {
panic!(
"local daemon did not come up within 5s; socket exists={}",
socket.exists()
);
}
tokio::time::sleep(Duration::from_millis(20)).await;
}
(temp, heddle_dir, tx, task)
}
#[tokio::test(flavor = "multi_thread")]
async fn detect_local_daemon_warm_is_significantly_faster_than_cold() {
let (_temp, heddle_dir, shutdown, task) = spawn_local_daemon().await;
let _ = detect_local_daemon(&heddle_dir);
let warm_iters = 100;
let warm_start = Instant::now();
for _ in 0..warm_iters {
let target = detect_local_daemon(&heddle_dir).expect("daemon present");
std::hint::black_box(target);
}
let warm_elapsed = warm_start.elapsed();
let cold_iters = 100;
let cold_start = Instant::now();
for _ in 0..cold_iters {
let result = probe(&heddle_dir);
std::hint::black_box(result);
}
let cold_elapsed = cold_start.elapsed();
let cold_ns = cold_elapsed.as_nanos();
let warm_ns = warm_elapsed.as_nanos().max(1);
let ratio = cold_ns as f64 / warm_ns as f64;
let warm_per_call = warm_elapsed / warm_iters;
assert!(
warm_per_call < Duration::from_micros(50),
"warm detect_local_daemon should be in low-µs territory; \
warm_per_call={warm_per_call:?}, total warm={warm_elapsed:?}"
);
assert!(
ratio >= 2.0,
"expected warm detect_local_daemon to be at least 2x faster than cold probe; \
warm={warm_elapsed:?}, cold={cold_elapsed:?}, ratio={ratio:.2}x"
);
let connected = connect_local_daemon_channel(&heddle_dir, Duration::from_millis(500)).await;
assert!(
connected.is_some(),
"connect_local_daemon_channel must succeed against a live daemon"
);
let _ = shutdown.send(());
let _ = tokio::time::timeout(Duration::from_secs(5), task).await;
}
#[tokio::test(flavor = "multi_thread")]
async fn channel_cache_returns_clones_in_o1() {
let (_temp, heddle_dir, shutdown, task) = spawn_local_daemon().await;
let cold = Instant::now();
let first = connect_local_daemon_channel(&heddle_dir, Duration::from_millis(500))
.await
.expect("first connect");
let cold_elapsed = cold.elapsed();
let warm = Instant::now();
let second = connect_local_daemon_channel(&heddle_dir, Duration::from_millis(500))
.await
.expect("cached connect");
let warm_elapsed = warm.elapsed();
assert_eq!(first.target.socket_path, second.target.socket_path);
let cold_ns = cold_elapsed.as_nanos();
let warm_ns = warm_elapsed.as_nanos().max(1);
let ratio = cold_ns as f64 / warm_ns as f64;
assert!(
ratio >= 5.0,
"expected channel cache to be at least 5x faster on the second call; \
cold={cold_elapsed:?}, warm={warm_elapsed:?}, ratio={ratio:.2}x"
);
let _ = shutdown.send(());
let _ = tokio::time::timeout(Duration::from_secs(5), task).await;
}