Skip to content

Commit 0b793ef

Browse files
uucore/buf_copy: add tests
1 parent 982885e commit 0b793ef

File tree

2 files changed

+194
-3
lines changed

2 files changed

+194
-3
lines changed

src/uucore/src/lib/features/buf_copy.rs

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,190 @@ pub use linux::*;
2020
pub mod other;
2121
#[cfg(not(any(target_os = "linux", target_os = "android")))]
2222
pub use other::copy_stream;
23+
24+
#[cfg(test)]
25+
mod tests {
26+
use super::*;
27+
use std::fs::File;
28+
use tempfile::tempdir;
29+
30+
#[cfg(unix)]
31+
use crate::pipes;
32+
#[cfg(unix)]
33+
use std::fs::OpenOptions;
34+
#[cfg(unix)]
35+
use std::{
36+
io::{Seek, SeekFrom},
37+
thread,
38+
};
39+
40+
#[cfg(any(target_os = "linux", target_os = "android"))]
41+
use nix::unistd;
42+
#[cfg(any(target_os = "linux", target_os = "android"))]
43+
use std::os::fd::AsRawFd;
44+
45+
use std::io::{Read, Write};
46+
47+
#[cfg(unix)]
48+
fn new_temp_file() -> File {
49+
let temp_dir = tempdir().unwrap();
50+
OpenOptions::new()
51+
.read(true)
52+
.write(true)
53+
.create(true)
54+
.open(temp_dir.path().join("file.txt"))
55+
.unwrap()
56+
}
57+
58+
#[cfg(any(target_os = "linux", target_os = "android"))]
59+
#[test]
60+
fn test_file_is_pipe() {
61+
let temp_file = new_temp_file();
62+
let (pipe_read, pipe_write) = pipes::pipe().unwrap();
63+
64+
assert!(is_pipe(&pipe_read).unwrap());
65+
assert!(is_pipe(&pipe_write).unwrap());
66+
assert!(!is_pipe(&temp_file).unwrap());
67+
}
68+
69+
#[cfg(any(target_os = "linux", target_os = "android"))]
70+
#[test]
71+
fn test_valid_splice_errs() {
72+
use nix::errno::Errno;
73+
use nix::Error;
74+
75+
let err = Error::from(Errno::EINVAL);
76+
assert_eq!(maybe_unsupported(err).unwrap(), (0, true));
77+
78+
let err = Error::from(Errno::ENOSYS);
79+
assert_eq!(maybe_unsupported(err).unwrap(), (0, true));
80+
81+
let err = Error::from(Errno::EBADF);
82+
assert_eq!(maybe_unsupported(err).unwrap(), (0, true));
83+
84+
let err = Error::from(Errno::EPERM);
85+
assert!(maybe_unsupported(err).is_err());
86+
}
87+
88+
#[cfg(any(target_os = "linux", target_os = "android"))]
89+
#[test]
90+
fn test_splice_data_to_pipe() {
91+
let (pipe_read, pipe_write) = pipes::pipe().unwrap();
92+
let data = b"Hello, world!";
93+
let (bytes, _) = splice_data_to_pipe(data, &pipe_write).unwrap();
94+
let mut buf = [0; 1024];
95+
let n = unistd::read(pipe_read.as_raw_fd(), &mut buf).unwrap();
96+
assert_eq!(&buf[..n], data);
97+
assert_eq!(bytes as usize, data.len());
98+
}
99+
100+
#[cfg(any(target_os = "linux", target_os = "android"))]
101+
#[test]
102+
fn test_splice_data_to_file() {
103+
use std::io::{Read, Seek, SeekFrom};
104+
105+
let mut temp_file = new_temp_file();
106+
let (pipe_read, pipe_write) = pipes::pipe().unwrap();
107+
let data = b"Hello, world!";
108+
let (bytes, _) = splice_data_to_fd(data, &pipe_read, &pipe_write, &temp_file).unwrap();
109+
assert_eq!(bytes as usize, data.len());
110+
111+
// We would have been at the end already, so seek again to the start.
112+
temp_file.seek(SeekFrom::Start(0)).unwrap();
113+
114+
let mut buf = Vec::new();
115+
temp_file.read_to_end(&mut buf).unwrap();
116+
assert_eq!(buf, data);
117+
}
118+
119+
#[cfg(any(target_os = "linux", target_os = "android"))]
120+
#[test]
121+
fn test_copy_exact() {
122+
let (mut pipe_read, mut pipe_write) = pipes::pipe().unwrap();
123+
let data = b"Hello, world!";
124+
let n = pipe_write.write(data).unwrap();
125+
assert_eq!(n, data.len());
126+
let mut buf = [0; 1024];
127+
let n = copy_exact(pipe_read.as_raw_fd(), &pipe_write, data.len()).unwrap();
128+
let n2 = pipe_read.read(&mut buf).unwrap();
129+
assert_eq!(n, n2);
130+
assert_eq!(&buf[..n], data);
131+
}
132+
133+
#[test]
134+
#[cfg(unix)]
135+
fn test_copy_stream() {
136+
let mut dest_file = new_temp_file();
137+
138+
let (mut pipe_read, mut pipe_write) = pipes::pipe().unwrap();
139+
let data = b"Hello, world!";
140+
let thread = thread::spawn(move || {
141+
pipe_write.write_all(data).unwrap();
142+
});
143+
let result = copy_stream(&mut pipe_read, &mut dest_file).unwrap();
144+
thread.join().unwrap();
145+
assert!(result == data.len() as u64);
146+
147+
// We would have been at the end already, so seek again to the start.
148+
dest_file.seek(SeekFrom::Start(0)).unwrap();
149+
150+
let mut buf = Vec::new();
151+
dest_file.read_to_end(&mut buf).unwrap();
152+
153+
assert_eq!(buf, data);
154+
}
155+
156+
#[test]
157+
#[cfg(not(unix))]
158+
// Test for non-unix platforms. We use regular files instead.
159+
fn test_copy_stream() {
160+
let temp_dir = tempdir().unwrap();
161+
let src_path = temp_dir.path().join("src.txt");
162+
let dest_path = temp_dir.path().join("dest.txt");
163+
164+
let mut src_file = File::create(&src_path).unwrap();
165+
let mut dest_file = File::create(&dest_path).unwrap();
166+
167+
let data = b"Hello, world!";
168+
src_file.write_all(data).unwrap();
169+
src_file.sync_all().unwrap();
170+
171+
let mut src_file = File::open(&src_path).unwrap();
172+
let bytes_copied = copy_stream(&mut src_file, &mut dest_file).unwrap();
173+
174+
let mut dest_file = File::open(&dest_path).unwrap();
175+
let mut buf = Vec::new();
176+
dest_file.read_to_end(&mut buf).unwrap();
177+
178+
assert_eq!(bytes_copied as usize, data.len());
179+
assert_eq!(buf, data);
180+
}
181+
182+
#[cfg(any(target_os = "linux", target_os = "android"))]
183+
#[test]
184+
fn test_splice_write() {
185+
use std::{
186+
io::{Read, Seek, SeekFrom, Write},
187+
thread,
188+
};
189+
190+
let (pipe_read, mut pipe_write) = pipes::pipe().unwrap();
191+
let mut dest_file = new_temp_file();
192+
let data = b"Hello, world!";
193+
let thread = thread::spawn(move || {
194+
pipe_write.write_all(data).unwrap();
195+
});
196+
let (bytes, _) = splice_write(&pipe_read, &dest_file).unwrap();
197+
thread.join().unwrap();
198+
199+
assert!(bytes == data.len() as u64);
200+
201+
// We would have been at the end already, so seek again to the start.
202+
dest_file.seek(SeekFrom::Start(0)).unwrap();
203+
204+
let mut buf = Vec::new();
205+
dest_file.read_to_end(&mut buf).unwrap();
206+
207+
assert_eq!(buf, data);
208+
}
209+
}

