use std::sync::Arc; | |
use proptest::{prop_assert, prop_assert_eq, proptest}; | |
use smol_str::SmolStr; | |
#[test] | |
#[cfg(target_pointer_width = "64")] | |
fn smol_str_is_smol() { | |
assert_eq!( | |
::std::mem::size_of::<SmolStr>(), | |
::std::mem::size_of::<String>(), | |
); | |
} | |
#[test] | |
fn assert_traits() { | |
fn f<T: Send + Sync + ::std::fmt::Debug + Clone>() {} | |
f::<SmolStr>(); | |
} | |
#[test] | |
fn conversions() { | |
let s: SmolStr = "Hello, World!".into(); | |
let s: String = s.into(); | |
assert_eq!(s, "Hello, World!"); | |
let s: SmolStr = Arc::<str>::from("Hello, World!").into(); | |
let s: Arc<str> = s.into(); | |
assert_eq!(s.as_ref(), "Hello, World!"); | |
} | |
#[test] | |
fn const_fn_ctor() { | |
const EMPTY: SmolStr = SmolStr::new_inline(""); | |
const A: SmolStr = SmolStr::new_inline("A"); | |
const HELLO: SmolStr = SmolStr::new_inline("HELLO"); | |
const LONG: SmolStr = SmolStr::new_inline("ABCDEFGHIZKLMNOPQRSTUVW"); | |
assert_eq!(EMPTY, SmolStr::from("")); | |
assert_eq!(A, SmolStr::from("A")); | |
assert_eq!(HELLO, SmolStr::from("HELLO")); | |
assert_eq!(LONG, SmolStr::from("ABCDEFGHIZKLMNOPQRSTUVW")); | |
} | |
#[allow(deprecated)] | |
#[test] | |
fn old_const_fn_ctor() { | |
const EMPTY: SmolStr = SmolStr::new_inline_from_ascii(0, b""); | |
const A: SmolStr = SmolStr::new_inline_from_ascii(1, b"A"); | |
const HELLO: SmolStr = SmolStr::new_inline_from_ascii(5, b"HELLO"); | |
const LONG: SmolStr = SmolStr::new_inline_from_ascii(23, b"ABCDEFGHIZKLMNOPQRSTUVW"); | |
assert_eq!(EMPTY, SmolStr::from("")); | |
assert_eq!(A, SmolStr::from("A")); | |
assert_eq!(HELLO, SmolStr::from("HELLO")); | |
assert_eq!(LONG, SmolStr::from("ABCDEFGHIZKLMNOPQRSTUVW")); | |
} | |
fn check_props(std_str: &str, smol: SmolStr) -> Result<(), proptest::test_runner::TestCaseError> { | |
prop_assert_eq!(smol.as_str(), std_str); | |
prop_assert_eq!(smol.len(), std_str.len()); | |
prop_assert_eq!(smol.is_empty(), std_str.is_empty()); | |
if smol.len() <= 23 { | |
prop_assert!(!smol.is_heap_allocated()); | |
} | |
Ok(()) | |
} | |
proptest! { | |
#[test] | |
fn roundtrip(s: String) { | |
check_props(s.as_str(), SmolStr::new(s.clone()))?; | |
} | |
#[test] | |
fn roundtrip_spaces(s in r"( )*") { | |
check_props(s.as_str(), SmolStr::new(s.clone()))?; | |
} | |
#[test] | |
fn roundtrip_newlines(s in r"\n*") { | |
check_props(s.as_str(), SmolStr::new(s.clone()))?; | |
} | |
#[test] | |
fn roundtrip_ws(s in r"( |\n)*") { | |
check_props(s.as_str(), SmolStr::new(s.clone()))?; | |
} | |
#[test] | |
fn from_string_iter(slices in proptest::collection::vec(".*", 1..100)) { | |
let string: String = slices.iter().map(|x| x.as_str()).collect(); | |
let smol: SmolStr = slices.into_iter().collect(); | |
check_props(string.as_str(), smol)?; | |
} | |
#[test] | |
fn from_str_iter(slices in proptest::collection::vec(".*", 1..100)) { | |
let string: String = slices.iter().map(|x| x.as_str()).collect(); | |
let smol: SmolStr = slices.iter().collect(); | |
check_props(string.as_str(), smol)?; | |
} | |
} | |
#[cfg(feature = "serde")] | |
mod serde_tests { | |
use super::*; | |
use serde::{Deserialize, Serialize}; | |
use std::collections::HashMap; | |
#[derive(Serialize, Deserialize)] | |
struct SmolStrStruct { | |
pub(crate) s: SmolStr, | |
pub(crate) vec: Vec<SmolStr>, | |
pub(crate) map: HashMap<SmolStr, SmolStr>, | |
} | |
#[test] | |
fn test_serde() { | |
let s = SmolStr::new("Hello, World"); | |
let s = serde_json::to_string(&s).unwrap(); | |
assert_eq!(s, "\"Hello, World\""); | |
let s: SmolStr = serde_json::from_str(&s).unwrap(); | |
assert_eq!(s, "Hello, World"); | |
} | |
#[test] | |
fn test_serde_reader() { | |
let s = SmolStr::new("Hello, World"); | |
let s = serde_json::to_string(&s).unwrap(); | |
assert_eq!(s, "\"Hello, World\""); | |
let s: SmolStr = serde_json::from_reader(std::io::Cursor::new(s)).unwrap(); | |
assert_eq!(s, "Hello, World"); | |
} | |
#[test] | |
fn test_serde_struct() { | |
let mut map = HashMap::new(); | |
map.insert(SmolStr::new("a"), SmolStr::new("ohno")); | |
let struct_ = SmolStrStruct { | |
s: SmolStr::new("Hello, World"), | |
vec: vec![SmolStr::new("Hello, World"), SmolStr::new("Hello, World")], | |
map, | |
}; | |
let s = serde_json::to_string(&struct_).unwrap(); | |
let _new_struct: SmolStrStruct = serde_json::from_str(&s).unwrap(); | |
} | |
#[test] | |
fn test_serde_struct_reader() { | |
let mut map = HashMap::new(); | |
map.insert(SmolStr::new("a"), SmolStr::new("ohno")); | |
let struct_ = SmolStrStruct { | |
s: SmolStr::new("Hello, World"), | |
vec: vec![SmolStr::new("Hello, World"), SmolStr::new("Hello, World")], | |
map, | |
}; | |
let s = serde_json::to_string(&struct_).unwrap(); | |
let _new_struct: SmolStrStruct = serde_json::from_reader(std::io::Cursor::new(s)).unwrap(); | |
} | |
#[test] | |
fn test_serde_hashmap() { | |
let mut map = HashMap::new(); | |
map.insert(SmolStr::new("a"), SmolStr::new("ohno")); | |
let s = serde_json::to_string(&map).unwrap(); | |
let _s: HashMap<SmolStr, SmolStr> = serde_json::from_str(&s).unwrap(); | |
} | |
#[test] | |
fn test_serde_hashmap_reader() { | |
let mut map = HashMap::new(); | |
map.insert(SmolStr::new("a"), SmolStr::new("ohno")); | |
let s = serde_json::to_string(&map).unwrap(); | |
let _s: HashMap<SmolStr, SmolStr> = | |
serde_json::from_reader(std::io::Cursor::new(s)).unwrap(); | |
} | |
#[test] | |
fn test_serde_vec() { | |
let vec = vec![SmolStr::new(""), SmolStr::new("b")]; | |
let s = serde_json::to_string(&vec).unwrap(); | |
let _s: Vec<SmolStr> = serde_json::from_str(&s).unwrap(); | |
} | |
#[test] | |
fn test_serde_vec_reader() { | |
let vec = vec![SmolStr::new(""), SmolStr::new("b")]; | |
let s = serde_json::to_string(&vec).unwrap(); | |
let _s: Vec<SmolStr> = serde_json::from_reader(std::io::Cursor::new(s)).unwrap(); | |
} | |
} | |
#[test] | |
fn test_search_in_hashmap() { | |
let mut m = ::std::collections::HashMap::<SmolStr, i32>::new(); | |
m.insert("aaa".into(), 17); | |
assert_eq!(17, *m.get("aaa").unwrap()); | |
} | |
#[test] | |
fn test_from_char_iterator() { | |
let examples = [ | |
// Simple keyword-like strings | |
("if", false), | |
("for", false), | |
("impl", false), | |
// Strings containing two-byte characters | |
("パーティーへ行かないか", true), | |
("パーティーへ行か", true), | |
("パーティーへ行_", false), | |
("和製漢語", false), | |
("部落格", false), | |
("사회과학원 어학연구소", true), | |
// String containing diverse characters | |
("表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀", true), | |
]; | |
for (raw, is_heap) in &examples { | |
let s: SmolStr = raw.chars().collect(); | |
assert_eq!(s.as_str(), *raw); | |
assert_eq!(s.is_heap_allocated(), *is_heap); | |
} | |
// String which has too many characters to even consider inlining: Chars::size_hint uses | |
// (`len` + 3) / 4. With `len` = 89, this results in 23, so `from_iter` will immediately | |
// heap allocate | |
let raw: String = std::iter::repeat('a').take(23 * 4 + 1).collect(); | |
let s: SmolStr = raw.chars().collect(); | |
assert_eq!(s.as_str(), raw); | |
assert!(s.is_heap_allocated()); | |
} | |
#[test] | |
fn test_bad_size_hint_char_iter() { | |
struct BadSizeHint<I>(I); | |
impl<T, I: Iterator<Item = T>> Iterator for BadSizeHint<I> { | |
type Item = T; | |
fn next(&mut self) -> Option<Self::Item> { | |
self.0.next() | |
} | |
fn size_hint(&self) -> (usize, Option<usize>) { | |
(1024, None) | |
} | |
} | |
let data = "testing"; | |
let collected: SmolStr = BadSizeHint(data.chars()).collect(); | |
let new = SmolStr::new(data); | |
// Because of the bad size hint, `collected` will be heap allocated, but `new` will be inline | |
// If we try to use the type of the string (inline/heap) to quickly test for equality, we need to ensure | |
// `collected` is inline allocated instead | |
assert!(collected.is_heap_allocated()); | |
assert!(!new.is_heap_allocated()); | |
assert_eq!(new, collected); | |
} | |
#[test] | |
fn test_to_smolstr() { | |
use smol_str::ToSmolStr; | |
for i in 0..26 { | |
let a = &"abcdefghijklmnopqrstuvwxyz"[i..]; | |
assert_eq!(a, a.to_smolstr()); | |
assert_eq!(a, smol_str::format_smolstr!("{}", a)); | |
} | |
} |