blob: f52647062f027bc3b5ab75ac3b438e78b010cfb7 [file] [log] [blame]
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));
}
}