@@ -15,6 +15,13 @@ use std::ptr;
1515use std:: rc:: Rc ;
1616use std:: sync:: Arc ;
1717
18+ #[ cfg( unix) ]
19+ use std:: os:: unix:: ffi:: OsStrExt ;
20+ #[ cfg( windows) ]
21+ use std:: os:: windows:: ffi:: OsStrExt ;
22+ #[ cfg( windows) ]
23+ use std:: os:: windows:: ffi:: OsStringExt ;
24+
1825#[ allow( unknown_lints, unused_macro_rules) ]
1926macro_rules! t (
2027 ( $path: expr, iter: $iter: expr) => (
@@ -1235,7 +1242,7 @@ pub fn test_push() {
12351242 tp ! ( "foo//" , "bar" , r"foo//bar" ) ;
12361243 tp ! ( r"foo\\" , "bar" , r"foo\\bar" ) ;
12371244 tp ! ( "foo/." , "bar" , r"foo/.\bar" ) ;
1238- tp ! ( "foo./." , "bar" , r"foo./.\ bar" ) ;
1245+ tp ! ( "foo./." , "bar" , r"foo././ bar" ) ;
12391246 tp ! ( r"foo\." , "bar" , r"foo\.\bar" ) ;
12401247 tp ! ( r"foo.\." , "bar" , r"foo.\.\bar" ) ;
12411248 tp ! ( "foo" , "" , "foo\\ " ) ;
@@ -1976,3 +1983,102 @@ fn clone_to_uninit() {
19761983 unsafe { a. clone_to_uninit ( ptr:: from_mut :: < Path > ( & mut b) . cast ( ) ) } ;
19771984 assert_eq ! ( a, & * b) ;
19781985}
1986+
1987+ // Test: Only separators (e.g., "/" or "\\")
1988+ // This test checks how Path handles a string that consists only of path separators.
1989+ // It should recognize the root and not treat it as a normal component.
1990+ #[ test]
1991+ fn test_only_separators ( ) {
1992+ let path = Path :: new ( "/////" ) ;
1993+ assert ! ( path. has_root( ) ) ;
1994+ assert_eq ! ( path. iter( ) . count( ) , 1 ) ;
1995+ assert_eq ! ( path. parent( ) , None ) ;
1996+ }
1997+
1998+ // Test: Non-ASCII/Unicode
1999+ // This test verifies that Path can handle Unicode and non-ASCII characters in the path.
2000+ // It ensures that such paths are not rejected or misinterpreted.
2001+ #[ test]
2002+ fn test_non_ascii_unicode ( ) {
2003+ let path = Path :: new ( "/tmp/❤/🚀/file.txt" ) ;
2004+ assert ! ( path. to_str( ) . is_some( ) ) ;
2005+ assert_eq ! ( path. file_name( ) , Some ( OsStr :: new( "file.txt" ) ) ) ;
2006+ }
2007+
2008+ // Test: Embedded null bytes
2009+ // This test checks that Path can be constructed from a byte slice containing a null byte (on Unix).
2010+ // It ensures that null bytes are not treated as string terminators.
2011+ #[ test]
2012+ fn test_embedded_null_byte ( ) {
2013+ use std:: ffi:: OsStr ;
2014+ let bytes = b"foo\0 bar" ;
2015+ let os_str = OsStr :: from_bytes ( bytes) ;
2016+ let path = Path :: new ( os_str) ;
2017+ assert ! ( path. as_os_str( ) . as_bytes( ) . contains( & 0 ) ) ;
2018+ assert_eq ! ( path. file_name( ) , Some ( OsStr :: new( "foo\0 bar" ) ) ) ;
2019+ assert_eq ! ( path. to_str( ) , Some ( "foo\0 bar" ) ) ;
2020+ }
2021+
2022+ // Test: Reserved device names (Windows)
2023+ // This test ensures that reserved device names like "CON", "PRN", etc., are handled as normal paths on non-Windows platforms,
2024+ // and as special cases on Windows (if applicable).
2025+ #[ test]
2026+ #[ cfg( windows) ]
2027+ fn test_reserved_device_names ( ) {
2028+ for & name in & [ "CON" , "PRN" , "AUX" , "NUL" , "COM1" , "LPT1" ] {
2029+ let path = Path :: new ( name) ;
2030+ assert_eq ! ( path. file_name( ) , Some ( OsStr :: new( name) ) ) ;
2031+ assert_eq ! ( path. extension( ) , None ) ;
2032+ }
2033+ }
2034+
2035+ // Test: Trailing dots/spaces (Windows)
2036+ // This test checks how Path handles trailing dots or spaces, which are special on Windows.
2037+ // On Unix, these should be treated as normal characters.
2038+ #[ test]
2039+ #[ cfg( windows) ]
2040+ fn test_trailing_dots_and_spaces ( ) {
2041+ let path = Path :: new ( "foo. " ) ;
2042+ assert_eq ! ( path. file_stem( ) , Some ( OsStr :: new( "foo" ) ) ) ;
2043+ assert_eq ! ( path. extension( ) , Some ( OsStr :: new( " " ) ) ) ;
2044+ assert_eq ! ( path. file_name( ) , Some ( OsStr :: new( "foo. " ) ) ) ;
2045+ assert_eq ! ( path. to_str( ) , Some ( "foo. " ) ) ;
2046+ let path = Path :: new ( "bar..." ) ;
2047+ assert_eq ! ( path. file_stem( ) , Some ( OsStr :: new( "bar" ) ) ) ;
2048+ assert_eq ! ( path. extension( ) , Some ( OsStr :: new( "..." ) ) ) ;
2049+ assert_eq ! ( path. file_name( ) , Some ( OsStr :: new( "bar..." ) ) ) ;
2050+ assert_eq ! ( path. to_str( ) , Some ( "bar..." ) ) ;
2051+ }
2052+
2053+ // Test: Only extension (e.g., ".gitignore")
2054+ // This test verifies that files with only an extension and no base name are handled correctly.
2055+ // It checks that the extension is recognized and the file stem is None or empty as appropriate.
2056+ #[ test]
2057+ fn test_only_extension ( ) {
2058+ let path = Path :: new ( ".ext" ) ;
2059+ assert_eq ! ( path. extension( ) , None ) ;
2060+ assert_eq ! ( path. file_stem( ) , Some ( OsStr :: new( ".ext" ) ) ) ;
2061+ assert_eq ! ( path. file_name( ) , Some ( OsStr :: new( ".ext" ) ) ) ;
2062+ }
2063+
2064+ // Test: Long components
2065+ // This test checks that Path can handle very long path components without truncation or error.
2066+ // It ensures that the length of the component is preserved.
2067+ #[ test]
2068+ fn test_long_component ( ) {
2069+ let long = "a" . repeat ( 300 ) ;
2070+ let path = Path :: new ( & long) ;
2071+ assert_eq ! ( path. file_name( ) , Some ( OsStr :: new( & long) ) ) ;
2072+ assert_eq ! ( path. to_str( ) , Some ( long. as_str( ) ) ) ;
2073+ assert_eq ! ( path. iter( ) . count( ) , 1 ) ;
2074+ }
2075+
2076+ // Test: Embedded newlines
2077+ // This test verifies that newlines within path components are preserved and do not break path parsing.
2078+ // It ensures that Path treats newlines as normal characters.
2079+ #[ test]
2080+ fn test_embedded_newline ( ) {
2081+ let path = Path :: new ( "foo\n bar" ) ;
2082+ assert_eq ! ( path. file_name( ) , Some ( OsStr :: new( "foo\n bar" ) ) ) ;
2083+ assert_eq ! ( path. to_str( ) , Some ( "foo\n bar" ) ) ;
2084+ }
0 commit comments