use super::*;
#[test]
fn escape_specials() {
assert_eq!(escape("<test>"), "<test>");
assert_eq!(escape("a & b"), "a & b");
assert_eq!(escape("\"quote'"), ""quote'");
assert!(matches!(escape("plain"), Cow::Borrowed("plain")));
}
#[test]
fn partial_and_minimal() {
assert_eq!(partial_escape("<a> & \"x\""), "<a> & \"x\"");
assert_eq!(minimal_escape("<a> & \"x\""), "<a> & \"x\"");
}
#[test]
fn unescape_named() -> Result<(), EscapeError> {
assert_eq!(unescape("<test>")?, "<test>");
assert_eq!(unescape("a & b")?, "a & b");
assert_eq!(unescape(""q'")?, "\"q'");
assert!(matches!(unescape("plain")?, Cow::Borrowed("plain")));
Ok(())
}
#[test]
fn unescape_numeric() -> Result<(), EscapeError> {
assert_eq!(unescape("0")?, "0");
assert_eq!(unescape("0")?, "0");
assert_eq!(unescape("😀")?, "\u{1F600}");
assert_eq!(unescape("é")?, "é");
Ok(())
}
#[test]
fn escape_unescape_roundtrip() -> Result<(), EscapeError> {
for original in [
"plain text",
"<tag attr=\"v\">body & 'more'</tag>",
"ampersand & <less> >greater<",
"quotes \" and ' mixed",
] {
let escaped = escape(original);
assert_eq!(
unescape(&escaped)?,
original,
"roundtrip failed for {original:?}"
);
}
Ok(())
}
#[test]
fn unescape_invalid_refs() {
assert_eq!(unescape("<"), Err(EscapeError::UnterminatedEntity(0..3)));
assert_eq!(
unescape("&unknown;"),
Err(EscapeError::UnrecognizedEntity(1..8, "unknown".to_string()))
);
assert!(matches!(
unescape("&#xZZ;"),
Err(EscapeError::InvalidCharRef(
ParseCharRefError::InvalidNumber(_)
))
));
assert_eq!(
unescape("&#x-1;"),
Err(EscapeError::InvalidCharRef(
ParseCharRefError::UnexpectedSign
))
);
assert_eq!(
unescape("�"),
Err(EscapeError::InvalidCharRef(
ParseCharRefError::IllegalCharacter(0)
))
);
assert_eq!(
unescape("�"),
Err(EscapeError::InvalidCharRef(
ParseCharRefError::InvalidCodepoint(0xD800)
))
);
}
#[test]
fn unescape_with_override() -> Result<(), EscapeError> {
let resolver = |e: &str| match e {
"custom" => Some("VALUE"),
_ => resolve_xml_entity(e),
};
assert_eq!(unescape_with("&custom; &", resolver)?, "VALUE &");
Ok(())
}
#[test]
fn predefined_entities() {
assert_eq!(resolve_xml_entity("lt"), Some("<"));
assert_eq!(resolve_xml_entity("gt"), Some(">"));
assert_eq!(resolve_xml_entity("amp"), Some("&"));
assert_eq!(resolve_xml_entity("apos"), Some("'"));
assert_eq!(resolve_xml_entity("quot"), Some("\""));
assert_eq!(resolve_xml_entity("nbsp"), None);
}
#[cfg(not(feature = "escape-html"))]
#[test]
fn predefined_is_xml_only() {
assert_eq!(resolve_predefined_entity("lt"), Some("<"));
assert_eq!(resolve_predefined_entity("nbsp"), None);
}
#[cfg(feature = "escape-html")]
#[test]
fn predefined_is_html5() {
assert_eq!(resolve_predefined_entity("lt"), Some("<"));
assert_eq!(resolve_predefined_entity("nbsp"), Some("\u{A0}"));
assert_eq!(resolve_html5_entity("amp"), Some("&"));
assert_eq!(resolve_html5_entity("copy"), Some("\u{A9}"));
assert_eq!(resolve_html5_entity("not-an-entity"), None);
}
#[test]
fn eol_normalization_xml10() {
assert_eq!(normalize_xml10_eols("a\r\nb\rc\nd"), "a\nb\nc\nd");
assert!(matches!(
normalize_xml10_eols("no eol"),
Cow::Borrowed("no eol")
));
}
#[test]
fn eol_normalization_xml11() {
assert_eq!(
normalize_xml11_eols("a\r\nb\u{85}c\u{2028}d\re"),
"a\nb\nc\nd\ne"
);
}
#[test]
fn attribute_value_normalization() -> Result<(), EscapeError> {
assert_eq!(
normalize_xml10_attribute_value("a\tb\r\nc & d", 1, resolve_predefined_entity)?,
"a b c & d"
);
assert!(matches!(
normalize_xml10_attribute_value("plain", 1, resolve_predefined_entity)?,
Cow::Borrowed("plain")
));
Ok(())
}
#[test]
fn attribute_value_normalization_of_line_breaks() -> Result<(), EscapeError> {
let resolver = |e: &str| match e {
"charrefs" => Some("\r\n"),
"literal" => Some("\n"),
"quoted" => Some(" "),
_ => resolve_xml_entity(e),
};
assert_eq!(
normalize_xml10_attribute_value("a\r\nb", 4, resolver)?,
"a b"
);
assert_eq!(
normalize_xml10_attribute_value("a b", 4, resolver)?,
"a\r\nb"
);
assert_eq!(
normalize_xml10_attribute_value("a&charrefs;b", 4, resolver)?,
"a b"
);
assert_eq!(
normalize_xml10_attribute_value("a&literal;b", 4, resolver)?,
"a b"
);
assert_eq!(
normalize_xml10_attribute_value("a"ed;b", 4, resolver)?,
"a\rb"
);
assert_eq!(
normalize_xml11_attribute_value("a\r\u{85}b", 4, resolver)?,
"a b"
);
assert_eq!(
normalize_xml11_attribute_value("a&charrefs;b", 4, resolver)?,
"a b"
);
Ok(())
}
#[test]
fn attribute_value_nested_entities_limit() {
let resolver = |e: &str| match e {
"a" => Some("&b;"),
"b" => Some("&a;"),
_ => resolve_xml_entity(e),
};
assert_eq!(
normalize_xml10_attribute_value("&a;", 4, resolver),
Err(EscapeError::TooManyNestedEntities)
);
}