use aok::{OK, Void};
use compio::runtime::Runtime;
use log::info;
use tempfile::tempdir;
use wdev::{Device, Error, SegmentedDevice};
use wram::AlignedBuf;
#[test]
fn truncate_until_segment_removes_prior_segments() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let dir = tempdir()?;
let seg_size: u64 = 64 * 1024;
let device = SegmentedDevice::segmented(dir.path().join("trunc_seg.log"), seg_size)?;
for seg_id in 0..5u32 {
let buf = AlignedBuf::from_slice(&[(seg_id * 17 + 1) as u8; 4096], 4096)?;
let (res, _) = device.write_aligned((seg_id as u64) * seg_size, buf).await;
assert_eq!(res?, 4096);
assert!(device.segment_path(seg_id).exists());
}
device.truncate_until_segment(2).await?;
assert_eq!(device.start_segment(), 2);
for seg_id in 0..2u32 {
assert!(
!device.segment_path(seg_id).exists(),
"段 {seg_id} 必须已物理删除"
);
assert_eq!(
device.get_file_size(seg_id)?,
0,
"已删除段 get_file_size 必须为 0"
);
}
for seg_id in 2..5u32 {
assert!(device.segment_path(seg_id).exists(), "段 {seg_id} 应保留");
let expected = (seg_id * 17 + 1) as u8;
let check = AlignedBuf::new(4096, 4096)?;
let (res, check) = device.read_aligned((seg_id as u64) * seg_size, check).await;
assert_eq!(res?, 4096);
assert!(check.as_slice().iter().all(|&b| b == expected));
}
device.truncate_until_segment(1).await?;
assert_eq!(
device.start_segment(),
2,
"回退截断不得推进或回退 start_segment"
);
let single_path = dir.path().join("single_trunc.log");
let single = SegmentedDevice::single_file(&single_path)?;
let buf = AlignedBuf::from_slice(&[0x11u8; 4096], 4096)?;
let (res, _) = single.write_aligned(0, buf).await;
assert_eq!(res?, 4096);
single.truncate_until_segment(10).await?;
assert!(single_path.exists(), "单文件模式截断不应删除主文件");
info!("按段截断物理删除与单调语义校验通过 (TruncateUntilSegment)");
aok::Result::<()>::Ok(())
})?;
OK
}
#[test]
fn truncate_until_address_deletes_all_prior_segments() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let dir = tempdir()?;
let seg_size: u64 = 64 * 1024;
let device = SegmentedDevice::segmented(dir.path().join("trunc_addr.log"), seg_size)?;
for seg_id in 0..5u32 {
let buf = AlignedBuf::from_slice(&[(seg_id * 17 + 1) as u8; 4096], 4096)?;
let (res, _) = device.write_aligned((seg_id as u64) * seg_size, buf).await;
assert_eq!(res?, 4096);
}
device.truncate_until_address(4 * seg_size + 1000).await?;
assert_eq!(device.start_segment(), 4);
for seg_id in 2..4u32 {
assert!(
!device.segment_path(seg_id).exists(),
"段 {seg_id} 必须已物理删除"
);
assert_eq!(device.get_file_size(seg_id)?, 0);
}
assert!(device.segment_path(4).exists(), "段 4 文件应保留");
assert_eq!(device.get_file_size(4)?, 4096);
let check = AlignedBuf::new(4096, 4096)?;
let (res, check) = device.read_aligned(4 * seg_size, check).await;
assert_eq!(res?, 4096);
assert!(check.as_slice().iter().all(|&b| b == (4 * 17 + 1) as u8));
info!("按地址截断删除历史段校验通过 (TruncateUntilAddress)");
aok::Result::<()>::Ok(())
})?;
OK
}
#[test]
fn remove_segment_removes_persisted_data() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let dir = tempdir()?;
let seg_size: u64 = 64 * 1024;
let device = SegmentedDevice::segmented(dir.path().join("remove_seg.log"), seg_size)?;
let buf = AlignedBuf::from_slice(&[0x7Cu8; 4096], 4096)?;
let (res, _) = device.write_aligned(seg_size, buf).await;
assert_eq!(res?, 4096);
assert!(device.get_file_size(1)? >= 4096, "段 1 写入后应存在");
device.remove_segment(1).await?;
assert_eq!(device.get_file_size(1)?, 0, "段 1 删除后应报告空尺寸");
assert!(!device.segment_path(1).exists(), "段 1 文件必须被物理删除");
info!("单段删除与持久数据清理校验通过 (Native_RemoveSegment_RemovesPersistedData)");
aok::Result::<()>::Ok(())
})?;
OK
}
#[test]
fn get_file_size_reflects_writes() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let dir = tempdir()?;
let device = SegmentedDevice::segmented(dir.path().join("file_size.log"), 1 << 20)?;
assert_eq!(device.get_file_size(0)?, 0, "未写入时段 0 尺寸应为 0");
let size = 16 * 1024;
let buf = AlignedBuf::from_slice(&[0xCDu8; 16 * 1024], 4096)?;
let (res, _) = device.write_aligned(0, buf).await;
assert_eq!(res?, size);
assert!(
device.get_file_size(0)? >= size as u64,
"get_file_size 必须反映已写入的段"
);
info!("get_file_size 反映写入量校验通过 (Native_GetFileSize_ReflectsWrites)");
aok::Result::<()>::Ok(())
})?;
OK
}
#[test]
fn reset_closes_segments_and_device_remains_usable() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let dir = tempdir()?;
let device = SegmentedDevice::segmented(dir.path().join("reset.log"), 1 << 20)?;
let buf = AlignedBuf::from_slice(&[0xEEu8; 4096], 4096)?;
let (res, _) = device.write_aligned(0, buf).await;
assert_eq!(res?, 4096);
device.reset();
assert!(device.is_cached_empty(), "Reset 后句柄缓存必须已清空");
let check = AlignedBuf::new(4096, 4096)?;
let (res, check) = device.read_aligned(0, check).await;
assert_eq!(res?, 4096);
assert!(check.as_slice().iter().all(|&b| b == 0xEE));
info!("Reset 关闭句柄后按需重开校验通过 (Native_Reset_ClosesSegments_DeviceRemainsUsable)");
aok::Result::<()>::Ok(())
})?;
OK
}
#[test]
fn successive_truncations_defend_against_ghost_segments() -> Void {
let rt = Runtime::new()?;
rt.block_on(async {
let dir = tempdir()?;
let seg_size: u64 = 64 * 1024;
let device = SegmentedDevice::segmented(dir.path().join("rapid_trunc.log"), seg_size)?;
for seg_id in 0..=10u32 {
let pattern = vec![((seg_id * 23 + 7) & 0xFF) as u8; 4096];
let buf = AlignedBuf::from_slice(&pattern, 4096)?;
let (res, _) = device.write_aligned((seg_id as u64) * seg_size, buf).await;
assert_eq!(res?, 4096);
assert_eq!(device.get_file_size(seg_id)?, 4096);
}
device.truncate_until_segment(3).await?;
assert_eq!(device.start_segment(), 3);
for seg_id in 0..3u32 {
assert!(
!device.segment_path(seg_id).exists(),
"段 {seg_id} 必须已删除"
);
assert_eq!(device.get_file_size(seg_id)?, 0);
let check = AlignedBuf::new(4096, 4096)?;
let (res, _) = device.read_aligned((seg_id as u64) * seg_size, check).await;
assert!(
matches!(res, Err(Error::SegmentNotFound(id)) if id == seg_id),
"对已截断段读取必须返回 SegmentNotFound,实际为 {res:?}"
);
let wbuf = AlignedBuf::from_slice(&[0x55u8; 4096], 4096)?;
let (res, _) = device.write_aligned((seg_id as u64) * seg_size, wbuf).await;
assert!(
matches!(res, Err(Error::SegmentNotFound(id)) if id == seg_id),
"对已截断段写入必须返回 SegmentNotFound,实际为 {res:?}"
);
assert!(
!device.segment_path(seg_id).exists(),
"严禁幽灵文件重现于磁盘"
);
}
device.truncate_until_segment(7).await?;
assert_eq!(device.start_segment(), 7);
for seg_id in 3..7u32 {
assert!(!device.segment_path(seg_id).exists());
let check = AlignedBuf::new(4096, 4096)?;
let (res, _) = device.read_aligned((seg_id as u64) * seg_size, check).await;
assert!(matches!(res, Err(Error::SegmentNotFound(id)) if id == seg_id));
}
device.truncate_until_address(9 * seg_size + 2048).await?;
assert_eq!(device.start_segment(), 9);
for seg_id in 7..9u32 {
assert!(!device.segment_path(seg_id).exists());
assert_eq!(device.get_file_size(seg_id)?, 0);
}
for seg_id in 9..=10u32 {
assert!(device.segment_path(seg_id).exists());
let expected = vec![((seg_id * 23 + 7) & 0xFF) as u8; 4096];
let check = AlignedBuf::new(4096, 4096)?;
let (res, check) = device.read_aligned((seg_id as u64) * seg_size, check).await;
assert_eq!(res?, 4096);
assert_eq!(check.as_slice(), &expected[..]);
}
device.reset();
assert!(device.is_cached_empty());
for seg_id in 9..=10u32 {
let expected = vec![((seg_id * 23 + 7) & 0xFF) as u8; 4096];
let check = AlignedBuf::new(4096, 4096)?;
let (res, check) = device.read_aligned((seg_id as u64) * seg_size, check).await;
assert_eq!(res?, 4096);
assert_eq!(check.as_slice(), &expected[..]);
}
assert_eq!(device.cached_handle_count(), 2, "按需重开段 9 与 10");
info!("快速连续截断与幽灵段防御校验通过");
aok::Result::<()>::Ok(())
})?;
OK
}