1pub const NAME: &str = "甲哨 Sentri";
22
23pub const TAGLINE: &str = "只报告,不拦截";
25
26pub const ASCII: &str = r#" (o)(o)
28 ⌕┬─────────┬! 甲哨 Sentri
29 │ · · · · │ 只报告,不拦截
30 └──┬───┬──┘ 32 detectors / 4 categories
31 / \ deps = regex ×1"#;
32
33pub const SVG: &str = include_str!("../docs/pet.svg");
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39 use crate::AttackCategory;
40 use regex::Regex;
41
42 #[test]
43 fn svg_is_bundled_whole() {
44 assert!(SVG.starts_with("<svg"), "svg 头部: {:?}", &SVG[..40]);
45 assert!(SVG.trim_end().ends_with("</svg>"));
46 assert!(SVG.contains(r#"viewBox="0 0 480 460""#), "viewBox 变了");
47 }
48
49 #[test]
60 fn plates_account_for_every_detector() {
61 let row =
62 Regex::new(r"<!-- (\w+): (\d+) detectors, (\d+) plates -->\s*((?:<circle[^>]*/>\s*)+)")
63 .unwrap();
64 let rows: Vec<(String, usize, usize, usize)> = row
65 .captures_iter(SVG)
66 .map(|c| {
67 (
68 c[1].to_string(),
69 c[2].parse().unwrap(), c[3].parse().unwrap(), c[4].matches("<circle").count(), )
73 })
74 .collect();
75
76 let names: Vec<&str> = rows.iter().map(|(name, ..)| name.as_str()).collect();
77 for category in [
78 AttackCategory::Injection,
79 AttackCategory::Protocol,
80 AttackCategory::Data,
81 AttackCategory::File,
82 ] {
83 assert!(
84 names.contains(&category.to_string().as_str()),
85 "形象里少了 `{category}` 那一行甲片(注记格式:`<!-- 类别: N detectors, M plates -->`)"
86 );
87 }
88 assert_eq!(names.len(), 4, "四行甲片 = 四大类,多出来的行: {names:?}");
89
90 for (name, _, declared_plates, drawn) in &rows {
91 assert_eq!(
92 declared_plates, drawn,
93 "`{name}` 那行:注记声明 {declared_plates} 片,实际画了 {drawn} 片"
94 );
95 }
96 let declared: usize = rows.iter().map(|(_, detectors, ..)| detectors).sum();
97 let drawn: usize = rows.iter().map(|(_, _, _, plates)| plates).sum();
98 assert_eq!(declared, 32, "注记里的检测器总数");
99 assert_eq!(drawn, 32, "画出来的甲片总数 —— 32 片甲 = 32 个检测器");
100 }
101
102 #[test]
103 fn ascii_and_name_are_not_empty() {
104 assert!(!NAME.is_empty() && !TAGLINE.is_empty());
105 assert!(ASCII.lines().count() >= 5);
106 }
107}