extern crate std;
use super::*;
use proptest::prelude::*;
use std::vec::Vec;
fn std_ok(b: &[u8]) -> bool {
core::str::from_utf8(b).is_ok()
}
fn with_slack(b: &[u8]) -> (Vec<u8>, Range<usize>) {
let mut v = Vec::with_capacity(b.len() + SLACK);
v.extend_from_slice(b);
v.extend_from_slice(&[0xA5; SLACK]);
(v, 0..b.len())
}
fn check(b: &[u8]) {
let want = std_ok(b);
assert_eq!(verify(b), want, "verify mismatch on {b:x?}");
let (buf, range) = with_slack(b);
let got = unsafe { verify_with_slack(&buf, range) };
assert_eq!(got, want, "verify_with_slack mismatch on {b:x?}");
}
#[test]
fn empty() {
check(b"");
}
#[test]
fn ascii_short() {
for n in 0..=40 {
check(&[b'a'; 40][..n]);
}
}
#[test]
fn ascii_long() {
check(&[b'x'; 4096]);
check(&[0x7f; 33]);
}
#[test]
fn boundary_scalars() {
for s in [
"\u{0080}", "\u{07FF}", "\u{0800}", "\u{D7FF}", "\u{E000}", "\u{FFFD}", "\u{FFFF}", "\u{10000}", "\u{7FFFF}",
"\u{80000}",
"\u{10FFFF}", ] {
check(s.as_bytes());
}
}
#[test]
fn mixed() {
check("hello, 世界! 🌍 done".as_bytes());
check("naïve façade — résumé".as_bytes());
}
#[test]
fn overlong_rejected() {
for b in [
&[0xC0, 0x80][..],
&[0xC0, 0xAF],
&[0xE0, 0x80, 0x80],
&[0xE0, 0x82, 0x80],
&[0xF0, 0x80, 0x80, 0x80],
&[0xF0, 0x80, 0xA0, 0x80],
] {
assert!(!std_ok(b));
check(b);
}
}
#[test]
fn surrogates_rejected() {
for b in [&[0xED, 0xA0, 0x80][..], &[0xED, 0xBF, 0xBF]] {
assert!(!std_ok(b));
check(b);
}
}
#[test]
fn out_of_range_rejected() {
check(&[0xF4, 0x90, 0x80, 0x80]);
check(&[0xF5, 0x80, 0x80, 0x80]);
}
#[test]
fn truncated_rejected() {
for b in [
&[0xC2][..],
&[0xE2, 0x82],
&[0xF0, 0x9F, 0x98],
&[b'a', 0xE2, 0x82],
] {
assert!(!std_ok(b));
check(b);
}
}
#[test]
fn bad_continuation_rejected() {
for b in [
&[0x80][..],
&[0xBF],
&[0xC2, 0x20],
&[0xE2, 0x82, 0x20],
&[0xE2, 0x20, 0xAC],
&[0xF0, 0x9F, 0x20, 0x80],
] {
assert!(!std_ok(b));
check(b);
}
}
#[test]
fn lone_lead_bytes_rejected() {
check(&[0xFE]);
check(&[0xFF]);
check(&[0xF8, 0x80, 0x80, 0x80, 0x80]);
}
#[test]
fn straddle_chunk_boundary() {
let mut v = std::vec![b'a'; 7];
v.extend_from_slice("€".as_bytes());
check(&v);
let mut v = std::vec![b'a'; 7];
v.extend_from_slice(&[0xE2, 0x82, 0x20]);
check(&v);
}
#[test]
fn slack_garbage_is_masked() {
let mut buf = std::vec![b'a', b'b', b'c'];
buf.extend_from_slice(&[0xFF; SLACK]);
assert!(unsafe { verify_with_slack(&buf, 0..3) });
}
#[cfg(not(miri))] proptest! {
#![proptest_config(ProptestConfig::with_cases(2048))]
#[test]
fn prop_matches_std_on_random_bytes(b in proptest::collection::vec(any::<u8>(), 0..512)) {
check(&b);
}
#[test]
fn prop_accepts_all_valid_utf8(s in "\\PC{0,256}") {
check(s.as_bytes());
}
#[test]
fn prop_range_offset(
prefix in proptest::collection::vec(any::<u8>(), 0..32),
body in proptest::collection::vec(any::<u8>(), 0..128),
) {
let want = std_ok(&body);
let mut buf = prefix;
let start = buf.len();
buf.extend_from_slice(&body);
let end = buf.len();
buf.extend_from_slice(&[0x5A; SLACK]);
let got = unsafe { verify_with_slack(&buf, start..end) };
prop_assert_eq!(got, want);
}
}