src/uucore/src/lib/features/buf_copy/linux.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ where
8686
/// - `source` - source handle
8787
/// - `dest` - destination handle
8888
#[inline]
89-
fn splice_write<R, S>(source: &R, dest: &S) -> UResult<(u64, bool)>
89+
pub(crate) fn splice_write<R, S>(source: &R, dest: &S) -> UResult<(u64, bool)>
9090
where
9191
R: Read + AsFd + AsRawFd,
9292
S: AsRawFd + AsFd,
@@ -122,7 +122,11 @@ where
122122
/// Move exactly `num_bytes` bytes from `read_fd` to `write_fd` using the `read`
123123
/// and `write` calls.
124124
#[cfg(any(target_os = "linux", target_os = "android"))]
125-
fn copy_exact(read_fd: RawFd, write_fd: &impl AsFd, num_bytes: usize) -> std::io::Result<usize> {
125+
pub(crate) fn copy_exact(
126+
read_fd: RawFd,
127+
write_fd: &impl AsFd,
128+
num_bytes: usize,
129+
) -> std::io::Result<usize> {
126130
use nix::unistd;
127131

128132
let mut left = num_bytes;
@@ -247,7 +251,7 @@ where
247251
/// Result with tuple containing a `u64` `0` indicating that no data had been
248252
/// written and a `true` indicating we have to fall back, if error is still
249253
/// recoverable. Returns an `Error` implementing `UError` otherwise.
250-
fn maybe_unsupported(error: nix::Error) -> Result<(u64, bool)> {
254+
pub(crate) fn maybe_unsupported(error: nix::Error) -> Result<(u64, bool)> {
251255
match error {
252256
Errno::EINVAL | Errno::ENOSYS | Errno::EBADF => Ok((0, true)),
253257
_ => Err(error.into()),

0 commit comments

Comments
 (0)