use std::fs::{read_to_string, remove_file};
use std::hint::black_box;
use std::io;
use std::panic::{self, AssertUnwindSafe};
use std::path::{Path, PathBuf};
use alloc_tracker::{Allocator, Session};
use serde_json::Value;
#[global_allocator]
static ALLOCATOR: Allocator<std::alloc::System> = Allocator::system();
const BYTES_PER_ITERATION: usize = 64;
const ITERATIONS: u64 = 8;
fn output_path(operation: &str) -> PathBuf {
folo_utils::cargo_target_directory()
.unwrap_or_else(|| "target".into())
.join("alloc_tracker")
.join(format!("{operation}.json"))
}
fn read_json(path: &Path) -> Value {
serde_json::from_str(&read_to_string(path).unwrap()).unwrap()
}
fn remove_if_present(path: &Path) {
match remove_file(path) {
Ok(()) => {}
Err(error) if error.kind() == io::ErrorKind::NotFound => {}
Err(error) => panic!("failed to remove {}: {error}", path.display()),
}
}
fn record_work(session: &Session, operation: &str) {
let operation = session.operation(operation);
let _span = operation.measure_thread().iterations(ITERATIONS);
for _ in 0..ITERATIONS {
let data: Vec<u8> = black_box(vec![0_u8; BYTES_PER_ITERATION]);
black_box(&data);
}
}
#[test]
#[cfg_attr(miri, ignore)] fn dropping_session_writes_json_into_cargo_target_directory() {
const OPERATION: &str = "alloc_tracker_drop_writes_probe";
let expected = output_path(OPERATION);
remove_if_present(&expected);
{
let session = Session::new();
record_work(&session, OPERATION);
}
assert!(
expected.exists(),
"dropping the session should create {}",
expected.display()
);
let value = read_json(&expected);
assert_eq!(
value.get("operation").and_then(Value::as_str),
Some(OPERATION)
);
assert_eq!(
value.get("total_iterations").and_then(Value::as_u64),
Some(ITERATIONS)
);
assert!(
value
.get("total_bytes_allocated")
.and_then(Value::as_u64)
.is_some()
);
remove_file(&expected).unwrap();
}
#[test]
#[cfg_attr(miri, ignore)] fn no_stdout_session_still_writes_json() {
const OPERATION: &str = "alloc_tracker_no_stdout_writes_probe";
let expected = output_path(OPERATION);
remove_if_present(&expected);
{
let session = Session::new().no_stdout();
record_work(&session, OPERATION);
}
assert!(
expected.exists(),
"no_stdout() should still write {}",
expected.display()
);
let value = read_json(&expected);
assert_eq!(
value.get("operation").and_then(Value::as_str),
Some(OPERATION)
);
remove_file(&expected).unwrap();
}
#[test]
#[cfg_attr(miri, ignore)] fn no_file_suppresses_json_output() {
const OPERATION: &str = "alloc_tracker_no_file_probe";
let expected = output_path(OPERATION);
remove_if_present(&expected);
{
let session = Session::new().no_stdout().no_file();
record_work(&session, OPERATION);
}
assert!(
!expected.exists(),
"no_file() should suppress writing {}",
expected.display()
);
}
#[test]
#[cfg_attr(miri, ignore)] fn empty_session_writes_nothing_on_drop() {
const OPERATION: &str = "alloc_tracker_empty_drop_probe";
let expected = output_path(OPERATION);
remove_if_present(&expected);
{
let session = Session::new().no_stdout();
let _operation = session.operation(OPERATION);
}
assert!(
!expected.exists(),
"an empty session should not write {}",
expected.display()
);
}
#[test]
#[cfg_attr(miri, ignore)] fn panicking_thread_does_not_write_json() {
const OPERATION: &str = "alloc_tracker_panic_guard_probe";
let expected = output_path(OPERATION);
remove_if_present(&expected);
let result = panic::catch_unwind(AssertUnwindSafe(|| {
let session = Session::new().no_stdout();
record_work(&session, OPERATION);
panic!("intentional panic to exercise the drop guard");
}));
assert!(result.is_err(), "the closure should have panicked");
assert!(
!expected.exists(),
"a panicking thread should not write {}",
expected.display()
);
}