1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

Merge pull request #855 from jbcrail/fix-readlink-realpath-tests

Fix readlink/realpath tests on Windows
This commit is contained in:
Heather 2016-03-29 08:40:04 +04:00
commit 977d352c05
3 changed files with 70 additions and 18 deletions

View file

@ -49,6 +49,25 @@ macro_rules! assert_no_error(
); );
); );
#[macro_export]
macro_rules! path_concat {
($e:expr, ..$n:expr) => {{
let n = $n;
let mut pb = std::path::PathBuf::new();
for _ in 0..n {
pb.push($e);
}
pb.to_str().unwrap().to_owned()
}};
($($e:expr),*) => {{
let mut pb = std::path::PathBuf::new();
$(
pb.push($e);
)*
pb.to_str().unwrap().to_owned()
}};
}
pub struct CmdResult { pub struct CmdResult {
pub success: bool, pub success: bool,
pub stdout: String, pub stdout: String,
@ -60,32 +79,40 @@ impl CmdResult {
assert!(self.success); assert!(self.success);
Box::new(self) Box::new(self)
} }
pub fn failure(&self) -> Box<&CmdResult> { pub fn failure(&self) -> Box<&CmdResult> {
assert!(!self.success); assert!(!self.success);
Box::new(self) Box::new(self)
} }
pub fn no_stderr(&self) -> Box<&CmdResult> { pub fn no_stderr(&self) -> Box<&CmdResult> {
assert!(self.stderr.len() == 0); assert!(self.stderr.len() == 0);
Box::new(self) Box::new(self)
} }
pub fn no_stdout(&self) -> Box<&CmdResult> { pub fn no_stdout(&self) -> Box<&CmdResult> {
assert!(self.stdout.len() == 0); assert!(self.stdout.len() == 0);
Box::new(self) Box::new(self)
} }
pub fn stdout_is<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> { pub fn stdout_is<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> {
assert!(self.stdout.trim_right() == String::from(msg.as_ref()).trim_right()); assert!(self.stdout.trim_right() == String::from(msg.as_ref()).trim_right());
Box::new(self) Box::new(self)
} }
pub fn stderr_is<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> { pub fn stderr_is<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> {
assert!(self.stderr.trim_right() == String::from(msg.as_ref()).trim_right()); assert!(self.stderr.trim_right() == String::from(msg.as_ref()).trim_right());
Box::new(self) Box::new(self)
} }
pub fn stdout_only<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> { pub fn stdout_only<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> {
self.stdout_is(msg).no_stderr() self.stdout_is(msg).no_stderr()
} }
pub fn stderr_only<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> { pub fn stderr_only<T: AsRef<str>>(&self, msg: T) -> Box<&CmdResult> {
self.stderr_is(msg).no_stdout() self.stderr_is(msg).no_stdout()
} }
pub fn fails_silently(&self) -> Box<&CmdResult> { pub fn fails_silently(&self) -> Box<&CmdResult> {
assert!(!self.success); assert!(!self.success);
assert!(self.stderr.len() == 0); assert!(self.stderr.len() == 0);
@ -97,15 +124,6 @@ pub fn log_info<T: AsRef<str>, U: AsRef<str>>(msg: T, par: U) {
println!("{}: {}", msg.as_ref(), par.as_ref()); println!("{}: {}", msg.as_ref(), par.as_ref());
} }
pub fn repeat_str(s: &str, n: u32) -> String {
let mut repeated = String::new();
for _ in 0..n {
repeated.push_str(s);
}
repeated
}
pub fn recursive_copy(src: &Path, dest: &Path) -> Result<()> { pub fn recursive_copy(src: &Path, dest: &Path) -> Result<()> {
if try!(fs::metadata(src)).is_dir() { if try!(fs::metadata(src)).is_dir() {
for entry in try!(fs::read_dir(src)) { for entry in try!(fs::read_dir(src)) {
@ -123,6 +141,14 @@ pub fn recursive_copy(src: &Path, dest: &Path) -> Result<()> {
Ok(()) Ok(())
} }
pub fn get_root_path() -> &'static str {
if cfg!(windows) {
"C:\\"
} else {
"/"
}
}
/// A scoped, temporary file that is removed upon drop. /// A scoped, temporary file that is removed upon drop.
pub struct ScopedFile { pub struct ScopedFile {
path: PathBuf, path: PathBuf,
@ -160,21 +186,26 @@ impl Drop for ScopedFile {
pub struct AtPath { pub struct AtPath {
pub subdir: PathBuf, pub subdir: PathBuf,
} }
impl AtPath { impl AtPath {
pub fn new(subdir: &Path) -> AtPath { pub fn new(subdir: &Path) -> AtPath {
AtPath { subdir: PathBuf::from(subdir) } AtPath { subdir: PathBuf::from(subdir) }
} }
pub fn as_string(&self) -> String { pub fn as_string(&self) -> String {
self.subdir.to_str().unwrap().to_owned() self.subdir.to_str().unwrap().to_owned()
} }
pub fn plus(&self, name: &str) -> PathBuf { pub fn plus(&self, name: &str) -> PathBuf {
let mut pathbuf = self.subdir.clone(); let mut pathbuf = self.subdir.clone();
pathbuf.push(name); pathbuf.push(name);
pathbuf pathbuf
} }
pub fn plus_as_string(&self, name: &str) -> String { pub fn plus_as_string(&self, name: &str) -> String {
String::from(self.plus(name).to_str().unwrap()) String::from(self.plus(name).to_str().unwrap())
} }
fn minus(&self, name: &str) -> PathBuf { fn minus(&self, name: &str) -> PathBuf {
// relative_from is currently unstable // relative_from is currently unstable
let prefixed = PathBuf::from(name); let prefixed = PathBuf::from(name);
@ -189,23 +220,28 @@ impl AtPath {
prefixed prefixed
} }
} }
pub fn minus_as_string(&self, name: &str) -> String { pub fn minus_as_string(&self, name: &str) -> String {
String::from(self.minus(name).to_str().unwrap()) String::from(self.minus(name).to_str().unwrap())
} }
pub fn open(&self, name: &str) -> File { pub fn open(&self, name: &str) -> File {
log_info("open", self.plus_as_string(name)); log_info("open", self.plus_as_string(name));
File::open(self.plus(name)).unwrap() File::open(self.plus(name)).unwrap()
} }
pub fn read(&self, name: &str) -> String { pub fn read(&self, name: &str) -> String {
let mut f = self.open(name); let mut f = self.open(name);
let mut contents = String::new(); let mut contents = String::new();
let _ = f.read_to_string(&mut contents); let _ = f.read_to_string(&mut contents);
contents contents
} }
pub fn write(&self, name: &str, contents: &str) { pub fn write(&self, name: &str, contents: &str) {
let mut f = self.open(name); let mut f = self.open(name);
let _ = f.write(contents.as_bytes()); let _ = f.write(contents.as_bytes());
} }
pub fn mkdir(&self, dir: &str) { pub fn mkdir(&self, dir: &str) {
log_info("mkdir", self.plus_as_string(dir)); log_info("mkdir", self.plus_as_string(dir));
fs::create_dir(&self.plus(dir)).unwrap(); fs::create_dir(&self.plus(dir)).unwrap();
@ -214,24 +250,29 @@ impl AtPath {
log_info("mkdir_all", self.plus_as_string(dir)); log_info("mkdir_all", self.plus_as_string(dir));
fs::create_dir_all(self.plus(dir)).unwrap(); fs::create_dir_all(self.plus(dir)).unwrap();
} }
pub fn make_file(&self, name: &str) -> File { pub fn make_file(&self, name: &str) -> File {
match File::create(&self.plus(name)) { match File::create(&self.plus(name)) {
Ok(f) => f, Ok(f) => f,
Err(e) => panic!("{}", e), Err(e) => panic!("{}", e),
} }
} }
pub fn make_scoped_file(&self, name: &str) -> ScopedFile { pub fn make_scoped_file(&self, name: &str) -> ScopedFile {
ScopedFile::new(self.plus(name), self.make_file(name)) ScopedFile::new(self.plus(name), self.make_file(name))
} }
pub fn touch(&self, file: &str) { pub fn touch(&self, file: &str) {
log_info("touch", self.plus_as_string(file)); log_info("touch", self.plus_as_string(file));
File::create(&self.plus(file)).unwrap(); File::create(&self.plus(file)).unwrap();
} }
pub fn symlink(&self, src: &str, dst: &str) { pub fn symlink(&self, src: &str, dst: &str) {
log_info("symlink", log_info("symlink",
&format!("{},{}", self.plus_as_string(src), self.plus_as_string(dst))); &format!("{},{}", self.plus_as_string(src), self.plus_as_string(dst)));
symlink_file(&self.plus(src), &self.plus(dst)).unwrap(); symlink_file(&self.plus(src), &self.plus(dst)).unwrap();
} }
pub fn is_symlink(&self, path: &str) -> bool { pub fn is_symlink(&self, path: &str) -> bool {
log_info("is_symlink", self.plus_as_string(path)); log_info("is_symlink", self.plus_as_string(path));
match fs::symlink_metadata(&self.plus(path)) { match fs::symlink_metadata(&self.plus(path)) {
@ -314,6 +355,7 @@ pub struct TestSet {
pub fixtures: AtPath, pub fixtures: AtPath,
tmpd: Rc<TempDir>, tmpd: Rc<TempDir>,
} }
impl TestSet { impl TestSet {
pub fn new(util_name: &str) -> TestSet { pub fn new(util_name: &str) -> TestSet {
let tmpd = Rc::new(TempDir::new("uutils").unwrap()); let tmpd = Rc::new(TempDir::new("uutils").unwrap());
@ -338,14 +380,17 @@ impl TestSet {
} }
ts ts
} }
pub fn util_cmd(&self) -> UCommand { pub fn util_cmd(&self) -> UCommand {
let mut cmd = self.cmd(&self.bin_path); let mut cmd = self.cmd(&self.bin_path);
cmd.arg(&self.util_name); cmd.arg(&self.util_name);
cmd cmd
} }
pub fn cmd<S: AsRef<OsStr>>(&self, bin: S) -> UCommand { pub fn cmd<S: AsRef<OsStr>>(&self, bin: S) -> UCommand {
UCommand::new_from_tmp(bin, self.tmpd.clone(), true) UCommand::new_from_tmp(bin, self.tmpd.clone(), true)
} }
// different names are used rather than an argument // different names are used rather than an argument
// because the need to keep the environment is exceedingly rare. // because the need to keep the environment is exceedingly rare.
pub fn util_cmd_keepenv(&self) -> UCommand { pub fn util_cmd_keepenv(&self) -> UCommand {
@ -353,6 +398,7 @@ impl TestSet {
cmd.arg(&self.util_name); cmd.arg(&self.util_name);
cmd cmd
} }
pub fn cmd_keepenv<S: AsRef<OsStr>>(&self, bin: S) -> UCommand { pub fn cmd_keepenv<S: AsRef<OsStr>>(&self, bin: S) -> UCommand {
UCommand::new_from_tmp(bin, self.tmpd.clone(), false) UCommand::new_from_tmp(bin, self.tmpd.clone(), false)
} }
@ -365,6 +411,7 @@ pub struct UCommand {
has_run: bool, has_run: bool,
stdin: Option<Vec<u8>> stdin: Option<Vec<u8>>
} }
impl UCommand { impl UCommand {
pub fn new<T: AsRef<OsStr>, U: AsRef<OsStr>>(arg: T, curdir: U, env_clear: bool) -> UCommand { pub fn new<T: AsRef<OsStr>, U: AsRef<OsStr>>(arg: T, curdir: U, env_clear: bool) -> UCommand {
UCommand { UCommand {
@ -394,12 +441,14 @@ impl UCommand {
stdin: None stdin: None
} }
} }
pub fn new_from_tmp<T: AsRef<OsStr>>(arg: T, tmpd: Rc<TempDir>, env_clear: bool) -> UCommand { pub fn new_from_tmp<T: AsRef<OsStr>>(arg: T, tmpd: Rc<TempDir>, env_clear: bool) -> UCommand {
let tmpd_path_buf = String::from(&(*tmpd.as_ref().path().to_str().unwrap())); let tmpd_path_buf = String::from(&(*tmpd.as_ref().path().to_str().unwrap()));
let mut ucmd: UCommand = UCommand::new(arg.as_ref(), tmpd_path_buf, env_clear); let mut ucmd: UCommand = UCommand::new(arg.as_ref(), tmpd_path_buf, env_clear);
ucmd.tmpd = Some(tmpd); ucmd.tmpd = Some(tmpd);
ucmd ucmd
} }
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> Box<&mut UCommand> { pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> Box<&mut UCommand> {
if self.has_run { if self.has_run {
panic!(ALREADY_RUN); panic!(ALREADY_RUN);
@ -466,6 +515,7 @@ impl UCommand {
stderr: from_utf8(&prog.stderr).unwrap().to_string(), stderr: from_utf8(&prog.stderr).unwrap().to_string(),
} }
} }
pub fn pipe_in<T: Into<Vec<u8>>>(&mut self, input: T) -> Box<&mut UCommand> { pub fn pipe_in<T: Into<Vec<u8>>>(&mut self, input: T) -> Box<&mut UCommand> {
if self.stdin.is_some() { if self.stdin.is_some() {
panic!(MULTIPLE_STDIN_MEANINGLESS); panic!(MULTIPLE_STDIN_MEANINGLESS);
@ -473,14 +523,17 @@ impl UCommand {
self.stdin = Some(input.into()); self.stdin = Some(input.into());
Box::new(self) Box::new(self)
} }
pub fn run_piped_stdin<T: Into<Vec<u8>>>(&mut self, input: T) -> CmdResult { pub fn run_piped_stdin<T: Into<Vec<u8>>>(&mut self, input: T) -> CmdResult {
self.pipe_in(input).run() self.pipe_in(input).run()
} }
pub fn succeeds(&mut self) -> CmdResult { pub fn succeeds(&mut self) -> CmdResult {
let cmd_result = self.run(); let cmd_result = self.run();
cmd_result.success(); cmd_result.success();
cmd_result cmd_result
} }
pub fn fails(&mut self) -> CmdResult { pub fn fails(&mut self) -> CmdResult {
let cmd_result = self.run(); let cmd_result = self.run();
cmd_result.failure(); cmd_result.failure();
@ -495,6 +548,7 @@ pub fn testset_and_ucommand(utilname: &str) -> (TestSet, UCommand) {
let ucmd = ts.util_cmd(); let ucmd = ts.util_cmd();
(ts, ucmd) (ts, ucmd)
} }
pub fn testing(utilname: &str) -> (AtPath, UCommand) { pub fn testing(utilname: &str) -> (AtPath, UCommand) {
let ts = TestSet::new(utilname); let ts = TestSet::new(utilname);
let ucmd = ts.util_cmd(); let ucmd = ts.util_cmd();

View file

@ -33,9 +33,7 @@ fn test_canonicalize_existing() {
#[test] #[test]
fn test_canonicalize_missing() { fn test_canonicalize_missing() {
let (at, mut ucmd) = testing(UTIL_NAME); let (at, mut ucmd) = testing(UTIL_NAME);
let mut expected = at.root_dir_resolved(); let expected = path_concat!(at.root_dir_resolved(), GIBBERISH);
expected.push_str("/");
expected.push_str(GIBBERISH);
let out = ucmd.arg("-m") let out = ucmd.arg("-m")
.arg(GIBBERISH) .arg(GIBBERISH)
@ -49,7 +47,7 @@ fn test_canonicalize_missing() {
fn test_long_redirection_to_current_dir() { fn test_long_redirection_to_current_dir() {
let (at, mut ucmd) = testing(UTIL_NAME); let (at, mut ucmd) = testing(UTIL_NAME);
// Create a 256-character path to current directory // Create a 256-character path to current directory
let dir = repeat_str("./", 128); let dir = path_concat!(".", ..128);
let out = ucmd.arg("-n") let out = ucmd.arg("-n")
.arg("-m") .arg("-m")
.arg(dir) .arg(dir)
@ -63,12 +61,12 @@ fn test_long_redirection_to_current_dir() {
fn test_long_redirection_to_root() { fn test_long_redirection_to_root() {
let (_, mut ucmd) = testing(UTIL_NAME); let (_, mut ucmd) = testing(UTIL_NAME);
// Create a 255-character path to root // Create a 255-character path to root
let dir = repeat_str("../", 85); let dir = path_concat!("..", ..85);
let out = ucmd.arg("-n") let out = ucmd.arg("-n")
.arg("-m") .arg("-m")
.arg(dir) .arg(dir)
.run() .run()
.stdout; .stdout;
assert_eq!(out, "/"); assert_eq!(out, get_root_path());
} }

View file

@ -17,7 +17,7 @@ fn test_current_directory() {
fn test_long_redirection_to_current_dir() { fn test_long_redirection_to_current_dir() {
let (at, mut ucmd) = testing(UTIL_NAME); let (at, mut ucmd) = testing(UTIL_NAME);
// Create a 256-character path to current directory // Create a 256-character path to current directory
let dir = repeat_str("./", 128); let dir = path_concat!(".", ..128);
let out = ucmd.arg(dir).run().stdout; let out = ucmd.arg(dir).run().stdout;
assert_eq!(out.trim_right(), at.root_dir_resolved()); assert_eq!(out.trim_right(), at.root_dir_resolved());
@ -27,8 +27,8 @@ fn test_long_redirection_to_current_dir() {
fn test_long_redirection_to_root() { fn test_long_redirection_to_root() {
let (_, mut ucmd) = testing(UTIL_NAME); let (_, mut ucmd) = testing(UTIL_NAME);
// Create a 255-character path to root // Create a 255-character path to root
let dir = repeat_str("../", 85); let dir = path_concat!("..", ..85);
let out = ucmd.arg(dir).run().stdout; let out = ucmd.arg(dir).run().stdout;
assert_eq!(out.trim_right(), "/"); assert_eq!(out.trim_right(), get_root_path());
} }