From 49658e6a3c528dec342d82920f4606dab4d90ae8 Mon Sep 17 00:00:00 2001 From: GrantBirki Date: Fri, 16 May 2025 15:19:44 -0700 Subject: [PATCH 1/4] additional edge cases tests for `path.rs` --- library/std/tests/path.rs | 108 +++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index 978402b6fdae..7107d4160567 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -15,6 +15,13 @@ use std::ptr; use std::rc::Rc; use std::sync::Arc; +#[cfg(unix)] +use std::os::unix::ffi::OsStrExt; +#[cfg(windows)] +use std::os::windows::ffi::OsStrExt; +#[cfg(windows)] +use std::os::windows::ffi::OsStringExt; + #[allow(unknown_lints, unused_macro_rules)] macro_rules! t ( ($path:expr, iter: $iter:expr) => ( @@ -1235,7 +1242,7 @@ pub fn test_push() { tp!("foo//", "bar", r"foo//bar"); tp!(r"foo\\", "bar", r"foo\\bar"); tp!("foo/.", "bar", r"foo/.\bar"); - tp!("foo./.", "bar", r"foo./.\bar"); + tp!("foo./.", "bar", r"foo././bar"); tp!(r"foo\.", "bar", r"foo\.\bar"); tp!(r"foo.\.", "bar", r"foo.\.\bar"); tp!("foo", "", "foo\\"); @@ -1976,3 +1983,102 @@ fn clone_to_uninit() { unsafe { a.clone_to_uninit(ptr::from_mut::(&mut b).cast()) }; assert_eq!(a, &*b); } + +// Test: Only separators (e.g., "/" or "\\") +// This test checks how Path handles a string that consists only of path separators. +// It should recognize the root and not treat it as a normal component. +#[test] +fn test_only_separators() { + let path = Path::new("/////"); + assert!(path.has_root()); + assert_eq!(path.iter().count(), 1); + assert_eq!(path.parent(), None); +} + +// Test: Non-ASCII/Unicode +// This test verifies that Path can handle Unicode and non-ASCII characters in the path. +// It ensures that such paths are not rejected or misinterpreted. +#[test] +fn test_non_ascii_unicode() { + let path = Path::new("/tmp/❤/🚀/file.txt"); + assert!(path.to_str().is_some()); + assert_eq!(path.file_name(), Some(OsStr::new("file.txt"))); +} + +// Test: Embedded null bytes +// This test checks that Path can be constructed from a byte slice containing a null byte (on Unix). +// It ensures that null bytes are not treated as string terminators. +#[test] +fn test_embedded_null_byte() { + use std::ffi::OsStr; + let bytes = b"foo\0bar"; + let os_str = OsStr::from_bytes(bytes); + let path = Path::new(os_str); + assert!(path.as_os_str().as_bytes().contains(&0)); + assert_eq!(path.file_name(), Some(OsStr::new("foo\0bar"))); + assert_eq!(path.to_str(), Some("foo\0bar")); +} + +// Test: Reserved device names (Windows) +// This test ensures that reserved device names like "CON", "PRN", etc., are handled as normal paths on non-Windows platforms, +// and as special cases on Windows (if applicable). +#[test] +#[cfg(windows)] +fn test_reserved_device_names() { + for &name in &["CON", "PRN", "AUX", "NUL", "COM1", "LPT1"] { + let path = Path::new(name); + assert_eq!(path.file_name(), Some(OsStr::new(name))); + assert_eq!(path.extension(), None); + } +} + +// Test: Trailing dots/spaces (Windows) +// This test checks how Path handles trailing dots or spaces, which are special on Windows. +// On Unix, these should be treated as normal characters. +#[test] +#[cfg(windows)] +fn test_trailing_dots_and_spaces() { + let path = Path::new("foo. "); + assert_eq!(path.file_stem(), Some(OsStr::new("foo"))); + assert_eq!(path.extension(), Some(OsStr::new(" "))); + assert_eq!(path.file_name(), Some(OsStr::new("foo. "))); + assert_eq!(path.to_str(), Some("foo. ")); + let path = Path::new("bar..."); + assert_eq!(path.file_stem(), Some(OsStr::new("bar"))); + assert_eq!(path.extension(), Some(OsStr::new("..."))); + assert_eq!(path.file_name(), Some(OsStr::new("bar..."))); + assert_eq!(path.to_str(), Some("bar...")); +} + +// Test: Only extension (e.g., ".gitignore") +// This test verifies that files with only an extension and no base name are handled correctly. +// It checks that the extension is recognized and the file stem is None or empty as appropriate. +#[test] +fn test_only_extension() { + let path = Path::new(".ext"); + assert_eq!(path.extension(), None); + assert_eq!(path.file_stem(), Some(OsStr::new(".ext"))); + assert_eq!(path.file_name(), Some(OsStr::new(".ext"))); +} + +// Test: Long components +// This test checks that Path can handle very long path components without truncation or error. +// It ensures that the length of the component is preserved. +#[test] +fn test_long_component() { + let long = "a".repeat(300); + let path = Path::new(&long); + assert_eq!(path.file_name(), Some(OsStr::new(&long))); + assert_eq!(path.to_str(), Some(long.as_str())); + assert_eq!(path.iter().count(), 1); +} + +// Test: Embedded newlines +// This test verifies that newlines within path components are preserved and do not break path parsing. +// It ensures that Path treats newlines as normal characters. +#[test] +fn test_embedded_newline() { + let path = Path::new("foo\nbar"); + assert_eq!(path.file_name(), Some(OsStr::new("foo\nbar"))); + assert_eq!(path.to_str(), Some("foo\nbar")); +} From 604f0e27436b4940399873d0c23e5b609a52e831 Mon Sep 17 00:00:00 2001 From: GrantBirki Date: Fri, 16 May 2025 22:42:05 -0700 Subject: [PATCH 2/4] remove `test_embedded_null_byte()` test for now --- library/std/tests/path.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index 7107d4160567..a6f679d0ffb3 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -15,13 +15,6 @@ use std::ptr; use std::rc::Rc; use std::sync::Arc; -#[cfg(unix)] -use std::os::unix::ffi::OsStrExt; -#[cfg(windows)] -use std::os::windows::ffi::OsStrExt; -#[cfg(windows)] -use std::os::windows::ffi::OsStringExt; - #[allow(unknown_lints, unused_macro_rules)] macro_rules! t ( ($path:expr, iter: $iter:expr) => ( @@ -2005,19 +1998,6 @@ fn test_non_ascii_unicode() { assert_eq!(path.file_name(), Some(OsStr::new("file.txt"))); } -// Test: Embedded null bytes -// This test checks that Path can be constructed from a byte slice containing a null byte (on Unix). -// It ensures that null bytes are not treated as string terminators. -#[test] -fn test_embedded_null_byte() { - use std::ffi::OsStr; - let bytes = b"foo\0bar"; - let os_str = OsStr::from_bytes(bytes); - let path = Path::new(os_str); - assert!(path.as_os_str().as_bytes().contains(&0)); - assert_eq!(path.file_name(), Some(OsStr::new("foo\0bar"))); - assert_eq!(path.to_str(), Some("foo\0bar")); -} // Test: Reserved device names (Windows) // This test ensures that reserved device names like "CON", "PRN", etc., are handled as normal paths on non-Windows platforms, From 1bc8535e61595c87a041cdebd64f073918cce108 Mon Sep 17 00:00:00 2001 From: GrantBirki Date: Sat, 17 May 2025 10:36:39 -0700 Subject: [PATCH 3/4] revert forward slash to backslash --- library/std/tests/path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index a6f679d0ffb3..dc068db04a7b 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -1235,7 +1235,7 @@ pub fn test_push() { tp!("foo//", "bar", r"foo//bar"); tp!(r"foo\\", "bar", r"foo\\bar"); tp!("foo/.", "bar", r"foo/.\bar"); - tp!("foo./.", "bar", r"foo././bar"); + tp!("foo./.", "bar", r"foo./.\bar"); tp!(r"foo\.", "bar", r"foo\.\bar"); tp!(r"foo.\.", "bar", r"foo.\.\bar"); tp!("foo", "", "foo\\"); From 4358a1c05e8a8878edf22c8cb1f3eae07e09efc0 Mon Sep 17 00:00:00 2001 From: GrantBirki Date: Sat, 17 May 2025 10:49:15 -0700 Subject: [PATCH 4/4] remove extra tests that really might not be all that useful --- library/std/tests/path.rs | 55 --------------------------------------- 1 file changed, 55 deletions(-) diff --git a/library/std/tests/path.rs b/library/std/tests/path.rs index dc068db04a7b..87e0d226cbd3 100644 --- a/library/std/tests/path.rs +++ b/library/std/tests/path.rs @@ -1998,61 +1998,6 @@ fn test_non_ascii_unicode() { assert_eq!(path.file_name(), Some(OsStr::new("file.txt"))); } - -// Test: Reserved device names (Windows) -// This test ensures that reserved device names like "CON", "PRN", etc., are handled as normal paths on non-Windows platforms, -// and as special cases on Windows (if applicable). -#[test] -#[cfg(windows)] -fn test_reserved_device_names() { - for &name in &["CON", "PRN", "AUX", "NUL", "COM1", "LPT1"] { - let path = Path::new(name); - assert_eq!(path.file_name(), Some(OsStr::new(name))); - assert_eq!(path.extension(), None); - } -} - -// Test: Trailing dots/spaces (Windows) -// This test checks how Path handles trailing dots or spaces, which are special on Windows. -// On Unix, these should be treated as normal characters. -#[test] -#[cfg(windows)] -fn test_trailing_dots_and_spaces() { - let path = Path::new("foo. "); - assert_eq!(path.file_stem(), Some(OsStr::new("foo"))); - assert_eq!(path.extension(), Some(OsStr::new(" "))); - assert_eq!(path.file_name(), Some(OsStr::new("foo. "))); - assert_eq!(path.to_str(), Some("foo. ")); - let path = Path::new("bar..."); - assert_eq!(path.file_stem(), Some(OsStr::new("bar"))); - assert_eq!(path.extension(), Some(OsStr::new("..."))); - assert_eq!(path.file_name(), Some(OsStr::new("bar..."))); - assert_eq!(path.to_str(), Some("bar...")); -} - -// Test: Only extension (e.g., ".gitignore") -// This test verifies that files with only an extension and no base name are handled correctly. -// It checks that the extension is recognized and the file stem is None or empty as appropriate. -#[test] -fn test_only_extension() { - let path = Path::new(".ext"); - assert_eq!(path.extension(), None); - assert_eq!(path.file_stem(), Some(OsStr::new(".ext"))); - assert_eq!(path.file_name(), Some(OsStr::new(".ext"))); -} - -// Test: Long components -// This test checks that Path can handle very long path components without truncation or error. -// It ensures that the length of the component is preserved. -#[test] -fn test_long_component() { - let long = "a".repeat(300); - let path = Path::new(&long); - assert_eq!(path.file_name(), Some(OsStr::new(&long))); - assert_eq!(path.to_str(), Some(long.as_str())); - assert_eq!(path.iter().count(), 1); -} - // Test: Embedded newlines // This test verifies that newlines within path components are preserved and do not break path parsing. // It ensures that Path treats newlines as normal characters.