| use super::*; |
| use crate::mem::MaybeUninit; |
| use crate::ptr; |
| |
| #[test] |
| fn test_os_string_with_capacity() { |
| let os_string = OsString::with_capacity(0); |
| assert_eq!(0, os_string.inner.into_inner().capacity()); |
| |
| let os_string = OsString::with_capacity(10); |
| assert_eq!(10, os_string.inner.into_inner().capacity()); |
| |
| let mut os_string = OsString::with_capacity(0); |
| os_string.push("abc"); |
| assert!(os_string.inner.into_inner().capacity() >= 3); |
| } |
| |
| #[test] |
| fn test_os_string_clear() { |
| let mut os_string = OsString::from("abc"); |
| assert_eq!(3, os_string.inner.as_inner().len()); |
| |
| os_string.clear(); |
| assert_eq!(&os_string, ""); |
| assert_eq!(0, os_string.inner.as_inner().len()); |
| } |
| |
| #[test] |
| fn test_os_string_leak() { |
| let os_string = OsString::from("have a cake"); |
| let (len, cap) = (os_string.len(), os_string.capacity()); |
| let leaked = os_string.leak(); |
| assert_eq!(leaked.as_encoded_bytes(), b"have a cake"); |
| unsafe { drop(String::from_raw_parts(leaked as *mut OsStr as _, len, cap)) } |
| } |
| |
| #[test] |
| fn test_os_string_capacity() { |
| let os_string = OsString::with_capacity(0); |
| assert_eq!(0, os_string.capacity()); |
| |
| let os_string = OsString::with_capacity(10); |
| assert_eq!(10, os_string.capacity()); |
| |
| let mut os_string = OsString::with_capacity(0); |
| os_string.push("abc"); |
| assert!(os_string.capacity() >= 3); |
| } |
| |
| #[test] |
| fn test_os_string_reserve() { |
| let mut os_string = OsString::new(); |
| assert_eq!(os_string.capacity(), 0); |
| |
| os_string.reserve(2); |
| assert!(os_string.capacity() >= 2); |
| |
| for _ in 0..16 { |
| os_string.push("a"); |
| } |
| |
| assert!(os_string.capacity() >= 16); |
| os_string.reserve(16); |
| assert!(os_string.capacity() >= 32); |
| |
| os_string.push("a"); |
| |
| os_string.reserve(16); |
| assert!(os_string.capacity() >= 33) |
| } |
| |
| #[test] |
| fn test_os_string_reserve_exact() { |
| let mut os_string = OsString::new(); |
| assert_eq!(os_string.capacity(), 0); |
| |
| os_string.reserve_exact(2); |
| assert!(os_string.capacity() >= 2); |
| |
| for _ in 0..16 { |
| os_string.push("a"); |
| } |
| |
| assert!(os_string.capacity() >= 16); |
| os_string.reserve_exact(16); |
| assert!(os_string.capacity() >= 32); |
| |
| os_string.push("a"); |
| |
| os_string.reserve_exact(16); |
| assert!(os_string.capacity() >= 33) |
| } |
| |
| #[test] |
| fn test_os_string_join() { |
| let strings = [OsStr::new("hello"), OsStr::new("dear"), OsStr::new("world")]; |
| assert_eq!("hello", strings[..1].join(OsStr::new(" "))); |
| assert_eq!("hello dear world", strings.join(OsStr::new(" "))); |
| assert_eq!("hellodearworld", strings.join(OsStr::new(""))); |
| assert_eq!("hello.\n dear.\n world", strings.join(OsStr::new(".\n "))); |
| |
| assert_eq!("dear world", strings[1..].join(&OsString::from(" "))); |
| |
| let strings_abc = [OsString::from("a"), OsString::from("b"), OsString::from("c")]; |
| assert_eq!("a b c", strings_abc.join(OsStr::new(" "))); |
| } |
| |
| #[test] |
| fn test_os_string_default() { |
| let os_string: OsString = Default::default(); |
| assert_eq!("", &os_string); |
| } |
| |
| #[test] |
| fn test_os_str_is_empty() { |
| let mut os_string = OsString::new(); |
| assert!(os_string.is_empty()); |
| |
| os_string.push("abc"); |
| assert!(!os_string.is_empty()); |
| |
| os_string.clear(); |
| assert!(os_string.is_empty()); |
| } |
| |
| #[test] |
| fn test_os_str_len() { |
| let mut os_string = OsString::new(); |
| assert_eq!(0, os_string.len()); |
| |
| os_string.push("abc"); |
| assert_eq!(3, os_string.len()); |
| |
| os_string.clear(); |
| assert_eq!(0, os_string.len()); |
| } |
| |
| #[test] |
| fn test_os_str_default() { |
| let os_str: &OsStr = Default::default(); |
| assert_eq!("", os_str); |
| } |
| |
| #[test] |
| fn into_boxed() { |
| let orig = "Hello, world!"; |
| let os_str = OsStr::new(orig); |
| let boxed: Box<OsStr> = Box::from(os_str); |
| let os_string = os_str.to_owned().into_boxed_os_str().into_os_string(); |
| assert_eq!(os_str, &*boxed); |
| assert_eq!(&*boxed, &*os_string); |
| assert_eq!(&*os_string, os_str); |
| } |
| |
| #[test] |
| fn boxed_default() { |
| let boxed = <Box<OsStr>>::default(); |
| assert!(boxed.is_empty()); |
| } |
| |
| #[test] |
| fn test_os_str_clone_into() { |
| let mut os_string = OsString::with_capacity(123); |
| os_string.push("hello"); |
| let os_str = OsStr::new("bonjour"); |
| os_str.clone_into(&mut os_string); |
| assert_eq!(os_str, os_string); |
| assert!(os_string.capacity() >= 123); |
| } |
| |
| #[test] |
| fn into_rc() { |
| let orig = "Hello, world!"; |
| let os_str = OsStr::new(orig); |
| let rc: Rc<OsStr> = Rc::from(os_str); |
| let arc: Arc<OsStr> = Arc::from(os_str); |
| |
| assert_eq!(&*rc, os_str); |
| assert_eq!(&*arc, os_str); |
| |
| let rc2: Rc<OsStr> = Rc::from(os_str.to_owned()); |
| let arc2: Arc<OsStr> = Arc::from(os_str.to_owned()); |
| |
| assert_eq!(&*rc2, os_str); |
| assert_eq!(&*arc2, os_str); |
| } |
| |
| #[test] |
| fn slice_encoded_bytes() { |
| let os_str = OsStr::new("123θგ🦀"); |
| // ASCII |
| let digits = os_str.slice_encoded_bytes(..3); |
| assert_eq!(digits, "123"); |
| let three = os_str.slice_encoded_bytes(2..3); |
| assert_eq!(three, "3"); |
| // 2-byte UTF-8 |
| let theta = os_str.slice_encoded_bytes(3..5); |
| assert_eq!(theta, "θ"); |
| // 3-byte UTF-8 |
| let gani = os_str.slice_encoded_bytes(5..8); |
| assert_eq!(gani, "გ"); |
| // 4-byte UTF-8 |
| let crab = os_str.slice_encoded_bytes(8..); |
| assert_eq!(crab, "🦀"); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn slice_out_of_bounds() { |
| let crab = OsStr::new("🦀"); |
| let _ = crab.slice_encoded_bytes(..5); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn slice_mid_char() { |
| let crab = OsStr::new("🦀"); |
| let _ = crab.slice_encoded_bytes(..2); |
| } |
| |
| #[cfg(unix)] |
| #[test] |
| #[should_panic(expected = "byte index 1 is not an OsStr boundary")] |
| fn slice_invalid_data() { |
| use crate::os::unix::ffi::OsStrExt; |
| |
| let os_string = OsStr::from_bytes(b"\xFF\xFF"); |
| let _ = os_string.slice_encoded_bytes(1..); |
| } |
| |
| #[cfg(unix)] |
| #[test] |
| #[should_panic(expected = "byte index 1 is not an OsStr boundary")] |
| fn slice_partial_utf8() { |
| use crate::os::unix::ffi::{OsStrExt, OsStringExt}; |
| |
| let part_crab = OsStr::from_bytes(&"🦀".as_bytes()[..3]); |
| let mut os_string = OsString::from_vec(vec![0xFF]); |
| os_string.push(part_crab); |
| let _ = os_string.slice_encoded_bytes(1..); |
| } |
| |
| #[cfg(unix)] |
| #[test] |
| fn slice_invalid_edge() { |
| use crate::os::unix::ffi::{OsStrExt, OsStringExt}; |
| |
| let os_string = OsStr::from_bytes(b"a\xFFa"); |
| assert_eq!(os_string.slice_encoded_bytes(..1), "a"); |
| assert_eq!(os_string.slice_encoded_bytes(1..), OsStr::from_bytes(b"\xFFa")); |
| assert_eq!(os_string.slice_encoded_bytes(..2), OsStr::from_bytes(b"a\xFF")); |
| assert_eq!(os_string.slice_encoded_bytes(2..), "a"); |
| |
| let os_string = OsStr::from_bytes(&"abc🦀".as_bytes()[..6]); |
| assert_eq!(os_string.slice_encoded_bytes(..3), "abc"); |
| assert_eq!(os_string.slice_encoded_bytes(3..), OsStr::from_bytes(b"\xF0\x9F\xA6")); |
| |
| let mut os_string = OsString::from_vec(vec![0xFF]); |
| os_string.push("🦀"); |
| assert_eq!(os_string.slice_encoded_bytes(..1), OsStr::from_bytes(b"\xFF")); |
| assert_eq!(os_string.slice_encoded_bytes(1..), "🦀"); |
| } |
| |
| #[cfg(windows)] |
| #[test] |
| #[should_panic(expected = "byte index 3 lies between surrogate codepoints")] |
| fn slice_between_surrogates() { |
| use crate::os::windows::ffi::OsStringExt; |
| |
| let os_string = OsString::from_wide(&[0xD800, 0xD800]); |
| assert_eq!(os_string.as_encoded_bytes(), &[0xED, 0xA0, 0x80, 0xED, 0xA0, 0x80]); |
| let _ = os_string.slice_encoded_bytes(..3); |
| } |
| |
| #[cfg(windows)] |
| #[test] |
| fn slice_surrogate_edge() { |
| use crate::os::windows::ffi::OsStringExt; |
| |
| let surrogate = OsString::from_wide(&[0xD800]); |
| let mut pre_crab = surrogate.clone(); |
| pre_crab.push("🦀"); |
| assert_eq!(pre_crab.slice_encoded_bytes(..3), surrogate); |
| assert_eq!(pre_crab.slice_encoded_bytes(3..), "🦀"); |
| |
| let mut post_crab = OsString::from("🦀"); |
| post_crab.push(&surrogate); |
| assert_eq!(post_crab.slice_encoded_bytes(..4), "🦀"); |
| assert_eq!(post_crab.slice_encoded_bytes(4..), surrogate); |
| } |
| |
| #[test] |
| fn os_str_slice_at() { |
| #[track_caller] |
| fn slice_at_ok(input: &OsStr, index: usize, expected: (&str, &str)) { |
| let expected = (OsStr::new(expected.0), OsStr::new(expected.1)); |
| assert_eq!(input.split_at(index), expected); |
| assert_eq!(input.split_at_checked(index), Some(expected)); |
| } |
| |
| let os_str = OsStr::new("123გ🦀4"); |
| slice_at_ok(os_str, 0, ("", "123გ🦀4")); |
| slice_at_ok(os_str, 1, ("1", "23გ🦀4")); |
| slice_at_ok(os_str, 2, ("12", "3გ🦀4")); |
| slice_at_ok(os_str, 3, ("123", "გ🦀4")); |
| slice_at_ok(os_str, 6, ("123გ", "🦀4")); |
| slice_at_ok(os_str, 10, ("123გ🦀", "4")); |
| slice_at_ok(os_str, 11, ("123გ🦀4", "")); |
| |
| // Invalid boundaries should fail. |
| assert!(os_str.split_at_checked(4).is_none()); |
| assert!(os_str.split_at_checked(5).is_none()); |
| assert!(os_str.split_at_checked(7).is_none()); |
| assert!(os_str.split_at_checked(8).is_none()); |
| assert!(os_str.split_at_checked(9).is_none()); |
| // Out of bounds |
| assert!(os_str.split_at_checked(12).is_none()); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn os_str_slice_at_out_of_bounds() { |
| let crab = OsStr::new("🦀"); |
| let _ = crab.split_at(5); |
| } |
| |
| #[test] |
| #[should_panic] |
| fn os_str_slice_at_mid_char() { |
| let crab = OsStr::new("🦀"); |
| let _ = crab.split_at(2); |
| } |
| |
| #[cfg(unix)] |
| #[test] |
| fn os_str_slice_at_unix() { |
| use crate::os::unix::ffi::OsStrExt; |
| |
| let broken_utf8 = OsStr::from_bytes(&"🦀".as_bytes()[..3]); |
| let invalid = OsStr::from_bytes(b"\xFF"); |
| |
| // Check that broken UTF-8 isn't treated as if it's valid. |
| let mut os_string = invalid.to_os_string(); |
| os_string.push(broken_utf8); |
| assert_eq!(os_string.split_at_checked(1), None); |
| |
| // We should be able to split on ascii with invalid UTF-8 between |
| let os_string = OsStr::from_bytes(b"a\xFFa"); |
| assert_eq!(os_string.split_at_checked(1), Some(("a".as_ref(), OsStr::from_bytes(b"\xFFa")))); |
| assert_eq!(os_string.split_at_checked(2), Some((OsStr::from_bytes(b"a\xFF"), "a".as_ref(),))); |
| |
| let os_string = OsStr::from_bytes(&"abc🦀".as_bytes()[..6]); |
| assert_eq!( |
| os_string.split_at_checked(3), |
| Some(("abc".as_ref(), OsStr::from_bytes(b"\xF0\x9F\xA6"))) |
| ); |
| |
| let mut os_string = invalid.to_os_string(); |
| os_string.push("🦀"); |
| assert_eq!(os_string.split_at_checked(1), Some((invalid, "🦀".as_ref()))); |
| } |
| |
| #[test] |
| #[cfg(windows)] |
| fn os_str_slice_at_windows() { |
| use crate::os::windows::ffi::OsStringExt; |
| |
| // slicing between unpaired surrogates should not be possible |
| // checking is implemented as a loop so we're agnostic towards |
| // the internal encoding |
| let os_string = OsString::from_wide(&[0xD800, 0xD800]); |
| for i in 1..os_string.len() { |
| assert_eq!(os_string.split_at_checked(i), None); |
| } |
| // For completeness, check that splitting at the start and end still works. |
| assert!(os_string.split_at_checked(0).is_some()); |
| assert!(os_string.split_at_checked(os_string.len()).is_some()); |
| |
| // check that slicing before and after unpaired surrogates work |
| let surrogate = OsString::from_wide(&[0xD800]); |
| |
| let mut os_string = surrogate.clone(); |
| os_string.push("🦀"); |
| assert_eq!( |
| os_string.split_at_checked(surrogate.len()), |
| Some((surrogate.as_ref(), "🦀".as_ref())) |
| ); |
| |
| let mut os_string = OsString::from("🦀"); |
| os_string.push(&surrogate); |
| assert_eq!(os_string.split_at_checked("🦀".len()), Some(("🦀".as_ref(), surrogate.as_ref()))); |
| } |
| |
| #[test] |
| fn clone_to_uninit() { |
| let a = OsStr::new("hello.txt"); |
| |
| let mut storage = vec![MaybeUninit::<u8>::uninit(); size_of_val::<OsStr>(a)]; |
| unsafe { a.clone_to_uninit(ptr::from_mut::<[_]>(storage.as_mut_slice()).cast()) }; |
| assert_eq!(a.as_encoded_bytes(), unsafe { storage.assume_init_ref() }); |
| |
| let mut b: Box<OsStr> = OsStr::new("world.exe").into(); |
| assert_eq!(size_of_val::<OsStr>(a), size_of_val::<OsStr>(&b)); |
| assert_ne!(a, &*b); |
| unsafe { a.clone_to_uninit(ptr::from_mut::<OsStr>(&mut b).cast()) }; |
| assert_eq!(a, &*b); |
| } |
| |
| #[test] |
| fn debug() { |
| let s = "'single quotes'"; |
| assert_eq!(format!("{:?}", OsStr::new(s)), format!("{:?}", s)); |
| } |