use std::path::Path;
use std::sync::Arc;
use std::time::{Duration, Instant};
use alba_core::{BeamId, load_str};
use alba_engine::{CacheOptions, Executors, RunOptions, Targets, run};
use alba_executors::FakeExecutor;
use tokio_util::sync::CancellationToken;
const SOURCE: &str = r#"
beam build {
inputs ["*.txt"]
run "work"
}
"#;
fn options(dir: &Path) -> RunOptions {
RunOptions {
jobs: 4,
keep_going: false,
params: Vec::new(),
cache: Some(CacheOptions {
dir: dir.join(".alba").join("cache"),
force: false,
}),
extra_env: Vec::new(),
}
}
async fn run_once(dir: &Path) -> alba_engine::RunSummary {
let mut project = load_str(SOURCE).expect("the fixture must load");
for beam in &mut project.beams {
beam.dir = dir.to_path_buf();
}
let (events, _incoming) = tokio::sync::mpsc::unbounded_channel();
run(
&project,
&Targets::beam(BeamId("build".to_string())),
options(dir),
Executors::uniform(Arc::new(FakeExecutor::new())),
events,
CancellationToken::new(),
)
.await
.expect("the run must not fail")
}
#[tokio::test]
async fn a_fully_cached_run_stays_imperceptible() {
let dir = tempfile::tempdir().unwrap();
for i in 0..100 {
std::fs::write(
dir.path().join(format!("file_{i:03}.txt")),
"x".repeat(1024),
)
.unwrap();
}
let first = run_once(dir.path()).await;
assert_eq!(first.succeeded.len(), 1, "the warm-up run must execute");
let mut samples = Vec::new();
for _ in 0..15 {
let started = Instant::now();
let summary = run_once(dir.path()).await;
samples.push(started.elapsed());
assert_eq!(summary.cached.len(), 1, "every measured run must be a hit");
}
samples.sort();
let median = samples[samples.len() / 2];
assert!(
median < Duration::from_millis(10),
"a fully cached 100-file run took a median of {median:?}, budget 10ms"
);
}
#[tokio::test]
#[ignore]
async fn arming_the_watcher_does_not_scan_the_tree() {
fn arm(root: &Path) -> Duration {
let started = Instant::now();
let watcher =
alba_engine::NotifyWatcher::new(&[root.to_path_buf()]).expect("the watcher must start");
let elapsed = started.elapsed();
drop(watcher);
elapsed
}
const FILES: usize = 20_000;
let empty = tempfile::tempdir().unwrap();
let populated = tempfile::tempdir().unwrap();
let artifacts = populated.path().join("target").join("debug");
std::fs::create_dir_all(&artifacts).unwrap();
for i in 0..FILES {
std::fs::write(artifacts.join(format!("artifact_{i:05}.o")), "x").unwrap();
}
arm(empty.path());
let baseline = arm(empty.path());
let loaded = arm(populated.path());
let overhead = loaded.saturating_sub(baseline);
assert!(
overhead < Duration::from_millis(300),
"arming over {FILES} files cost {overhead:?} more than over an empty \
root ({loaded:?} against {baseline:?}), budget 300ms"
);
}