use super::*;
use crate::events::{ActionDetail, EventWriter};
fn writer(store: &Path) -> EventWriter {
let writer = EventWriter::new(store);
writer.started(Path::new("/checkouts/fixture"), &["build".into()]);
writer
}
#[test]
fn a_session_takes_its_name_and_counts_from_its_stream() {
let store = tempfile::tempdir().unwrap();
let build = writer(store.path());
build.action(
ActionOutcome::Hit,
Some("serde".into()),
10,
ActionDetail {
avoided_compiler_ns: 5_000,
output_bytes: 128,
..ActionDetail::default()
},
);
build.action(
ActionOutcome::Miss,
Some("mbx".into()),
20,
ActionDetail::default(),
);
build.action(
ActionOutcome::Bypass {
reason: "incremental".into(),
},
None,
0,
ActionDetail::default(),
);
let mut app = App::new(store.path(), 10);
app.tick(10);
let session = app.selected_session().expect("the build should be listed");
assert_eq!(session.title(), "mbx build");
assert_eq!(session.workspace_name(), Some("fixture"));
assert_eq!(session.count("hit"), 1);
assert_eq!(session.count("miss"), 1);
assert_eq!(session.count("incremental"), 1);
assert_eq!(session.avoided_compiler_ns, 5_000);
assert_eq!(session.restored_bytes, 128);
assert_eq!(session.rows.len(), 3);
assert_eq!(session.bypasses(), vec![("incremental", 1)]);
assert_eq!(session.state, SessionState::Live);
}
#[test]
fn the_hit_rate_counts_only_attempted_lookups() {
let store = tempfile::tempdir().unwrap();
let build = writer(store.path());
build.action(ActionOutcome::Hit, None, 1, ActionDetail::default());
build.action(ActionOutcome::Hit, None, 1, ActionDetail::default());
build.action(ActionOutcome::Hit, None, 1, ActionDetail::default());
build.action(ActionOutcome::Miss, None, 1, ActionDetail::default());
build.action(ActionOutcome::Unconsulted, None, 1, ActionDetail::default());
build.action(
ActionOutcome::Bypass {
reason: "native-library".into(),
},
None,
0,
ActionDetail::default(),
);
let mut app = App::new(store.path(), 10);
app.tick(10);
let session = app.selected_session().unwrap();
assert_eq!(session.hit_rate(), Some(75.0));
}
#[test]
fn a_build_with_no_lookups_reports_no_hit_rate() {
let store = tempfile::tempdir().unwrap();
let build = writer(store.path());
build.action(ActionOutcome::Unconsulted, None, 1, ActionDetail::default());
let mut app = App::new(store.path(), 10);
app.tick(10);
assert_eq!(app.selected_session().unwrap().hit_rate(), None);
}
#[test]
fn a_finished_session_carries_the_totals_its_stream_ended_with() {
let store = tempfile::tempdir().unwrap();
let build = writer(store.path());
build.action(ActionOutcome::Hit, None, 1, ActionDetail::default());
build.finished(serde_json::json!({ "hits": 41, "misses": 1 }));
let mut app = App::new(store.path(), 10);
app.tick(10);
let session = app.selected_session().unwrap();
assert_eq!(session.state, SessionState::Finished);
let totals = session.totals.as_ref().expect("totals should be recorded");
assert_eq!(crate::events::stat(totals, "hits"), 41);
}
#[test]
fn a_running_build_sorts_above_a_finished_one() {
let store = tempfile::tempdir().unwrap();
let done = writer(store.path());
done.action(ActionOutcome::Hit, None, 1, ActionDetail::default());
done.finished(serde_json::json!({ "hits": 1 }));
drop(done);
let running = writer(store.path());
running.action(ActionOutcome::Miss, None, 1, ActionDetail::default());
let mut app = App::new(store.path(), 10);
app.tick(10);
let states: Vec<SessionState> = app.sessions().map(|session| session.state).collect();
assert_eq!(
states,
vec![SessionState::Live, SessionState::Finished],
"the build somebody is watching belongs at the top"
);
}
#[test]
fn a_build_that_starts_later_is_picked_up_without_a_restart() {
let store = tempfile::tempdir().unwrap();
let mut app = App::new(store.path(), 10);
app.tick(10);
assert!(app.is_empty());
let build = writer(store.path());
build.action(
ActionOutcome::Hit,
Some("serde".into()),
1,
ActionDetail::default(),
);
app.tick(10);
assert_eq!(app.sessions().count(), 1);
assert_eq!(app.selected_session().unwrap().count("hit"), 1);
}
#[test]
fn pausing_stops_reading_and_resuming_catches_up() {
let store = tempfile::tempdir().unwrap();
let build = writer(store.path());
build.action(ActionOutcome::Hit, None, 1, ActionDetail::default());
let mut app = App::new(store.path(), 10);
app.tick(10);
assert_eq!(app.selected_session().unwrap().count("hit"), 1);
app.toggle_pause();
build.action(ActionOutcome::Hit, None, 1, ActionDetail::default());
app.tick(10);
assert_eq!(
app.selected_session().unwrap().count("hit"),
1,
"a paused dashboard should not move"
);
app.toggle_pause();
app.tick(10);
assert_eq!(
app.selected_session().unwrap().count("hit"),
2,
"resuming should pick up what was appended while paused"
);
}
#[test]
fn a_capped_stream_says_so() {
let store = tempfile::tempdir().unwrap();
let build = crate::events::EventWriter::with_cap_for_test(store.path(), 1);
build.started(Path::new("/checkouts/fixture"), &["build".into()]);
build.action(ActionOutcome::Hit, None, 1, ActionDetail::default());
let mut app = App::new(store.path(), 10);
app.tick(10);
assert!(app.selected_session().unwrap().truncated);
}
#[test]
fn tabs_cycle_and_can_be_jumped_to() {
let store = tempfile::tempdir().unwrap();
let mut app = App::new(store.path(), 10);
assert_eq!(app.tab, Tab::Live);
app.next_tab();
assert_eq!(app.tab, Tab::Sessions);
app.next_tab();
assert_eq!(app.tab, Tab::Store);
app.next_tab();
assert_eq!(app.tab, Tab::Live);
app.select_tab(Tab::Store);
assert_eq!(app.tab, Tab::Store);
}
#[test]
fn selection_stays_inside_the_list() {
let store = tempfile::tempdir().unwrap();
let build = writer(store.path());
build.action(ActionOutcome::Hit, None, 1, ActionDetail::default());
let mut app = App::new(store.path(), 10);
app.tick(10);
app.select_next();
assert_eq!(app.selected, 0);
app.select_previous();
assert_eq!(app.selected, 0);
assert!(app.selected_session().is_some());
}