lex_core/lex/testing/
text_diff.rs1pub fn assert_text_eq(expected: &str, actual: &str) {
29 if expected == actual {
30 return;
31 }
32
33 let expected_lines: Vec<&str> = expected.lines().collect();
34 let actual_lines: Vec<&str> = actual.lines().collect();
35
36 let mut diff_lines = Vec::new();
37 let max_lines = expected_lines.len().max(actual_lines.len());
38
39 for i in 0..max_lines {
40 let expected_line = expected_lines.get(i);
41 let actual_line = actual_lines.get(i);
42
43 match (expected_line, actual_line) {
44 (Some(exp), Some(act)) if exp == act => {
45 }
47 (Some(exp), Some(act)) => {
48 diff_lines.push(format!("Line {}: MISMATCH", i + 1));
49 diff_lines.push(format!(" Expected: {exp:?}"));
50 diff_lines.push(format!(" Actual: {act:?}"));
51 }
52 (Some(exp), None) => {
53 diff_lines.push(format!("Line {}: MISSING in actual", i + 1));
54 diff_lines.push(format!(" Expected: {exp:?}"));
55 }
56 (None, Some(act)) => {
57 diff_lines.push(format!("Line {}: EXTRA in actual", i + 1));
58 diff_lines.push(format!(" Actual: {act:?}"));
59 }
60 (None, None) => unreachable!(),
61 }
62 }
63
64 if !diff_lines.is_empty() {
65 panic!(
66 "\n\nText comparison failed:\n{}\n\nExpected ({} lines):\n{}\n\nActual ({} lines):\n{}\n",
67 diff_lines.join("\n"),
68 expected_lines.len(),
69 expected,
70 actual_lines.len(),
71 actual
72 );
73 }
74}
75
76pub fn diff_text(expected: &str, actual: &str) -> Option<String> {
80 if expected == actual {
81 return None;
82 }
83
84 let expected_lines: Vec<&str> = expected.lines().collect();
85 let actual_lines: Vec<&str> = actual.lines().collect();
86
87 let mut diff_lines = Vec::new();
88 let max_lines = expected_lines.len().max(actual_lines.len());
89
90 for i in 0..max_lines {
91 let expected_line = expected_lines.get(i);
92 let actual_line = actual_lines.get(i);
93
94 match (expected_line, actual_line) {
95 (Some(exp), Some(act)) if exp == act => {
96 }
98 (Some(exp), Some(act)) => {
99 diff_lines.push(format!("Line {}: MISMATCH", i + 1));
100 diff_lines.push(format!(" Expected: {exp:?}"));
101 diff_lines.push(format!(" Actual: {act:?}"));
102 }
103 (Some(exp), None) => {
104 diff_lines.push(format!("Line {}: MISSING in actual", i + 1));
105 diff_lines.push(format!(" Expected: {exp:?}"));
106 }
107 (None, Some(act)) => {
108 diff_lines.push(format!("Line {}: EXTRA in actual", i + 1));
109 diff_lines.push(format!(" Actual: {act:?}"));
110 }
111 (None, None) => unreachable!(),
112 }
113 }
114
115 if diff_lines.is_empty() {
116 None
117 } else {
118 Some(format!(
119 "Text differs:\n{}\n\nExpected ({} lines):\n{}\n\nActual ({} lines):\n{}",
120 diff_lines.join("\n"),
121 expected_lines.len(),
122 expected,
123 actual_lines.len(),
124 actual
125 ))
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_assert_text_eq_identical() {
135 assert_text_eq("hello\nworld", "hello\nworld");
136 }
137
138 #[test]
139 fn test_assert_text_eq_empty() {
140 assert_text_eq("", "");
141 }
142
143 #[test]
144 #[should_panic(expected = "Text comparison failed")]
145 fn test_assert_text_eq_different() {
146 assert_text_eq("hello\nworld", "hello\nplanet");
147 }
148
149 #[test]
150 #[should_panic(expected = "MISSING in actual")]
151 fn test_assert_text_eq_missing_line() {
152 assert_text_eq("hello\nworld\nfoo", "hello\nworld");
153 }
154
155 #[test]
156 #[should_panic(expected = "EXTRA in actual")]
157 fn test_assert_text_eq_extra_line() {
158 assert_text_eq("hello\nworld", "hello\nworld\nfoo");
159 }
160
161 #[test]
162 fn test_diff_text_identical() {
163 assert_eq!(diff_text("hello\nworld", "hello\nworld"), None);
164 }
165
166 #[test]
167 fn test_diff_text_different() {
168 let result = diff_text("hello\nworld", "hello\nplanet");
169 assert!(result.is_some());
170 let diff = result.unwrap();
171 assert!(diff.contains("Line 2: MISMATCH"));
172 assert!(diff.contains("world"));
173 assert!(diff.contains("planet"));
174 }
175}