Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/backends/qemu/libvirt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,42 @@ mod tests {
let parsed = parse_qmp_envelope("query", &body).unwrap();
prop_assert_eq!(parsed.get(), payload);
}

/// Ensure that the blockstats parsing function cannot panic on any
/// input.
#[test]
fn test_libvirt_blockstats_cannot_panic(payload in "\\PC*") {
let _ = parse_blockstats(&payload);
}

/// Check that the blockstats parsing function can correctly populate
/// an [`InstancePerfSample`] when given a single entry.
#[test]
fn test_libvirt_blockstats_parses_single_entry(
rd_ops: u64,
wr_ops: u64,
flush_ops: u64,
unmap_ops: u64,
rd_bytes: u64,
wr_bytes: u64)
{
let string = format!(r#"[{{
"stats": {{
"rd_operations":{rd_ops},
"wr_operations":{wr_ops},
"flush_operations":{flush_ops},
"unmap_operations":{unmap_ops},
"rd_bytes":{rd_bytes},
"wr_bytes":{wr_bytes}
}}
}}]"#);
let parsed = parse_blockstats(&string).unwrap().unwrap();
assert_eq!(parsed.read_io_count, rd_ops);
assert_eq!(parsed.write_io_count, wr_ops);
assert_eq!(parsed.other_io_count, flush_ops.saturating_add(unmap_ops));
assert_eq!(parsed.read_bytes_total, rd_bytes);
assert_eq!(parsed.write_bytes_total, wr_bytes);
}
}

/// Test that device counters are added, with flush and unmap folded into
Expand All @@ -538,4 +574,22 @@ mod tests {
fn test_libvirt_blockstats_empty_array_is_none() {
assert!(parse_blockstats("[]").unwrap().is_none());
}

/// Test that a counter sum past `u64::MAX` saturates.
#[test]
fn test_libvirt_blockstats_saturates_on_overflow() {
let max = u64::MAX;
let body = format!(
r#"[
{{"stats":{{"rd_bytes":{max},"wr_bytes":{max},"rd_operations":{max},"wr_operations":{max},"flush_operations":{max},"unmap_operations":1}}}},
{{"stats":{{"rd_bytes":1,"wr_bytes":1,"rd_operations":1,"wr_operations":1,"flush_operations":1,"unmap_operations":{max}}}}}
]"#
);
let perf = parse_blockstats(&body).unwrap().unwrap();
assert_eq!(perf.read_io_count, max);
assert_eq!(perf.write_io_count, max);
assert_eq!(perf.other_io_count, max);
assert_eq!(perf.read_bytes_total, max);
assert_eq!(perf.write_bytes_total, max);
}
}
22 changes: 22 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,28 @@ mod tests {
test_round_trips!(refresh_cgroup_on_each_read in any::<bool>());
test_round_trips!(dry_run in any::<bool>());

/// Ensure that the serde-constructed default is equivalent to the
/// Default::default implementation, while filling out the required serde
/// fields.
#[test]
#[ignore = "current fails"]
fn test_config_serde_default_equivalent() {
let serde_cfg: Config = serde_json::from_str(
r#"
{
"engine":"threshold",
"engine_config_dir":"/etc/io-thread-controller.d/engines",
"backend_config_dir":"/etc/io-thread-controller.d/backends",
"vm_state_path":"/run/io-thread-controller/vm-ownership.json",
"scale_poll_secs":10.0
}"#,
)
.unwrap();
let default_cfg = Config::default();

assert_eq!(serde_cfg, default_cfg);
}

/// Test that loading JSON with an unknown field returns
/// `ConfigError::SerdeJson`.
#[rstest]
Expand Down
Loading
Loading