use std::sync::Arc;
use std::thread;
use simd_normalizer::{
CaseFoldMode, IsNormalized, MatchingOptions, NfcNormalizer, NfdNormalizer, NfkcNormalizer,
NfkdNormalizer, UnicodeNormalization, nfc, nfd, nfkc, nfkd,
};
#[test]
fn concurrent_normalization_8_threads() {
let inputs: &[(&str, &str)] = &[
(
"ascii",
"The quick brown fox jumps over the lazy dog. 0123456789!",
),
(
"cjk",
"\u{4E16}\u{754C}\u{4F60}\u{597D}\u{6D4B}\u{8BD5}\u{6587}\u{672C}",
),
(
"emoji",
"\u{1F600}\u{1F60D}\u{1F389}\u{1F680}\u{2764}\u{FE0F}\u{1F308}\u{1F3B6}",
),
(
"combining",
"A\u{0300}\u{0301}\u{0302}e\u{0308}\u{0304}o\u{0327}\u{0328}\u{030A}",
),
(
"hangul",
"\u{AC00}\u{B098}\u{B2E4}\u{B77C}\u{B9C8}\u{BC14}\u{C0AC}\u{C544}\u{C790}\u{CC28}",
),
(
"arabic",
"\u{0627}\u{0644}\u{0639}\u{0631}\u{0628}\u{064A}\u{0629}\u{0020}\u{0641}\u{062D}\u{0635}",
),
(
"mixed",
"Hello\u{4E16}\u{754C}\u{0410}\u{043B}\u{043B}\u{043E}\u{3053}\u{3093}\u{306B}\u{3061}\u{306F}",
),
(
"long",
&"A\u{0300}\u{0301}B\u{0327}C\u{030A}D\u{0308}E\u{0303}F\u{0304}G\u{0306}H\u{030C}"
.repeat(50),
),
];
let expected: Vec<(String, String, String, String)> = inputs
.iter()
.map(|(_, input)| {
(
nfc().normalize(input).into_owned(),
nfd().normalize(input).into_owned(),
nfkc().normalize(input).into_owned(),
nfkd().normalize(input).into_owned(),
)
})
.collect();
let inputs_arc: Arc<Vec<(String, String, String, String)>> = Arc::new(expected);
let raw_inputs: Arc<Vec<String>> =
Arc::new(inputs.iter().map(|(_, s)| s.to_string()).collect());
let handles: Vec<_> = (0..8)
.map(|i| {
let expected = Arc::clone(&inputs_arc);
let raw = Arc::clone(&raw_inputs);
thread::spawn(move || {
for _ in 0..1000 {
let input = &raw[i];
let (exp_nfc, exp_nfd, exp_nfkc, exp_nfkd) = &expected[i];
let got_nfc = nfc().normalize(input);
let got_nfd = nfd().normalize(input);
let got_nfkc = nfkc().normalize(input);
let got_nfkd = nfkd().normalize(input);
assert_eq!(&*got_nfc, exp_nfc.as_str());
assert_eq!(&*got_nfd, exp_nfd.as_str());
assert_eq!(&*got_nfkc, exp_nfkc.as_str());
assert_eq!(&*got_nfkd, exp_nfkd.as_str());
}
})
})
.collect();
for h in handles {
h.join()
.expect("thread panicked during concurrent normalization");
}
}
#[test]
fn concurrent_is_normalized_and_normalize() {
let inputs: &[&str] = &[
"hello world",
"\u{00C5}\u{03A9}",
"A\u{0300}\u{0301}B\u{0327}",
"\u{AC00}\u{B098}\u{B2E4}",
"\u{FB01}\u{FB02}\u{2126}",
"\u{1F600}\u{200D}\u{1F525}",
];
let shared: Arc<Vec<String>> = Arc::new(inputs.iter().map(|s| s.to_string()).collect());
let mut handles = Vec::new();
for _ in 0..4 {
let data = Arc::clone(&shared);
handles.push(thread::spawn(move || {
for _ in 0..500 {
for input in data.iter() {
let _ = nfc().normalize(input.as_str());
let _ = nfd().normalize(input.as_str());
let _ = nfkc().normalize(input.as_str());
let _ = nfkd().normalize(input.as_str());
}
}
}));
}
for _ in 0..4 {
let data = Arc::clone(&shared);
handles.push(thread::spawn(move || {
for _ in 0..500 {
for input in data.iter() {
let _ = nfc().is_normalized(input.as_str());
let _ = nfd().is_normalized(input.as_str());
let _ = nfkc().is_normalized(input.as_str());
let _ = nfkd().is_normalized(input.as_str());
}
}
}));
}
for h in handles {
h.join()
.expect("thread panicked during concurrent is_normalized + normalize");
}
}
#[test]
fn send_impls() {
fn assert_send<T: Send>() {}
assert_send::<NfcNormalizer>();
assert_send::<NfdNormalizer>();
assert_send::<NfkcNormalizer>();
assert_send::<NfkdNormalizer>();
assert_send::<IsNormalized>();
assert_send::<CaseFoldMode>();
assert_send::<MatchingOptions>();
}
#[test]
fn sync_impls() {
fn assert_sync<T: Sync>() {}
assert_sync::<NfcNormalizer>();
assert_sync::<NfdNormalizer>();
assert_sync::<NfkcNormalizer>();
assert_sync::<NfkdNormalizer>();
assert_sync::<IsNormalized>();
assert_sync::<CaseFoldMode>();
assert_sync::<MatchingOptions>();
}
#[test]
fn send_and_sync_combined() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<NfcNormalizer>();
assert_send_sync::<NfdNormalizer>();
assert_send_sync::<NfkcNormalizer>();
assert_send_sync::<NfkdNormalizer>();
assert_send_sync::<IsNormalized>();
assert_send_sync::<CaseFoldMode>();
assert_send_sync::<MatchingOptions>();
}
#[test]
fn nostd_alloc_compiles() {
}
#[test]
fn concurrent_trait_methods() {
let input = Arc::new(String::from("\u{00C5}\u{03A9}\u{FB01}A\u{0300}\u{0301}"));
let expected_nfc = input.as_str().nfc().into_owned();
let expected_nfd = input.as_str().nfd().into_owned();
let expected_nfkc = input.as_str().nfkc().into_owned();
let expected_nfkd = input.as_str().nfkd().into_owned();
let handles: Vec<_> = (0..4)
.map(|_| {
let inp = Arc::clone(&input);
let exp_nfc = expected_nfc.clone();
let exp_nfd = expected_nfd.clone();
let exp_nfkc = expected_nfkc.clone();
let exp_nfkd = expected_nfkd.clone();
thread::spawn(move || {
for _ in 0..500 {
assert_eq!(&*inp.as_str().nfc(), exp_nfc.as_str());
assert_eq!(&*inp.as_str().nfd(), exp_nfd.as_str());
assert_eq!(&*inp.as_str().nfkc(), exp_nfkc.as_str());
assert_eq!(&*inp.as_str().nfkd(), exp_nfkd.as_str());
}
})
})
.collect();
for h in handles {
h.join()
.expect("thread panicked during concurrent trait method calls");
}
}