use rusty_pv::{Progress, PvBuilder, PvError, Reporter, UnitSystem};
use static_assertions::assert_impl_all;
use std::io::Cursor;
use std::time::Duration;
#[test]
fn send_sync_clone_bounds_sc028() {
assert_impl_all!(rusty_pv::Pv: Send);
assert_impl_all!(PvBuilder: Send);
assert_impl_all!(Progress: Send, Sync);
assert_impl_all!(PvError: Send, Sync);
}
#[test]
fn copy_through_in_memory_buffer_returns_bytes_transferred() {
let src = vec![0xABu8; 4096];
let mut reader = Cursor::new(src.clone());
let mut writer = Vec::with_capacity(src.len());
let pv = PvBuilder::new().build();
let n = pv.copy(&mut reader, &mut writer).unwrap();
assert_eq!(n, src.len() as u64);
assert_eq!(writer, src);
}
#[test]
fn builder_three_permutations_yield_equivalent_pv_sc029() {
let canonical = PvBuilder::new()
.total_bytes(1024)
.rate_limit(500)
.buffer_size(2048)
.interval(Duration::from_millis(50))
.name("alpha")
.build();
let reverse = PvBuilder::new()
.name("alpha")
.interval(Duration::from_millis(50))
.buffer_size(2048)
.rate_limit(500)
.total_bytes(1024)
.build();
let shuffled = PvBuilder::new()
.name("alpha")
.buffer_size(2048)
.total_bytes(1024)
.interval(Duration::from_millis(50))
.rate_limit(500)
.build();
for p in [&canonical, &reverse, &shuffled] {
assert_eq!(p.total_bytes(), Some(1024));
assert_eq!(p.rate_limit(), Some(500));
assert_eq!(p.buffer_size(), 2048);
assert_eq!(p.interval(), Duration::from_millis(50));
assert_eq!(p.name(), Some("alpha"));
}
}
#[test]
fn custom_reporter_receives_at_least_one_update() {
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
struct Counter(Arc<AtomicUsize>);
impl Reporter for Counter {
fn report(&mut self, _progress: &Progress) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
let count = Arc::new(AtomicUsize::new(0));
let pv = PvBuilder::new()
.interval(Duration::from_millis(1))
.reporter(Box::new(Counter(count.clone())))
.build();
let mut reader = Cursor::new(vec![0u8; 65536]);
let mut writer = Vec::new();
pv.copy(&mut reader, &mut writer).unwrap();
assert!(count.load(Ordering::Relaxed) >= 1);
}
#[test]
fn unit_system_iec_default_si_opt_in_sc023() {
assert_eq!(UnitSystem::Iec.format_bytes(1024), "1.00KiB");
assert_eq!(UnitSystem::Si.format_bytes(1000), "1.00kB");
assert_eq!(UnitSystem::Iec.format_rate(1_048_576.0), "1.00MiB/s");
assert_eq!(UnitSystem::Si.format_rate(1_000_000.0), "1.00MB/s");
}
#[test]
fn parse_size_iec_and_si() {
use rusty_pv::units::parse_size;
assert_eq!(parse_size("1K", UnitSystem::Iec), Some(1024));
assert_eq!(parse_size("1k", UnitSystem::Si), Some(1000));
assert_eq!(parse_size("1.5M", UnitSystem::Iec), Some(1_572_864));
assert_eq!(parse_size("garbage", UnitSystem::Iec), None);
}
#[test]
fn ema_alpha_is_locked_at_three_tenths_sc031() {
use rusty_pv::ema::EMA_ALPHA;
assert!(
(EMA_ALPHA - 0.3).abs() < f64::EPSILON,
"EMA α MUST be locked at 0.3 — changing it requires a MAJOR semver bump (FR-005)"
);
}
#[test]
fn pv_error_supports_source_chain() {
use std::error::Error;
let io_err = std::io::Error::new(std::io::ErrorKind::BrokenPipe, "test");
let pv_err: PvError = io_err.into();
assert!(pv_err.source().is_some());
}