#![allow(
clippy::must_use_candidate,
clippy::missing_const_for_fn,
clippy::panic,
reason = "test scaffolding, not production code"
)]
use core::cell::Cell;
use core::fmt;
use core::panic::AssertUnwindSafe;
use core::sync::atomic::{AtomicUsize, Ordering};
use core::time::Duration;
use std::io::{self, Write};
use std::path::Path;
use std::process::Command;
use std::sync::{Mutex, OnceLock, mpsc};
use std::{fs, panic, thread};
use camino::{Utf8Path, Utf8PathBuf};
use crate::commands::Host;
use crate::exec::Workspace;
type PauseChannels = (mpsc::SyncSender<()>, mpsc::Receiver<()>);
type Pauses = OnceLock<Mutex<crate::HashMap<Utf8PathBuf, PauseChannels>>>;
static CACHE_ADOPTION_PAUSES: Pauses = OnceLock::new();
static WORKSPACE_PREPARATION_PAUSES: Pauses = OnceLock::new();
#[derive(Debug)]
pub struct CommandPause {
reached: mpsc::Receiver<()>,
release: Option<mpsc::SyncSender<()>>,
waiting: bool,
}
impl CommandPause {
pub fn wait(&mut self) {
self.reached
.recv_timeout(Duration::from_mins(2))
.expect("the command should reach its registered pause before the test budget expires");
self.waiting = true;
}
pub fn release(mut self) {
self.release_waiter();
}
fn release_waiter(&mut self) {
if self.waiting {
let sender = self
.release
.take()
.expect("a pause that reached the boundary retains exactly one release sender");
let _released = sender.send(());
self.waiting = false;
}
}
}
impl Drop for CommandPause {
fn drop(&mut self) {
self.release_waiter();
}
}
fn hold_at(pauses: &'static Pauses, root: Utf8PathBuf) -> CommandPause {
let (reached_sender, reached) = mpsc::sync_channel(0);
let (release, release_receiver) = mpsc::sync_channel(0);
let prior = pauses
.get_or_init(|| Mutex::new(crate::HashMap::default()))
.lock()
.expect("a prior command-pause test panicked while registering its pause")
.insert(root, (reached_sender, release_receiver));
assert!(prior.is_none(), "only one command pause may target a workspace boundary");
CommandPause {
reached,
release: Some(release),
waiting: false,
}
}
pub fn hold_after_cache_adoption(root: Utf8PathBuf) -> CommandPause {
hold_at(&CACHE_ADOPTION_PAUSES, root)
}
pub fn hold_during_workspace_preparation(root: Utf8PathBuf) -> CommandPause {
hold_at(&WORKSPACE_PREPARATION_PAUSES, root)
}
fn pause_at(pauses: &Pauses, root: &Utf8Path) {
let Some(pauses) = pauses.get() else {
return;
};
let pause = pauses
.lock()
.expect("a prior command-pause test panicked while taking its pause")
.remove(root);
if let Some((reached, release)) = pause
&& reached.send(()).is_ok()
{
let _released = release.recv();
}
}
pub(crate) fn pause_after_cache_adoption(root: &Utf8Path) {
pause_at(&CACHE_ADOPTION_PAUSES, root);
}
pub(crate) fn pause_during_workspace_preparation(root: &Utf8Path) {
pause_at(&WORKSPACE_PREPARATION_PAUSES, root);
}
#[derive(Debug, Default)]
pub struct Sink {
pub out: Vec<u8>,
pub err: Vec<u8>,
terminal: bool,
width: Option<u16>,
env: Vec<(String, String)>,
}
impl Sink {
#[must_use]
pub fn terminal(mut self, width: u16) -> Self {
self.terminal = true;
self.width = Some(width);
self
}
#[must_use]
pub fn with_env(mut self, name: &str, value: &str) -> Self {
self.env.push((name.to_owned(), value.to_owned()));
self
}
#[must_use]
pub fn out(&self) -> String {
String::from_utf8(self.out.clone()).expect("output should be utf-8")
}
#[must_use]
pub fn err(&self) -> String {
String::from_utf8(self.err.clone()).expect("diagnostics should be utf-8")
}
}
impl Host for Sink {
fn output(&mut self) -> impl Write {
&mut self.out
}
fn error(&mut self) -> impl Write {
&mut self.err
}
fn is_terminal(&self) -> bool {
self.terminal
}
fn terminal_width(&self) -> Option<u16> {
self.width
}
fn env(&self, name: &str) -> Option<String> {
self.env.iter().find(|(key, _)| key == name).map(|(_, value)| value.clone())
}
}
#[derive(Debug)]
pub struct Broken;
impl Write for Broken {
fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
Err(io::Error::from(io::ErrorKind::BrokenPipe))
}
fn flush(&mut self) -> io::Result<()> {
Err(io::Error::from(io::ErrorKind::BrokenPipe))
}
}
#[derive(Debug)]
pub struct BrokenHost;
impl Host for BrokenHost {
fn output(&mut self) -> impl Write {
Broken
}
fn error(&mut self) -> impl Write {
Broken
}
fn is_terminal(&self) -> bool {
false
}
fn terminal_width(&self) -> Option<u16> {
None
}
fn env(&self, _name: &str) -> Option<String> {
None
}
}
#[cfg(unix)]
pub fn shell_workspace(prefix: &str, body: &str) -> (tempfile::TempDir, Workspace) {
let directory = workdir(prefix);
let root = Utf8PathBuf::from_path_buf(directory.path().to_path_buf()).expect("the scratch path is UTF-8");
let target = root.join("target");
let mut work = Workspace::adopt(root, target);
work.set_test_args(vec!["-c".to_owned(), body.to_owned()]);
(directory, work)
}
const HELPER_SOURCE: &str = r#"
use std::io::Write as _;
thread_local! {
/// Whatever `eat` faulted in, kept alive so the pages stay resident until the process ends.
static HELD: std::cell::RefCell<Vec<Vec<u8>>> = const { std::cell::RefCell::new(Vec::new()) };
}
fn main() {
let mut code = 0;
for argument in std::env::args().skip(1) {
let Some(directive) = argument.strip_prefix("--gamma-step=") else {
continue;
};
if let Some(status) = step(directive) {
code = status;
break;
}
}
let _ = std::io::stdout().flush();
std::process::exit(code);
}
fn step(directive: &str) -> Option<i32> {
let (name, payload) = directive.split_once(':').unwrap_or((directive, ""));
match name {
"print" => {
let mut out = std::io::stdout();
let _ = writeln!(out, "{payload}");
let _ = out.flush();
None
}
"sleep" => {
let ms = payload.parse().unwrap_or(0);
std::thread::sleep(std::time::Duration::from_millis(ms));
None
}
"exit" => Some(payload.parse().unwrap_or(0)),
"require-file" => {
if std::path::Path::new(payload).is_file() {
None
} else {
Some(1)
}
}
"wait-file" => {
let (path, timeout) = payload.split_once('|').unwrap_or((payload, "0"));
let deadline = std::time::Instant::now()
+ std::time::Duration::from_millis(timeout.parse().unwrap_or(0));
while !std::path::Path::new(path).is_file() && std::time::Instant::now() < deadline {
std::thread::sleep(std::time::Duration::from_millis(10));
}
if std::path::Path::new(path).is_file() {
None
} else {
Some(1)
}
}
"write-env" => {
let mut parts = payload.split('|');
let file = parts.next().unwrap_or_default();
let values: Vec<String> = parts.map(|name| std::env::var(name).unwrap_or_default()).collect();
let _ = std::fs::write(file, values.join(" "));
None
}
"write-le" => {
let mut parts = payload.split('|');
let file = std::env::var(parts.next().unwrap_or_default()).unwrap_or_default();
let bytes: Vec<u8> = parts
.flat_map(|word| word.parse::<u32>().unwrap_or(0).to_le_bytes())
.collect();
let _ = std::fs::write(file, bytes);
None
}
"flood" => {
let wanted: usize = payload.parse().unwrap_or(0);
let line = [b'y'; 64];
let mut out = std::io::stdout();
let mut written = 0;
while written < wanted {
let _ = out.write_all(&line[..63]);
let _ = out.write_all(b"\n");
written += line.len();
}
let _ = out.flush();
None
}
"touch" => {
let _ = std::fs::write(payload, b"");
None
}
// The grandchild the containment tests need: it outlives its parent deliberately, which is
// the shape that leaves an orphan holding the scratch tree when a kill reaches only the
// process it was aimed at.
"spawn" => {
let mut child = std::process::Command::new(std::env::current_exe().expect("the helper knows its own path"));
for inner in payload.split('|') {
let _ = child.arg(format!("--gamma-step={inner}"));
}
let spawned = child.spawn().expect("the helper can start another copy of itself");
let mut out = std::io::stdout();
let _ = writeln!(out, "spawned {}", spawned.id());
let _ = out.flush();
None
}
// Touched a page at a time, because a ceiling is enforced against pages a process has
// really faulted in rather than against an allocator's bookkeeping.
"eat" => {
let mib: usize = payload.parse().unwrap_or(0);
let mut held: Vec<Vec<u8>> = Vec::new();
for _ in 0..mib {
let mut block = vec![0_u8; 1024 * 1024];
for at in (0..block.len()).step_by(4096) {
block[at] = 1;
}
held.push(block);
}
HELD.with(|slot| slot.borrow_mut().extend(held));
None
}
"when-env" => match payload.split_once('|') {
Some((name, inner)) if std::env::var_os(name).is_some() => step(inner),
Some(_) => None,
None => Some(97),
},
"when-arg" => match payload.split_once('|') {
Some((target, inner)) if std::env::args().any(|arg| arg == target) => step(inner),
Some(_) => None,
None => Some(97),
},
_ => Some(97),
}
}
"#;
const HELPER_VERSION: u32 = 7;
pub fn helper_binary_path() -> &'static camino::Utf8Path {
static BUILT: OnceLock<Utf8PathBuf> = OnceLock::new();
BUILT
.get_or_init(|| {
let work = Utf8PathBuf::from_path_buf(Path::new(env!("CARGO_MANIFEST_DIR")).join("../../target/test-work"))
.expect("the target path is UTF-8");
fs::create_dir_all(work.as_std_path()).expect("the test work directory should be creatable");
let suffix = if cfg!(windows) { ".exe" } else { "" };
let helper = work.join(format!("gamma-helper-{HELPER_VERSION}{suffix}"));
if helper.exists() {
return helper;
}
let staging = tempfile::Builder::new()
.prefix("gamma-helper-build")
.tempdir_in(work.as_std_path())
.expect("the staging directory should be creatable");
let source = staging.path().join("helper.rs");
let staged = staging.path().join(format!("helper{suffix}"));
fs::write(&source, HELPER_SOURCE).expect("the helper source should be writable");
let built = Command::new("rustc")
.arg("--edition")
.arg("2024")
.arg("-C")
.arg("debuginfo=0")
.arg("-o")
.arg(&staged)
.arg(&source)
.output()
.expect("rustc should be on the path of anything running these tests");
assert!(
built.status.success(),
"the test helper should compile: {}",
String::from_utf8_lossy(&built.stderr)
);
let _moved = fs::rename(&staged, helper.as_std_path());
assert!(helper.exists(), "the test helper should be in place at {helper}");
helper
})
.as_path()
}
pub fn helper_workspace(prefix: &str, script: &[&str]) -> (tempfile::TempDir, Workspace) {
let directory = workdir(prefix);
let root = Utf8PathBuf::from_path_buf(directory.path().to_path_buf()).expect("the scratch path is UTF-8");
let target = root.join("target");
let mut work = Workspace::adopt(root, target);
work.set_test_args(
script
.iter()
.map(|step| {
if step.starts_with("--") {
(*step).to_owned()
} else {
format!("--gamma-step={step}")
}
})
.collect(),
);
(directory, work)
}
pub fn directive(step: impl fmt::Display) -> String {
format!("--gamma-step={step}")
}
pub fn helper() -> crate::exec::TestBinary {
test_binary(helper_binary_path().as_str())
}
pub fn test_binary(path: &str) -> crate::exec::TestBinary {
crate::exec::TestBinary {
path: Utf8PathBuf::from(path),
package: String::new(),
package_id: String::new(),
target: String::new(),
manifest_dir: Utf8PathBuf::new(),
baseline: Duration::ZERO,
budget: None,
tests: None,
peak: None,
memory: None,
}
}
#[derive(Debug, Default)]
pub struct Recorder {
pub phases: Vec<(String, String)>,
pub mutants: usize,
pub warnings: Vec<String>,
}
impl crate::exec::Events for Recorder {
fn phase(&mut self, verb: &str, detail: &str) {
self.phases.push((verb.to_owned(), detail.to_owned()));
}
fn warn(&mut self, message: &str) {
self.warnings.push(message.to_owned());
}
fn mutant(&mut self, _mutant: &crate::model::Mutant) {
self.mutants = self.mutants.saturating_add(1);
}
}
#[must_use]
pub fn workdir(prefix: &str) -> tempfile::TempDir {
let work = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../target/test-work");
fs::create_dir_all(&work).expect("the test work directory should be creatable");
tempfile::Builder::new()
.prefix(prefix)
.tempdir_in(work)
.expect("the temporary directory should be creatable")
}
pub fn standing_down(what: &str, why: &str) -> bool {
let count = STOOD_DOWN.fetch_add(1, Ordering::Relaxed) + 1;
let line = format!("standing down ({count}): {what} — {why}\n");
let _written = io::stderr().write_all(line.as_bytes());
true
}
static STOOD_DOWN: AtomicUsize = AtomicUsize::new(0);
#[must_use]
pub fn without_memory_support(what: &str) -> bool {
match crate::exec::memory_support() {
Ok(()) => false,
Err(reason) => standing_down(what, &reason.to_string()),
}
}
pub const WATCHDOG: Duration = Duration::from_mins(1);
pub fn within<T: Send + 'static>(budget: Duration, what: &str, body: impl FnOnce() -> T + Send + 'static) -> T {
let (sender, receiver) = mpsc::channel();
let _worker = thread::spawn(move || {
let outcome = panic::catch_unwind(AssertUnwindSafe(body));
let _delivered = sender.send(outcome);
});
match receiver.recv_timeout(budget) {
Ok(Ok(value)) => value,
Ok(Err(panic)) => panic::resume_unwind(panic),
Err(mpsc::RecvTimeoutError::Timeout) => {
panic!("{what} did not finish within {budget:?}; it is hung")
}
Err(mpsc::RecvTimeoutError::Disconnected) => {
panic!("{what} ended without an answer")
}
}
}
#[derive(Debug)]
pub struct Flaky<'a> {
remaining: &'a Cell<usize>,
}
impl Write for Flaky<'_> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if self.remaining.get() == 0 {
return Err(io::Error::from(io::ErrorKind::BrokenPipe));
}
#[expect(clippy::naive_bytecount, reason = "a test double is not worth a dependency")]
let lines = buf.iter().filter(|byte| **byte == b'\n').count();
self.remaining.set(self.remaining.get().saturating_sub(lines));
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[derive(Debug)]
pub struct FlakyHost {
remaining: Cell<usize>,
}
impl FlakyHost {
#[must_use]
pub const fn new(lines: usize) -> Self {
Self {
remaining: Cell::new(lines),
}
}
}
impl Host for FlakyHost {
fn output(&mut self) -> impl Write {
Flaky {
remaining: &self.remaining,
}
}
fn error(&mut self) -> impl Write {
Flaky {
remaining: &self.remaining,
}
}
fn is_terminal(&self) -> bool {
false
}
fn terminal_width(&self) -> Option<u16> {
None
}
fn env(&self, _name: &str) -> Option<String> {
None
}
}
pub fn fails_at_every_line<E: fmt::Display>(lines: usize, run: impl Fn(&mut FlakyHost) -> Result<(), E>) {
for limit in 0..lines {
let mut host = FlakyHost::new(limit);
let error = run(&mut host).err().unwrap_or_else(|| panic!("line {limit} should have failed"));
assert!(error.to_string().contains("broken pipe"), "line {limit}: {error}");
}
}
#[cfg(test)]
#[cfg(not(miri))]
mod tests {
#[test]
fn a_closure_that_finishes_gets_its_value_back() {
let id = within(WATCHDOG, "reporting a thread id", || thread::current().id());
assert_eq!(within(WATCHDOG, "adding", || 21 + 21), 42);
assert_ne!(id, thread::current().id(), "the body must not run on the caller's thread");
}
#[test]
fn a_body_that_never_finishes_is_reported_as_hung() {
let (release, released) = mpsc::channel::<()>();
let hung = panic::catch_unwind(|| {
within(Duration::from_millis(50), "a deliberately hung body", move || {
let _woken = released.recv();
});
})
.expect_err("a hung body must fail the test");
let message = hung
.downcast_ref::<String>()
.map_or_else(|| "not a string".to_owned(), Clone::clone);
assert!(message.contains("a deliberately hung body"), "{message}");
assert!(message.contains("is hung"), "{message}");
drop(release);
}
#[test]
fn a_body_that_panics_fails_with_its_own_message_rather_than_as_a_hang() {
let raised = panic::catch_unwind(|| {
within(WATCHDOG, "a body that fails", || panic!("the assertion the body made"));
})
.expect_err("a panicking body must fail the test");
let message = raised
.downcast_ref::<&str>()
.map_or_else(|| "not a string".to_owned(), |text| (*text).to_owned());
assert_eq!(message, "the assertion the body made");
}
use super::*;
#[test]
fn a_default_sink_captures_both_streams_and_reports_no_terminal() {
let mut sink = Sink::default();
write!(sink.output(), "result").expect("write");
write!(sink.error(), "progress").expect("write");
assert_eq!(sink.out(), "result");
assert_eq!(sink.err(), "progress");
assert!(!sink.is_terminal());
assert_eq!(sink.terminal_width(), None);
assert_eq!(sink.env("GAMMA_NOT_SET"), None);
}
#[test]
fn the_builders_override_the_terminal_and_the_environment() {
let sink = Sink::default().terminal(80).with_env("CI", "true");
assert!(sink.is_terminal());
assert_eq!(sink.terminal_width(), Some(80));
assert_eq!(sink.env("CI").as_deref(), Some("true"));
assert_eq!(sink.env("OTHER"), None);
}
#[test]
fn a_broken_host_fails_every_write_and_every_flush() {
let mut host = BrokenHost;
assert_eq!(host.output().write(b"x").unwrap_err().kind(), io::ErrorKind::BrokenPipe);
assert_eq!(host.output().flush().unwrap_err().kind(), io::ErrorKind::BrokenPipe);
assert_eq!(host.error().write(b"x").unwrap_err().kind(), io::ErrorKind::BrokenPipe);
assert_eq!(host.error().flush().unwrap_err().kind(), io::ErrorKind::BrokenPipe);
assert!(!host.is_terminal());
assert_eq!(host.terminal_width(), None);
assert_eq!(host.env("PATH"), None);
}
#[test]
fn a_flaky_host_fails_once_its_line_budget_is_spent() {
let mut host = FlakyHost::new(2);
writeln!(host.error(), "first").expect("first line");
writeln!(host.error(), "second").expect("second line");
assert_eq!(writeln!(host.output(), "third").unwrap_err().kind(), io::ErrorKind::BrokenPipe);
host.error().flush().expect("flushing a live stream should succeed");
assert!(!host.is_terminal());
assert_eq!(host.terminal_width(), None);
assert_eq!(host.env("PATH"), None);
}
#[test]
fn the_sweep_helper_moves_the_failure_along_one_line_at_a_time() {
fails_at_every_line(3, |host| {
let mut stream = host.error();
writeln!(stream, "one")?;
writeln!(stream, "two")?;
writeln!(stream, "three")
});
}
#[test]
fn a_work_directory_is_created_under_the_target_directory() {
let dir = workdir("testing-workdir-");
assert!(dir.path().is_dir());
assert!(dir.path().to_string_lossy().contains("test-work"));
}
#[test]
fn the_portable_helper_obeys_its_directives() {
let script = [
"--nocapture",
"print:test a::b ... ok",
"write-env:seen.txt|GAMMA_HELPER_UNSET",
"when-env:PATH|print:seen the path",
"when-env:GAMMA_HELPER_UNSET|exit:97",
"exit:3",
];
let (directory, _work) = helper_workspace("helper-self-", &script);
let output = Command::new(helper().path.as_std_path())
.args(script.iter().map(|step| {
if step.starts_with("--") {
(*step).to_owned()
} else {
directive(step)
}
}))
.current_dir(directory.path())
.output()
.expect("the helper runs");
assert_eq!(output.status.code(), Some(3));
let text = String::from_utf8_lossy(&output.stdout);
assert!(text.contains("test a::b ... ok"), "{text}");
assert!(text.contains("seen the path"), "{text}");
assert_eq!(
fs::read_to_string(directory.path().join("seen.txt")).expect("the helper wrote the file"),
""
);
}
#[test]
fn the_portable_helper_refuses_a_directive_it_does_not_know() {
let status = Command::new(helper().path.as_std_path())
.arg(directive("dance:jig"))
.status()
.expect("the helper runs");
assert_eq!(status.code(), Some(97));
}
}
pub mod ci_fixture {
use camino::Utf8PathBuf;
use crate::model::{Mutant, Outcome};
use crate::ops::collect::Shape;
pub fn mutant(file: &str, line: usize, mutator: &str, outcome: Outcome) -> Mutant {
Mutant {
id: format!("{file}:{line}:{mutator}").into(),
ordinal: 0,
file: (Utf8PathBuf::from(file)).into(),
package: ("subject".to_owned()).into(),
span: 0..1,
line,
end_line: line,
column: 5,
mutator: (mutator.to_owned()).into(),
item_path: ("subject::f".to_owned()).into(),
trait_impl: None,
occurrence: 0,
replacement_index: 0,
original: "a > b".into(),
replacement: "a >= b".into(),
shape: Shape::Expr,
outcome,
suppression: None,
expectation: None,
test_timeout_multiplier: None,
elapsed_ms: 0,
killed_by: None,
note: None,
}
}
pub fn root() -> Utf8PathBuf {
Utf8PathBuf::from("/w")
}
}
pub mod advise_fixture {
use core::time::Duration;
use camino::Utf8PathBuf;
use crate::advise::{Finding, Timing};
use crate::exec::TestBinary;
use crate::model::{Mutant, Outcome};
use crate::ops::collect::Shape;
pub fn mutant(file: &str, mutator: &str, outcome: Outcome, ms: u64) -> Mutant {
Mutant {
id: format!("{file}{mutator}{ms}").into(),
ordinal: 1,
package: ("p".to_owned()).into(),
mutator: (mutator.to_owned()).into(),
file: (Utf8PathBuf::from(file)).into(),
line: 1,
end_line: 1,
column: 1,
span: 0..1,
item_path: ("f".to_owned()).into(),
trait_impl: None,
occurrence: 0,
replacement_index: 0,
original: "a".into(),
replacement: "b".into(),
shape: Shape::Expr,
outcome,
suppression: None,
expectation: None,
test_timeout_multiplier: None,
elapsed_ms: ms,
killed_by: None,
note: None,
}
}
pub fn timing(build: u64, baseline: u64, wall: u64) -> Timing {
Timing {
build: Duration::from_secs(build),
baseline: Duration::from_secs(baseline),
wall: Duration::from_secs(wall),
jobs: 4,
}
}
pub fn binary(package: &str, target: &str, baseline: u64, tests: Option<usize>) -> TestBinary {
TestBinary {
path: Utf8PathBuf::from(format!("/target/{target}")),
package: package.to_owned(),
package_id: format!("path+file:///w/{package}#0.0.0"),
target: target.to_owned(),
manifest_dir: Utf8PathBuf::from(format!("/w/{package}")),
baseline: Duration::from_secs(baseline),
tests,
budget: None,
peak: None,
memory: None,
}
}
pub fn find<'a>(findings: &'a [Finding], code: &str) -> Option<&'a Finding> {
findings.iter().find(|finding| finding.code == code)
}
}
pub mod discover_fixture {
use camino::Utf8PathBuf;
use crate::model::{Mutant, Outcome};
use crate::ops::collect;
pub fn counting_mutant(package: &str) -> Mutant {
Mutant {
id: "aaaaaaaaaaaa".into(),
ordinal: 1,
file: (Utf8PathBuf::from("src/lib.rs")).into(),
package: (package.to_owned()).into(),
span: 0..1,
line: 1,
end_line: 1,
column: 1,
mutator: ("relational.gt_to_ge".to_owned()).into(),
item_path: ("f".to_owned()).into(),
trait_impl: None,
occurrence: 0,
replacement_index: 0,
original: "a > b".into(),
replacement: "a >= b".into(),
shape: collect::Shape::Expr,
outcome: Outcome::Survived,
suppression: None,
expectation: None,
test_timeout_multiplier: None,
elapsed_ms: 0,
killed_by: None,
note: None,
}
}
}
#[must_use]
pub fn token(text: &str) -> String {
let kept: String = text
.chars()
.filter(|c| c.is_ascii_alphanumeric() || *c == '_' || *c == ':')
.collect();
if kept.is_empty() { "t".to_owned() } else { kept }
}
#[must_use]
pub fn spliced(noise: &[String], line: &str, at: usize) -> String {
let mut lines: Vec<&str> = noise.iter().map(String::as_str).collect();
let at = at % lines.len().saturating_add(1);
lines.insert(at, line);
lines.join("\n")
}