use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use core::time::Duration;
use std::sync::{Arc, Mutex, mpsc};
use std::time::Instant;
use std::{fs, io, thread};
use camino::{Utf8Path, Utf8PathBuf};
use cargo_gamma_process::{MemoryRequest, ProcessTree, prepare};
use gamma_rt::{OVERFLOW, SEAL};
use super::events::Events;
#[cfg(test)]
use super::faults::{self, Fault};
use super::harness_filters::HarnessFilters;
#[cfg(test)]
use super::loader::LOADER_VAR;
use super::loader::configure_loader;
use super::stall::Stall;
use super::test_binary::TestBinary;
use super::verdict::{Attempt, Only, Verdict, observe};
use super::workspace::Workspace;
use crate::{HashMap, HashSet};
const CENSUS_FACTOR: u32 = 4;
#[derive(Debug, Default)]
pub(super) struct Census {
binaries: HashMap<Utf8PathBuf, Reach>,
walked: usize,
}
#[derive(Debug, Default)]
struct Reach {
names: Vec<Box<str>>,
reached: HashMap<u32, u32>,
intern: Vec<Arc<[u32]>>,
intern_index: HashMap<Arc<[u32]>, u32>,
times: Vec<Duration>,
complete: bool,
}
impl Reach {
fn tests_for(&self, ordinal: u32) -> Option<&[u32]> {
let index = self.reached.get(&ordinal)?;
self.intern.get(*index as usize).map(|s| &**s)
}
fn intern_set(&mut self, mut set: Vec<u32>) -> u32 {
set.sort_unstable();
set.dedup();
let key: Arc<[u32]> = set.into();
if let Some(&existing) = self.intern_index.get(&key) {
return existing;
}
let index = u32::try_from(self.intern.len()).unwrap_or(u32::MAX);
let _previous = self.intern_index.insert(Arc::clone(&key), index);
self.intern.push(key);
index
}
fn finish(&mut self) {
self.intern_index = HashMap::default();
}
}
pub(super) enum CensusWork {
Whole,
Uncovered,
Selected(Duration),
Hinted(Duration),
}
#[derive(Debug, PartialEq, Eq)]
pub(super) enum CensusSelection<'census> {
Whole,
Uncovered,
Selected(Vec<&'census str>),
Hinted(Vec<&'census str>),
}
impl Census {
pub(super) fn selection(&self, binary: &TestBinary, ordinal: u32) -> CensusSelection<'_> {
let Some(reach) = self.binaries.get(&binary.path) else {
return CensusSelection::Whole;
};
let Some(indices) = reach.tests_for(ordinal) else {
return if reach.complete {
CensusSelection::Uncovered
} else {
CensusSelection::Whole
};
};
if indices.len().saturating_mul(2) > reach.names.len() {
return CensusSelection::Whole;
}
let names = indices
.iter()
.filter_map(|index| usize::try_from(*index).ok())
.filter_map(|index| reach.names.get(index))
.map(Box::as_ref)
.collect();
if reach.complete {
CensusSelection::Selected(names)
} else {
CensusSelection::Hinted(names)
}
}
#[cfg(test)]
fn reaching(&self, binary: &TestBinary, ordinal: u32) -> Option<Vec<&str>> {
match self.selection(binary, ordinal) {
CensusSelection::Whole | CensusSelection::Hinted(_) => None,
CensusSelection::Uncovered => Some(Vec::new()),
CensusSelection::Selected(names) => Some(names),
}
}
pub(super) fn work(&self, binary: &TestBinary, ordinal: u32) -> CensusWork {
let Some(reach) = self.binaries.get(&binary.path) else {
return CensusWork::Whole;
};
let Some(indices) = reach.tests_for(ordinal) else {
return if reach.complete { CensusWork::Uncovered } else { CensusWork::Whole };
};
if indices.len().saturating_mul(2) > reach.names.len() {
return CensusWork::Whole;
}
let duration = indices
.iter()
.filter_map(|index| usize::try_from(*index).ok())
.filter_map(|index| reach.times.get(index))
.copied()
.sum();
if reach.complete {
CensusWork::Selected(duration)
} else {
CensusWork::Hinted(duration)
}
}
pub(super) fn len(&self) -> usize {
self.binaries.len()
}
pub(super) const fn walked(&self) -> usize {
self.walked
}
#[cfg(all(test, unix))]
pub(super) fn examined(binary: &Utf8Path, site: u32, reached: usize, total: usize) -> Self {
Self::fixture(binary, site, reached, total, true)
}
#[cfg(test)]
pub(super) fn partial(binary: &Utf8Path, site: u32, reached: usize, total: usize) -> Self {
Self::fixture(binary, site, reached, total, false)
}
#[cfg(test)]
fn fixture(binary: &Utf8Path, site: u32, reached: usize, total: usize, complete: bool) -> Self {
let names: Vec<Box<str>> = (0..total).map(|index| format!("tests::t{index}").into()).collect();
let indices: Vec<u32> = (0..reached).filter_map(|index| u32::try_from(index).ok()).collect();
let mut reach = Reach {
names,
reached: HashMap::default(),
intern: Vec::new(),
intern_index: HashMap::default(),
times: vec![Duration::from_millis(1); total],
complete,
};
let set_index = reach.intern_set(indices);
let _previous2 = reach.reached.insert(site, set_index);
reach.finish();
let mut census = Self::default();
let _previous = census.binaries.insert(binary.to_owned(), reach);
census
}
}
pub(super) fn take(
work: &Workspace,
binaries: &[TestBinary],
targets: &HashMap<Utf8PathBuf, HashSet<u32>>,
maximum_savings: Duration,
jobs: usize,
stall: Stall,
events: &mut impl Events,
) -> Census {
let mut census = Census::default();
let binaries: Vec<&TestBinary> = binaries.iter().filter(|binary| targets.contains_key(&binary.path)).collect();
events.begin(
"Optimizing",
"Optimized",
&format!("{} test {}", binaries.len(), plural(binaries.len())),
);
let total = binaries.len();
let unit = format!("test {}", plural(total));
let mut completed = 0_usize;
events.phase_progress(completed, total, &unit);
let mut listed: Vec<(&TestBinary, Vec<Box<str>>)> = Vec::with_capacity(binaries.len());
let mut estimated_cost = Duration::ZERO;
for binary in &binaries {
let started = Instant::now();
let Some(names) = list(work, binary) else {
completed += 1;
events.phase_progress(completed, total, &unit);
continue;
};
let listing_cost = started.elapsed();
estimated_cost = estimated_cost.saturating_add(listing_cost.saturating_mul(u32::try_from(names.len()).unwrap_or(u32::MAX)));
listed.push((binary, names));
}
if !can_repay(estimated_cost, maximum_savings) {
events.end("");
return census;
}
let deadline = Instant::now().checked_add(maximum_savings);
let (mapped, walked) = walk(work, listed, targets, deadline, jobs, stall, || {
completed += 1;
events.phase_progress(completed, total, &unit);
});
for (path, reach) in mapped {
let _previous = census.binaries.insert(path, reach);
}
census.walked = walked;
events.end(&format!(
", {} of {} {} mapped, over {walked} {}",
census.len(),
binaries.len(),
plural(binaries.len()),
if walked == 1 { "test" } else { "tests" }
));
census
}
fn can_repay(estimated_cost: Duration, maximum_savings: Duration) -> bool {
!maximum_savings.is_zero() && estimated_cost < maximum_savings
}
const fn plural(count: usize) -> &'static str {
if count == 1 { "binary" } else { "binaries" }
}
const LIST_BUDGET: Duration = Duration::from_secs(30);
const LIST_CAP: usize = 4 * 1024 * 1024;
const LIST_POLL: Duration = Duration::from_millis(5);
fn list(work: &Workspace, binary: &TestBinary) -> Option<Vec<Box<str>>> {
let command = listing_command(work, binary);
listed(command, LIST_BUDGET).filter(|names| !names.is_empty())
}
fn listing_command(work: &Workspace, binary: &TestBinary) -> std::process::Command {
let mut command = std::process::Command::new(binary.path.as_std_path());
let _ = command
.args(["--list", "--format=terse"])
.args(HarnessFilters::parse(work.test_arguments()).selecting())
.current_dir(working_directory(work, binary).as_std_path())
.stderr(std::process::Stdio::null());
configure_loader(&mut command, work.launch());
command
}
fn listed(mut command: std::process::Command, budget: Duration) -> Option<Vec<Box<str>>> {
use std::process::Stdio;
let request = MemoryRequest { meter: false, limit: None };
let _ = command.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::null());
let prepared = prepare(command, request).ok()?;
let spawned = prepared.spawn().ok()?;
let mut subtree = match ProcessTree::adopt(spawned) {
Ok(subtree) => subtree,
Err(_unwatchable) => return None,
};
let (sender, receiver) = mpsc::sync_channel(1);
let reading = subtree.take_stdout().map(|mut pipe| {
let sender = sender.clone();
let failed = sender.clone();
#[cfg(test)]
let refused = faults::fired(Fault::Thread);
#[cfg(not(test))]
let refused = false;
let spawned = if refused {
Err(std::io::Error::other("the reader thread a test asked to fail"))
} else {
thread::Builder::new().name("cargo-gamma-census-output".to_owned()).spawn(move || {
use std::io::Read as _;
let mut text = Vec::new();
let mut buffer = [0_u8; 8192];
let mut whole = true;
loop {
match pipe.read(&mut buffer) {
Ok(0) => break,
Ok(read) => {
let room = LIST_CAP.saturating_sub(text.len());
text.extend_from_slice(&buffer[..read.min(room)]);
whole &= read <= room;
}
Err(cause) if cause.kind() == std::io::ErrorKind::Interrupted => {}
Err(_truncated) => {
whole = false;
break;
}
}
}
let _sent = sender.send((text, whole));
})
};
if spawned.is_err() {
let _sent = failed.send((Vec::new(), false));
}
});
drop(sender);
let deadline = Instant::now() + budget;
let status = loop {
match subtree.observe() {
Ok(Some(status)) => break Some(status),
Ok(None) if Instant::now() >= deadline => break None,
Ok(None) => thread::sleep(LIST_POLL.min(deadline.saturating_duration_since(Instant::now()))),
Err(_unaskable) => break None,
}
};
if status.is_none() {
let _reaped = subtree.terminate();
} else {
debug_assert!(
subtree.released(),
"the observed listing released containment before its output was read"
);
}
let (text, whole) = reading
.and_then(|()| receiver.recv_timeout(deadline.saturating_duration_since(Instant::now())).ok())
.unwrap_or_default();
if !whole || !status?.success() {
return None;
}
let text = String::from_utf8(text).ok()?;
Some(
text.lines()
.filter_map(|line| line.strip_suffix(": test"))
.map(|name| name.trim().into())
.collect(),
)
}
fn working_directory<'work>(work: &'work Workspace, binary: &'work TestBinary) -> &'work Utf8Path {
if binary.manifest_dir.as_str().is_empty() {
work.root()
} else {
&binary.manifest_dir
}
}
fn walk(
work: &Workspace,
listed: Vec<(&TestBinary, Vec<Box<str>>)>,
targets: &HashMap<Utf8PathBuf, HashSet<u32>>,
deadline: Option<Instant>,
jobs: usize,
stall: Stall,
completed: impl FnMut(),
) -> (Vec<(Utf8PathBuf, Reach)>, usize) {
walk_with(
work,
listed,
targets,
jobs,
stall,
sample,
|| deadline.is_some_and(|limit| Instant::now() >= limit),
completed,
)
}
#[expect(
clippy::too_many_arguments,
clippy::too_many_lines,
reason = "the streaming walk shares its inputs and borrowed state across scoped workers"
)]
fn walk_with<S, E>(
work: &Workspace,
listed: Vec<(&TestBinary, Vec<Box<str>>)>,
targets: &HashMap<Utf8PathBuf, HashSet<u32>>,
jobs: usize,
stall: Stall,
sampler: S,
expired: E,
mut completed: impl FnMut(),
) -> (Vec<(Utf8PathBuf, Reach)>, usize)
where
S: Fn(&Workspace, &TestBinary, &str, &Utf8Path, Stall) -> Option<(Vec<u32>, Duration)> + Sync,
E: Fn() -> bool + Sync,
{
let directory = work.base().join("census");
if fs::create_dir_all(directory.as_std_path()).is_err() {
return (Vec::new(), 0);
}
let offsets: Vec<usize> = listed
.iter()
.scan(0_usize, |acc, (_binary, names)| {
let start = *acc;
*acc += names.len();
Some(start)
})
.collect();
let total_tasks: usize = listed.iter().map(|(_binary, names)| names.len()).sum();
let next = AtomicUsize::new(0);
let launches = AtomicUsize::new(0);
let site_positions: Vec<HashMap<u32, usize>> = listed
.iter()
.map(|(binary, _names)| match targets.get(&binary.path) {
Some(wanted) => {
let mut sorted: Vec<u32> = wanted.iter().copied().collect();
sorted.sort_unstable();
sorted.into_iter().enumerate().map(|(position, site)| (site, position)).collect()
}
None => HashMap::default(),
})
.collect();
let site_counts: Vec<Vec<AtomicUsize>> = site_positions
.iter()
.map(|positions| (0..positions.len()).map(|_position| AtomicUsize::new(0)).collect())
.collect();
let times: Vec<Vec<Mutex<Duration>>> = listed
.iter()
.map(|(_binary, names)| names.iter().map(|_name| Mutex::new(Duration::ZERO)).collect())
.collect();
let spoiled: Vec<AtomicBool> = listed.iter().map(|_entry| AtomicBool::new(false)).collect();
let saturated: Vec<AtomicBool> = listed.iter().map(|_entry| AtomicBool::new(false)).collect();
let sample_counts: Vec<AtomicUsize> = listed.iter().map(|_entry| AtomicUsize::new(0)).collect();
let remaining: Vec<AtomicUsize> = listed.iter().map(|(_binary, names)| AtomicUsize::new(names.len())).collect();
let (completion, completions) = mpsc::channel();
let notes = crate::notes::current();
let locals: Vec<Vec<HashMap<u32, Vec<u32>>>> = thread::scope(|scope| {
let mut handles = Vec::with_capacity(jobs.max(1));
for worker in 0..jobs.max(1) {
let (next, listed, offsets, site_positions, site_counts, times, spoiled, saturated, sample_counts, remaining, launches) = (
&next,
&listed,
&offsets,
&site_positions,
&site_counts,
×,
&spoiled,
&saturated,
&sample_counts,
&remaining,
&launches,
);
let completion = completion.clone();
let notes = notes.clone();
let sampler = &sampler;
let expired = &expired;
let path = directory.join(format!("{worker}.bin"));
let handle = scope.spawn(move || {
let _notes = crate::notes::enter(notes.as_ref());
let mut local: Vec<HashMap<u32, Vec<u32>>> = listed.iter().map(|_entry| HashMap::default()).collect();
loop {
let at = next.fetch_add(1, Ordering::Relaxed);
if at >= total_tasks {
break;
}
let binary_at = match offsets.binary_search(&at) {
Ok(exact) => exact,
Err(after) => after.saturating_sub(1),
};
let test_at = at - offsets[binary_at];
let (binary, names) = &listed[binary_at];
let spoiled = &spoiled[binary_at];
let saturated = &saturated[binary_at];
let sample_count = &sample_counts[binary_at];
let positions = &site_positions[binary_at];
let counts = &site_counts[binary_at];
let times = ×[binary_at];
let remaining = &remaining[binary_at];
let name = &names[test_at];
if !spoiled.load(Ordering::Relaxed) && !saturated.load(Ordering::Relaxed) && !expired() {
let _previous = launches.fetch_add(1, Ordering::Relaxed);
match (sampler(work, binary, name, &path, stall), u32::try_from(test_at)) {
(Some((sites, elapsed)), Ok(test_index)) => {
let _previous = sample_count.fetch_add(1, Ordering::Relaxed);
#[expect(clippy::unwrap_used, reason = "the lock only poisons if a worker panicked, and none can")]
{
let mut time = times[test_at].lock().unwrap();
*time = elapsed;
}
let wanted = targets.get(&binary.path);
let reached = &mut local[binary_at];
let mut sites = sites;
sites.sort_unstable();
sites.dedup();
for site in sites.into_iter().filter(|site| wanted.is_some_and(|wanted| wanted.contains(site))) {
let tests = reached.entry(site).or_default();
tests.push(test_index);
if let Some(&position) = positions.get(&site) {
let _previous = counts[position].fetch_add(1, Ordering::Relaxed);
}
}
if wanted.is_some_and(|wanted| {
wanted.iter().all(|site| {
positions.get(site).is_some_and(|&position| {
counts[position].load(Ordering::Relaxed).saturating_mul(2) > names.len()
})
})
}) {
saturated.store(true, Ordering::Relaxed);
}
}
_spoiled => spoiled.store(true, Ordering::Relaxed),
}
}
if remaining.fetch_sub(1, Ordering::Relaxed) == 1 {
let _sent = completion.send(());
}
}
local
});
handles.push(handle);
}
drop(completion);
for () in completions {
completed();
}
handles
.into_iter()
.map(|handle| handle.join().expect("a census worker exits normally; nothing in its loop panics"))
.collect()
});
let mut reached: Vec<HashMap<u32, Vec<u32>>> = (0..listed.len()).map(|_entry| HashMap::default()).collect();
for worker_local in locals {
for (binary_at, site_map) in worker_local.into_iter().enumerate() {
let target = &mut reached[binary_at];
for (site, mut tests) in site_map {
target.entry(site).or_default().append(&mut tests);
}
}
}
let mut mapped = Vec::with_capacity(listed.len());
for ((binary, names), (raw_reached, (times, (spoiled, sample_count)))) in listed.into_iter().zip(
reached
.into_iter()
.zip(times.into_iter().zip(spoiled.into_iter().zip(sample_counts))),
) {
if spoiled.into_inner() {
continue;
}
#[expect(clippy::unwrap_used, reason = "the lock only poisons if a worker panicked, and none can")]
let times: Vec<Duration> = times.into_iter().map(|time| time.into_inner().unwrap()).collect();
let complete = sample_count.into_inner() == times.len();
let mut reach = Reach {
names,
reached: HashMap::default(),
intern: Vec::new(),
intern_index: HashMap::default(),
times,
complete,
};
for (site, indices) in raw_reached {
let set_index = reach.intern_set(indices);
let _previous = reach.reached.insert(site, set_index);
}
reach.finish();
mapped.push((binary.path.clone(), reach));
}
(mapped, launches.into_inner())
}
fn sample(work: &Workspace, binary: &TestBinary, name: &str, path: &Utf8Path, stall: Stall) -> Option<(Vec<u32>, Duration)> {
let _removed = fs::remove_file(path.as_std_path());
let attempt = Attempt {
active: None,
timeout: binary
.budget
.map(|budget| binary.baseline.saturating_mul(CENSUS_FACTOR).max(budget)),
stall,
request: MemoryRequest { meter: false, limit: None },
only: Only::One(name),
census: Some(path),
};
let started = Instant::now();
match observe(work, binary, attempt).verdict {
Verdict::Passed => {}
_spoiled => return None,
}
let elapsed = started.elapsed();
let bytes = read_bounded(path).ok()?;
decode(&bytes).map(|sites| (sites, elapsed))
}
const MAX_CENSUS_BYTES: u64 = (gamma_rt::MAX_CENSUS_SITES as u64 + 2) * 4;
fn read_bounded(path: &Utf8Path) -> io::Result<Vec<u8>> {
use std::io::Read as _;
let file = fs::File::open(path.as_std_path())?;
let mut bytes = Vec::new();
let _read = file.take(MAX_CENSUS_BYTES.saturating_add(1)).read_to_end(&mut bytes)?;
if u64::try_from(bytes.len()).is_ok_and(|len| len > MAX_CENSUS_BYTES) {
return Err(io::Error::other(
"the census file is larger than any whole census the runtime protocol can produce",
));
}
Ok(bytes)
}
fn decode(bytes: &[u8]) -> Option<Vec<u32>> {
if !bytes.len().is_multiple_of(4) {
return None;
}
let mut sites = Vec::with_capacity(bytes.len() / 4);
let mut sealed = false;
for record in bytes.chunks_exact(4) {
if sealed {
return None;
}
match u32::from_le_bytes(record.try_into().ok()?) {
OVERFLOW => return None,
SEAL => sealed = true,
ordinal => sites.push(ordinal),
}
}
sealed.then_some(sites)
}
#[cfg(test)]
#[cfg(not(miri))]
mod tests {
use super::*;
#[test]
fn listing_output_beyond_the_cap_is_not_authoritative() {
let (_directory, work) =
crate::testing::helper_workspace("census-list-cap", &["flood:4194305", "print:meaningful::killer: test", "exit:0"]);
let command = listing_command(&work, &crate::testing::helper());
assert!(listed(command, Duration::from_secs(30)).is_none());
}
#[test]
fn listing_reader_thread_failure_degrades_to_no_census() {
let (_directory, work) = crate::testing::helper_workspace("census-list-thread", &["print:a::test: test", "exit:0"]);
let command = listing_command(&work, &crate::testing::helper());
let _refused = faults::arm(Fault::Thread);
assert!(listed(command, Duration::from_secs(30)).is_none());
}
static WALK_TEST: Mutex<()> = Mutex::new(());
#[test]
fn listing_a_binary_uses_the_runs_loader_path() {
let (_scratch, work) = crate::testing::helper_workspace("list-loader", &["exit:0"]);
let binary = crate::testing::helper();
let command = listing_command(&work, &binary);
let configured = command
.get_envs()
.find(|(name, _value)| *name == std::ffi::OsStr::new(LOADER_VAR))
.and_then(|(_name, value)| value);
assert_eq!(
configured,
work.launch().loader.as_deref(),
"the listing launch omitted the libraries used to build its executable"
);
}
#[test]
#[cfg(unix)]
fn a_binary_that_never_answers_a_listing_is_abandoned_when_its_budget_runs_out() {
let mut command = std::process::Command::new("/bin/sh");
let _ = command.args(["-c", "sleep 300"]);
let started = Instant::now();
let names = listed(command, Duration::from_millis(200));
assert!(names.is_none(), "a binary that never listed was believed: {names:?}");
assert!(
started.elapsed() < Duration::from_secs(30),
"the listing waited far past its budget: {:?}",
started.elapsed()
);
}
#[test]
#[cfg(unix)]
fn an_escaped_descendant_holding_listing_stdout_does_not_outlive_the_listing_budget() {
if !std::process::Command::new("setsid")
.arg("true")
.status()
.is_ok_and(|status| status.success())
{
eprintln!("skipping escaped-descendant fixture because `setsid` is unavailable");
return;
}
let directory = crate::testing::workdir("census-escaped-listing-");
let marker = Utf8PathBuf::from_path_buf(directory.path().join("escaped.pid")).expect("UTF-8 path");
let mut command = std::process::Command::new("/bin/sh");
let script = format!("setsid sh -c 'echo $$ > \"{marker}\"; sleep 5' & while [ ! -s \"{marker}\" ]; do sleep 0.01; done");
let _ = command.args(["-c", &script]);
let started = Instant::now();
let names = listed(command, Duration::from_millis(100));
for _attempt in 0..20 {
if marker.as_std_path().exists() {
break;
}
thread::sleep(Duration::from_millis(10));
}
let pid = fs::read_to_string(marker.as_std_path()).expect("the escaped descendant started");
let _stopped = std::process::Command::new("kill").args(["-TERM", pid.trim()]).status();
assert!(names.is_none(), "an incomplete listing was accepted: {names:?}");
assert!(
started.elapsed() < Duration::from_secs(1),
"the listing waited for an escaped pipe holder: {:?}",
started.elapsed()
);
}
#[test]
#[cfg(unix)]
fn a_binary_that_answers_the_listing_is_read_as_the_tests_it_named() {
let mut command = std::process::Command::new("/bin/sh");
let _ = command.args(["-c", "printf 'suite::first: test\\nsuite::second: test\\n'"]);
let names = listed(command, Duration::from_secs(30)).expect("the listing was answered");
assert_eq!(names, vec!["suite::first".into(), "suite::second".into()]);
}
#[test]
#[cfg(unix)]
fn a_binary_that_fails_while_listing_is_not_read_as_the_tests_it_managed_to_print() {
let mut command = std::process::Command::new("/bin/sh");
let _ = command.args(["-c", "printf 'suite::first: test\\n'; exit 1"]);
assert!(listed(command, Duration::from_secs(30)).is_none());
}
#[test]
fn a_listing_is_narrowed_by_the_filters_the_user_gave() {
const SCRIPT: &str = "when-arg:parser|print:suite::parser_works: test";
let (_scratch, mut work) = crate::testing::helper_workspace("census-list-filtered", &[SCRIPT]);
let binary = crate::testing::helper();
work.set_test_args(vec![crate::testing::directive(SCRIPT), "parser".to_owned()]);
assert_eq!(
list(&work, &binary),
Some(vec!["suite::parser_works".into()]),
"the user's filter should have reached the listing"
);
work.set_test_args(vec![crate::testing::directive(SCRIPT)]);
assert_eq!(list(&work, &binary), None);
}
fn binary(path: &str) -> TestBinary {
TestBinary {
path: Utf8PathBuf::from(path),
package: "p".to_owned(),
package_id: String::new(),
target: "t".to_owned(),
manifest_dir: Utf8PathBuf::new(),
baseline: Duration::from_secs(1),
tests: None,
budget: Some(Duration::from_secs(1)),
peak: None,
memory: None,
}
}
#[test]
fn an_uncensused_binary_is_not_the_same_as_one_that_reaches_nothing() {
let mut census = Census::default();
assert_eq!(census.reaching(&binary("/t/a"), 7), None);
let _previous = census.binaries.insert(
Utf8PathBuf::from("/t/a"),
Reach {
complete: true,
..Reach::default()
},
);
assert_eq!(census.reaching(&binary("/t/a"), 7), Some(Vec::new()));
}
fn reach_from(names: Vec<Box<str>>, sites: Vec<(u32, Vec<u32>)>, times: Vec<Duration>) -> Reach {
let mut reach = Reach {
names,
reached: HashMap::default(),
intern: Vec::new(),
intern_index: HashMap::default(),
times,
complete: true,
};
for (site, indices) in sites {
let set_index = reach.intern_set(indices);
let _previous = reach.reached.insert(site, set_index);
}
reach.finish();
reach
}
#[test]
fn a_site_reports_the_tests_that_reached_it_and_no_others() {
let mut census = Census::default();
let _previous = census.binaries.insert(
Utf8PathBuf::from("/t/a"),
reach_from(
vec!["first".into(), "second".into(), "third".into()],
vec![(7, vec![0, 2]), (9, vec![1])],
vec![Duration::from_secs(1), Duration::from_secs(2), Duration::from_secs(3)],
),
);
assert_eq!(census.reaching(&binary("/t/a"), 9), Some(vec!["second"]));
assert_eq!(census.reaching(&binary("/t/a"), 11), Some(Vec::new()));
assert_eq!(census.reaching(&binary("/t/a"), 7), None);
}
#[test]
fn a_site_most_of_the_suite_reaches_is_not_worth_naming() {
let mut census = Census::default();
let names: Vec<Box<str>> = (0..9).map(|index| format!("test{index}").into()).collect();
let _previous = census.binaries.insert(
Utf8PathBuf::from("/t/a"),
reach_from(
names,
vec![(7, vec![0, 1, 2, 3, 4]), (9, vec![0, 1, 2, 3])],
vec![Duration::from_secs(1); 9],
),
);
assert_eq!(census.reaching(&binary("/t/a"), 7), None);
assert_eq!(census.reaching(&binary("/t/a"), 9).map(|tests| tests.len()), Some(4));
}
fn suite_of(total: usize, reached: usize) -> Census {
let mut census = Census::default();
let names: Vec<Box<str>> = (0..total).map(|index| format!("test{index}").into()).collect();
let indices: Vec<u32> = (0..reached).filter_map(|index| u32::try_from(index).ok()).collect();
let _previous = census.binaries.insert(
Utf8PathBuf::from("/t/a"),
reach_from(names, vec![(7, indices)], vec![Duration::from_secs(1); total]),
);
census
}
#[test]
fn exactly_half_the_suite_is_still_worth_naming() {
assert_eq!(suite_of(10, 5).reaching(&binary("/t/a"), 7).map(|tests| tests.len()), Some(5));
}
#[test]
fn one_test_past_half_the_suite_is_not_worth_naming() {
assert_eq!(suite_of(10, 6).reaching(&binary("/t/a"), 7), None);
}
#[test]
fn partial_reach_is_only_a_checked_hint() {
let path = Utf8Path::new("/t/a");
let binary = binary("/t/a");
let census = Census::partial(path, 7, 2, 5);
assert_eq!(
census.selection(&binary, 7),
CensusSelection::Hinted(vec!["tests::t0", "tests::t1"])
);
assert!(matches!(
census.work(&binary, 7),
CensusWork::Hinted(duration) if duration == Duration::from_millis(2)
));
assert_eq!(census.selection(&binary, 8), CensusSelection::Whole);
}
#[test]
fn census_must_cost_less_than_its_maximum_possible_savings() {
assert!(can_repay(Duration::from_secs(1), Duration::from_secs(2)));
assert!(!can_repay(Duration::from_secs(2), Duration::from_secs(2)));
assert!(!can_repay(Duration::from_secs(3), Duration::from_secs(2)));
assert!(!can_repay(Duration::ZERO, Duration::ZERO));
}
#[test]
fn the_bail_out_boundary_sits_at_the_same_place_for_an_odd_suite() {
assert_eq!(suite_of(11, 5).reaching(&binary("/t/a"), 7).map(|tests| tests.len()), Some(5));
assert_eq!(suite_of(11, 6).reaching(&binary("/t/a"), 7), None);
}
#[test]
fn a_site_only_one_test_reaches_is_always_worth_naming() {
assert_eq!(suite_of(1000, 1).reaching(&binary("/t/a"), 7).map(|tests| tests.len()), Some(1));
}
#[test]
fn an_index_naming_no_test_is_dropped_rather_than_panicking() {
let mut census = Census::default();
let _previous = census.binaries.insert(
Utf8PathBuf::from("/t/a"),
reach_from(
vec!["first".into(), "second".into(), "third".into(), "fourth".into()],
vec![(7, vec![0, 40])],
vec![Duration::from_secs(1); 4],
),
);
assert_eq!(census.reaching(&binary("/t/a"), 7), Some(vec!["first"]));
}
#[test]
fn estimate_uses_measured_tests_and_keeps_whole_and_uncovered_distinct() {
let mut census = Census::default();
let _previous = census.binaries.insert(
Utf8PathBuf::from("/t/a"),
reach_from(
vec!["first".into(), "second".into(), "third".into()],
vec![(7, vec![0]), (9, vec![0, 1])],
vec![Duration::from_secs(2), Duration::from_secs(3), Duration::from_secs(5)],
),
);
assert!(matches!(
census.work(&binary("/t/a"), 7),
CensusWork::Selected(duration) if duration == Duration::from_secs(2)
));
assert!(matches!(census.work(&binary("/t/a"), 8), CensusWork::Uncovered));
assert!(matches!(census.work(&binary("/t/a"), 9), CensusWork::Whole));
assert!(matches!(census.work(&binary("/t/b"), 7), CensusWork::Whole));
}
#[test]
fn whole_sealed_records_decode_in_the_order_they_were_written() {
assert_eq!(decode(&sealed(&[3, 9, 1])), Some(vec![3, 9, 1]));
}
#[test]
fn a_lone_seal_is_an_empty_reach_not_a_failure() {
assert_eq!(decode(&sealed(&[])), Some(Vec::new()));
}
#[test]
fn an_unsealed_census_is_refused_even_when_its_records_are_whole() {
let whole: Vec<u8> = [3_u32, 9, 1].iter().flat_map(|site| site.to_le_bytes()).collect();
assert_eq!(decode(&whole), None);
assert_eq!(decode(&[]), None);
}
#[test]
fn a_census_cut_short_during_its_exit_write_is_refused() {
let prefix = 73_u32.to_le_bytes();
assert_eq!(decode(&prefix), None);
}
#[test]
fn a_truncated_census_is_refused_rather_than_read_as_far_as_it_goes() {
assert_eq!(decode(&[1, 0, 0]), None);
assert_eq!(decode(&[1, 0, 0, 0, 2]), None);
}
#[test]
fn anything_after_the_seal_discards_the_census() {
let trailing: Vec<u8> = [SEAL, 7].iter().flat_map(|record| record.to_le_bytes()).collect();
let doubled: Vec<u8> = [SEAL, SEAL].iter().flat_map(|record| record.to_le_bytes()).collect();
assert_eq!(decode(&trailing), None);
assert_eq!(decode(&doubled), None);
}
#[test]
fn the_overflow_marker_discards_the_whole_census_even_when_it_is_sealed() {
let bytes: Vec<u8> = [3_u32, OVERFLOW, 9, SEAL].iter().flat_map(|record| record.to_le_bytes()).collect();
assert_eq!(decode(&bytes), None);
}
#[test]
fn read_bounded_refuses_a_file_past_the_protocol_maximum() {
let scratch = crate::testing::workdir("sec2-oversized");
let path = Utf8PathBuf::from_path_buf(scratch.path().join("census.bin")).expect("a temp path is valid UTF-8");
let oversized =
usize::try_from(MAX_CENSUS_BYTES.saturating_add(4)).expect("the bound fits in memory on any host that runs this test");
fs::write(path.as_std_path(), vec![0_u8; oversized]).expect("the oversized fixture file is writable");
let error = read_bounded(&path).expect_err("a file past the protocol's maximum size must be refused");
assert!(error.to_string().contains("protocol"), "{error}");
}
#[test]
fn read_bounded_refuses_a_sparse_file_without_believing_its_claimed_length() {
use std::io::Write as _;
let scratch = crate::testing::workdir("sec2-sparse");
let path = Utf8PathBuf::from_path_buf(scratch.path().join("census.bin")).expect("a temp path is valid UTF-8");
let file = fs::File::create(path.as_std_path()).expect("the sparse fixture file is creatable");
file.set_len(1 << 34)
.expect("the filesystem under the test work directory supports sparse files");
drop(file);
let error = read_bounded(&path).expect_err("a sparse file past the protocol's maximum must be refused");
assert!(error.to_string().contains("protocol"), "{error}");
let mut confirm = fs::File::options()
.append(true)
.open(path.as_std_path())
.expect("the sparse fixture file remains open-able");
confirm
.write_all(&sealed(&[1]))
.expect("appending a small whole census to the sparse claim succeeds");
drop(confirm);
let _still_refused =
read_bounded(&path).expect_err("a sparse claim past the bound is refused regardless of what real data follows it");
}
#[test]
fn walked_counts_launches_that_happened_not_tests_that_were_listed() {
const SCRIPT: &[&str] = &["when-arg:spoiler|exit:1", "write-le:GAMMA_CENSUS|4294967293"];
let _serial = WALK_TEST.lock().expect("the census walk test lock is not poisoned");
let (_directory, work) = crate::testing::helper_workspace("cor13-walked", SCRIPT);
let binary = TestBinary {
package: "subject".to_owned(),
baseline: Duration::from_millis(1),
budget: Some(Duration::from_mins(1)),
..crate::testing::helper()
};
let names: Vec<Box<str>> = vec!["reaches".into(), "spoiler".into(), "after".into()];
let targets = HashMap::from_iter([(binary.path.clone(), HashSet::from_iter([7]))]);
let mut completed = 0;
let (mapped, walked) = walk(&work, vec![(&binary, names)], &targets, None, 1, Stall::NONE, || completed += 1);
assert_eq!(
walked, 2,
"reaches launched, spoiler launched and spoiled the binary, after was skipped"
);
assert_eq!(completed, 1, "the spoiled binary completed exactly once");
assert!(mapped.is_empty(), "a spoiled binary contributes no reach at all");
}
#[test]
fn walking_stops_when_every_target_site_already_requires_the_whole_binary() {
let _serial = WALK_TEST.lock().expect("the census walk test lock is not poisoned");
let (_directory, work) = crate::testing::helper_workspace("census-saturation-", &[]);
let binary = TestBinary {
package: "subject".to_owned(),
..crate::testing::helper()
};
let names: Vec<Box<str>> = (0..5).map(|index| format!("tests::t{index}").into()).collect();
let targets = HashMap::from_iter([(binary.path.clone(), HashSet::from_iter([7]))]);
let sampled = AtomicUsize::new(0);
let (mapped, walked) = walk_with(
&work,
vec![(&binary, names)],
&targets,
1,
Stall::NONE,
|_work, _binary, _name, _path, _stall| {
let _ = sampled.fetch_add(1, Ordering::Relaxed);
Some((vec![7], Duration::ZERO))
},
|| false,
|| {},
);
let census = Census {
binaries: HashMap::from_iter(mapped),
walked,
};
assert_eq!(sampled.load(Ordering::Relaxed), 3);
assert_eq!(walked, 3);
assert_eq!(census.selection(&binary, 7), CensusSelection::Whole);
}
#[test]
fn a_duplicated_site_within_one_sample_does_not_inflate_the_saturation_count() {
let _serial = WALK_TEST.lock().expect("the census walk test lock is not poisoned");
let (_directory, work) = crate::testing::helper_workspace("census-dup-site-", &[]);
let binary = TestBinary {
package: "subject".to_owned(),
..crate::testing::helper()
};
let names: Vec<Box<str>> = (0..5).map(|index| format!("tests::t{index}").into()).collect();
let targets = HashMap::from_iter([(binary.path.clone(), HashSet::from_iter([42]))]);
let sampled = AtomicUsize::new(0);
let (mapped, walked) = walk_with(
&work,
vec![(&binary, names)],
&targets,
1,
Stall::NONE,
|_work, _binary, _name, _path, _stall| {
let _ = sampled.fetch_add(1, Ordering::Relaxed);
Some((vec![42, 42, 42, 42, 42], Duration::ZERO))
},
|| false,
|| {},
);
let census = Census {
binaries: HashMap::from_iter(mapped),
walked,
};
assert_eq!(sampled.load(Ordering::Relaxed), 3);
assert_eq!(walked, 3);
assert_eq!(census.selection(&binary, 42), CensusSelection::Whole);
}
#[test]
fn a_duplicated_site_within_one_sample_stays_race_free_across_many_workers() {
let _serial = WALK_TEST.lock().expect("the census walk test lock is not poisoned");
let (_directory, work) = crate::testing::helper_workspace("census-dup-site-race-", &[]);
let binary = TestBinary {
package: "subject".to_owned(),
..crate::testing::helper()
};
let names: Vec<Box<str>> = (0..5).map(|index| format!("tests::t{index}").into()).collect();
let targets = HashMap::from_iter([(binary.path.clone(), HashSet::from_iter([42]))]);
let (mapped, walked) = walk_with(
&work,
vec![(&binary, names)],
&targets,
8,
Stall::NONE,
|_work, _binary, _name, _path, _stall| Some((vec![42, 42, 42, 42, 42], Duration::from_millis(1))),
|| false,
|| {},
);
let census = Census {
binaries: HashMap::from_iter(mapped),
walked,
};
assert!(
(3..=5).contains(&walked),
"walked {walked} samples, expected saturation between 3 and 5 tests"
);
assert_eq!(census.selection(&binary, 42), CensusSelection::Whole);
}
#[test]
fn many_workers_reach_the_same_census_as_one_worker() {
const TESTS: usize = 40;
const SITES: u32 = 10;
let _serial = WALK_TEST.lock().expect("the census walk test lock is not poisoned");
let (_directory, work) = crate::testing::helper_workspace("census-race-", &[]);
let binary = TestBinary {
package: "subject".to_owned(),
..crate::testing::helper()
};
let names: Vec<Box<str>> = (0..TESTS).map(|index| format!("tests::t{index}").into()).collect();
let targets = HashMap::from_iter([(binary.path.clone(), (0..SITES).collect::<HashSet<u32>>())]);
let sampler = |_work: &Workspace, _binary: &TestBinary, name: &str, _path: &Utf8Path, _stall: Stall| {
let index: u32 = name
.strip_prefix("tests::t")
.and_then(|digits| digits.parse().ok())
.expect("these test names are always `tests::t<index>`");
let sites: Vec<u32> = (0..SITES).filter(|site| (index + site).is_multiple_of(3)).collect();
Some((sites, Duration::from_millis(1)))
};
let run = |jobs: usize| {
let (mapped, walked) = walk_with(
&work,
vec![(&binary, names.clone())],
&targets,
jobs,
Stall::NONE,
sampler,
|| false,
|| {},
);
(
Census {
binaries: HashMap::from_iter(mapped),
walked,
},
walked,
)
};
let (single, single_walked) = run(1);
let (many, many_walked) = run(8);
assert_eq!(single_walked, TESTS);
assert_eq!(
single_walked, many_walked,
"the same fixed suite launches the same number of samples regardless of worker count"
);
for site in 0..SITES {
assert_eq!(
single.reaching(&binary, site),
many.reaching(&binary, site),
"site {site} disagrees between one worker and many"
);
}
}
#[test]
fn the_budget_keeps_completed_samples_as_hints_without_trusting_missing_ones() {
let _serial = WALK_TEST.lock().expect("the census walk test lock is not poisoned");
let (_directory, work) = crate::testing::helper_workspace("census-budget-", &[]);
let binary = TestBinary {
package: "subject".to_owned(),
..crate::testing::helper()
};
let names: Vec<Box<str>> = (0..5).map(|index| format!("tests::t{index}").into()).collect();
let targets = HashMap::from_iter([(binary.path.clone(), HashSet::from_iter([7, 8]))]);
let spent = AtomicBool::new(false);
let (mapped, walked) = walk_with(
&work,
vec![(&binary, names)],
&targets,
1,
Stall::NONE,
|_work, _binary, _name, _path, _stall| {
spent.store(true, Ordering::Relaxed);
Some((vec![7], Duration::from_millis(20)))
},
|| spent.load(Ordering::Relaxed),
|| {},
);
let census = Census {
binaries: HashMap::from_iter(mapped),
walked,
};
assert_eq!(walked, 1);
assert_eq!(census.selection(&binary, 7), CensusSelection::Hinted(vec!["tests::t0"]));
assert_eq!(census.selection(&binary, 8), CensusSelection::Whole);
}
#[test]
fn a_partial_stream_note_raised_by_a_census_worker_reaches_the_run() {
crate::notes::alone(|| {
let _serial = WALK_TEST.lock().expect("the census walk test lock is not poisoned");
let (_directory, work) = crate::testing::helper_workspace("census-worker-notes-", &[]);
let binary = TestBinary {
package: "subject".to_owned(),
..crate::testing::helper()
};
let targets = HashMap::from_iter([(binary.path.clone(), HashSet::from_iter([7]))]);
let (_mapped, walked) = walk_with(
&work,
vec![(&binary, vec!["partial".into()])],
&targets,
1,
Stall::NONE,
|_work, _binary, _name, _path, _stall| {
crate::notes::note("census sample produced a partial stream");
None
},
|| false,
|| {},
);
assert_eq!(walked, 1);
assert_eq!(crate::notes::drain(), ["census sample produced a partial stream"]);
});
}
#[test]
fn a_clean_run_that_leaves_no_census_spoils_rather_than_reading_empty() {
let _serial = WALK_TEST.lock().expect("the census walk test lock is not poisoned");
let (_directory, work) = crate::testing::helper_workspace("cor4-absent", &["exit:0"]);
let binary = TestBinary {
package: "subject".to_owned(),
baseline: Duration::from_millis(1),
budget: Some(Duration::from_mins(1)),
..crate::testing::helper()
};
let targets = HashMap::from_iter([(binary.path.clone(), HashSet::from_iter([7]))]);
let mut completed = 0;
let (mapped, walked) = walk(&work, vec![(&binary, vec!["quiet".into()])], &targets, None, 1, Stall::NONE, || {
completed += 1;
});
assert_eq!(walked, 1, "the one test was launched");
assert_eq!(completed, 1, "the spoiled binary completed exactly once");
assert!(
mapped.is_empty(),
"an absent census spoils the binary rather than reading as an empty reach"
);
}
fn sealed(sites: &[u32]) -> Vec<u8> {
sites
.iter()
.chain(core::iter::once(&SEAL))
.flat_map(|record| record.to_le_bytes())
.collect()
}
#[test]
fn identical_reach_sets_are_interned() {
let mut reach = Reach {
names: vec!["a".into(), "b".into(), "c".into()],
times: vec![Duration::from_secs(1); 3],
..Reach::default()
};
let idx1 = reach.intern_set(vec![0, 2]);
let idx2 = reach.intern_set(vec![2, 0]);
assert_eq!(idx1, idx2, "identical sets should map to the same intern index");
assert_eq!(reach.intern.len(), 1, "only one interned set should exist");
}
#[test]
fn different_reach_sets_are_distinct() {
let mut reach = Reach {
names: vec!["a".into(), "b".into(), "c".into()],
times: vec![Duration::from_secs(1); 3],
..Reach::default()
};
let idx1 = reach.intern_set(vec![0, 1]);
let idx2 = reach.intern_set(vec![0, 2]);
assert_ne!(idx1, idx2);
assert_eq!(reach.intern.len(), 2);
}
#[test]
fn interned_reach_answers_match_raw_vectors() {
let mut census = Census::default();
let _previous = census.binaries.insert(
Utf8PathBuf::from("/t/a"),
reach_from(
vec!["alpha".into(), "beta".into(), "gamma".into(), "delta".into(), "epsilon".into()],
vec![(1, vec![0, 2]), (2, vec![0, 2]), (3, vec![1])],
vec![Duration::from_millis(10); 5],
),
);
assert_eq!(census.reaching(&binary("/t/a"), 1), Some(vec!["alpha", "gamma"]));
assert_eq!(census.reaching(&binary("/t/a"), 2), Some(vec!["alpha", "gamma"]));
assert_eq!(census.reaching(&binary("/t/a"), 3), Some(vec!["beta"]));
assert_eq!(census.reaching(&binary("/t/a"), 4), Some(Vec::new()));
let reach = &census.binaries[&Utf8PathBuf::from("/t/a")];
assert_eq!(reach.intern.len(), 2, "sites 1 and 2 share one interned set");
}
#[test]
fn streaming_cursor_resolves_tasks_correctly() {
let sizes = [2_usize, 3, 1];
let offsets: Vec<usize> = sizes
.iter()
.scan(0_usize, |acc, &size| {
let start = *acc;
*acc += size;
Some(start)
})
.collect();
let total: usize = sizes.iter().sum();
let mut resolved = Vec::new();
for at in 0..total {
let binary_at = match offsets.binary_search(&at) {
Ok(exact) => exact,
Err(after) => after.saturating_sub(1),
};
let test_at = at - offsets[binary_at];
resolved.push((binary_at, test_at));
}
assert_eq!(resolved, vec![(0, 0), (0, 1), (1, 0), (1, 1), (1, 2), (2, 0)]);
}
}