1c67d6573Sopenharmony_ciuse regex::internal::ExecBuilder;
2c67d6573Sopenharmony_ci
3c67d6573Sopenharmony_ci/// Given a regex, check if all of the backends produce the same
4c67d6573Sopenharmony_ci/// results on a number of different inputs.
5c67d6573Sopenharmony_ci///
6c67d6573Sopenharmony_ci/// For now this just throws quickcheck at the problem, which
7c67d6573Sopenharmony_ci/// is not very good because it only really tests half of the
8c67d6573Sopenharmony_ci/// problem space. It is pretty unlikely that a random string
9c67d6573Sopenharmony_ci/// will match any given regex, so this will probably just
10c67d6573Sopenharmony_ci/// be checking that the different backends fail in the same
11c67d6573Sopenharmony_ci/// way. This is still worthwhile to test, but is definitely not
12c67d6573Sopenharmony_ci/// the whole story.
13c67d6573Sopenharmony_ci///
14c67d6573Sopenharmony_ci/// TODO(ethan): In order to cover the other half of the problem
15c67d6573Sopenharmony_ci/// space, we should generate a random matching string by inspecting
16c67d6573Sopenharmony_ci/// the AST of the input regex. The right way to do this probably
17c67d6573Sopenharmony_ci/// involves adding a custom Arbitrary instance around a couple
18c67d6573Sopenharmony_ci/// of newtypes. That way we can respect the quickcheck size hinting
19c67d6573Sopenharmony_ci/// and shrinking and whatnot.
20c67d6573Sopenharmony_cipub fn backends_are_consistent(re: &str) -> Result<u64, String> {
21c67d6573Sopenharmony_ci    let standard_backends = vec![
22c67d6573Sopenharmony_ci        (
23c67d6573Sopenharmony_ci            "bounded_backtracking_re",
24c67d6573Sopenharmony_ci            ExecBuilder::new(re)
25c67d6573Sopenharmony_ci                .bounded_backtracking()
26c67d6573Sopenharmony_ci                .build()
27c67d6573Sopenharmony_ci                .map(|exec| exec.into_regex())
28c67d6573Sopenharmony_ci                .map_err(|err| format!("{}", err))?,
29c67d6573Sopenharmony_ci        ),
30c67d6573Sopenharmony_ci        (
31c67d6573Sopenharmony_ci            "pikevm_re",
32c67d6573Sopenharmony_ci            ExecBuilder::new(re)
33c67d6573Sopenharmony_ci                .nfa()
34c67d6573Sopenharmony_ci                .build()
35c67d6573Sopenharmony_ci                .map(|exec| exec.into_regex())
36c67d6573Sopenharmony_ci                .map_err(|err| format!("{}", err))?,
37c67d6573Sopenharmony_ci        ),
38c67d6573Sopenharmony_ci        (
39c67d6573Sopenharmony_ci            "default_re",
40c67d6573Sopenharmony_ci            ExecBuilder::new(re)
41c67d6573Sopenharmony_ci                .build()
42c67d6573Sopenharmony_ci                .map(|exec| exec.into_regex())
43c67d6573Sopenharmony_ci                .map_err(|err| format!("{}", err))?,
44c67d6573Sopenharmony_ci        ),
45c67d6573Sopenharmony_ci    ];
46c67d6573Sopenharmony_ci
47c67d6573Sopenharmony_ci    let utf8bytes_backends = vec![
48c67d6573Sopenharmony_ci        (
49c67d6573Sopenharmony_ci            "bounded_backtracking_utf8bytes_re",
50c67d6573Sopenharmony_ci            ExecBuilder::new(re)
51c67d6573Sopenharmony_ci                .bounded_backtracking()
52c67d6573Sopenharmony_ci                .bytes(true)
53c67d6573Sopenharmony_ci                .build()
54c67d6573Sopenharmony_ci                .map(|exec| exec.into_regex())
55c67d6573Sopenharmony_ci                .map_err(|err| format!("{}", err))?,
56c67d6573Sopenharmony_ci        ),
57c67d6573Sopenharmony_ci        (
58c67d6573Sopenharmony_ci            "pikevm_utf8bytes_re",
59c67d6573Sopenharmony_ci            ExecBuilder::new(re)
60c67d6573Sopenharmony_ci                .nfa()
61c67d6573Sopenharmony_ci                .bytes(true)
62c67d6573Sopenharmony_ci                .build()
63c67d6573Sopenharmony_ci                .map(|exec| exec.into_regex())
64c67d6573Sopenharmony_ci                .map_err(|err| format!("{}", err))?,
65c67d6573Sopenharmony_ci        ),
66c67d6573Sopenharmony_ci        (
67c67d6573Sopenharmony_ci            "default_utf8bytes_re",
68c67d6573Sopenharmony_ci            ExecBuilder::new(re)
69c67d6573Sopenharmony_ci                .bytes(true)
70c67d6573Sopenharmony_ci                .build()
71c67d6573Sopenharmony_ci                .map(|exec| exec.into_regex())
72c67d6573Sopenharmony_ci                .map_err(|err| format!("{}", err))?,
73c67d6573Sopenharmony_ci        ),
74c67d6573Sopenharmony_ci    ];
75c67d6573Sopenharmony_ci
76c67d6573Sopenharmony_ci    let bytes_backends = vec![
77c67d6573Sopenharmony_ci        (
78c67d6573Sopenharmony_ci            "bounded_backtracking_bytes_re",
79c67d6573Sopenharmony_ci            ExecBuilder::new(re)
80c67d6573Sopenharmony_ci                .bounded_backtracking()
81c67d6573Sopenharmony_ci                .only_utf8(false)
82c67d6573Sopenharmony_ci                .build()
83c67d6573Sopenharmony_ci                .map(|exec| exec.into_byte_regex())
84c67d6573Sopenharmony_ci                .map_err(|err| format!("{}", err))?,
85c67d6573Sopenharmony_ci        ),
86c67d6573Sopenharmony_ci        (
87c67d6573Sopenharmony_ci            "pikevm_bytes_re",
88c67d6573Sopenharmony_ci            ExecBuilder::new(re)
89c67d6573Sopenharmony_ci                .nfa()
90c67d6573Sopenharmony_ci                .only_utf8(false)
91c67d6573Sopenharmony_ci                .build()
92c67d6573Sopenharmony_ci                .map(|exec| exec.into_byte_regex())
93c67d6573Sopenharmony_ci                .map_err(|err| format!("{}", err))?,
94c67d6573Sopenharmony_ci        ),
95c67d6573Sopenharmony_ci        (
96c67d6573Sopenharmony_ci            "default_bytes_re",
97c67d6573Sopenharmony_ci            ExecBuilder::new(re)
98c67d6573Sopenharmony_ci                .only_utf8(false)
99c67d6573Sopenharmony_ci                .build()
100c67d6573Sopenharmony_ci                .map(|exec| exec.into_byte_regex())
101c67d6573Sopenharmony_ci                .map_err(|err| format!("{}", err))?,
102c67d6573Sopenharmony_ci        ),
103c67d6573Sopenharmony_ci    ];
104c67d6573Sopenharmony_ci
105c67d6573Sopenharmony_ci    Ok(string_checker::check_backends(&standard_backends)?
106c67d6573Sopenharmony_ci        + string_checker::check_backends(&utf8bytes_backends)?
107c67d6573Sopenharmony_ci        + bytes_checker::check_backends(&bytes_backends)?)
108c67d6573Sopenharmony_ci}
109c67d6573Sopenharmony_ci
110c67d6573Sopenharmony_ci//
111c67d6573Sopenharmony_ci// A consistency checker parameterized by the input type (&str or &[u8]).
112c67d6573Sopenharmony_ci//
113c67d6573Sopenharmony_ci
114c67d6573Sopenharmony_cimacro_rules! checker {
115c67d6573Sopenharmony_ci    ($module_name:ident, $regex_type:path, $mk_input:expr) => {
116c67d6573Sopenharmony_ci        mod $module_name {
117c67d6573Sopenharmony_ci            use quickcheck;
118c67d6573Sopenharmony_ci            use quickcheck::{Arbitrary, TestResult};
119c67d6573Sopenharmony_ci
120c67d6573Sopenharmony_ci            pub fn check_backends(
121c67d6573Sopenharmony_ci                backends: &[(&str, $regex_type)],
122c67d6573Sopenharmony_ci            ) -> Result<u64, String> {
123c67d6573Sopenharmony_ci                let mut total_passed = 0;
124c67d6573Sopenharmony_ci                for regex in backends[1..].iter() {
125c67d6573Sopenharmony_ci                    total_passed += quickcheck_regex_eq(&backends[0], regex)?;
126c67d6573Sopenharmony_ci                }
127c67d6573Sopenharmony_ci
128c67d6573Sopenharmony_ci                Ok(total_passed)
129c67d6573Sopenharmony_ci            }
130c67d6573Sopenharmony_ci
131c67d6573Sopenharmony_ci            fn quickcheck_regex_eq(
132c67d6573Sopenharmony_ci                &(name1, ref re1): &(&str, $regex_type),
133c67d6573Sopenharmony_ci                &(name2, ref re2): &(&str, $regex_type),
134c67d6573Sopenharmony_ci            ) -> Result<u64, String> {
135c67d6573Sopenharmony_ci                quickcheck::QuickCheck::new()
136c67d6573Sopenharmony_ci                    .quicktest(RegexEqualityTest::new(
137c67d6573Sopenharmony_ci                        re1.clone(),
138c67d6573Sopenharmony_ci                        re2.clone(),
139c67d6573Sopenharmony_ci                    ))
140c67d6573Sopenharmony_ci                    .map_err(|err| {
141c67d6573Sopenharmony_ci                        format!(
142c67d6573Sopenharmony_ci                            "{}(/{}/) and {}(/{}/) are inconsistent.\
143c67d6573Sopenharmony_ci                             QuickCheck Err: {:?}",
144c67d6573Sopenharmony_ci                            name1, re1, name2, re2, err
145c67d6573Sopenharmony_ci                        )
146c67d6573Sopenharmony_ci                    })
147c67d6573Sopenharmony_ci            }
148c67d6573Sopenharmony_ci
149c67d6573Sopenharmony_ci            struct RegexEqualityTest {
150c67d6573Sopenharmony_ci                re1: $regex_type,
151c67d6573Sopenharmony_ci                re2: $regex_type,
152c67d6573Sopenharmony_ci            }
153c67d6573Sopenharmony_ci            impl RegexEqualityTest {
154c67d6573Sopenharmony_ci                fn new(re1: $regex_type, re2: $regex_type) -> Self {
155c67d6573Sopenharmony_ci                    RegexEqualityTest { re1: re1, re2: re2 }
156c67d6573Sopenharmony_ci                }
157c67d6573Sopenharmony_ci            }
158c67d6573Sopenharmony_ci
159c67d6573Sopenharmony_ci            impl quickcheck::Testable for RegexEqualityTest {
160c67d6573Sopenharmony_ci                fn result(&self, gen: &mut quickcheck::Gen) -> TestResult {
161c67d6573Sopenharmony_ci                    let input = $mk_input(gen);
162c67d6573Sopenharmony_ci                    let input = &input;
163c67d6573Sopenharmony_ci
164c67d6573Sopenharmony_ci                    if self.re1.find(&input) != self.re2.find(input) {
165c67d6573Sopenharmony_ci                        return TestResult::error(format!(
166c67d6573Sopenharmony_ci                            "find mismatch input={:?}",
167c67d6573Sopenharmony_ci                            input
168c67d6573Sopenharmony_ci                        ));
169c67d6573Sopenharmony_ci                    }
170c67d6573Sopenharmony_ci
171c67d6573Sopenharmony_ci                    let cap1 = self.re1.captures(input);
172c67d6573Sopenharmony_ci                    let cap2 = self.re2.captures(input);
173c67d6573Sopenharmony_ci                    match (cap1, cap2) {
174c67d6573Sopenharmony_ci                        (None, None) => {}
175c67d6573Sopenharmony_ci                        (Some(cap1), Some(cap2)) => {
176c67d6573Sopenharmony_ci                            for (c1, c2) in cap1.iter().zip(cap2.iter()) {
177c67d6573Sopenharmony_ci                                if c1 != c2 {
178c67d6573Sopenharmony_ci                                    return TestResult::error(format!(
179c67d6573Sopenharmony_ci                                        "captures mismatch input={:?}",
180c67d6573Sopenharmony_ci                                        input
181c67d6573Sopenharmony_ci                                    ));
182c67d6573Sopenharmony_ci                                }
183c67d6573Sopenharmony_ci                            }
184c67d6573Sopenharmony_ci                        }
185c67d6573Sopenharmony_ci                        _ => {
186c67d6573Sopenharmony_ci                            return TestResult::error(format!(
187c67d6573Sopenharmony_ci                                "captures mismatch input={:?}",
188c67d6573Sopenharmony_ci                                input
189c67d6573Sopenharmony_ci                            ))
190c67d6573Sopenharmony_ci                        }
191c67d6573Sopenharmony_ci                    }
192c67d6573Sopenharmony_ci
193c67d6573Sopenharmony_ci                    let fi1 = self.re1.find_iter(input);
194c67d6573Sopenharmony_ci                    let fi2 = self.re2.find_iter(input);
195c67d6573Sopenharmony_ci                    for (m1, m2) in fi1.zip(fi2) {
196c67d6573Sopenharmony_ci                        if m1 != m2 {
197c67d6573Sopenharmony_ci                            return TestResult::error(format!(
198c67d6573Sopenharmony_ci                                "find_iter mismatch input={:?}",
199c67d6573Sopenharmony_ci                                input
200c67d6573Sopenharmony_ci                            ));
201c67d6573Sopenharmony_ci                        }
202c67d6573Sopenharmony_ci                    }
203c67d6573Sopenharmony_ci
204c67d6573Sopenharmony_ci                    let ci1 = self.re1.captures_iter(input);
205c67d6573Sopenharmony_ci                    let ci2 = self.re2.captures_iter(input);
206c67d6573Sopenharmony_ci                    for (cap1, cap2) in ci1.zip(ci2) {
207c67d6573Sopenharmony_ci                        for (c1, c2) in cap1.iter().zip(cap2.iter()) {
208c67d6573Sopenharmony_ci                            if c1 != c2 {
209c67d6573Sopenharmony_ci                                return TestResult::error(format!(
210c67d6573Sopenharmony_ci                                    "captures_iter mismatch input={:?}",
211c67d6573Sopenharmony_ci                                    input
212c67d6573Sopenharmony_ci                                ));
213c67d6573Sopenharmony_ci                            }
214c67d6573Sopenharmony_ci                        }
215c67d6573Sopenharmony_ci                    }
216c67d6573Sopenharmony_ci
217c67d6573Sopenharmony_ci                    let s1 = self.re1.split(input);
218c67d6573Sopenharmony_ci                    let s2 = self.re2.split(input);
219c67d6573Sopenharmony_ci                    for (chunk1, chunk2) in s1.zip(s2) {
220c67d6573Sopenharmony_ci                        if chunk1 != chunk2 {
221c67d6573Sopenharmony_ci                            return TestResult::error(format!(
222c67d6573Sopenharmony_ci                                "split mismatch input={:?}",
223c67d6573Sopenharmony_ci                                input
224c67d6573Sopenharmony_ci                            ));
225c67d6573Sopenharmony_ci                        }
226c67d6573Sopenharmony_ci                    }
227c67d6573Sopenharmony_ci
228c67d6573Sopenharmony_ci                    TestResult::from_bool(true)
229c67d6573Sopenharmony_ci                }
230c67d6573Sopenharmony_ci            }
231c67d6573Sopenharmony_ci        } // mod
232c67d6573Sopenharmony_ci    }; // rule case
233c67d6573Sopenharmony_ci} // macro_rules!
234c67d6573Sopenharmony_ci
235c67d6573Sopenharmony_cichecker!(string_checker, ::regex::Regex, |gen| String::arbitrary(gen));
236c67d6573Sopenharmony_cichecker!(bytes_checker, ::regex::bytes::Regex, |gen| Vec::<u8>::arbitrary(
237c67d6573Sopenharmony_ci    gen
238c67d6573Sopenharmony_ci));
239