pub const NAME: &str = "甲哨 Sentri";
pub const TAGLINE: &str = "只报告,不拦截";
pub const ASCII: &str = r#" (o)(o)
⌕┬─────────┬! 甲哨 Sentri
│ · · · · │ 只报告,不拦截
└──┬───┬──┘ 32 detectors / 4 categories
/ \ deps = regex ×1"#;
pub const SVG: &str = include_str!("../docs/pet.svg");
#[cfg(test)]
mod tests {
use super::*;
use crate::AttackCategory;
use regex::Regex;
#[test]
fn svg_is_bundled_whole() {
assert!(SVG.starts_with("<svg"), "svg 头部: {:?}", &SVG[..40]);
assert!(SVG.trim_end().ends_with("</svg>"));
assert!(SVG.contains(r#"viewBox="0 0 480 460""#), "viewBox 变了");
}
#[test]
fn plates_account_for_every_detector() {
let row =
Regex::new(r"<!-- (\w+): (\d+) detectors, (\d+) plates -->\s*((?:<circle[^>]*/>\s*)+)")
.unwrap();
let rows: Vec<(String, usize, usize, usize)> = row
.captures_iter(SVG)
.map(|c| {
(
c[1].to_string(),
c[2].parse().unwrap(), c[3].parse().unwrap(), c[4].matches("<circle").count(), )
})
.collect();
let names: Vec<&str> = rows.iter().map(|(name, ..)| name.as_str()).collect();
for category in [
AttackCategory::Injection,
AttackCategory::Protocol,
AttackCategory::Data,
AttackCategory::File,
] {
assert!(
names.contains(&category.to_string().as_str()),
"形象里少了 `{category}` 那一行甲片(注记格式:`<!-- 类别: N detectors, M plates -->`)"
);
}
assert_eq!(names.len(), 4, "四行甲片 = 四大类,多出来的行: {names:?}");
for (name, _, declared_plates, drawn) in &rows {
assert_eq!(
declared_plates, drawn,
"`{name}` 那行:注记声明 {declared_plates} 片,实际画了 {drawn} 片"
);
}
let declared: usize = rows.iter().map(|(_, detectors, ..)| detectors).sum();
let drawn: usize = rows.iter().map(|(_, _, _, plates)| plates).sum();
assert_eq!(declared, 32, "注记里的检测器总数");
assert_eq!(drawn, 32, "画出来的甲片总数 —— 32 片甲 = 32 个检测器");
}
#[test]
fn ascii_and_name_are_not_empty() {
assert!(!NAME.is_empty() && !TAGLINE.is_empty());
assert!(ASCII.lines().count() >= 5);
}
}