use backbeat::{
global,
wire::DumpReader,
zerocopy::{Immutable, IntoBytes},
Event,
};
use std::time::Duration;
#[derive(Event, IntoBytes, Immutable)]
#[event(namespace = "test::global")]
#[repr(C)]
struct Tick {
#[event(key)]
seq: u64,
}
#[test]
fn record_trigger_and_async_dump() {
let dir = std::env::temp_dir().join(format!("backbeat-global-test-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let base = dir.join("trace.bb");
std::env::set_var("BACKBEAT_PATH", &base);
std::env::set_var("BACKBEAT_BYTES", "65536");
std::env::set_var("BACKBEAT_THROTTLE_MS", "0");
std::env::set_var("BACKBEAT_HOST", "test-host");
assert!(!global::is_enabled() || cfg!(not(feature = "capture")));
global::enable();
assert_eq!(global::is_enabled(), cfg!(feature = "capture"));
assert_eq!(global::dump_path(), base);
assert_eq!(global::host(), "test-host");
for seq in 0..16u64 {
global::record(&Tick { seq });
}
global::trigger();
let bytes = wait_for_dump(&dir, 1).expect("background dumper should write a dump");
let reader = DumpReader::new(bytes).expect("dump decodes");
let metas = reader.metas().unwrap();
assert_eq!(metas.len(), 1);
assert_eq!(metas[0].host, "test-host");
let shards = reader.shards().unwrap();
assert!(!shards.is_empty());
#[cfg(feature = "capture")]
{
use backbeat::ring;
let mut ticks = 0usize;
for shard in &shards {
let region = shard.region.clone();
ring::walk(®ion, shard.head as usize, region.len(), |rec| {
if rec.len() >= 16 {
let id = u64::from_le_bytes(rec[8..16].try_into().unwrap());
if id == Tick::ID.get() {
ticks += 1;
}
}
true
});
}
assert!(ticks > 0, "recorded Tick events should appear in the dump");
}
global::record(&Tick { seq: 99 });
global::trigger();
assert!(
wait_for_dump(&dir, 2).is_some(),
"a second trigger should write a second dump file (throttle is 0)"
);
let _ = std::fs::remove_dir_all(&dir);
}
fn dump_files(dir: &std::path::Path) -> Vec<std::path::PathBuf> {
let mut files: Vec<_> = std::fs::read_dir(dir)
.into_iter()
.flatten()
.flatten()
.map(|e| e.path())
.filter(|p| {
let name = p.file_name().and_then(|n| n.to_str()).unwrap_or("");
name.starts_with("trace.") && name.ends_with(".bb")
})
.collect();
files.sort();
files
}
fn wait_for_dump(dir: &std::path::Path, want: usize) -> Option<Vec<u8>> {
for _ in 0..300 {
let files = dump_files(dir);
if files.len() >= want {
if let Some(last) = files.last() {
if let Ok(b) = std::fs::read(last) {
if !b.is_empty() {
return Some(b);
}
}
}
}
std::thread::sleep(Duration::from_millis(10));
}
None
}