use crate::constants::div_by_interval;
use crate::{decode_frozen, AppendError, DecodeError, Encoder};
#[test]
fn test_encoder_struct_sizes_guard() {
use std::mem::size_of;
assert_eq!(
size_of::<Encoder<i8>>(),
40,
"Encoder<i8> size changed! Expected 40 bytes."
);
assert_eq!(
size_of::<Encoder<i16>>(),
48,
"Encoder<i16> size changed! Expected 48 bytes."
);
assert_eq!(
size_of::<Encoder<i32>>(),
48,
"Encoder<i32> size changed! Expected 48 bytes."
);
}
#[test]
fn test_div_by_interval() {
for x in [0, 1, 299, 300, 301, 599, 600, 1000, 10000, 100000, 200000] {
assert_eq!(div_by_interval(x, 300), x / 300, "failed for x={}", x);
}
}
#[test]
fn test_roundtrip() {
let base = 1761955455u32;
let temps = [22, 23, 23, 22, 21, 22, 22, 22, 25, 20];
let mut enc = Encoder::<i32>::new();
for (i, &t) in temps.iter().enumerate() {
enc.append(base + i as u32 * 300, t).unwrap();
}
let dec = enc.decode().unwrap();
assert_eq!(dec.len(), temps.len());
for (i, r) in dec.iter().enumerate() {
assert_eq!(r.value, temps[i]);
}
}
#[test]
fn test_empty() {
let enc = Encoder::<i32>::new();
assert_eq!(enc.count(), 0);
assert!(enc.to_bytes().is_empty());
}
#[test]
fn test_single_reading() {
let mut enc = Encoder::<i32>::new();
enc.append(1761955455, 22).unwrap();
let dec = enc.decode().unwrap();
assert_eq!(dec.len(), 1);
assert_eq!(dec[0].value, 22);
}
#[test]
fn test_gaps() {
let base = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base, 22).unwrap();
enc.append(base + 900, 23).unwrap();
let dec = enc.decode().unwrap();
assert_eq!(dec.len(), 2);
assert_eq!(dec[0].value, 22);
assert_eq!(dec[1].value, 23);
assert_eq!(dec[1].ts - dec[0].ts, 900);
}
#[test]
fn test_long_run() {
let base = 1761955455u32;
let mut enc = Encoder::<i32>::new();
for i in 0..200 {
enc.append(base + i as u32 * 300, 22).unwrap();
}
let dec = enc.decode().unwrap();
assert_eq!(dec.len(), 200);
for r in &dec {
assert_eq!(r.value, 22);
}
}
#[test]
fn test_all_deltas() {
let base = 1761955455u32;
let mut enc = Encoder::<i32>::new();
let mut temp = 70;
let mut temps = vec![temp];
for d in -10i32..=10 {
if d == 0 {
continue;
}
temp += d;
temps.push(temp);
}
temps.push(temp + 50);
temps.push(temp);
for (i, &t) in temps.iter().enumerate() {
enc.append(base + i as u32 * 300, t).unwrap();
}
let dec = enc.decode().unwrap();
assert_eq!(dec.len(), temps.len());
for (i, r) in dec.iter().enumerate() {
assert_eq!(r.value, temps[i], "mismatch at {}", i);
}
}
#[test]
fn test_temp_range_25_to_39() {
let base = 1761955455u32;
let mut enc = Encoder::<i32>::new();
let mut temps: Vec<i32> = (25..=39).collect();
temps.extend((25..39).rev());
for (i, &t) in temps.iter().enumerate() {
enc.append(base + i as u32 * 300, t).unwrap();
}
let dec = enc.decode().unwrap();
assert_eq!(dec.len(), temps.len(), "count mismatch");
for (i, r) in dec.iter().enumerate() {
assert_eq!(r.value, temps[i], "mismatch at {}", i);
}
}
#[test]
fn test_temp_range_neg10_to_39() {
let base = 1761955455u32;
let mut enc = Encoder::<i32>::new();
let temps: Vec<i32> = (-10..=39).collect();
for (i, &t) in temps.iter().enumerate() {
enc.append(base + i as u32 * 300, t).unwrap();
}
let dec = enc.decode().unwrap();
assert_eq!(dec.len(), temps.len(), "count mismatch");
for (i, r) in dec.iter().enumerate() {
assert_eq!(r.value, temps[i], "mismatch at {}", i);
}
}
#[test]
fn test_compression_ratio() {
let base = 1761955455u32;
let mut enc = Encoder::<i32>::new();
let mut temp = 22;
for i in 0..288 {
if i == 50 {
temp = 23;
}
if i == 150 {
temp = 22;
}
enc.append(base + i as u32 * 300, temp).unwrap();
}
let bytes = enc.to_bytes();
assert!(bytes.len() < 60, "encoded size {} too large", bytes.len());
assert!(bytes.len() > 10, "encoded size {} too small", bytes.len());
}
#[test]
fn test_constant_temperature() {
let mut encoder = Encoder::<i32>::new();
let base_ts = 1761955455u32;
for i in 0..10 {
encoder.append(base_ts + i as u32 * 300, 22).unwrap();
}
let decoded = encoder.decode().unwrap();
assert_eq!(decoded.len(), 10);
for reading in &decoded {
assert_eq!(reading.value, 22);
}
}
#[test]
fn test_small_deltas() {
let mut encoder = Encoder::<i32>::new();
let base_ts = 1761955455u32;
let temps = [22, 23, 22, 21, 22];
for (i, &temp) in temps.iter().enumerate() {
encoder.append(base_ts + i as u32 * 300, temp).unwrap();
}
let decoded = encoder.decode().unwrap();
assert_eq!(decoded.len(), 5);
for (i, reading) in decoded.iter().enumerate() {
assert_eq!(reading.value, temps[i], "mismatch at index {}", i);
}
}
#[test]
fn test_medium_delta() {
let mut encoder = Encoder::<i32>::new();
let base_ts = 1761955455u32;
encoder.append(base_ts, 20).unwrap();
encoder.append(base_ts + 300, 25).unwrap(); encoder.append(base_ts + 600, 20).unwrap();
let decoded = encoder.decode().unwrap();
assert_eq!(decoded.len(), 3);
assert_eq!(decoded[0].value, 20);
assert_eq!(decoded[1].value, 25);
assert_eq!(decoded[2].value, 20);
}
#[test]
fn test_large_delta() {
let mut encoder = Encoder::<i32>::new();
let base_ts = 1761955455u32;
encoder.append(base_ts, 20).unwrap();
encoder.append(base_ts + 300, 520).unwrap(); encoder.append(base_ts + 600, 20).unwrap();
let decoded = encoder.decode().unwrap();
assert_eq!(decoded.len(), 3);
assert_eq!(decoded[0].value, 20);
assert_eq!(decoded[1].value, 520);
assert_eq!(decoded[2].value, 20);
}
#[test]
fn test_long_zero_run() {
let mut encoder = Encoder::<i32>::new();
let base_ts = 1761955455u32;
for i in 0..50 {
encoder.append(base_ts + i as u32 * 300, 22).unwrap();
}
let decoded = encoder.decode().unwrap();
assert_eq!(decoded.len(), 50);
for reading in &decoded {
assert_eq!(reading.value, 22);
}
}
#[test]
fn test_with_timestamp_jitter() {
let base_ts = 1761955455u32;
let temps = [22, 23, 23, 22, 21, 21, 22, 23, 22, 21];
let jitter = [0u32, 3, 2, 5, 5, 1, 3, 4, 1, 2];
let mut encoder = Encoder::<i32>::new();
for (i, (&temp, &j)) in temps.iter().zip(jitter.iter()).enumerate() {
let ts = base_ts + (i as u32 * 300) + j;
encoder.append(ts, temp).unwrap();
}
let decoded = encoder.decode().unwrap();
assert_eq!(decoded.len(), 10);
for (i, reading) in decoded.iter().enumerate() {
assert_eq!(
reading.value, temps[i],
"temp mismatch at index {}",
i
);
assert_eq!(
(reading.ts - base_ts) % 300,
0,
"ts {} not aligned to interval at index {}",
reading.ts,
i
);
}
}
#[test]
fn test_specific_day_with_jitter() {
let day_start = 1764547200u32;
let inputs: [(u32, i32); 5] = [
(day_start + 0 * 60 + 3, 25), (day_start + 5 * 60 + 10, 25), (day_start + 10 * 60 + 20, 26), (day_start + 15 * 60 + 2, 25), (day_start + 95 * 60 + 5, 26), ];
let mut encoder = Encoder::<i32>::new();
for (ts, temp) in inputs {
encoder.append(ts, temp).unwrap();
}
let decoded = encoder.decode().unwrap();
assert_eq!(decoded.len(), 4);
let base_ts = inputs[0].0;
let expected: [(u32, i32); 4] = [
(base_ts + 0 * 300, 25), (base_ts + 1 * 300, 25), (base_ts + 2 * 300, 25), (base_ts + 19 * 300, 26), ];
for (i, reading) in decoded.iter().enumerate() {
assert_eq!(
reading.ts, expected[i].0,
"ts mismatch at index {}: got {}, expected {}",
i, reading.ts, expected[i].0
);
assert_eq!(
reading.value, expected[i].1,
"temp mismatch at index {}: got {}, expected {}",
i, reading.value, expected[i].1
);
}
assert_eq!(
decoded[3].ts - decoded[2].ts,
17 * 300,
"gap between readings 2 and 3 should be 17 intervals (5100 seconds)"
);
}
#[test]
fn test_out_of_order_readings_return_error() {
let base_ts = 1761955455u32;
let mut encoder = Encoder::<i32>::new();
encoder.append(base_ts + 600, 24).unwrap(); let actual_base = base_ts + 600;
assert_eq!(
encoder.append(base_ts, 22),
Err(AppendError::TimestampBeforeBase {
ts: base_ts,
base_ts: actual_base
})
);
assert_eq!(
encoder.append(base_ts + 300, 23),
Err(AppendError::TimestampBeforeBase {
ts: base_ts + 300,
base_ts: actual_base
})
);
encoder.append(base_ts + 900, 25).unwrap();
let decoded = encoder.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].value, 24);
assert_eq!(decoded[1].value, 25);
}
#[test]
fn test_reading_before_base_ts_returns_error() {
let base_ts = 1761955455u32;
let mut encoder = Encoder::<i32>::new();
encoder.append(base_ts, 22).unwrap();
assert_eq!(encoder.count(), 1);
assert_eq!(
encoder.append(base_ts - 1, 99),
Err(AppendError::TimestampBeforeBase {
ts: base_ts - 1,
base_ts
})
);
assert_eq!(encoder.count(), 1);
assert_eq!(
encoder.append(base_ts - 100, 99),
Err(AppendError::TimestampBeforeBase {
ts: base_ts - 100,
base_ts
})
);
assert_eq!(encoder.count(), 1);
assert_eq!(
encoder.append(base_ts - 300, 99),
Err(AppendError::TimestampBeforeBase {
ts: base_ts - 300,
base_ts
})
);
assert_eq!(encoder.count(), 1);
encoder.append(base_ts + 300, 23).unwrap();
assert_eq!(encoder.count(), 2);
let decoded = encoder.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].value, 22);
assert_eq!(decoded[1].value, 23);
}
#[test]
fn test_reading_before_epoch_base_as_first() {
let mut encoder = Encoder::<i32>::new();
let old_ts = 1_760_000_000u32 - 1000;
encoder.append(old_ts, 22).unwrap();
assert_eq!(encoder.count(), 1);
let bytes = encoder.to_bytes();
assert_eq!(bytes.len(), 23);
let base_ts = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
assert_eq!(base_ts, old_ts);
}
#[test]
fn test_size_matches_to_bytes() {
let enc = Encoder::<i32>::new();
assert_eq!(enc.size(), enc.to_bytes().len());
let mut enc = Encoder::<i32>::new();
enc.append(1761955455, 22).unwrap();
assert_eq!(enc.size(), enc.to_bytes().len());
let mut enc = Encoder::<i32>::new();
for i in 0..10 {
enc.append(1761955455 + i as u32 * 300, 22).unwrap();
}
assert_eq!(enc.size(), enc.to_bytes().len());
let mut enc = Encoder::<i32>::new();
let temps = [22, 23, 21, 25, 20, 30, 15];
for (i, &t) in temps.iter().enumerate() {
enc.append(1761955455 + i as u32 * 300, t).unwrap();
}
assert_eq!(enc.size(), enc.to_bytes().len());
let mut enc = Encoder::<i32>::new();
for i in 0..200 {
enc.append(1761955455 + i as u32 * 300, 22).unwrap();
}
assert_eq!(enc.size(), enc.to_bytes().len());
let mut enc = Encoder::<i32>::new();
for i in 0..50 {
let temp = if i % 10 == 0 { 25 } else { 22 };
enc.append(1761955455 + i as u32 * 300, temp).unwrap();
}
assert_eq!(enc.size(), enc.to_bytes().len());
}
#[test]
fn test_duplicate_day_events_return_error() {
let base_ts = 1761955455u32;
let mut encoder = Encoder::<i32>::new();
for i in 0..288 {
encoder.append(base_ts + i as u32 * 300, 22).unwrap();
}
assert_eq!(encoder.count(), 288);
for i in 0..287 {
let result = encoder.append(base_ts + i as u32 * 300, 22);
assert!(
matches!(result, Err(AppendError::OutOfOrder { .. })),
"Expected OutOfOrder error for i={}",
i
);
}
encoder.append(base_ts + 287 * 300, 22).unwrap();
assert_eq!(encoder.count(), 288);
}
#[test]
fn test_keep_last_semantics() {
let base_ts = 1761955455u32;
let mut encoder = Encoder::<i32>::new();
encoder.append(base_ts, 22).unwrap();
encoder.append(base_ts + 1, 23).unwrap();
let decoded = encoder.decode().unwrap();
assert_eq!(decoded[0].value, 23);
let mut encoder = Encoder::<i32>::new();
encoder.append(base_ts, 22).unwrap();
encoder.append(base_ts + 1, 22).unwrap();
encoder.append(base_ts + 2, 23).unwrap();
let decoded = encoder.decode().unwrap();
assert_eq!(decoded[0].value, 23);
let mut encoder = Encoder::<i32>::new();
encoder.append(base_ts, 20).unwrap();
encoder.append(base_ts + 1, 21).unwrap();
encoder.append(base_ts + 2, 22).unwrap();
encoder.append(base_ts + 3, 23).unwrap();
let decoded = encoder.decode().unwrap();
assert_eq!(decoded[0].value, 23);
let mut encoder = Encoder::<i32>::new();
encoder.append(base_ts, -16).unwrap();
encoder.append(base_ts + 1, -17).unwrap();
let decoded = encoder.decode().unwrap();
assert_eq!(decoded[0].value, -17);
let mut encoder = Encoder::<i32>::new();
encoder.append(base_ts, -15).unwrap();
encoder.append(base_ts + 1, -16).unwrap();
let decoded = encoder.decode().unwrap();
assert_eq!(decoded[0].value, -16); }
#[test]
fn test_alternating_readings_same_interval_keep_last() {
let base_ts = 1761955455u32;
let mut encoder = Encoder::<i32>::new();
let readings = [25, 21, 25, 21, 25, 21, 25, 21, 25, 21];
for (i, &temp) in readings.iter().enumerate() {
let interval = i / 2;
let offset_within_interval = (i % 2) * 150; encoder
.append(
base_ts + (interval as u32) * 300 + offset_within_interval as u32,
temp,
)
.unwrap();
}
let decoded = encoder.decode().unwrap();
assert_eq!(
decoded.len(),
5,
"expected 5 keep-last readings, got {}",
decoded.len()
);
for (i, reading) in decoded.iter().enumerate() {
assert_eq!(
reading.value, 21,
"expected temp 21 (keep-last) at index {}, got {}",
i, reading.value
);
assert_eq!(
reading.ts,
base_ts + (i as u32) * 300,
"wrong timestamp at index {}",
i
);
}
let expected_header_size = 23;
let size = encoder.size();
assert_eq!(
size, expected_header_size,
"expected size of {} bytes (header only, 4 bits still in accumulator), got {}",
expected_header_size,
size
);
}
#[test]
fn test_custom_interval() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32, 60>::new();
assert_eq!(Encoder::<i32, 60>::interval(), 60);
enc.append(base_ts, 22).unwrap();
enc.append(base_ts + 60, 23).unwrap();
enc.append(base_ts + 120, 24).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 3);
assert_eq!(decoded[0].ts, base_ts);
assert_eq!(decoded[1].ts, base_ts + 60);
assert_eq!(decoded[2].ts, base_ts + 120);
assert_eq!(decoded[0].value, 22);
assert_eq!(decoded[1].value, 23);
assert_eq!(decoded[2].value, 24);
let bytes = enc.to_bytes();
let restored = Encoder::<i32, 60>::from_bytes(&bytes).unwrap();
let decoded_bytes = restored.decode().unwrap();
assert_eq!(decoded_bytes.len(), 3);
assert_eq!(decoded_bytes[1].ts, base_ts + 60);
}
#[test]
fn test_custom_interval_keep_last() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32, 60>::new();
enc.append(base_ts, 20).unwrap();
enc.append(base_ts + 30, 24).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].value, 24); }
#[test]
fn test_single_reading_per_interval_exact() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
let temps = [22, 23, 24, 25, 26, 27, 28, 29, 30, 31];
for (i, &temp) in temps.iter().enumerate() {
enc.append(base_ts + (i as u32) * 300, temp).unwrap();
}
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), temps.len());
for (i, reading) in decoded.iter().enumerate() {
assert_eq!(
reading.value, temps[i],
"Single reading at interval {} should be exact: expected {}, got {}",
i, temps[i], reading.value
);
}
}
#[test]
fn test_max_readings_65535() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
for i in 0..65535u64 {
let temp = ((i % 20) as i32) + 15; enc.append(base_ts + i as u32 * 300, temp).unwrap();
}
assert_eq!(enc.count(), 65535);
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 65535);
let bytes = enc.to_bytes();
let decoded_bytes = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded_bytes.len(), 65535);
}
#[test]
fn test_beyond_max_readings() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
for i in 0..65535u64 {
enc.append(base_ts + i as u32 * 300, 22).unwrap();
}
assert_eq!(enc.count(), 65535);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
enc.append(base_ts + 65535 * 300, 23).unwrap();
}));
if result.is_ok() {
let decoded = enc.decode().unwrap();
assert!(decoded.len() <= 65535);
}
}
#[test]
fn test_extreme_temps_boundaries() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, -500_000).unwrap();
enc.append(base_ts + 300, -499_500).unwrap(); let decoded = enc.decode().unwrap();
assert_eq!(decoded[0].value, -500_000);
assert_eq!(decoded[1].value, -499_500);
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 500_000).unwrap();
enc.append(base_ts + 300, 500_500).unwrap(); let decoded = enc.decode().unwrap();
assert_eq!(decoded[0].value, 500_000);
assert_eq!(decoded[1].value, 500_500);
let mut enc = Encoder::<i32>::new();
let temps = [-1000, -500, 0, 500, 1000, 500, 0, -500, -1000];
for (i, &temp) in temps.iter().enumerate() {
enc.append(base_ts + (i as u32) * 300, temp).unwrap();
}
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), temps.len());
for (i, reading) in decoded.iter().enumerate() {
assert_eq!(
reading.value, temps[i],
"Extreme temp at index {}: expected {}, got {}",
i, temps[i], reading.value
);
}
let mut enc2 = Encoder::<i32>::new();
enc2.append(base_ts, 0).unwrap();
enc2.append(base_ts + 300, 1023).unwrap(); enc2.append(base_ts + 600, 0).unwrap();
let decoded2 = enc2.decode().unwrap();
assert_eq!(decoded2[0].value, 0);
assert_eq!(decoded2[1].value, 1023);
assert_eq!(decoded2[2].value, 0);
let mut enc3 = Encoder::<i32>::new();
enc3.append(base_ts, 100_000).unwrap();
enc3.append(base_ts + 300, 100_500).unwrap();
let bytes = enc3.to_bytes();
let decoded3 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded3[0].value, 100_000);
assert_eq!(decoded3[1].value, 100_500);
}
#[test]
fn test_interval_1_second() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32, 1>::new();
for i in 0..100u32 {
enc.append(base_ts + i, 22 + (i % 5) as i32).unwrap();
}
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 100);
for window in decoded.windows(2) {
assert_eq!(window[1].ts - window[0].ts, 1);
}
}
#[test]
fn test_interval_65535_seconds() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32, 65535>::new();
enc.append(base_ts, 22).unwrap();
enc.append(base_ts + 65535, 23).unwrap();
enc.append(base_ts + 65535 * 2, 24).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 3);
assert_eq!(decoded[1].ts - decoded[0].ts, 65535);
assert_eq!(decoded[2].ts - decoded[1].ts, 65535);
}
#[test]
fn test_zero_run_tier_boundaries() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
enc.append(base_ts + 300, 22).unwrap(); enc.append(base_ts + 600, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 3);
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
for i in 1..=5 {
enc.append(base_ts + i as u32 * 300, 22).unwrap(); }
enc.append(base_ts + 6 * 300, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 7);
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
for i in 1..=21 {
enc.append(base_ts + i as u32 * 300, 22).unwrap(); }
enc.append(base_ts + 22 * 300, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 23);
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
for i in 1..=149 {
enc.append(base_ts + i as u32 * 300, 22).unwrap(); }
enc.append(base_ts + 150 * 300, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 151);
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
for i in 1..=150 {
enc.append(base_ts + i as u32 * 300, 22).unwrap(); }
enc.append(base_ts + 151 * 300, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 152);
}
#[test]
fn test_zero_run_not_split() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 100).unwrap();
for i in 1..=82 {
enc.append(base_ts + i as u32 * 300, 113).unwrap();
}
enc.append(base_ts + 83 * 300, 114).unwrap();
let bytes = enc.to_bytes();
let decoded = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded.len(), 84);
assert_eq!(decoded[0].value, 100);
for i in 1..=82 {
assert_eq!(
decoded[i].value, 113,
"Reading {} should be 113, got {}",
i, decoded[i].value
);
}
assert_eq!(decoded[83].value, 114);
}
#[test]
fn test_zero_run_after_gap() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
for i in 0..60 {
enc.append(base_ts + i as u32 * 300, 22 + (i % 5) as i32).unwrap();
}
enc.append(base_ts + 60 * 300, 120).unwrap();
enc.append(base_ts + 62 * 300, 113).unwrap();
for i in 63..=143 {
enc.append(base_ts + i as u32 * 300, 113).unwrap();
}
enc.append(base_ts + 144 * 300, 114).unwrap();
let bytes = enc.to_bytes();
let decoded = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
let start_113 = decoded.iter().position(|r| r.value == 113).unwrap();
let end_113 = decoded.iter().rposition(|r| r.value == 113).unwrap();
let count_113 = end_113 - start_113 + 1;
assert_eq!(
count_113, 82,
"Expected 82 consecutive readings with value 113, got {}",
count_113
);
assert_eq!(
decoded[end_113 + 1].value, 114,
"Expected value 114 after the last 113"
);
}
#[test]
fn test_gap_encoding_boundaries() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
enc.append(base_ts + 65 * 300, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].ts, base_ts);
assert_eq!(decoded[1].ts, base_ts + 65 * 300);
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
enc.append(base_ts + 66 * 300, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[1].ts, base_ts + 66 * 300);
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
enc.append(base_ts + 129 * 300, 24).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[1].ts, base_ts + 129 * 300);
}
#[test]
fn test_large_timestamp_offset() {
let base_ts = 1_760_000_000u32 + 1_000_000; let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
let large_offset = 1000u32 * 300;
enc.append(base_ts + large_offset, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
let expected_ts_diff = large_offset;
assert_eq!(decoded[1].ts - decoded[0].ts, expected_ts_diff);
}
#[test]
fn test_decode_truncated_header() {
let i32_header_size = 23;
assert_eq!(i32_header_size, 23);
assert!(Encoder::<i32, 300>::from_bytes(&[]).unwrap().is_empty());
assert!(Encoder::<i32, 300>::from_bytes(&[0]).is_err());
assert!(Encoder::<i32, 300>::from_bytes(&[0; 22]).is_err());
let mut header = vec![0u8; i32_header_size];
let enc = Encoder::<i32, 300>::from_bytes(&header).unwrap();
let decoded = enc.decode().unwrap();
assert!(decoded.is_empty());
header[4] = 1; header[5] = 0;
header[16] = 22;
header[17] = 0;
header[18] = 0;
header[19] = 0;
let enc = Encoder::<i32, 300>::from_bytes(&header).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 1);
assert_eq!(decoded[0].value, 22);
}
#[test]
fn test_decode_corrupted_count() {
let mut enc = Encoder::<i32>::new();
enc.append(1761955455, 22).unwrap();
enc.append(1761955455 + 300, 23).unwrap();
let mut bytes = enc.to_bytes();
bytes[4] = 255; bytes[5] = 0;
let restored = Encoder::<i32, 300>::from_bytes(&bytes).unwrap();
let decoded = restored.decode().unwrap();
assert!(decoded.len() <= 255);
}
#[test]
fn test_decode_zero_interval() {
let mut header = [0u8; 10];
header[4] = 1; header[5] = 0;
header[6] = 22;
header[7] = 0;
header[8] = 0;
header[9] = 0;
let decoded = decode_frozen::<i32, 1>(&header).unwrap();
assert!(decoded.len() <= 1);
}
#[test]
fn test_31_readings_same_interval() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
for i in 0..31 {
enc.append(base_ts + i * 5, 20 + (i as i32 % 10)).unwrap(); }
enc.append(base_ts + 300, 25).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].value, 20);
}
#[test]
fn test_delta_overflow_returns_error() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 0).unwrap(); enc.append(base_ts + 300, 0).unwrap(); assert!(matches!(
enc.append(base_ts + 600, 2000),
Err(AppendError::DeltaOverflow {
delta: 2000,
current_value: 0,
new_value: 2000
})
));
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 2000).unwrap(); enc.append(base_ts + 300, 2000).unwrap(); assert!(matches!(
enc.append(base_ts + 600, 0),
Err(AppendError::DeltaOverflow {
delta: -2000,
current_value: 2000,
new_value: 0
})
));
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 0).unwrap();
enc.append(base_ts + 300, 0).unwrap();
assert!(matches!(
enc.append(base_ts + 600, 1024),
Err(AppendError::DeltaOverflow {
delta: 1024,
current_value: 0,
new_value: 1024
})
));
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 1025).unwrap();
enc.append(base_ts + 300, 1025).unwrap();
assert!(matches!(
enc.append(base_ts + 600, 0),
Err(AppendError::DeltaOverflow {
delta: -1025,
current_value: 1025,
new_value: 0
})
));
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 0).unwrap();
enc.append(base_ts + 300, 0).unwrap();
enc.append(base_ts + 600, 1023).unwrap(); enc.append(base_ts + 900, 0).unwrap();
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 1024).unwrap();
enc.append(base_ts + 300, 1024).unwrap();
enc.append(base_ts + 600, 0).unwrap(); enc.append(base_ts + 900, 0).unwrap(); }
#[test]
fn test_timestamp_at_epoch_base() {
let mut enc = Encoder::<i32>::new();
enc.append(1_760_000_000, 22).unwrap(); enc.append(1_760_000_300, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].ts, 1_760_000_000);
}
#[test]
fn test_count_at_max_u16() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32, 1>::new();
for i in 0..65535u32 {
enc.append(base_ts + i, 22).unwrap();
}
assert_eq!(enc.count(), 65535);
}
#[test]
fn test_error_display_formatting() {
let err: AppendError<i32> = AppendError::TimestampBeforeBase { ts: 100, base_ts: 200 };
assert!(err.to_string().contains("100"));
assert!(err.to_string().contains("200"));
let err: AppendError<i32> = AppendError::OutOfOrder { ts: 300, logical_idx: 1, prev_logical_idx: 2 };
assert!(err.to_string().contains("interval"));
let err: AppendError<i32> = AppendError::IntervalOverflow { count: 1023 };
assert!(err.to_string().contains("1023"));
let err: AppendError<i32> = AppendError::CountOverflow;
assert!(err.to_string().contains("65535"));
let err: AppendError<i32> = AppendError::DeltaOverflow { delta: 2000, current_value: 0, new_value: 2000 };
assert!(err.to_string().contains("2000"));
}
#[test]
fn test_error_trait_impl() {
use std::error::Error;
let err: &dyn Error = &AppendError::<i32>::CountOverflow;
assert!(err.source().is_none());
}
#[test]
fn test_zero_run_encoding_structure() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 15).unwrap();
enc.append(base_ts + 300, 12).unwrap();
for i in 2..=17 {
enc.append(base_ts + i as u32 * 300, 12).unwrap();
}
enc.append(base_ts + 18 * 300, 5).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 19, "Should have 19 readings");
assert_eq!(decoded[0].ts, base_ts);
assert_eq!(decoded[0].value, 15);
assert_eq!(decoded[1].ts, base_ts + 300);
assert_eq!(decoded[1].value, 12);
for i in 2..=17 {
assert_eq!(decoded[i].ts, base_ts + i as u32 * 300, "Timestamp at index {}", i);
assert_eq!(decoded[i].value, 12, "Value at index {}", i);
}
assert_eq!(decoded[18].ts, base_ts + 18 * 300);
assert_eq!(decoded[18].value, 5);
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2, "Roundtrip should preserve readings");
}
#[test]
fn test_single_gap_encoding() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap(); enc.append(base_ts + 600, 22).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].ts, base_ts);
assert_eq!(decoded[1].ts, base_ts + 600);
assert_eq!(decoded[1].ts - decoded[0].ts, 600); assert_eq!(decoded[0].value, 22);
assert_eq!(decoded[1].value, 22);
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2);
}
#[test]
fn test_single_gap_with_delta() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap(); enc.append(base_ts + 600, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].value, 22);
assert_eq!(decoded[1].value, 23);
assert_eq!(decoded[1].ts - decoded[0].ts, 600);
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2);
}
#[test]
fn test_multiple_single_gaps() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap(); enc.append(base_ts + 600, 22).unwrap(); enc.append(base_ts + 1200, 22).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 3);
assert_eq!(decoded[0].ts, base_ts);
assert_eq!(decoded[1].ts, base_ts + 600);
assert_eq!(decoded[2].ts, base_ts + 1200);
for r in &decoded {
assert_eq!(r.value, 22);
}
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2);
}
#[test]
fn test_two_interval_gap_encoding() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap(); enc.append(base_ts + 900, 22).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[1].ts - decoded[0].ts, 900);
assert_eq!(decoded[0].value, 22);
assert_eq!(decoded[1].value, 22);
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2);
}
#[test]
fn test_plus_two_delta_encoding() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 20).unwrap();
enc.append(base_ts + 300, 22).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].value, 20);
assert_eq!(decoded[1].value, 22);
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2);
}
#[test]
fn test_minus_two_delta_encoding() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
enc.append(base_ts + 300, 20).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].value, 22);
assert_eq!(decoded[1].value, 20);
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2);
}
#[test]
fn test_multiple_two_deltas() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 20).unwrap();
enc.append(base_ts + 300, 22).unwrap(); enc.append(base_ts + 600, 20).unwrap(); enc.append(base_ts + 900, 22).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 4);
assert_eq!(decoded.iter().map(|r| r.value).collect::<Vec<_>>(), vec![20, 22, 20, 22]);
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2);
}
#[test]
fn test_zero_run_150_split_encoding() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
for i in 1..=150 {
enc.append(base_ts + i as u32 * 300, 22).unwrap();
}
enc.append(base_ts + 151 * 300, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 152);
for i in 0..151 {
assert_eq!(decoded[i].value, 22, "Expected 22 at index {}", i);
}
assert_eq!(decoded[151].value, 23);
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2);
}
#[test]
fn test_zero_run_300_split_encoding() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
for i in 1..=300 {
enc.append(base_ts + i as u32 * 300, 22).unwrap();
}
enc.append(base_ts + 301 * 300, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 302);
for i in 0..301 {
assert_eq!(decoded[i].value, 22, "Expected 22 at index {}", i);
}
assert_eq!(decoded[301].value, 23);
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2);
}
#[test]
fn test_gap_65_single_marker() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
enc.append(base_ts + 66 * 300, 22).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[1].ts - decoded[0].ts, 66 * 300);
assert_eq!(decoded[0].value, 22);
assert_eq!(decoded[1].value, 22);
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2);
}
#[test]
fn test_gap_66_split_encoding() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
enc.append(base_ts + 67 * 300, 22).unwrap(); enc.append(base_ts + 68 * 300, 23).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 3, "Should have 3 readings");
assert_eq!(decoded[0].ts, base_ts, "First timestamp");
assert_eq!(decoded[1].ts, base_ts + 67 * 300, "Second timestamp (67 intervals later)");
assert_eq!(decoded[2].ts, base_ts + 68 * 300, "Third timestamp (68 intervals later)");
assert_eq!(decoded[0].value, 22, "First value");
assert_eq!(decoded[1].value, 22, "Second value (same)");
assert_eq!(decoded[2].value, 23, "Third value (+1 delta)");
let bytes = enc.to_bytes();
let decoded2 = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded, decoded2, "Roundtrip should preserve readings");
}
#[test]
fn test_all_encoding_prefixes() {
let base_ts = 1761955455u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 100).unwrap();
enc.append(base_ts + 300, 100).unwrap();
enc.append(base_ts + 600, 101).unwrap();
enc.append(base_ts + 900, 100).unwrap();
enc.append(base_ts + 1500, 100).unwrap();
enc.append(base_ts + 1800, 102).unwrap();
enc.append(base_ts + 2100, 100).unwrap();
enc.append(base_ts + 2400, 105).unwrap();
enc.append(base_ts + 2700, 205).unwrap();
enc.append(base_ts + 3600, 205).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 10);
let expected_values = [100, 100, 101, 100, 100, 102, 100, 105, 205, 205];
for (i, r) in decoded.iter().enumerate() {
assert_eq!(r.value, expected_values[i], "Value mismatch at index {}", i);
}
let expected_ts = [
base_ts,
base_ts + 300,
base_ts + 600,
base_ts + 900,
base_ts + 1500, base_ts + 1800,
base_ts + 2100,
base_ts + 2400,
base_ts + 2700,
base_ts + 3600, ];
for (i, r) in decoded.iter().enumerate() {
assert_eq!(r.ts, expected_ts[i], "Timestamp mismatch at index {}", i);
}
}
#[test]
fn test_large_gap_exceeding_u16() {
let base_ts = 1_760_000_000u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 100).unwrap();
let large_gap: u32 = 70_000;
let second_ts = base_ts + large_gap * 300;
let result = enc.append(second_ts, 200);
assert!(matches!(result, Err(AppendError::TimeSpanOverflow { .. })));
assert_eq!(enc.count(), 1);
}
#[test]
fn test_very_large_gap_100k_intervals() {
let base_ts = 1_760_000_000u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 50).unwrap();
let large_gap: u32 = 100_000;
let second_ts = base_ts + large_gap * 300;
let result = enc.append(second_ts, 75);
assert!(matches!(result, Err(AppendError::TimeSpanOverflow { .. })));
assert_eq!(enc.count(), 1);
}
#[test]
fn test_gap_at_u16_boundary() {
let base_ts = 1_760_000_000u32;
let mut enc = Encoder::<i32, 1>::new();
enc.append(base_ts, 10).unwrap();
let gap: u32 = u32::from(u16::MAX);
let second_ts = base_ts + gap; enc.append(second_ts, 20).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].ts, base_ts);
assert_eq!(decoded[0].value, 10);
assert_eq!(decoded[1].ts, second_ts);
assert_eq!(decoded[1].value, 20);
}
#[test]
fn test_gap_just_over_u16_max() {
let base_ts = 1_760_000_000u32;
let mut enc = Encoder::<i32, 1>::new();
enc.append(base_ts, 10).unwrap();
let gap: u32 = u32::from(u16::MAX) + 1;
let second_ts = base_ts + gap; let result = enc.append(second_ts, 20);
assert!(matches!(result, Err(AppendError::TimeSpanOverflow { .. })));
assert_eq!(enc.count(), 1);
}
#[test]
fn test_zero_run_with_concurrent_averaging() {
let base_ts = 1_760_000_000u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 20).unwrap();
for i in 1..=10 {
enc.append(base_ts + i as u32 * 300, 20).unwrap();
}
let new_interval_ts = base_ts + 11 * 300;
enc.append(new_interval_ts, 25).unwrap();
enc.append(new_interval_ts + 100, 27).unwrap(); enc.append(new_interval_ts + 200, 26).unwrap();
enc.append(base_ts + 12 * 300, 30).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 13);
for i in 0..=10 {
assert_eq!(decoded[i].value, 20, "Zero run reading {} should be 20", i);
}
assert_eq!(decoded[11].value, 26, "Averaged reading should be 26");
assert_eq!(decoded[12].value, 30);
let bytes = enc.to_bytes();
let decoded_bytes = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded_bytes.len(), 13);
assert_eq!(decoded_bytes[11].value, 26);
}
#[test]
fn test_zero_run_max_accumulation() {
let base_ts = 1_760_000_000u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 22).unwrap();
for i in 1..=500 {
enc.append(base_ts + i as u32 * 300, 22).unwrap();
}
enc.append(base_ts + 501 * 300, 23).unwrap();
assert_eq!(enc.count(), 502);
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 502);
for i in 0..501 {
assert_eq!(decoded[i].value, 22, "Reading {} should be 22", i);
}
assert_eq!(decoded[501].value, 23);
let bytes = enc.to_bytes();
let decoded_bytes = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded_bytes.len(), 502);
}
#[test]
fn test_interval_boundary_u16_max_minus_one() {
let base_ts = 1_760_000_000u32;
let mut enc = Encoder::<i32, 1>::new();
enc.append(base_ts, 10).unwrap();
let gap: u32 = u32::from(u16::MAX) - 1;
let second_ts = base_ts + gap;
enc.append(second_ts, 20).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 2);
assert_eq!(decoded[0].ts, base_ts);
assert_eq!(decoded[1].ts, second_ts);
let bytes = enc.to_bytes();
let decoded_bytes = Encoder::<i32, 1>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded_bytes.len(), 2);
assert_eq!(decoded_bytes[1].ts, second_ts);
}
#[test]
fn test_multiple_readings_at_max_interval() {
let base_ts = 1_760_000_000u32;
let mut enc = Encoder::<i32, 1>::new();
enc.append(base_ts, 10).unwrap();
let one_third = u32::from(u16::MAX) / 3;
enc.append(base_ts + one_third, 15).unwrap();
let two_thirds = (u32::from(u16::MAX) / 3) * 2;
enc.append(base_ts + two_thirds, 20).unwrap();
let max_gap = u32::from(u16::MAX);
enc.append(base_ts + max_gap, 25).unwrap();
assert_eq!(enc.count(), 4);
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 4);
assert_eq!(decoded[0].value, 10);
assert_eq!(decoded[1].value, 15);
assert_eq!(decoded[2].value, 20);
assert_eq!(decoded[3].value, 25);
assert_eq!(decoded[3].ts, base_ts + max_gap);
let bytes = enc.to_bytes();
let restored = Encoder::<i32, 1>::from_bytes(&bytes).unwrap();
let decoded_bytes = restored.decode().unwrap();
assert_eq!(decoded_bytes.len(), 4);
}
#[test]
fn test_time_span_overflow_error_details() {
let base_ts = 1_760_000_000u32;
let mut enc = Encoder::<i32, 1>::new();
enc.append(base_ts, 10).unwrap();
let overflow_ts = base_ts + u32::from(u16::MAX) + 1;
let result = enc.append(overflow_ts, 20);
match result {
Err(AppendError::TimeSpanOverflow { ts, base_ts: err_base, max_intervals }) => {
assert_eq!(ts, overflow_ts);
assert_eq!(err_base, base_ts);
assert_eq!(max_intervals, u32::from(u16::MAX));
}
_ => panic!("Expected TimeSpanOverflow error"),
}
let err = result.unwrap_err();
let msg = err.to_string();
assert!(msg.contains(&overflow_ts.to_string()));
assert!(msg.contains("65535"));
}
#[test]
fn test_zero_run_then_gap_then_zero_run() {
let base_ts = 1_760_000_000u32;
let mut enc = Encoder::<i32>::new();
enc.append(base_ts, 20).unwrap();
for i in 1..=5 {
enc.append(base_ts + i as u32 * 300, 20).unwrap();
}
enc.append(base_ts + 9 * 300, 20).unwrap();
for i in 10..=14 {
enc.append(base_ts + i as u32 * 300, 20).unwrap();
}
enc.append(base_ts + 15 * 300, 25).unwrap();
let decoded = enc.decode().unwrap();
assert_eq!(decoded.len(), 13);
for i in 0..6 {
assert_eq!(decoded[i].value, 20, "Pre-gap reading {} should be 20", i);
}
for i in 6..12 {
assert_eq!(decoded[i].value, 20, "Post-gap reading {} should be 20", i);
}
assert_eq!(decoded[12].value, 25);
let bytes = enc.to_bytes();
let decoded_bytes = Encoder::<i32, 300>::from_bytes(&bytes).unwrap().decode().unwrap();
assert_eq!(decoded_bytes.len(), 13);
}
#[test]
fn test_decode_frozen_empty_input() {
let result = decode_frozen::<i8, 300>(&[]).unwrap();
assert!(result.is_empty(), "Empty input should return empty vec");
}
#[test]
fn test_decode_frozen_too_short() {
let short = [0u8; 6];
let result = decode_frozen::<i8, 300>(&short);
assert!(result.is_err(), "Too-short input should return error");
}
#[test]
fn test_decode_frozen_zero_count() {
let mut buf = [0u8; 7];
buf[4] = 0; buf[5] = 0; let result = decode_frozen::<i8, 300>(&buf).unwrap();
assert!(result.is_empty(), "Zero count should return empty vec");
}
#[test]
fn test_decode_frozen_max_count_no_data() {
let mut buf = [0u8; 7];
buf[4] = 0xFF; buf[5] = 0xFF; buf[6] = 20; let result = decode_frozen::<i8, 300>(&buf).unwrap();
assert_eq!(result.len(), 1, "Should decode first reading from header");
assert_eq!(result[0].value, 20);
}
#[test]
fn test_decode_frozen_garbage_data() {
let mut buf = vec![0u8; 100];
buf[0] = 0x00; buf[1] = 0x00;
buf[2] = 0x10;
buf[3] = 0x69; buf[4] = 10; buf[5] = 0;
buf[6] = 22; let result = decode_frozen::<i8, 300>(&buf).unwrap();
assert!(!result.is_empty(), "Should decode at least one reading");
assert!(result.len() <= 10, "Should not exceed claimed count");
}
#[test]
fn test_from_bytes_empty_input() {
let result = Encoder::<i8, 300>::from_bytes(&[]).unwrap();
assert!(result.is_empty(), "Empty input should return empty encoder");
}
#[test]
fn test_from_bytes_too_short() {
let short = [0u8; 13];
let result = Encoder::<i8, 300>::from_bytes(&short);
assert!(result.is_err(), "Too-short input should return error");
}
#[test]
fn test_from_bytes_zero_count() {
let mut buf = [0u8; 14];
buf[4] = 0; buf[5] = 0;
let enc = Encoder::<i8, 300>::from_bytes(&buf).unwrap();
let result = enc.decode().unwrap();
assert!(result.is_empty(), "Zero count should return empty vec");
}
#[test]
fn test_from_bytes_max_count_no_data() {
let mut buf = [0u8; 14];
buf[4] = 0xFF; buf[5] = 0xFF;
buf[8] = 20; buf[9] = 20; buf[10] = 20; buf[11] = 0; buf[12] = 0; buf[13] = 0; let enc = Encoder::<i8, 300>::from_bytes(&buf).unwrap();
let result = enc.decode().unwrap();
assert!(result.len() <= 65535, "Should not exceed claimed count");
assert!(!result.is_empty(), "Should decode at least one reading");
}
#[test]
fn test_from_bytes_corrupted_bit_count() {
let mut buf = vec![0u8; 20];
buf[4] = 5; buf[5] = 0;
buf[8] = 20; buf[9] = 20; buf[10] = 20; buf[11] = 0; buf[12] = 255; buf[13] = 0xFF; let result = Encoder::<i8, 300>::from_bytes(&buf);
assert!(matches!(result, Err(AppendError::MalformedData)));
}
#[test]
fn test_from_bytes_max_zero_run() {
let mut buf = vec![0u8; 20];
buf[4] = 10; buf[5] = 0;
buf[8] = 20; buf[9] = 20; buf[10] = 20; buf[11] = 255; buf[12] = 0; buf[13] = 0; let enc = Encoder::<i8, 300>::from_bytes(&buf).unwrap();
let result = enc.decode().unwrap();
assert!(result.len() <= 10, "Should not exceed claimed count");
}
#[test]
fn test_decode_frozen_all_ones() {
let buf = [0xFFu8; 100];
let result = decode_frozen::<i8, 300>(&buf);
assert!(result.is_err());
assert!(matches!(result, Err(DecodeError::MalformedData)));
}
#[test]
fn test_from_bytes_all_ones() {
let buf = [0xFFu8; 100];
let result = Encoder::<i8, 300>::from_bytes(&buf);
assert!(matches!(result, Err(AppendError::MalformedData)));
}
#[test]
fn test_decode_frozen_specific_crash_case() {
let buf = [0xf0, 0xff, 0xff, 0xff, 0xd0, 0xf7, 0xf5, 0xfe];
let result = decode_frozen::<i8, 300>(&buf);
let _ = result; }
#[test]
fn test_from_bytes_specific_crash_case() {
let buf = [0x0a, 0xcd, 0x7a, 0x7a, 0x04, 0x00, 0xf6, 0xff,
0x0f, 0xff, 0xff, 0xff, 0x7a, 0x7a];
let result = Encoder::<i8, 300>::from_bytes(&buf);
assert!(matches!(result, Err(AppendError::MalformedData)));
}
#[test]
fn test_from_bytes_subtract_overflow_crash() {
let buf: [u8; 25] = [
0xed, 0x1b, 0x00, 0x00, 0x00, 0x04, 0x03, 0xe5,
0x1b, 0x13, 0x00, 0x00, 0x00, 0x00, 0x04, 0xa7,
0x5b, 0xdb, 0x00, 0x58, 0x04, 0xfe, 0xff, 0xf7,
0x00
];
if let Ok(enc) = Encoder::<i8, 300>::from_bytes(&buf) {
let _ = enc.decode().unwrap();
}
if let Ok(enc) = Encoder::<i16, 300>::from_bytes(&buf) {
let _ = enc.decode().unwrap();
}
let result = Encoder::<i32, 300>::from_bytes(&buf);
assert!(result.is_err(), "i32 variant should reject malformed data");
assert!(matches!(result, Err(AppendError::MalformedData)));
}