use std::io::Write; use std::path::PathBuf; use std::sync::mpsc; use std::time::Duration; use tempfile::NamedTempFile; use tokio::fs::File; use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; use tokio::runtime::{Builder, Runtime}; use tokio_util::task::TaskTracker;
#[tokio::test] asyncfn test_file_read_empty() { let (_tmp, path) = create_temp_file(b"");
letmut file = File::open(&path).await.unwrap(); letmut buf = vec![0u8; 100]; let n = file.read(&mut buf).await.unwrap();
assert_eq!(n, 0);
}
#[tokio::test] asyncfn test_file_read_large() { let data: Vec<u8> = (0..3_000_000u32).map(|i| (i % 256) as u8).collect(); let (_tmp, path) = create_temp_file(&data);
#[tokio::test] asyncfn test_file_read_custom_buf_size() { let data: Vec<u8> = (0..1000u16).map(|i| (i % 256) as u8).collect(); let (_tmp, path) = create_temp_file(&data);
#[tokio::test] asyncfn test_file_read_cancel() { let data: Vec<u8> = (0..10_000).map(|i| (i % 256) as u8).collect(); let (_tmp, path) = create_temp_file(&data);
let files: Vec<_> = (0..NUM_FILES)
.map(|i| { let data: Vec<u8> = (0..1024).map(|j| ((i as u16 + j) % 256) as u8).collect();
create_temp_file(&data)
})
.collect();
let tracker = TaskTracker::new();
for (i, (_tmp, path)) in files.iter().enumerate() { let path = path.clone(); let expected: Vec<u8> = (0..1024).map(|j| ((i as u16 + j) % 256) as u8).collect();
tracker.spawn(asyncmove { letmut file = File::open(&path).await.unwrap(); letmut buf = Vec::new();
file.read_to_end(&mut buf).await.unwrap();
assert_eq!(buf, expected);
});
}
tracker.close();
tracker.wait().await;
}
#[test] fn test_file_read_multi_runtime() { for rt_factory in rt_combinations() { let rt = rt_factory(); let data: Vec<u8> = (0..10_000).map(|i| (i % 256) as u8).collect(); let (_tmp, path) = create_temp_file(&data);
let result = rt.block_on(async { letmut file = File::open(&path).await.unwrap(); letmut buf = Vec::new();
file.read_to_end(&mut buf).await.unwrap();
buf
});
assert_eq!(result, data);
}
}
#[test] fn shutdown_runtime_with_pending_reads() { for rt_factory in rt_combinations() { let rt = rt_factory(); let (done_tx, done_rx) = mpsc::channel();
let data: Vec<u8> = (0..10_000).map(|i| (i % 256) as u8).collect(); let (_tmp, path) = create_temp_file(&data);
for _ in0..50 { let path = path.clone();
rt.spawn(asyncmove { letmut file = File::open(&path).await.unwrap(); letmut buf = Vec::new(); let _ = file.read_to_end(&mut buf).await;
});
}
/// Read with a buffer smaller than the file content. Verifies internal /// buffering serves subsequent small reads without issuing new underlying read /// operations. #[tokio::test] asyncfn test_file_read_with_smaller_buf() { let data: Vec<u8> = (0..1024u16).map(|i| (i % 256) as u8).collect(); let (_tmp, path) = create_temp_file(&data);
letmut file = File::open(&path).await.unwrap();
// triggers an underlying read that fills the internal buffer with more data // than we consume here letmut buf = vec![0u8; 4]; let n = file.read(&mut buf).await.unwrap();
assert_eq!(n, 4);
assert_eq!(&buf, &data[..4]);
// Second read: still smaller than what's buffered internally letmut buf = vec![0u8; 32]; let n = file.read(&mut buf).await.unwrap();
assert!(n > 0);
assert_eq!(&buf[..n], &data[4..4 + n]);
// Read the rest letmut rest = Vec::new();
file.read_to_end(&mut rest).await.unwrap(); let total = 4 + n + rest.len();
assert_eq!(total, data.len());
}
#[tokio::test] asyncfn test_file_read_with_bigger_buf() { let data = b"hello io-uring"; let (_tmp, path) = create_temp_file(data);
letmut file = File::open(&path).await.unwrap();
// Read with buffer larger than file's contents letmut buf = vec![0u8; 1024]; let n = file.read(&mut buf).await.unwrap();
assert!(n > 0 && n <= data.len());
assert_eq!(&buf[..n], &data[..n]);
if n < data.len() { letmut rest = vec![0u8; 1024]; let n2 = file.read(&mut rest).await.unwrap();
assert_eq!(&rest[..n2], &data[n..n + n2]);
}
}
/// Read a file larger than DEFAULT_MAX_BUF_SIZE (2 MiB). Verifies that /// chunked reads across multiple underlying operations produce correct data. #[tokio::test] asyncfn test_file_read_buffer_larger_than_max() { // 4 MiB + 1000 bytes to cross multiple chunk boundaries. let size = (4 << 20) + 1000; let data: Vec<u8> = (0..size).map(|i| (i % 256) as u8).collect(); let (_tmp, path) = create_temp_file(&data);
/// Read some bytes from a file, then write, verifying the implicit seek-back /// works correctly. #[tokio::test] asyncfn test_file_read_then_write() { let original = b"hello world, io-uring!"; let (_tmp, path) = create_temp_file(original);
// Write at the current position
file.write_all(b" REPLACED").await.unwrap();
file.flush().await.unwrap();
// Re-read the full file and verify the write landed correctly
file.seek(std::io::SeekFrom::Start(0)).await.unwrap(); letmut result = Vec::new();
file.read_to_end(&mut result).await.unwrap();
assert_eq!(&result[..5], b"hello");
assert_eq!(&result[5..14], b" REPLACED");
}
/// Partial read followed by write at a different position. Verifies the /// seek-back accounts for partially consumed internal buffer. #[tokio::test] asyncfn test_file_partial_read_then_write() { let data = b"abcdefghijklmnopqrstuvwxyz"; let (_tmp, path) = create_temp_file(data);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.