1 /*
2 * Copyright © 2019-today Peter M. Stahl pemistahl@gmail.com
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #![allow(deprecated)]
18
19 use grex::{Feature, RegExpBuilder};
20 use proptest::prelude::*;
21 use regex::{Error, Regex, RegexBuilder};
22
23 proptest! {
24 #![proptest_config(ProptestConfig::with_cases(500))]
25
26 #[test]
27 fn valid_regexes_with_default_settings(
28 test_cases in prop::collection::hash_set(".{1,10}", 1..=5)
29 ) {
30 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
31 let regexp = RegExpBuilder::from(&test_cases_vec).build();
32 prop_assert!(compile_regexp(®exp).is_ok());
33 }
34
35 #[test]
36 fn valid_regexes_with_escape_sequences(
37 test_cases in prop::collection::hash_set(".{1,10}", 1..=5)
38 ) {
39 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
40 let regexp = RegExpBuilder::from(&test_cases_vec)
41 .with_escaping_of_non_ascii_chars(false)
42 .build();
43 prop_assert!(compile_regexp(®exp).is_ok());
44 }
45
46 #[test]
47 fn valid_regexes_with_verbose_mode(
48 test_cases in prop::collection::hash_set(".{1,10}", 1..=5)
49 ) {
50 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
51 let regexp = RegExpBuilder::from(&test_cases_vec)
52 .with_verbose_mode()
53 .build();
54 prop_assert!(compile_regexp(®exp).is_ok());
55 }
56
57 #[test]
58 fn valid_regexes_with_escape_sequences_and_verbose_mode(
59 test_cases in prop::collection::hash_set(".{1,10}", 1..=5)
60 ) {
61 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
62 let regexp = RegExpBuilder::from(&test_cases_vec)
63 .with_escaping_of_non_ascii_chars(false)
64 .with_verbose_mode()
65 .build();
66 prop_assert!(compile_regexp(®exp).is_ok());
67 }
68
69 #[test]
70 fn valid_regexes_with_conversion_features(
71 test_cases in prop::collection::hash_set(".{1,10}", 1..=5),
72 conversion_features in prop::collection::hash_set(conversion_feature_strategy(), 1..=9),
73 minimum_repetitions in 1..100u32,
74 minimum_substring_length in 1..100u32
75 ) {
76 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
77 let regexp = RegExpBuilder::from(&test_cases_vec)
78 .with_conversion_of(&conversion_features.into_iter().collect::<Vec<_>>())
79 .with_minimum_repetitions(minimum_repetitions)
80 .with_minimum_substring_length(minimum_substring_length)
81 .build();
82 prop_assert!(compile_regexp(®exp).is_ok());
83 }
84
85 #[test]
86 fn valid_regexes_with_conversion_features_and_escape_sequences(
87 test_cases in prop::collection::hash_set(".{1,10}", 1..=5),
88 conversion_features in prop::collection::hash_set(conversion_feature_strategy(), 1..=9),
89 minimum_repetitions in 1..100u32,
90 minimum_substring_length in 1..100u32
91 ) {
92 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
93 let regexp = RegExpBuilder::from(&test_cases_vec)
94 .with_conversion_of(&conversion_features.into_iter().collect::<Vec<_>>())
95 .with_minimum_repetitions(minimum_repetitions)
96 .with_minimum_substring_length(minimum_substring_length)
97 .with_escaping_of_non_ascii_chars(false)
98 .build();
99 prop_assert!(compile_regexp(®exp).is_ok());
100 }
101
102 #[test]
103 fn valid_regexes_with_conversion_features_and_verbose_mode(
104 test_cases in prop::collection::hash_set(".{1,10}", 1..=5),
105 conversion_features in prop::collection::hash_set(conversion_feature_strategy(), 1..=9),
106 minimum_repetitions in 1..100u32,
107 minimum_substring_length in 1..100u32
108 ) {
109 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
110 let regexp = RegExpBuilder::from(&test_cases_vec)
111 .with_conversion_of(&conversion_features.into_iter().collect::<Vec<_>>())
112 .with_minimum_repetitions(minimum_repetitions)
113 .with_minimum_substring_length(minimum_substring_length)
114 .with_verbose_mode()
115 .build();
116 prop_assert!(compile_regexp(®exp).is_ok());
117 }
118
119 #[test]
120 fn matching_regexes_with_default_settings(
121 test_cases in prop::collection::hash_set(".{1,10}", 1..=5)
122 ) {
123 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
124 let regexp = RegExpBuilder::from(&test_cases_vec).build();
125 if let Ok(compiled_regexp) = compile_regexp(®exp) {
126 prop_assert!(test_cases.iter().all(|test_case| compiled_regexp.is_match(&test_case)));
127 }
128 }
129
130 #[test]
131 fn matching_regexes_with_escape_sequences(
132 test_cases in prop::collection::hash_set(".{1,10}", 1..=5)
133 ) {
134 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
135 let regexp = RegExpBuilder::from(&test_cases_vec)
136 .with_escaping_of_non_ascii_chars(false)
137 .build();
138 if let Ok(compiled_regexp) = compile_regexp(®exp) {
139 prop_assert!(test_cases.iter().all(|test_case| compiled_regexp.is_match(&test_case)));
140 }
141 }
142
143 #[test]
144 fn matching_regexes_with_verbose_mode(
145 test_cases in prop::collection::hash_set(".{1,10}", 1..=5)
146 ) {
147 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
148 let regexp = RegExpBuilder::from(&test_cases_vec)
149 .with_verbose_mode()
150 .build();
151 if let Ok(compiled_regexp) = compile_regexp(®exp) {
152 prop_assert!(test_cases.iter().all(|test_case| compiled_regexp.is_match(&test_case)));
153 }
154 }
155
156 #[test]
157 fn matching_regexes_with_escape_sequences_and_verbose_mode(
158 test_cases in prop::collection::hash_set(".{1,10}", 1..=5)
159 ) {
160 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
161 let regexp = RegExpBuilder::from(&test_cases_vec)
162 .with_escaping_of_non_ascii_chars(false)
163 .with_verbose_mode()
164 .build();
165 if let Ok(compiled_regexp) = compile_regexp(®exp) {
166 prop_assert!(test_cases.iter().all(|test_case| compiled_regexp.is_match(&test_case)));
167 }
168 }
169
170 #[test]
171 fn matching_regexes_with_conversion_features(
172 test_cases in prop::collection::hash_set(".{1,10}", 1..=5),
173 conversion_features in prop::collection::hash_set(conversion_feature_strategy(), 1..=9),
174 minimum_repetitions in 1..100u32,
175 minimum_substring_length in 1..100u32
176 ) {
177 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
178 let regexp = RegExpBuilder::from(&test_cases_vec)
179 .with_conversion_of(&conversion_features.into_iter().collect::<Vec<_>>())
180 .with_minimum_repetitions(minimum_repetitions)
181 .with_minimum_substring_length(minimum_substring_length)
182 .build();
183 if let Ok(compiled_regexp) = compile_regexp(®exp) {
184 prop_assert!(test_cases.iter().all(|test_case| compiled_regexp.is_match(&test_case)));
185 }
186 }
187
188 #[test]
189 fn matching_regexes_with_conversion_features_and_escape_sequences(
190 test_cases in prop::collection::hash_set(".{1,10}", 1..=5),
191 conversion_features in prop::collection::hash_set(conversion_feature_strategy(), 1..=9),
192 minimum_repetitions in 1..100u32,
193 minimum_substring_length in 1..100u32
194 ) {
195 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
196 let regexp = RegExpBuilder::from(&test_cases_vec)
197 .with_conversion_of(&conversion_features.into_iter().collect::<Vec<_>>())
198 .with_minimum_repetitions(minimum_repetitions)
199 .with_minimum_substring_length(minimum_substring_length)
200 .with_escaping_of_non_ascii_chars(false)
201 .build();
202 if let Ok(compiled_regexp) = compile_regexp(®exp) {
203 prop_assert!(test_cases.iter().all(|test_case| compiled_regexp.is_match(&test_case)));
204 }
205 }
206
207 #[test]
208 fn matching_regexes_with_conversion_features_and_verbose_mode(
209 test_cases in prop::collection::hash_set(".{1,10}", 1..=5),
210 conversion_features in prop::collection::hash_set(conversion_feature_strategy(), 1..=9),
211 minimum_repetitions in 1..100u32,
212 minimum_substring_length in 1..100u32
213 ) {
214 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
215 let regexp = RegExpBuilder::from(&test_cases_vec)
216 .with_conversion_of(&conversion_features.into_iter().collect::<Vec<_>>())
217 .with_minimum_repetitions(minimum_repetitions)
218 .with_minimum_substring_length(minimum_substring_length)
219 .with_verbose_mode()
220 .build();
221 if let Ok(compiled_regexp) = compile_regexp(®exp) {
222 prop_assert!(test_cases.iter().all(|test_case| compiled_regexp.is_match(&test_case)));
223 }
224 }
225
226 #[test]
227 fn matching_regexes_without_start_anchor(
228 test_cases in prop::collection::hash_set("[A-C]{1,10}", 1..=5)
229 ) {
230 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
231 let regexp = RegExpBuilder::from(&test_cases_vec).without_start_anchor().build();
232 if let Ok(compiled_regexp) = compile_regexp(®exp) {
233 for test_case in test_cases_vec {
234 let substrings = compiled_regexp.find_iter(&test_case).map(|m| m.as_str()).collect::<Vec<_>>();
235 prop_assert_eq!(
236 substrings.len(),
237 1,
238 "expression '{}' does not match test case '{}' entirely but {} of its substrings: {:?}",
239 regexp,
240 test_case,
241 substrings.len(),
242 substrings
243 );
244 }
245 }
246 }
247
248 #[test]
249 fn matching_regexes_without_end_anchor(
250 test_cases in prop::collection::hash_set("[A-C]{1,10}", 1..=5)
251 ) {
252 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
253 let regexp = RegExpBuilder::from(&test_cases_vec).without_end_anchor().build();
254 if let Ok(compiled_regexp) = compile_regexp(®exp) {
255 for test_case in test_cases_vec {
256 let substrings = compiled_regexp.find_iter(&test_case).map(|m| m.as_str()).collect::<Vec<_>>();
257 prop_assert_eq!(
258 substrings.len(),
259 1,
260 "expression '{}' does not match test case '{}' entirely but {} of its substrings: {:?}",
261 regexp,
262 test_case,
263 substrings.len(),
264 substrings
265 );
266 }
267 }
268 }
269
270 #[test]
271 fn matching_regexes_without_anchors(
272 test_cases in prop::collection::hash_set("[A-C]{1,10}", 1..=5)
273 ) {
274 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
275 let regexp = RegExpBuilder::from(&test_cases_vec).without_anchors().build();
276 if let Ok(compiled_regexp) = compile_regexp(®exp) {
277 for test_case in test_cases_vec {
278 let substrings = compiled_regexp.find_iter(&test_case).map(|m| m.as_str()).collect::<Vec<_>>();
279 prop_assert_eq!(
280 substrings.len(),
281 1,
282 "expression '{}' does not match test case '{}' entirely but {} of its substrings: {:?}",
283 regexp,
284 test_case,
285 substrings.len(),
286 substrings
287 );
288 }
289 }
290 }
291
292 #[test]
293 fn regexes_not_matching_other_strings_with_default_settings(
294 test_cases in prop::collection::hash_set(".{1,10}", 1..=5),
295 other_strings in prop::collection::hash_set(".{1,10}", 1..=5)
296 ) {
297 if test_cases.is_disjoint(&other_strings) {
298 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
299 let regexp = RegExpBuilder::from(&test_cases_vec).build();
300 if let Ok(compiled_regexp) = compile_regexp(®exp) {
301 prop_assert!(other_strings.iter().all(|other_string| !compiled_regexp.is_match(&other_string)));
302 }
303 }
304 }
305
306 #[test]
307 fn regexes_not_matching_other_strings_with_escape_sequences(
308 test_cases in prop::collection::hash_set(".{1,10}", 1..=5),
309 other_strings in prop::collection::hash_set(".{1,10}", 1..=5)
310 ) {
311 if test_cases.is_disjoint(&other_strings) {
312 let test_cases_vec = test_cases.iter().cloned().collect::<Vec<_>>();
313 let regexp = RegExpBuilder::from(&test_cases_vec)
314 .with_escaping_of_non_ascii_chars(false)
315 .build();
316 if let Ok(compiled_regexp) = compile_regexp(®exp) {
317 prop_assert!(other_strings.iter().all(|other_string| !compiled_regexp.is_match(&other_string)));
318 }
319 }
320 }
321 }
322
conversion_feature_strategy() -> impl Strategy<Value = Feature>323 fn conversion_feature_strategy() -> impl Strategy<Value = Feature> {
324 prop_oneof![
325 Just(Feature::Digit),
326 Just(Feature::NonDigit),
327 Just(Feature::Space),
328 Just(Feature::NonSpace),
329 Just(Feature::Word),
330 Just(Feature::NonWord),
331 Just(Feature::Repetition),
332 Just(Feature::CaseInsensitivity),
333 Just(Feature::CapturingGroup)
334 ]
335 }
336
compile_regexp(regexp: &str) -> Result<Regex, Error>337 fn compile_regexp(regexp: &str) -> Result<Regex, Error> {
338 RegexBuilder::new(regexp).size_limit(20000000).build()
339 }
340