tor_netdoc/
test_support.rs1#![allow(clippy::bool_assert_comparison)]
4#![allow(clippy::clone_on_copy)]
5#![allow(clippy::dbg_macro)]
6#![allow(clippy::mixed_attributes_style)]
7#![allow(clippy::print_stderr)]
8#![allow(clippy::print_stdout)]
9#![allow(clippy::single_char_pattern)]
10#![allow(clippy::unwrap_used)]
11#![allow(clippy::unchecked_time_subtraction)]
12#![allow(clippy::useless_vec)]
13#![allow(clippy::needless_pass_by_value)]
14#![allow(clippy::string_slice)] use crate::parse2::{NetdocParseable, NetdocParseableFields, ParseInput, parse_netdoc};
18use derive_deftly::Deftly;
19use itertools::chain;
20use std::any::{Any, TypeId};
21use std::collections::HashMap;
22use std::fmt::Display;
23use std::sync::Mutex;
24
25#[macro_export]
29macro_rules! assert_eq_or_diff {
30 { $a:expr, $b:expr $(,)? } => {
31 assert_eq_or_diff!($a, $b, "")
32 };
33 { $a:expr, $b:expr , $($message:tt)*} => {
34 $crate::assert_eq_or_diff(
35 &$a,
36 stringify!($a),
37 &$b,
38 stringify!($b),
39 &format_args!($($message)*),
40 )
41 };
42}
43
44pub fn assert_eq_or_diff(a: &str, a_what: &str, b: &str, b_what: &str, message: &dyn Display) {
48 use imara_diff::{Algorithm, BasicLineDiffPrinter, Diff, InternedInput, UnifiedDiffConfig};
49
50 if a == b {
51 return;
52 }
53 let input = InternedInput::new(a, b);
54 let mut diff = Diff::compute(Algorithm::Histogram, &input);
55 diff.postprocess_lines(&input);
56 panic!(
57 "===== document {a_what} =====
59{a}
60===== document {b_what} =====
61{b}
62===== diff ====
63{}
64===== documents differ: {a_what} != {b_what} =====
65{message}
66",
67 diff.unified_diff(
68 &BasicLineDiffPrinter(&input.interner),
69 UnifiedDiffConfig::default(),
70 &input,
71 ),
72 );
73}
74
75pub fn regsub(update: &mut String, re: &str, repl: impl regex::Replacer) {
82 *update = regex::Regex::new(&format!("(?m){re}"))
83 .expect(re)
84 .replace_all(update, repl)
85 .to_string();
86}
87
88pub fn parse_testcase_from_netdoc<T: NetdocParseableFields>(input_doc: &str) -> T {
99 #[derive(Deftly)]
100 #[derive_deftly(NetdocParseable)]
101 struct Document<T: NetdocParseableFields> {
102 #[allow(unused)]
104 parse_testcase_from_netdoc_intro: (),
105
106 #[deftly(netdoc(flatten))]
107 fields: T,
108 }
109
110 eprintln!("\n&&&&&&& input test case\n{input_doc}");
111 let doc = chain!(
112 ["parse-testcase-from-netdoc-intro\n"],
113 input_doc
114 .lines()
115 .map(|l| l.split_once('#').map(|(l, _)| l).unwrap_or(l).trim())
116 .filter(|l| !l.is_empty())
117 .flat_map(|l| [l, "\n"]),
118 )
119 .collect::<String>();
120
121 eprintln!(
122 "---- tidied \n{}----",
123 doc.split_inclusive('\n')
124 .enumerate()
126 .map(|(lno, l)| format!("| {:5} {l}", lno + 1))
127 .collect::<String>()
128 );
129
130 let pinput = ParseInput::new(&doc, "<input doc>");
131 let case: Document<T> = parse_netdoc(&pinput).expect("parse failed");
132
133 case.fields
134}
135
136pub fn parse_test_document<D>(text: &'static str) -> &'static D
147where
148 D: NetdocParseable + Sync,
149{
150 type MemoKey = (&'static str, TypeId);
152
153 static MEMO: Mutex<Option<HashMap<MemoKey, &'static (dyn Any + Sync)>>> = Mutex::new(None);
155
156 let mut memo = MEMO.lock().unwrap_or_else(|poison| poison.into_inner());
159 let key = (text, TypeId::of::<D>());
160
161 let map_entry = memo.get_or_insert_default().entry(key).or_insert_with(|| {
162 let mut input = ParseInput::new(text, "<test document>");
163
164 #[cfg(feature = "retain-unknown")]
165 input.retain_unknown_values();
166
167 let doc = parse_netdoc::<D>(&input).expect(text);
168
169 Box::leak(Box::new(doc))
170 });
171
172 (*map_entry as &dyn Any)
173 .downcast_ref::<D>()
174 .expect("wrong type is impossible")
175}