#![cfg(all(feature = "testing", feature = "render-stats"))]
mod common;
#[path = "../examples/bench_list/list.rs"]
mod list;
#[path = "../benches/scroll_list/scripted.rs"]
mod scripted;
const ROWS: usize = 200;
const FRAMES_PER_PHASE: usize = 6;
#[global_allocator]
static HEAP: guido::heap::CountingAllocator = guido::heap::CountingAllocator;
fn play(rows: usize) -> Option<scripted::Run> {
let _alone = common::one_play_at_a_time();
let mut app = common::headless()?;
let script = scripted::script(FRAMES_PER_PHASE);
Some(scripted::play(
&mut app,
list::VIEWPORT,
move || list::list(rows),
&script,
))
}
#[test]
fn two_runs_of_the_same_script_agree_on_every_count() {
let Some(first) = play(ROWS) else {
return;
};
let second = play(ROWS).expect("the first run had an adapter");
assert_eq!(
first.counts, second.counts,
"the same script over the same tree did two different amounts of work"
);
}
#[test]
fn two_runs_of_the_same_script_allocate_the_same_way() {
let Some(_warm) = play(ROWS) else {
return;
};
let first = play(ROWS).expect("the first run had an adapter");
let second = play(ROWS).expect("the first run had an adapter");
assert!(
first.heap.frame_allocations > 0,
"nothing was counted: this binary did not install guido::heap::CountingAllocator"
);
common::heap_figures_agree(
first.heap.frame_allocations,
second.heap.frame_allocations,
"allocations",
common::COUNTS_AGREE_TO,
);
common::heap_figures_agree(
first.heap.frame_bytes,
second.heap.frame_bytes,
"bytes",
common::BYTES_AGREE_TO,
);
common::heap_figures_agree(
first.heap.play_allocations,
second.heap.play_allocations,
"allocations over the whole play",
100,
);
common::heap_figures_agree(
first.heap.play_bytes,
second.heap.play_bytes,
"bytes over the whole play",
common::BYTES_AGREE_TO,
);
}
#[test]
fn every_scripted_delta_paints_a_frame_and_no_pause_does() {
let Some(run) = play(ROWS) else {
return;
};
let script = scripted::script(FRAMES_PER_PHASE);
let moved = script.iter().filter(|d| **d != 0.0).count() as u64;
assert_eq!(run.counts.frames_painted, moved);
assert_eq!(
run.counts.frames_not_painted,
script.len() as u64 - moved,
"a frame that was neither painted nor still"
);
}
#[test]
fn a_longer_list_is_reported_as_more_work() {
let Some(short) = play(ROWS) else {
return;
};
let long = play(ROWS * 4).expect("the first run had an adapter");
assert_ne!(short.counts, long.counts);
assert!(
long.counts.window_children_total > short.counts.window_children_total,
"four times the rows offered the paint window {} children against {}",
long.counts.window_children_total,
short.counts.window_children_total
);
assert!(
long.heap.peak_live_bytes > short.heap.peak_live_bytes,
"four times the rows held {} bytes at their peak against {}",
long.heap.peak_live_bytes,
short.heap.peak_live_bytes
);
}
#[test]
fn the_table_that_says_it_repeats_repeats() {
let Some(first) = play(ROWS) else {
return;
};
let second = play(ROWS).expect("the first run had an adapter");
assert_eq!(
counts_table(&scripted::report(&first, ROWS)),
counts_table(&scripted::report(&second, ROWS)),
"a column under the header that promises to repeat did not"
);
}
#[test]
fn the_heap_section_prints_the_figure_each_of_its_names_promises() {
let Some(run) = play(ROWS) else {
return;
};
let report = scripted::report(&run, ROWS);
let heap = &run.heap;
for (name, value) in [
("heap.frame_allocations", heap.frame_allocations as i64),
("heap.frame_bytes", heap.frame_bytes as i64),
("heap.play_allocations", heap.play_allocations as i64),
("heap.play_bytes", heap.play_bytes as i64),
("heap.peak_live_bytes", heap.peak_live_bytes as i64),
("heap.retained_bytes", heap.retained_bytes),
] {
let line = report
.lines()
.find(|line| line.trim_start().starts_with(name))
.unwrap_or_else(|| panic!("the report never names {name}"));
assert_eq!(
line.split_whitespace().nth(1),
Some(value.to_string().as_str()),
"{name} is printed as {line:?} and the run says {value}"
);
}
}
fn counts_table(report: &str) -> Vec<&str> {
report
.lines()
.skip_while(|line| !line.starts_with("counts ("))
.take_while(|line| !line.starts_with("cpu phases"))
.collect()
}
#[test]
fn a_gesture_that_runs_off_the_end_of_the_list_says_so() {
let Some(inside) = play(ROWS) else {
return;
};
assert_eq!(
inside.counts.frames_pinned, 0,
"the script was supposed to stay inside a list this long"
);
assert!(!scripted::report(&inside, ROWS).contains("moved nothing"));
let pinned = play(4).expect("the first run had an adapter");
assert!(
pinned.counts.frames_pinned > 0,
"a four-row list absorbed the whole gesture"
);
assert!(
scripted::report(&pinned, 4).contains("moved nothing"),
"the report never mentions the deltas that moved nothing"
);
}
#[test]
fn the_report_names_the_adapter_behind_its_gpu_number() {
let Some(run) = play(ROWS) else {
return;
};
let report = scripted::report(&run, ROWS);
assert!(!run.adapter.is_empty(), "the adapter had no name");
let gpu_line = report
.lines()
.find(|line| line.contains("gpu phase"))
.expect("no gpu section in the report");
assert!(
gpu_line.contains(&run.adapter),
"the gpu section says {gpu_line:?} and never names {}",
run.adapter
);
}