#![allow(clippy::missing_panics_doc)]
#![allow(clippy::missing_errors_doc)]
#[cfg(any(test, feature = "test-internals"))]
use std::collections::HashMap;
#[cfg(any(test, feature = "test-internals"))]
use std::io::{IoSlice, IoSliceMut};
#[cfg(any(test, feature = "test-internals"))]
use std::path::{Path, PathBuf};
#[cfg(any(test, feature = "test-internals"))]
#[derive(Debug, Clone)]
pub struct MockUringProcessor {
read_operations: Vec<MockUringRead>,
write_operations: Vec<MockUringWrite>,
consistency_mode: UringConsistencyMode,
}
#[cfg(any(test, feature = "test-internals"))]
#[derive(Debug, Clone)]
pub struct MockUringRead {
pub fd: i32,
pub buffers: Vec<Vec<u8>>,
pub offset: u64,
pub bytes_read: usize,
}
#[cfg(any(test, feature = "test-internals"))]
#[derive(Debug, Clone)]
pub struct MockUringWrite {
pub fd: i32,
pub buffers: Vec<Vec<u8>>,
pub offset: u64,
pub bytes_written: usize,
}
#[cfg(any(test, feature = "test-internals"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UringConsistencyMode {
Strict, Permissive, }
#[cfg(any(test, feature = "test-internals"))]
impl MockUringProcessor {
pub fn new(mode: UringConsistencyMode) -> Self {
Self {
read_operations: Vec::new(),
write_operations: Vec::new(),
consistency_mode: mode,
}
}
pub fn readv(
&mut self,
fd: i32,
bufs: &mut [IoSliceMut<'_>],
offset: u64,
) -> Result<usize, std::io::Error> {
let mut total_read = 0;
let mut buffer_contents = Vec::new();
for buf in bufs.iter_mut() {
let data = vec![0x42; buf.len()]; let read_len = std::cmp::min(data.len(), buf.len());
buf[..read_len].copy_from_slice(&data[..read_len]);
buffer_contents.push(data);
total_read += read_len;
}
self.read_operations.push(MockUringRead {
fd,
buffers: buffer_contents,
offset,
bytes_read: total_read,
});
Ok(total_read)
}
pub fn writev(
&mut self,
fd: i32,
bufs: &[IoSlice<'_>],
offset: u64,
) -> Result<usize, std::io::Error> {
let mut total_written = 0;
let mut buffer_contents = Vec::new();
for buf in bufs {
buffer_contents.push(buf.to_vec());
total_written += buf.len();
}
self.write_operations.push(MockUringWrite {
fd,
buffers: buffer_contents,
offset,
bytes_written: total_written,
});
if self.consistency_mode == UringConsistencyMode::Strict {
if total_written % bufs.len() != 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::Interrupted,
"Partial vectored write violates atomicity",
));
}
}
Ok(total_written)
}
pub fn validate_consistency(&self) -> Result<(), String> {
let mut current_offset = 0u64;
for read_op in &self.read_operations {
if read_op.offset < current_offset {
return Err(format!(
"Read ordering violation: offset {} < current {}",
read_op.offset, current_offset
));
}
current_offset = read_op.offset + read_op.bytes_read as u64;
}
for write_op in &self.write_operations {
if write_op.buffers.is_empty() {
return Err("Empty buffer vector in write operation".to_string());
}
}
Ok(())
}
}
#[cfg(any(test, feature = "test-internals"))]
#[derive(Debug, Clone)]
pub struct MockVfsCanonicalizer {
symlinks: HashMap<PathBuf, PathBuf>,
mount_points: HashMap<PathBuf, PathBuf>,
canonicalization_cache: HashMap<PathBuf, PathBuf>,
}
#[cfg(any(test, feature = "test-internals"))]
impl MockVfsCanonicalizer {
pub fn new() -> Self {
let mut symlinks = HashMap::new();
let mut mount_points = HashMap::new();
symlinks.insert(PathBuf::from("/link/to/file"), PathBuf::from("/real/file"));
symlinks.insert(
PathBuf::from("/recursive/link"),
PathBuf::from("/another/link"),
);
symlinks.insert(
PathBuf::from("/another/link"),
PathBuf::from("/final/target"),
);
mount_points.insert(PathBuf::from("/mnt"), PathBuf::from("/dev/sda1"));
mount_points.insert(PathBuf::from("/proc"), PathBuf::from("procfs"));
Self {
symlinks,
mount_points,
canonicalization_cache: HashMap::new(),
}
}
pub fn canonicalize<P: AsRef<Path>>(&mut self, path: P) -> Result<PathBuf, std::io::Error> {
let path = path.as_ref().to_path_buf();
if let Some(canonical) = self.canonicalization_cache.get(&path) {
return Ok(canonical.clone());
}
let mut canonical = self.normalize_components(&path)?;
let mut visited = std::collections::HashSet::new();
while let Some(target) = self.symlinks.get(&canonical) {
if !visited.insert(canonical.clone()) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Symlink cycle detected",
));
}
canonical = self.normalize_components(target)?;
}
self.canonicalization_cache
.insert(path.clone(), canonical.clone());
Ok(canonical)
}
pub fn mount_source_for_path(&self, path: &Path) -> Option<&Path> {
self.mount_points
.iter()
.filter(|(mount_point, _)| path.starts_with(mount_point.as_path()))
.max_by_key(|(mount_point, _)| mount_point.components().count())
.map(|(_, source)| source.as_path())
}
fn normalize_components(&self, path: &Path) -> Result<PathBuf, std::io::Error> {
let mut components = Vec::new();
for component in path.components() {
match component {
std::path::Component::CurDir => continue,
std::path::Component::ParentDir => {
if components.is_empty() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Cannot resolve .. beyond root",
));
}
components.pop();
}
_ => components.push(component),
}
}
Ok(components.iter().collect())
}
pub fn add_symlink<P: Into<PathBuf>, Q: Into<PathBuf>>(&mut self, link: P, target: Q) {
self.symlinks.insert(link.into(), target.into());
self.canonicalization_cache.clear(); }
pub fn validate_canonicalization(
&self,
_original: &Path,
canonical: &Path,
) -> Result<(), String> {
if !canonical.is_absolute() {
return Err(format!(
"Canonical path must be absolute: {}",
canonical.display()
));
}
for component in canonical.components() {
match component {
std::path::Component::CurDir | std::path::Component::ParentDir => {
return Err(format!(
"Canonical path contains . or ..: {}",
canonical.display()
));
}
_ => continue,
}
}
if let Some(mount_source) = self.mount_source_for_path(canonical) {
if mount_source.as_os_str().is_empty() {
return Err(format!(
"Mount point for {} has empty backing source",
canonical.display()
));
}
}
Ok(())
}
}
#[cfg(any(test, feature = "test-internals"))]
#[derive(Debug)]
pub struct MockBufProcessor {
read_buffer: Vec<u8>,
write_buffer: Vec<u8>,
buffer_size: usize,
read_pos: usize,
write_pos: usize,
flush_count: usize,
}
#[cfg(any(test, feature = "test-internals"))]
impl MockBufProcessor {
pub fn new(buffer_size: usize) -> Self {
Self {
read_buffer: vec![0; buffer_size],
write_buffer: Vec::with_capacity(buffer_size),
buffer_size,
read_pos: 0,
write_pos: 0,
flush_count: 0,
}
}
pub fn fill_read_buffer(&mut self, data: &[u8]) {
self.read_buffer.clear();
self.read_buffer.extend_from_slice(data);
self.read_pos = 0;
}
pub fn buf_read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
let available = self.read_buffer.len().saturating_sub(self.read_pos);
let to_read = std::cmp::min(buf.len(), available);
if to_read == 0 {
return Ok(0); }
buf[..to_read].copy_from_slice(&self.read_buffer[self.read_pos..self.read_pos + to_read]);
self.read_pos += to_read;
Ok(to_read)
}
pub fn buf_write(&mut self, data: &[u8]) -> Result<usize, std::io::Error> {
let mut written = 0;
for &byte in data {
if self.write_buffer.len() >= self.buffer_size {
self.flush()?;
}
self.write_buffer.push(byte);
written += 1;
}
Ok(written)
}
pub fn flush(&mut self) -> Result<(), std::io::Error> {
self.write_pos += self.write_buffer.len();
self.write_buffer.clear();
self.flush_count += 1;
Ok(())
}
pub fn validate_boundary_semantics(&self) -> Result<(), String> {
if self.read_pos > self.read_buffer.len() {
return Err(format!(
"Read position {} exceeds buffer length {}",
self.read_pos,
self.read_buffer.len()
));
}
if self.write_buffer.len() > self.buffer_size && self.write_buffer.len() > 1 {
return Err(format!(
"Write buffer size {} exceeds limit {}",
self.write_buffer.len(),
self.buffer_size
));
}
if self.flush_count == 0 && self.write_pos > 0 {
return Err("Write position advanced without flush".to_string());
}
Ok(())
}
pub fn bytes_written(&self) -> usize {
self.write_pos
}
pub fn pending_bytes(&self) -> usize {
self.write_buffer.len()
}
}
#[cfg(any(test, feature = "test-internals"))]
#[derive(Debug)]
pub struct FsProtocolConformanceHarness {
uring_processor: MockUringProcessor,
vfs_canonicalizer: MockVfsCanonicalizer,
buf_processor: MockBufProcessor,
test_results: Vec<ConformanceTestResult>,
}
#[cfg(any(test, feature = "test-internals"))]
#[derive(Debug, Clone)]
pub struct ConformanceTestResult {
pub test_name: String,
pub requirement_level: RequirementLevel,
pub status: TestStatus,
pub error_message: Option<String>,
}
#[cfg(any(test, feature = "test-internals"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RequirementLevel {
Must, Should, May, }
#[cfg(any(test, feature = "test-internals"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TestStatus {
Pass,
Fail,
Skip,
}
#[cfg(any(test, feature = "test-internals"))]
impl FsProtocolConformanceHarness {
pub fn new() -> Self {
Self {
uring_processor: MockUringProcessor::new(UringConsistencyMode::Strict),
vfs_canonicalizer: MockVfsCanonicalizer::new(),
buf_processor: MockBufProcessor::new(8192), test_results: Vec::new(),
}
}
pub fn run_all_tests(&mut self) -> Result<(), String> {
self.test_uring_readv_writev_consistency()?;
self.test_vfs_canonicalization_invariants()?;
self.test_buf_reader_writer_boundaries()?;
self.generate_compliance_report()
}
fn test_uring_readv_writev_consistency(&mut self) -> Result<(), String> {
let mut buf1 = [0u8; 1024];
let mut buf2 = [0u8; 2048];
let mut buffers = [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)];
match self.uring_processor.readv(1, &mut buffers, 0) {
Ok(bytes_read) => {
self.record_test(
"uring_readv_basic",
RequirementLevel::Must,
TestStatus::Pass,
None,
);
if bytes_read != 3072 {
self.record_test(
"uring_readv_byte_count",
RequirementLevel::Must,
TestStatus::Fail,
Some(format!("Expected 3072 bytes, got {}", bytes_read)),
);
}
}
Err(e) => {
self.record_test(
"uring_readv_basic",
RequirementLevel::Must,
TestStatus::Fail,
Some(e.to_string()),
);
}
}
let write_bufs = [IoSlice::new(b"hello"), IoSlice::new(b"world")];
match self.uring_processor.writev(2, &write_bufs, 0) {
Ok(bytes_written) => {
if bytes_written == 10 {
self.record_test(
"uring_writev_atomicity",
RequirementLevel::Must,
TestStatus::Pass,
None,
);
} else {
self.record_test(
"uring_writev_atomicity",
RequirementLevel::Must,
TestStatus::Fail,
Some(format!("Partial write: {} of 10 bytes", bytes_written)),
);
}
}
Err(e) => {
self.record_test(
"uring_writev_atomicity",
RequirementLevel::Must,
TestStatus::Fail,
Some(e.to_string()),
);
}
}
self.uring_processor.validate_consistency().map_err(|e| {
self.record_test(
"uring_operation_ordering",
RequirementLevel::Must,
TestStatus::Fail,
Some(e.clone()),
);
e
})?;
self.record_test(
"uring_operation_ordering",
RequirementLevel::Must,
TestStatus::Pass,
None,
);
Ok(())
}
fn test_vfs_canonicalization_invariants(&mut self) -> Result<(), String> {
let test_paths = [
("/foo/bar/../baz", "/foo/baz"),
("/foo/./bar", "/foo/bar"),
("//double//slash", "/double/slash"),
("/trailing/slash/", "/trailing/slash"),
];
for (input, expected) in &test_paths {
match self.vfs_canonicalizer.canonicalize(input) {
Ok(canonical) => {
if canonical == Path::new(expected) {
self.record_test(
&format!("vfs_canonicalize_{}", input.replace('/', "_")),
RequirementLevel::Must,
TestStatus::Pass,
None,
);
} else {
self.record_test(
&format!("vfs_canonicalize_{}", input.replace('/', "_")),
RequirementLevel::Must,
TestStatus::Fail,
Some(format!(
"Expected {}, got {}",
expected,
canonical.display()
)),
);
}
}
Err(e) => {
self.record_test(
&format!("vfs_canonicalize_{}", input.replace('/', "_")),
RequirementLevel::Must,
TestStatus::Fail,
Some(e.to_string()),
);
}
}
}
match self.vfs_canonicalizer.canonicalize("/link/to/file") {
Ok(canonical) => {
if canonical == Path::new("/real/file") {
self.record_test(
"vfs_symlink_resolution",
RequirementLevel::Must,
TestStatus::Pass,
None,
);
} else {
self.record_test(
"vfs_symlink_resolution",
RequirementLevel::Must,
TestStatus::Fail,
Some(format!("Expected /real/file, got {}", canonical.display())),
);
}
}
Err(e) => {
self.record_test(
"vfs_symlink_resolution",
RequirementLevel::Must,
TestStatus::Fail,
Some(e.to_string()),
);
}
}
self.vfs_canonicalizer.add_symlink("/cycle/a", "/cycle/b");
self.vfs_canonicalizer.add_symlink("/cycle/b", "/cycle/a");
match self.vfs_canonicalizer.canonicalize("/cycle/a") {
Ok(_) => {
self.record_test(
"vfs_cycle_detection",
RequirementLevel::Must,
TestStatus::Fail,
Some("Cycle not detected".to_string()),
);
}
Err(_) => {
self.record_test(
"vfs_cycle_detection",
RequirementLevel::Must,
TestStatus::Pass,
None,
);
}
}
Ok(())
}
fn test_buf_reader_writer_boundaries(&mut self) -> Result<(), String> {
self.buf_processor
.fill_read_buffer(b"hello world test data");
let mut small_buf = [0u8; 5];
match self.buf_processor.buf_read(&mut small_buf) {
Ok(bytes_read) => {
if bytes_read == 5 && &small_buf == b"hello" {
self.record_test(
"buf_reader_boundary",
RequirementLevel::Must,
TestStatus::Pass,
None,
);
} else {
self.record_test(
"buf_reader_boundary",
RequirementLevel::Must,
TestStatus::Fail,
Some(format!("Unexpected read result: {} bytes", bytes_read)),
);
}
}
Err(e) => {
self.record_test(
"buf_reader_boundary",
RequirementLevel::Must,
TestStatus::Fail,
Some(e.to_string()),
);
}
}
let large_data = vec![42u8; 10000]; match self.buf_processor.buf_write(&large_data) {
Ok(bytes_written) => {
if bytes_written == 10000 {
self.record_test(
"buf_writer_large_write",
RequirementLevel::Must,
TestStatus::Pass,
None,
);
} else {
self.record_test(
"buf_writer_large_write",
RequirementLevel::Must,
TestStatus::Fail,
Some(format!("Partial write: {} of 10000 bytes", bytes_written)),
);
}
}
Err(e) => {
self.record_test(
"buf_writer_large_write",
RequirementLevel::Must,
TestStatus::Fail,
Some(e.to_string()),
);
}
}
self.buf_processor
.validate_boundary_semantics()
.map_err(|e| {
self.record_test(
"buf_boundary_semantics",
RequirementLevel::Must,
TestStatus::Fail,
Some(e.clone()),
);
e
})?;
self.record_test(
"buf_boundary_semantics",
RequirementLevel::Must,
TestStatus::Pass,
None,
);
Ok(())
}
fn record_test(
&mut self,
name: &str,
level: RequirementLevel,
status: TestStatus,
error: Option<String>,
) {
self.test_results.push(ConformanceTestResult {
test_name: name.to_string(),
requirement_level: level,
status,
error_message: error,
});
}
fn generate_compliance_report(&self) -> Result<(), String> {
let mut must_pass = 0;
let mut must_total = 0;
for result in &self.test_results {
if result.requirement_level == RequirementLevel::Must {
must_total += 1;
if result.status == TestStatus::Pass {
must_pass += 1;
}
}
}
let must_score = if must_total > 0 {
(must_pass as f64 / must_total as f64) * 100.0
} else {
100.0
};
if must_score < 95.0 {
return Err(format!(
"MUST requirement compliance below 95%: {:.1}% ({}/{})",
must_score, must_pass, must_total
));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fs_protocol_conformance_comprehensive() {
let mut harness = FsProtocolConformanceHarness::new();
harness.run_all_tests().unwrap_or_else(|e| {
panic!("FS protocol conformance test failed: {}", e);
});
let test_names: Vec<&str> = harness
.test_results
.iter()
.map(|r| r.test_name.as_str())
.collect();
assert!(test_names.iter().any(|name| name.contains("uring_readv")));
assert!(test_names.iter().any(|name| name.contains("uring_writev")));
assert!(
test_names
.iter()
.any(|name| name.contains("vfs_canonicalize"))
);
assert!(test_names.iter().any(|name| name.contains("vfs_symlink")));
assert!(test_names.iter().any(|name| name.contains("buf_reader")));
assert!(test_names.iter().any(|name| name.contains("buf_writer")));
}
#[test]
fn test_uring_readv_writev_round_trip() {
let mut processor = MockUringProcessor::new(UringConsistencyMode::Strict);
let write_data = [IoSlice::new(b"test"), IoSlice::new(b"data")];
let bytes_written = processor.writev(1, &write_data, 0).unwrap();
assert_eq!(bytes_written, 8);
let mut read_buf1 = [0u8; 4];
let mut read_buf2 = [0u8; 4];
let mut read_bufs = [
IoSliceMut::new(&mut read_buf1),
IoSliceMut::new(&mut read_buf2),
];
let bytes_read = processor.readv(1, &mut read_bufs, 0).unwrap();
assert_eq!(bytes_read, 8);
processor.validate_consistency().unwrap();
}
#[test]
fn test_vfs_canonicalization_edge_cases() {
let mut canonicalizer = MockVfsCanonicalizer::new();
let result = canonicalizer.canonicalize("/foo//bar").unwrap();
assert_eq!(result, PathBuf::from("/foo/bar"));
let result = canonicalizer.canonicalize("/foo/./bar").unwrap();
assert_eq!(result, PathBuf::from("/foo/bar"));
let result = canonicalizer.canonicalize("/foo/bar/../baz").unwrap();
assert_eq!(result, PathBuf::from("/foo/baz"));
let result = canonicalizer.canonicalize("/link/to/file").unwrap();
assert_eq!(result, PathBuf::from("/real/file"));
let result = canonicalizer.canonicalize("/mnt/volume/file").unwrap();
assert_eq!(result, PathBuf::from("/mnt/volume/file"));
assert_eq!(
canonicalizer.mount_source_for_path(&result),
Some(Path::new("/dev/sda1"))
);
}
#[test]
fn test_buf_processor_boundary_semantics() {
let mut processor = MockBufProcessor::new(1024);
processor.fill_read_buffer(b"hello world");
let mut small_buf = [0u8; 5];
let bytes_read = processor.buf_read(&mut small_buf).unwrap();
assert_eq!(bytes_read, 5);
assert_eq!(&small_buf, b"hello");
let large_data = vec![1u8; 2048]; let bytes_written = processor.buf_write(&large_data).unwrap();
assert_eq!(bytes_written, 2048);
assert!(processor.bytes_written() > 0);
processor.validate_boundary_semantics().unwrap();
}
#[test]
fn test_uring_consistency_modes() {
let mut strict = MockUringProcessor::new(UringConsistencyMode::Strict);
let mut permissive = MockUringProcessor::new(UringConsistencyMode::Permissive);
let bufs = [IoSlice::new(b"test")];
assert!(strict.writev(1, &bufs, 0).is_ok());
assert!(permissive.writev(1, &bufs, 0).is_ok());
assert!(strict.validate_consistency().is_ok());
assert!(permissive.validate_consistency().is_ok());
}
#[test]
fn test_conformance_result_tracking() {
let mut harness = FsProtocolConformanceHarness::new();
harness.record_test("test1", RequirementLevel::Must, TestStatus::Pass, None);
harness.record_test(
"test2",
RequirementLevel::Should,
TestStatus::Fail,
Some("error".to_string()),
);
assert_eq!(harness.test_results.len(), 2);
assert_eq!(harness.test_results[0].test_name, "test1");
assert_eq!(
harness.test_results[0].requirement_level,
RequirementLevel::Must
);
assert_eq!(harness.test_results[0].status, TestStatus::Pass);
assert_eq!(harness.test_results[1].status, TestStatus::Fail);
assert_eq!(
harness.test_results[1].error_message,
Some("error".to_string())
);
}
}