apsis-rs/crates/sim/apsis-output/tests/output.rs

286 lines
9.2 KiB
Rust

//! Tests for the output trace and recorder.
//!
//! 输出轨迹与记录器的测试。
use apsis_output::{Record, RecordKind, Recorder, Trace};
#[test]
fn an_empty_trace_has_nothing() {
let trace = Trace::new();
assert!(trace.is_empty());
assert_eq!(trace.len(), 0);
assert!(trace.is_stable());
assert!(trace.records().is_empty());
assert_eq!(trace.labels(), Vec::<&str>::new());
assert_eq!(trace.value_of("anything"), None);
assert_eq!(trace.summarize("anything"), None);
assert_eq!(trace.warning_count(), 0);
assert_eq!(trace.longest_series(), 0);
}
#[test]
fn a_trace_keeps_insertion_order() {
// The order a model recorded in is part of what it decided, so the trace
// does not sort.
// 模型记录所用的次序是其决策的一部分,故轨迹不排序。
let mut trace = Trace::new();
trace.push(Record::observation(10.0, "b", 1.0));
trace.push(Record::observation(5.0, "a", 2.0));
let labels: Vec<&str> = trace
.records()
.iter()
.map(|record| record.label.as_str())
.collect();
assert_eq!(labels, vec!["b", "a"]);
}
#[test]
fn time_order_is_available_on_request() {
let mut trace = Trace::new();
trace.push(Record::observation(10.0, "b", 1.0));
trace.push(Record::observation(5.0, "a", 2.0));
trace.push(Record::observation(7.0, "c", 3.0));
let ordered = trace.in_time_order();
let times: Vec<f64> = ordered.iter().map(|record| record.time).collect();
assert_eq!(times, vec![5.0, 7.0, 10.0]);
// The original is unchanged.
// 原件不变。
assert_eq!(trace.records()[0].time, 10.0);
}
#[test]
fn time_order_is_stable_for_equal_times() {
let mut trace = Trace::new();
trace.push(Record::observation(5.0, "first", 1.0));
trace.push(Record::observation(5.0, "second", 2.0));
trace.push(Record::observation(5.0, "third", 3.0));
let ordered = trace.in_time_order();
let labels: Vec<&str> = ordered.iter().map(|record| record.label.as_str()).collect();
assert_eq!(labels, vec!["first", "second", "third"]);
}
#[test]
fn records_can_be_filtered_by_kind() {
let mut trace = Trace::new();
trace.push(Record::observation(1.0, "a", 1.0));
trace.push(Record::warning(2.0, "b", 2.0));
trace.push(Record::state(3.0, "c", 3.0));
trace.push(Record::warning(4.0, "d", 4.0));
assert_eq!(trace.of_kind(RecordKind::Observation).len(), 1);
assert_eq!(trace.of_kind(RecordKind::Warning).len(), 2);
assert_eq!(trace.of_kind(RecordKind::State).len(), 1);
assert_eq!(trace.warning_count(), 2);
}
#[test]
fn records_can_be_filtered_by_label() {
let mut trace = Trace::new();
trace.push(Record::observation(1.0, "range", 1_000.0));
trace.push(Record::observation(2.0, "range", 900.0));
trace.push(Record::observation(3.0, "rate", 5.0));
assert_eq!(trace.with_label("range").len(), 2);
assert_eq!(trace.with_label("rate").len(), 1);
assert_eq!(trace.with_label("absent").len(), 0);
}
#[test]
fn the_last_value_of_a_label_is_reported() {
let mut trace = Trace::new();
trace.push(Record::observation(1.0, "range", 1_000.0));
trace.push(Record::observation(5.0, "range", 900.0));
trace.push(Record::observation(9.0, "range", 950.0));
assert_eq!(trace.value_of("range"), Some(950.0));
assert_eq!(trace.last_time_of("range"), Some(9.0));
assert_eq!(trace.value_of("absent"), None);
}
#[test]
fn the_labels_are_unique_and_sorted() {
let mut trace = Trace::new();
trace.push(Record::observation(1.0, "zeta", 1.0));
trace.push(Record::observation(2.0, "alpha", 2.0));
trace.push(Record::observation(3.0, "zeta", 3.0));
assert_eq!(trace.labels(), vec!["alpha", "zeta"]);
}
#[test]
fn a_series_summarizes_its_values() {
let mut trace = Trace::new();
for (time, value) in [(0.0, 10.0), (1.0, 20.0), (2.0, 30.0), (3.0, 5.0)] {
trace.push(Record::observation(time, "x", value));
}
let summary = trace.summarize("x").expect("the series exists");
assert_eq!(summary.label, "x");
assert_eq!(summary.count, 4);
assert_eq!(summary.min, 5.0);
assert_eq!(summary.max, 30.0);
assert_eq!(summary.first, 10.0);
assert_eq!(summary.last, 5.0);
assert_eq!(summary.change, -5.0);
assert_eq!(summary.mean, 65.0 / 4.0);
assert_eq!(summary.range(), 25.0);
assert!(!summary.is_constant());
}
#[test]
fn a_constant_series_is_recognized() {
let mut trace = Trace::new();
for time in 0..3 {
trace.push(Record::observation(f64::from(time), "x", 7.0));
}
let summary = trace.summarize("x").expect("the series exists");
assert!(summary.is_constant());
assert_eq!(summary.range(), 0.0);
assert_eq!(summary.change, 0.0);
}
#[test]
fn the_summaries_cover_every_label_in_order() {
let mut trace = Trace::new();
trace.push(Record::observation(1.0, "b", 1.0));
trace.push(Record::observation(1.0, "a", 2.0));
trace.push(Record::observation(2.0, "b", 3.0));
let summaries = trace.summaries();
assert_eq!(summaries.len(), 2);
assert_eq!(summaries[0].label, "a");
assert_eq!(summaries[1].label, "b");
assert_eq!(summaries[1].count, 2);
}
#[test]
fn the_longest_series_is_reported() {
let mut trace = Trace::new();
for index in 0..5 {
trace.push(Record::observation(f64::from(index), "x", 1.0));
}
for index in 0..2 {
trace.push(Record::observation(f64::from(index), "y", 1.0));
}
assert_eq!(trace.longest_series(), 5);
}
#[test]
fn a_non_finite_record_is_refused() {
let mut trace = Trace::new();
assert!(!trace.push(Record::observation(f64::NAN, "x", 1.0)));
assert!(!trace.push(Record::observation(1.0, "x", f64::INFINITY)));
assert!(!trace.push(Record::observation(1.0, "", 1.0)));
assert!(trace.is_empty());
// A finite record is accepted.
// 有限的记录被接受。
assert!(trace.push(Record::observation(1.0, "x", 1.0)));
assert_eq!(trace.len(), 1);
assert!(trace.is_stable());
}
#[test]
fn clearing_empties_the_trace() {
let mut trace = Trace::new();
trace.push(Record::observation(1.0, "x", 1.0));
trace.push(Record::observation(2.0, "x", 2.0));
assert_eq!(trace.clear(), 2);
assert!(trace.is_empty());
}
#[test]
fn the_recorder_collects_records_of_each_kind() {
let mut recorder = Recorder::new();
assert!(recorder.observe(1.0, "range", 100.0));
assert!(recorder.note(2.0, "eclipse", 1.0));
assert!(recorder.warn(3.0, "temperature", 90.0));
assert_eq!(recorder.len(), 3);
assert!(!recorder.is_empty());
let trace = recorder.trace();
assert_eq!(trace.of_kind(RecordKind::Observation).len(), 1);
assert_eq!(trace.of_kind(RecordKind::State).len(), 1);
assert_eq!(trace.of_kind(RecordKind::Warning).len(), 1);
}
#[test]
fn the_recorder_counts_what_it_refused() {
let mut recorder = Recorder::new();
assert!(!recorder.observe(f64::NAN, "x", 1.0));
assert!(!recorder.observe(1.0, "x", f64::NAN));
assert!(recorder.observe(1.0, "x", 1.0));
assert_eq!(recorder.refused(), 2);
assert_eq!(recorder.len(), 1);
}
#[test]
fn taking_the_trace_leaves_the_recorder_empty() {
let mut recorder = Recorder::new();
recorder.observe(1.0, "x", 1.0);
recorder.observe(2.0, "x", 2.0);
let trace = recorder.take_trace();
assert_eq!(trace.len(), 2);
assert!(recorder.is_empty());
// The refused count belongs to the recorder and is not reset.
// 被拒绝计数属于记录器且不重置。
recorder.observe(f64::NAN, "x", 1.0);
assert_eq!(recorder.refused(), 1);
}
#[test]
fn the_recorder_can_be_cleared_without_taking() {
let mut recorder = Recorder::new();
recorder.observe(1.0, "x", 1.0);
assert_eq!(recorder.clear(), 1);
assert!(recorder.is_empty());
assert_eq!(recorder.trace().len(), 0);
}
#[test]
fn a_trace_can_be_built_from_records() {
let records = vec![
Record::observation(1.0, "a", 1.0),
Record::observation(2.0, "b", 2.0),
];
let trace = Trace::from_records(records);
assert_eq!(trace.len(), 2);
assert_eq!(trace.value_of("b"), Some(2.0));
}
#[test]
fn a_record_displays_its_shape() {
let record = Record::warning(3.5, "temperature", 90.0);
let text = record.to_string();
assert!(text.contains("warning"), "{text}");
assert!(text.contains("temperature"), "{text}");
assert!(text.contains("90"), "{text}");
}
#[test]
fn the_record_kinds_report_themselves() {
assert_eq!(RecordKind::Observation.name(), "observation");
assert_eq!(RecordKind::State.name(), "state");
assert_eq!(RecordKind::Warning.name(), "warning");
assert!(RecordKind::Warning.is_warning());
assert!(!RecordKind::State.is_warning());
assert_eq!(RecordKind::State.to_string(), "state");
}
#[test]
fn a_record_reports_whether_it_is_usable() {
assert!(Record::observation(1.0, "x", 1.0).is_valid());
assert!(!Record::observation(f64::NAN, "x", 1.0).is_valid());
assert!(!Record::observation(1.0, "x", f64::NAN).is_valid());
assert!(!Record::observation(1.0, "", 1.0).is_valid());
}
#[test]
fn the_trace_default_is_empty() {
let trace = Trace::default();
assert!(trace.is_empty());
}
#[test]
fn the_recorder_default_is_empty() {
let recorder = Recorder::default();
assert!(recorder.is_empty());
assert_eq!(recorder.refused(), 0);
}