#![expect(clippy::tests_outside_test_module, reason = "tests directory")]
#![expect(
clippy::std_instead_of_alloc,
reason = "We are testing, this needs std"
)]
mod func {
use core::borrow::Borrow as _;
use core::hash::{Hash as _, Hasher as _};
use std::hash::DefaultHasher;
use assert2::{check, let_assert};
use token_string::TokenString;
#[test]
fn empty_is_empty() {
let empty = TokenString::default();
check!(empty.is_empty());
check!(empty.len() == 0);
}
#[test]
fn is_not_empty() {
let_assert!(
Ok(res) = TokenString::try_from("Hello!"),
"this should be OK"
);
check!(res.is_empty() == false);
check!(res.len() == "Hello!".len());
}
#[test]
fn same_prefix_eq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello!"),
"this should be OK"
);
check!(res == res);
check!(res.is_small());
}
#[test]
fn same_small_eq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello World!"),
"this should be OK"
);
check!(res == res);
check!(res.is_small());
}
#[test]
fn same_heap_eq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello funny world!"),
"this should be OK"
);
check!(res == res);
check!(!res.is_small());
}
#[test]
fn clone_prefix_eq() {
let_assert!(
Ok(s1) = TokenString::try_from("Hello!"),
"this should be OK"
);
let res = s1.clone();
check!(res == s1);
check!(res.is_small());
}
#[test]
fn clone_small_eq() {
let_assert!(
Ok(s1) = TokenString::try_from("Hello World!"),
"this should be OK"
);
let res = s1.clone();
check!(res == s1);
check!(res.is_small());
}
#[test]
fn clone_heap_eq() {
let_assert!(
Ok(s1) = TokenString::try_from("Hello funny world!"),
"this should be OK"
);
let res = s1.clone();
check!(res == s1);
check!(!res.is_small());
}
#[test]
fn as_ref() {
let_assert!(
Ok(res) = TokenString::try_from("Hello!"),
"this should be OK"
);
let s = res.as_ref();
check!(&res == s);
check!(res.is_small());
}
#[test]
fn borrow() {
let_assert!(
Ok(res) = TokenString::try_from("Hello!"),
"this should be OK"
);
let s: &str = res.borrow();
check!(&res == s);
check!(res.is_small());
}
#[test]
fn prefix_eq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello!"),
"this should be OK"
);
let_assert!(
Ok(res2) = TokenString::try_from("Hello!"),
"this should be OK"
);
check!(res == res2);
check!(res.is_small());
}
#[test]
fn small_eq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello World!"),
"this should be OK"
);
let_assert!(
Ok(res2) = TokenString::try_from("Hello World!"),
"this should be OK"
);
check!(res == res2);
check!(res.is_small());
}
#[test]
fn heap_eq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello funny world!"),
"this should be OK"
);
let_assert!(
Ok(res2) = TokenString::try_from("Hello funny world!"),
"this should be OK"
);
check!(res == res2);
check!(!res.is_small());
}
#[test]
fn as_chars() {
let chars: Vec<char> = "Hello funny world!".chars().collect();
let_assert!(
Ok(res) = TokenString::try_from(chars.as_slice()),
"this should be OK"
);
check!(res.as_chars() == chars);
check!(!res.is_small());
}
#[test]
fn heap_string_display() {
let string = "Hello funny world!".to_owned();
let_assert!(
Ok(res) = TokenString::try_from(&string),
"this should be OK"
);
check!(res.to_string() == string);
check!(format!("{res}") == string);
check!(!res.is_small());
}
#[test]
fn prefix_neq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello!"),
"this should be OK"
);
let_assert!(
Ok(res2) = TokenString::try_from("Hello1"),
"this should be OK"
);
check!(res != res2);
check!(res.is_small() == res2.is_small());
}
#[test]
fn small_neq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello World!"),
"this should be OK"
);
let_assert!(
Ok(res2) = TokenString::try_from("Hello World1"),
"this should be OK"
);
check!(res != res2);
check!(res.is_small() == res2.is_small());
}
#[test]
fn heap_neq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello funny world!"),
"this should be OK"
);
let_assert!(
Ok(res2) = TokenString::try_from("Hello funny world1"),
"this should be OK"
);
check!(res != res2);
check!(res.is_small() == res2.is_small());
}
#[test]
fn string_neq() {
let s = "Hello funny world!".to_owned();
let_assert!(
Ok(res) = TokenString::try_from("Hello funny world"),
"this should be OK"
);
check!(res != s);
check!(res.is_small() == false);
}
#[test]
fn prefix_str_neq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello!"),
"this should be OK"
);
let res2 = "Hello";
check!(&res != res2);
}
#[test]
fn small_str_neq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello World!"),
"this should be OK"
);
let res2 = "Hello World";
check!(&res != res2);
}
#[test]
fn heap_str_neq() {
let_assert!(
Ok(res) = TokenString::try_from("Hello funny world!"),
"this should be OK"
);
let res2 = "Hello funny world";
check!(&res != res2);
}
#[test]
fn debug_print_prefix() {
let_assert!(Ok(res) = TokenString::try_from("à €\0¡"));
let a = format!("Debug: {res:?}");
check!(
a == "Debug: TokenString { len: 6, prefix: [224, 160, 128, 0, \
194, 161], small: [], string: \"à €\\0¡\" }"
);
}
#[test]
fn debug_print_small() {
let_assert!(Ok(res) = TokenString::try_from("à €\0A¡"));
let a = format!("Debug: {res:?}");
check!(
a == "Debug: TokenString { len: 7, prefix: [224, 160, 128, 0, 65, \
194], small: [161], string: \"à €\\0A¡\" }"
);
}
#[test]
fn debug_print_heap() {
let_assert!(Ok(res) = TokenString::try_from("à €\0A¡ \u{b}\u{b}à €\0a"));
let a = format!("Debug: {res:?}");
let ptr_str = format!("{:?}", res.as_str().as_ptr());
check!(
a == format!(
"Debug: TokenString {{ len: 15, prefix: [224, 160, 128, 0, \
65, 194], ptr: ManuallyDrop {{ value: StringPtr {{ ptr: \
{ptr_str} }} }}, string: \"à €\\0A¡ \\u{{b}}\\u{{b}}à €\\0a\" }}"
)
);
}
#[test]
fn ord_array() {
let_assert!(Ok(s1) = TokenString::try_from("Aaaaaaaa"));
let_assert!(Ok(s2) = TokenString::try_from("Aaaaaaa"));
let_assert!(Ok(s3) = TokenString::try_from("Aaaaaa"));
let_assert!(Ok(s4) = TokenString::try_from("Aabaaa"));
let_assert!(Ok(s5) = TokenString::try_from("Aab"));
let res = [&s3, &s2, &s1, &s5, &s4];
let mut res2 = [&s4, &s1, &s3, &s2, &s5];
res2.sort();
check!(res2 == res);
}
#[test]
fn slice_prefix() {
let_assert!(
Ok(res) = TokenString::try_from("Hello!"),
"this should be OK"
);
let res2 = &res[0 .. 4];
check!(&res.as_str()[0 .. 4] == res2);
}
#[test]
fn slice_small() {
let_assert!(
Ok(res) = TokenString::try_from("Hello, world!"),
"this should be OK"
);
let res2 = &res[5 .. 12];
check!(&res.as_str()[5 .. 12] == res2);
}
#[test]
fn slice_heap() {
let_assert!(
Ok(res) = TokenString::try_from("Hello, funny world!"),
"this should be OK"
);
let res2 = &res[7 .. 12];
check!(&res.as_str()[7 .. 12] == res2);
}
#[test]
fn get_prefix() {
let_assert!(
Ok(res) = TokenString::try_from("Hello!"),
"this should be OK"
);
let_assert!(Ok(el) = res.get(4));
check!(el == b'o');
}
#[test]
fn get_small() {
let_assert!(
Ok(res) = TokenString::try_from("Hello, world!"),
"this should be OK"
);
let_assert!(Ok(el) = res.get(9));
check!(el == b'r');
}
#[test]
fn get_heap() {
let_assert!(
Ok(res) = TokenString::try_from("Hello, funny world!"),
"this should be OK"
);
let_assert!(Ok(el) = res.get(15));
check!(el == b'r');
}
#[test]
fn get_unchecked_prefix() {
let_assert!(
Ok(res) = TokenString::try_from("Hello!"),
"this should be OK"
);
let el = res.get_unchecked(4);
check!(el == b'o');
}
#[test]
fn get_unchecked_small() {
let_assert!(
Ok(res) = TokenString::try_from("Hello, world!"),
"this should be OK"
);
let el = res.get_unchecked(9);
check!(el == b'r');
}
#[test]
fn get_unchecked_heap() {
let_assert!(
Ok(res) = TokenString::try_from("Hello, funny world!"),
"this should be OK"
);
let el = res.get_unchecked(15);
check!(el == b'r');
}
#[test]
fn trim_prefix() {
let_assert!(
Ok(res) = TokenString::try_from(" Hell "),
"this should be OK"
);
check!(&res.trim_ascii() == "Hell");
}
#[test]
fn trim_small() {
let_assert!(
Ok(res) = TokenString::try_from(" Hello, world "),
"this should be OK"
);
check!(&res.trim_ascii() == "Hello, world");
check!(res.is_small() == true);
}
#[test]
fn trim_heap() {
let_assert!(
Ok(res) = TokenString::try_from(" Hello, funny world "),
"this should be OK"
);
check!(&res.trim_ascii() == "Hello, funny world");
check!(res.is_small() == false);
}
#[test]
fn trim_start_heap() {
let_assert!(
Ok(res) = TokenString::try_from(" Hello, funny world "),
"this should be OK"
);
check!(&res.trim_ascii_start() == "Hello, funny world ");
check!(res.is_small() == false);
}
#[test]
fn trim_end_heap() {
let_assert!(
Ok(res) = TokenString::try_from(" Hello, funny world "),
"this should be OK"
);
check!(&res.trim_ascii_end() == " Hello, funny world");
check!(res.is_small() == false);
}
#[test]
fn is_uppercase() {
let_assert!(
Ok(res) = TokenString::try_from("Hello!"),
"this should be OK"
);
check!(res.starts_ascii_uppercase() == true);
check!(res.starts_ascii_lowercase() == false);
}
#[test]
fn is_lowercase() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
check!(res.starts_ascii_uppercase() == false);
check!(res.starts_ascii_lowercase() == true);
}
#[test]
fn is_ascii() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
check!(res.is_ascii() == true);
}
#[test]
fn is_not_ascii() {
let_assert!(
Ok(res) = TokenString::try_from("hěllo!"),
"this should be OK"
);
check!(res.is_ascii() == false);
}
#[test]
fn starts_with() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
let_assert!(
Ok(pref) = TokenString::try_from("hell"),
"this should be OK"
);
check!(res.starts_with(&pref) == true);
}
#[test]
fn starts_with_bytes() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
check!(res.starts_with_bytes(b"hell") == true);
}
#[test]
fn starts_with_str() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
check!(res.starts_with_str("hell") == true);
}
#[test]
fn ends_with() {
let_assert!(
Ok(res) = TokenString::try_from("hello world!"),
"this should be OK"
);
let_assert!(
Ok(suf) = TokenString::try_from("world!"),
"this should be OK"
);
check!(res.ends_with(&suf) == true);
}
#[test]
fn ends_with_str() {
let_assert!(
Ok(res) = TokenString::try_from("hello world!"),
"this should be OK"
);
check!(res.ends_with_str("world!") == true);
}
#[test]
fn ends_with_bytes() {
let_assert!(
Ok(res) = TokenString::try_from("hello world!"),
"this should be OK"
);
check!(res.ends_with_bytes(b"world!") == true);
}
#[test]
fn not_starts_with_bytes() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
check!(res.starts_with_bytes(b"ell") == false);
}
#[test]
fn not_starts_with() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
let_assert!(
Ok(pref) = TokenString::try_from("ell"),
"this should be OK"
);
check!(res.starts_with(&pref) == false);
}
#[test]
fn not_starts_with_str() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
check!(res.starts_with_str("ell") == false);
}
#[test]
fn not_ends_with() {
let_assert!(
Ok(res) = TokenString::try_from("hello world!"),
"this should be OK"
);
let_assert!(
Ok(suf) = TokenString::try_from("world"),
"this should be OK"
);
check!(res.ends_with(&suf) == false);
}
#[test]
fn not_ends_with_str() {
let_assert!(
Ok(res) = TokenString::try_from("hello world!"),
"this should be OK"
);
check!(res.ends_with_str("world") == false);
}
#[test]
fn not_ends_with_bytes() {
let_assert!(
Ok(res) = TokenString::try_from("hello world!"),
"this should be OK"
);
check!(res.ends_with_bytes(b"world") == false);
}
#[test]
fn to_lower() {
let_assert!(
Ok(res) = TokenString::try_from("HELLO!"),
"this should be OK"
);
check!(&res.to_ascii_lowercase() == "hello!");
}
#[test]
fn to_upper_small() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
check!(&res.to_ascii_uppercase() == "HELLO!");
}
#[test]
fn to_lower_small() {
let_assert!(
Ok(res) = TokenString::try_from("HELLO, FUNNY WORLD!"),
"this should be OK"
);
check!(&res.to_ascii_lowercase() == "hello, funny world!");
}
#[test]
fn to_upper() {
let_assert!(
Ok(res) = TokenString::try_from("hello, funny world!"),
"this should be OK"
);
check!(&res.to_ascii_uppercase() == "HELLO, FUNNY WORLD!");
}
#[test]
fn to_iter_owned() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
let mut iter = res.into_iter();
check!(iter.next() == Some(b'h'));
}
#[test]
fn to_iter() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
let mut iter = (&res).into_iter();
check!(iter.next() == Some(b'h'));
}
#[test]
fn hashing() {
let_assert!(
Ok(res) = TokenString::try_from("hello!"),
"this should be OK"
);
let_assert!(
Ok(res2) = TokenString::try_from("world"),
"this should be OK"
);
let mut hasher1 = DefaultHasher::new();
res.hash(&mut hasher1);
let hash1 = hasher1.finish();
let mut hasher2 = DefaultHasher::new();
res2.hash(&mut hasher2);
let hash2 = hasher2.finish();
check!(hash1 != hash2);
}
#[test]
#[cfg(feature = "pattern")]
fn strip_suffix() {
let_assert!(
Ok(res) = TokenString::try_from("hello world!"),
"this should be OK"
);
let_assert!(Some(stripped) = res.strip_suffix("world!"));
check!(&stripped == "hello ");
}
#[test]
#[cfg(feature = "pattern")]
fn strip_prefix() {
let_assert!(
Ok(res) = TokenString::try_from("hello world!"),
"this should be OK"
);
let_assert!(Some(stripped) = res.strip_prefix("hello "));
check!(&stripped == "world!");
}
#[test]
#[cfg(feature = "pattern")]
fn contains() {
let_assert!(
Ok(res) = TokenString::try_from("hello world!"),
"this should be OK"
);
check!(res.contains("o w") == true);
check!(res.contains("o q") == false);
}
#[test]
fn debug_iter() {
let act = " \u{b}\u{b}à €a";
let_assert!(Ok(res) = TokenString::try_from(act));
check!(res == *act, "{res:?} != '{act:?}'");
let_assert!(
Ok(string_from_iter) =
String::from_utf8(res.into_iter().collect::<Vec<u8>>())
);
check!(
string_from_iter.len() == act.len(),
"lengths differ {} != {}",
string_from_iter.len(),
act.len()
);
check!(string_from_iter == act, "'{string_from_iter}' != '{act}'");
}
}
mod errors {
use assert2::{check, let_assert};
use token_string::{TkStrError, TokenString};
#[test]
fn too_long() {
let too_big = token_string::MAX_LENGTH + 10;
let_assert!(
Ok(s1) = std::string::String::from_utf8(vec![b'1'; too_big])
);
let_assert!(
Err(e) = TokenString::try_from(&s1),
"this should yield an Error"
);
check!(e == TkStrError::TooBig(too_big));
}
#[test]
fn invalid_utf8() {
let s1: &[u8] = &[0xED, 0xB0, 0x80];
let_assert!(
Err(e) = TokenString::try_from(s1),
"this should yield an Error"
);
let_assert!(
TkStrError::UnicodeError(_) = e,
"this should yield an unicode error"
);
}
#[test]
fn out_of_bounds() {
let s1 = "Hello!";
#[expect(
clippy::cast_possible_truncation,
reason = "does not overflow u16"
)]
let too_big = s1.len() as u16;
let_assert!(Ok(res) = TokenString::try_from(s1));
let_assert!(Err(e) = res.get(too_big), "this must yield an error");
check!(e == TkStrError::OutOfBounds(too_big as usize));
}
#[test]
fn out_of_bounds_panic() {
let s1 = "Hello!";
#[expect(
clippy::cast_possible_truncation,
reason = "does not overflow u16"
)]
let too_big = s1.len() as u16;
let_assert!(Ok(res) = TokenString::try_from(s1));
let_assert!(
Err(panics) =
std::panic::catch_unwind(|| res.get_unchecked(too_big)),
"this should panic with an out of bounds message"
);
let_assert!(Some(msg) = panics.downcast_ref::<String>());
let exp = format!("index {too_big} out of bounds");
check!(msg == &exp);
}
}
mod properties {
use assert2::let_assert;
use proptest::prelude::*;
use token_string::TokenString;
proptest! {
#[test]
fn roundtrip(act in ".*") {
let_assert!(Ok(res) = TokenString::try_from(&act));
prop_assert!(res.len() == act.len(), "lengths differ: {} != {}",
res.len(), act.len());
prop_assert!(res == act, "{res:?} != '{act}'");
}
#[test]
fn roundtrip_bytes(s in ".*") {
let act = s.as_bytes();
let_assert!(Ok(res) = TokenString::try_from(act));
prop_assert!(res.len() == act.len(), "lengths differ: {} != {}",
res.len(), act.len());
prop_assert!(res == *act, "{res:?} != '{act:?}'");
}
#[test]
fn roundtrip_str(s in ".*") {
let act = s.as_str();
let_assert!(Ok(res) = TokenString::try_from(act));
prop_assert!(res.len() == act.len(), "lengths differ: {} != {}",
res.len(), act.len());
prop_assert!(res == *act, "{res:?} != '{act:?}'");
}
#[test]
fn roundtrip_to_string(act in ".*") {
let_assert!(Ok(res) = TokenString::try_from(&act));
prop_assert!(res.len() == act.len(), "lengths differ: {} != {}",
res.len(), act.len());
let res_ = res.to_string();
prop_assert!(res_ == *act, "{res:?} != '{act:?}'");
}
#[test]
fn roundtrip_iter_owned(act in ".*") {
let_assert!(Ok(res) = TokenString::try_from(&act));
prop_assert!(res == *act, "{res:?} != '{act:?}'");
let_assert!(
Ok(string_from_iter) =
String::from_utf8(res.into_iter().collect::<Vec<u8>>())
);
prop_assert!(
string_from_iter.len() == act.len(),
"lengths differ {} != {}",
string_from_iter.len(),
act.len()
);
prop_assert!(string_from_iter == act,
"'{string_from_iter}' != '{act}'");
}
#[test]
fn roundtrip_iter(act in ".*") {
let_assert!(Ok(res) = TokenString::try_from(&act));
prop_assert!(res == *act, "{res:?} != '{act:?}'");
let_assert!(
Ok(string_from_iter) =
String::from_utf8(res.iter().collect::<Vec<u8>>())
);
prop_assert!(
string_from_iter.len() == act.len(),
"lengths differ {} != {}",
string_from_iter.len(),
act.len()
);
prop_assert!(string_from_iter == act,
"'{string_from_iter}' != '{act}'");
}
#[test]
fn roundtrip_ref_iter(act in ".*") {
let_assert!(Ok(res) = TokenString::try_from(&act));
prop_assert!(res == act, "{res:?} != '{act:?}'");
let_assert!(
Ok(string_from_iter) =
String::from_utf8((&res).into_iter().collect::<Vec<u8>>())
);
prop_assert!(
string_from_iter.len() == act.len(),
"lengths differ {} != {}",
string_from_iter.len(),
act.len()
);
prop_assert!(string_from_iter == act,
"'{string_from_iter}' != '{act}'");
}
#[test]
fn roundtrip_char_iter(act in ".*") {
let_assert!(Ok(res) = TokenString::try_from(&act));
prop_assert!(res == *act, "{res:?} != '{act:?}'");
let string_from_iter = res.chars().collect::<String>();
prop_assert!(
string_from_iter.len() == act.len(),
"lengths differ {} != {}",
string_from_iter.len(),
act.len()
);
prop_assert!(string_from_iter == act,
"'{string_from_iter}' != '{act}'");
}
}
}