1use crate::{errors::TestFilterBuildError, record::ComputedRerunInfo, run_mode::NextestRunMode};
9use aho_corasick::AhoCorasick;
10use nextest_filtering::{BinaryQuery, EvalContext, Filterset, GroupLookup, TestQuery};
11use nextest_metadata::{FilterMatch, MismatchReason, RustBinaryId, RustTestKind, TestCaseName};
12use std::{collections::HashSet, fmt, mem};
13
14#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
16pub enum RunIgnored {
17 #[default]
21 Default,
22
23 Only,
25
26 All,
28}
29
30#[derive(Clone, Copy, Debug)]
32pub enum FilterBound {
33 DefaultSet,
35
36 All,
38}
39
40#[derive(Clone, Debug, Eq, PartialEq)]
41pub struct BinaryFilter {
43 exprs: TestFilterExprs,
44}
45
46impl BinaryFilter {
47 pub fn new(exprs: Vec<Filterset>) -> Self {
51 let exprs = if exprs.is_empty() {
52 TestFilterExprs::All
53 } else {
54 TestFilterExprs::Sets(exprs)
55 };
56 Self { exprs }
57 }
58
59 pub fn check_match(
62 &self,
63 query: &BinaryQuery<'_>,
64 ecx: &EvalContext<'_>,
65 bound: FilterBound,
66 ) -> FilterBinaryMatch {
67 let expr_result = match &self.exprs {
68 TestFilterExprs::All => FilterBinaryMatch::Definite,
69 TestFilterExprs::Sets(exprs) => exprs.iter().fold(
70 FilterBinaryMatch::Mismatch {
71 reason: BinaryMismatchReason::Expression,
73 },
74 |acc, expr| {
75 acc.logic_or(FilterBinaryMatch::from_result(
76 expr.matches_binary(query, ecx),
77 BinaryMismatchReason::Expression,
78 ))
79 },
80 ),
81 };
82
83 if !expr_result.is_match() {
85 return expr_result;
86 }
87
88 match bound {
89 FilterBound::All => expr_result,
90 FilterBound::DefaultSet => expr_result.logic_and(FilterBinaryMatch::from_result(
91 ecx.default_filter.matches_binary(query, ecx),
92 BinaryMismatchReason::DefaultSet,
93 )),
94 }
95 }
96}
97
98#[derive(Clone, Debug)]
102pub struct TestFilter {
103 mode: NextestRunMode,
104 rerun_info: Option<ComputedRerunInfo>,
105 run_ignored: RunIgnored,
106 patterns: ResolvedFilterPatterns,
107 binary_filter: BinaryFilter,
108}
109
110#[derive(Clone, Debug, Eq, PartialEq)]
111enum TestFilterExprs {
112 All,
114
115 Sets(Vec<Filterset>),
117}
118
119#[derive(Clone, Debug, Eq, PartialEq)]
121pub enum TestFilterPatterns {
122 SkipOnly {
125 skip_patterns: Vec<String>,
127
128 skip_exact_patterns: HashSet<String>,
130 },
131
132 Patterns {
139 patterns: Vec<String>,
141
142 exact_patterns: HashSet<String>,
144
145 skip_patterns: Vec<String>,
147
148 skip_exact_patterns: HashSet<String>,
150 },
151}
152
153impl Default for TestFilterPatterns {
154 fn default() -> Self {
155 Self::SkipOnly {
156 skip_patterns: Vec::new(),
157 skip_exact_patterns: HashSet::new(),
158 }
159 }
160}
161
162impl TestFilterPatterns {
163 pub fn new(substring_patterns: Vec<String>) -> Self {
168 if substring_patterns.is_empty() {
169 Self::default()
170 } else {
171 Self::Patterns {
172 patterns: substring_patterns,
173 exact_patterns: HashSet::new(),
174 skip_patterns: Vec::new(),
175 skip_exact_patterns: HashSet::new(),
176 }
177 }
178 }
179
180 pub fn add_substring_pattern(&mut self, pattern: String) {
182 match self {
183 Self::SkipOnly {
184 skip_patterns,
185 skip_exact_patterns,
186 } => {
187 *self = Self::Patterns {
188 patterns: vec![pattern],
189 exact_patterns: HashSet::new(),
190 skip_patterns: mem::take(skip_patterns),
191 skip_exact_patterns: mem::take(skip_exact_patterns),
192 };
193 }
194 Self::Patterns { patterns, .. } => {
195 patterns.push(pattern);
196 }
197 }
198 }
199
200 pub fn add_exact_pattern(&mut self, pattern: String) {
202 match self {
203 Self::SkipOnly {
204 skip_patterns,
205 skip_exact_patterns,
206 } => {
207 *self = Self::Patterns {
208 patterns: Vec::new(),
209 exact_patterns: [pattern].into_iter().collect(),
210 skip_patterns: mem::take(skip_patterns),
211 skip_exact_patterns: mem::take(skip_exact_patterns),
212 };
213 }
214 Self::Patterns { exact_patterns, .. } => {
215 exact_patterns.insert(pattern);
216 }
217 }
218 }
219
220 pub fn add_skip_pattern(&mut self, pattern: String) {
222 match self {
223 Self::SkipOnly { skip_patterns, .. } => {
224 skip_patterns.push(pattern);
225 }
226 Self::Patterns { skip_patterns, .. } => {
227 skip_patterns.push(pattern);
228 }
229 }
230 }
231
232 pub fn add_skip_exact_pattern(&mut self, pattern: String) {
234 match self {
235 Self::SkipOnly {
236 skip_exact_patterns,
237 ..
238 } => {
239 skip_exact_patterns.insert(pattern);
240 }
241 Self::Patterns {
242 skip_exact_patterns,
243 ..
244 } => {
245 skip_exact_patterns.insert(pattern);
246 }
247 }
248 }
249
250 fn resolve(self) -> Result<ResolvedFilterPatterns, TestFilterBuildError> {
251 match self {
252 Self::SkipOnly {
253 mut skip_patterns,
254 skip_exact_patterns,
255 } => {
256 if skip_patterns.is_empty() {
257 Ok(ResolvedFilterPatterns::All)
258 } else {
259 skip_patterns.sort_unstable();
261 let skip_pattern_matcher = Box::new(AhoCorasick::new(&skip_patterns)?);
262 Ok(ResolvedFilterPatterns::SkipOnly {
263 skip_patterns,
264 skip_pattern_matcher,
265 skip_exact_patterns,
266 })
267 }
268 }
269 Self::Patterns {
270 mut patterns,
271 exact_patterns,
272 mut skip_patterns,
273 skip_exact_patterns,
274 } => {
275 patterns.sort_unstable();
277 skip_patterns.sort_unstable();
278
279 let pattern_matcher = Box::new(AhoCorasick::new(&patterns)?);
280 let skip_pattern_matcher = Box::new(AhoCorasick::new(&skip_patterns)?);
281
282 Ok(ResolvedFilterPatterns::Patterns {
283 patterns,
284 exact_patterns,
285 skip_patterns,
286 skip_exact_patterns,
287 pattern_matcher,
288 skip_pattern_matcher,
289 })
290 }
291 }
292 }
293}
294
295#[derive(Clone, Debug, Default)]
296enum ResolvedFilterPatterns {
297 #[default]
302 All,
303
304 SkipOnly {
306 skip_patterns: Vec<String>,
307 skip_pattern_matcher: Box<AhoCorasick>,
308 skip_exact_patterns: HashSet<String>,
309 },
310
311 Patterns {
313 patterns: Vec<String>,
314 exact_patterns: HashSet<String>,
315 skip_patterns: Vec<String>,
316 skip_exact_patterns: HashSet<String>,
317 pattern_matcher: Box<AhoCorasick>,
318 skip_pattern_matcher: Box<AhoCorasick>,
319 },
320}
321
322impl ResolvedFilterPatterns {
323 fn name_match(&self, test_name: &TestCaseName) -> FilterNameMatch {
324 let test_name = test_name.as_str();
325 match self {
326 Self::All => FilterNameMatch::MatchEmptyPatterns,
327 Self::SkipOnly {
328 skip_patterns: _,
330 skip_exact_patterns,
331 skip_pattern_matcher,
332 } => {
333 if skip_exact_patterns.contains(test_name)
334 || skip_pattern_matcher.is_match(test_name)
335 {
336 FilterNameMatch::Mismatch(MismatchReason::String)
337 } else {
338 FilterNameMatch::MatchWithPatterns
339 }
340 }
341 Self::Patterns {
342 patterns: _,
344 exact_patterns,
345 skip_patterns: _,
347 skip_exact_patterns,
348 pattern_matcher,
349 skip_pattern_matcher,
350 } => {
351 if skip_exact_patterns.contains(test_name)
353 || skip_pattern_matcher.is_match(test_name)
354 {
355 FilterNameMatch::Mismatch(MismatchReason::String)
356 } else if exact_patterns.contains(test_name) || pattern_matcher.is_match(test_name)
357 {
358 FilterNameMatch::MatchWithPatterns
359 } else {
360 FilterNameMatch::Mismatch(MismatchReason::String)
361 }
362 }
363 }
364 }
365}
366
367impl PartialEq for ResolvedFilterPatterns {
368 fn eq(&self, other: &Self) -> bool {
369 match (self, other) {
370 (Self::All, Self::All) => true,
371 (
372 Self::SkipOnly {
373 skip_patterns,
374 skip_exact_patterns,
375 skip_pattern_matcher: _,
377 },
378 Self::SkipOnly {
379 skip_patterns: other_skip_patterns,
380 skip_exact_patterns: other_skip_exact_patterns,
381 skip_pattern_matcher: _,
382 },
383 ) => {
384 skip_patterns == other_skip_patterns
385 && skip_exact_patterns == other_skip_exact_patterns
386 }
387 (
388 Self::Patterns {
389 patterns,
390 exact_patterns,
391 skip_patterns,
392 skip_exact_patterns,
393 pattern_matcher: _,
396 skip_pattern_matcher: _,
397 },
398 Self::Patterns {
399 patterns: other_patterns,
400 exact_patterns: other_exact_patterns,
401 skip_patterns: other_skip_patterns,
402 skip_exact_patterns: other_skip_exact_patterns,
403 pattern_matcher: _,
404 skip_pattern_matcher: _,
405 },
406 ) => {
407 patterns == other_patterns
408 && exact_patterns == other_exact_patterns
409 && skip_patterns == other_skip_patterns
410 && skip_exact_patterns == other_skip_exact_patterns
411 }
412 _ => false,
413 }
414 }
415}
416
417impl Eq for ResolvedFilterPatterns {}
418
419impl TestFilter {
420 pub fn new(
424 mode: NextestRunMode,
425 run_ignored: RunIgnored,
426 patterns: TestFilterPatterns,
427 exprs: Vec<Filterset>,
428 ) -> Result<Self, TestFilterBuildError> {
429 let patterns = patterns.resolve()?;
430
431 let binary_filter = BinaryFilter::new(exprs);
432
433 Ok(Self {
434 mode,
435 rerun_info: None,
436 run_ignored,
437 patterns,
438 binary_filter,
439 })
440 }
441
442 pub fn filter_binary_match(
445 &self,
446 query: &BinaryQuery<'_>,
447 ecx: &EvalContext<'_>,
448 bound: FilterBound,
449 ) -> FilterBinaryMatch {
450 self.binary_filter.check_match(query, ecx, bound)
451 }
452
453 pub fn default_set(mode: NextestRunMode, run_ignored: RunIgnored) -> Self {
455 let binary_filter = BinaryFilter::new(Vec::new());
456 Self {
457 mode,
458 rerun_info: None,
459 run_ignored,
460 patterns: ResolvedFilterPatterns::default(),
461 binary_filter,
462 }
463 }
464
465 pub fn set_outstanding_tests(&mut self, rerun_info: ComputedRerunInfo) {
467 self.rerun_info = Some(rerun_info);
468 }
469
470 pub fn mode(&self) -> NextestRunMode {
472 self.mode
473 }
474
475 pub fn patterns_eq(&self, other: &Self) -> bool {
477 self.patterns == other.patterns
478 }
479
480 pub fn has_group_predicates(&self) -> bool {
483 match &self.binary_filter.exprs {
484 TestFilterExprs::All => false,
485 TestFilterExprs::Sets(exprs) => {
486 exprs.iter().any(|expr| expr.compiled.has_group_matchers())
487 }
488 }
489 }
490
491 fn matches_expression(
498 &self,
499 query: &TestQuery<'_>,
500 ecx: &EvalContext<'_>,
501 groups: Option<&dyn GroupLookup>,
502 ) -> bool {
503 match &self.binary_filter.exprs {
504 TestFilterExprs::All => true,
505 TestFilterExprs::Sets(exprs) => exprs.iter().any(|expr| match groups {
506 Some(groups) => expr.matches_test_with_groups(query, ecx, groups),
507 None => expr.matches_test(query, ecx),
508 }),
509 }
510 }
511
512 pub fn into_rerun_info(self) -> Option<ComputedRerunInfo> {
514 self.rerun_info
515 }
516
517 #[expect(clippy::too_many_arguments)]
524 pub fn filter_match(
525 &self,
526 binary_query: BinaryQuery<'_>,
527 test_name: &TestCaseName,
528 test_kind: &RustTestKind,
529 ecx: &EvalContext<'_>,
530 bound: FilterBound,
531 ignored: bool,
532 groups: Option<&dyn GroupLookup>,
533 ) -> FilterMatch {
534 if let Some(mismatch) = self.filter_benchmark_mismatch(test_kind) {
536 return mismatch;
537 }
538
539 if self.is_rerun_already_passed(binary_query.binary_id, test_name) {
541 return FilterMatch::Mismatch {
542 reason: MismatchReason::RerunAlreadyPassed,
543 };
544 }
545
546 self.filter_match_base(binary_query, test_name, ecx, bound, ignored, groups)
547 }
548
549 fn filter_match_base(
551 &self,
552 binary_query: BinaryQuery<'_>,
553 test_name: &TestCaseName,
554 ecx: &EvalContext<'_>,
555 bound: FilterBound,
556 ignored: bool,
557 groups: Option<&dyn GroupLookup>,
558 ) -> FilterMatch {
559 if let Some(mismatch) = self.filter_ignored_mismatch(ignored) {
560 return mismatch;
561 }
562
563 {
564 use FilterNameMatch::*;
585 match (
586 self.filter_name_match(test_name),
587 self.filter_expression_match(binary_query, test_name, ecx, bound, groups),
588 ) {
589 (
591 MatchEmptyPatterns | MatchWithPatterns,
592 MatchEmptyPatterns | MatchWithPatterns,
593 ) => {}
594 (Mismatch(reason), _) | (_, Mismatch(reason)) => {
600 return FilterMatch::Mismatch { reason };
601 }
602 }
603 }
604
605 FilterMatch::Matches
606 }
607
608 fn is_rerun_already_passed(&self, binary_id: &RustBinaryId, test_name: &TestCaseName) -> bool {
610 if let Some(rerun_info) = &self.rerun_info
611 && let Some(suite) = rerun_info.test_suites.get(binary_id)
612 {
613 return suite.passing.contains(test_name);
614 }
615 false
616 }
617
618 fn filter_benchmark_mismatch(&self, test_kind: &RustTestKind) -> Option<FilterMatch> {
619 if self.mode == NextestRunMode::Benchmark && test_kind != &RustTestKind::BENCH {
620 Some(FilterMatch::Mismatch {
621 reason: MismatchReason::NotBenchmark,
622 })
623 } else {
624 None
625 }
626 }
627
628 fn filter_ignored_mismatch(&self, ignored: bool) -> Option<FilterMatch> {
629 let dominated = match self.run_ignored {
630 RunIgnored::Only => !ignored,
631 RunIgnored::Default => ignored,
632 RunIgnored::All => false,
633 };
634 dominated.then_some(FilterMatch::Mismatch {
635 reason: MismatchReason::Ignored,
636 })
637 }
638
639 fn filter_name_match(&self, test_name: &TestCaseName) -> FilterNameMatch {
640 self.patterns.name_match(test_name)
641 }
642
643 fn filter_expression_match(
644 &self,
645 binary_query: BinaryQuery<'_>,
646 test_name: &TestCaseName,
647 ecx: &EvalContext<'_>,
648 bound: FilterBound,
649 groups: Option<&dyn GroupLookup>,
650 ) -> FilterNameMatch {
651 let query = TestQuery {
652 binary_query,
653 test_name,
654 };
655
656 let expr_result = if self.matches_expression(&query, ecx, groups) {
657 match &self.binary_filter.exprs {
658 TestFilterExprs::All => FilterNameMatch::MatchEmptyPatterns,
659 TestFilterExprs::Sets(_) => FilterNameMatch::MatchWithPatterns,
660 }
661 } else {
662 return FilterNameMatch::Mismatch(MismatchReason::Expression);
663 };
664
665 match bound {
666 FilterBound::All => expr_result,
667 FilterBound::DefaultSet => {
668 if ecx.default_filter.matches_test(&query, ecx) {
671 expr_result
672 } else {
673 FilterNameMatch::Mismatch(MismatchReason::DefaultFilter)
674 }
675 }
676 }
677 }
678}
679
680#[derive(Copy, Clone, Debug)]
684pub enum FilterBinaryMatch {
685 Definite,
687
688 Possible,
690
691 Mismatch {
693 reason: BinaryMismatchReason,
695 },
696}
697
698impl FilterBinaryMatch {
699 fn from_result(result: Option<bool>, reason: BinaryMismatchReason) -> Self {
700 match result {
701 Some(true) => Self::Definite,
702 None => Self::Possible,
703 Some(false) => Self::Mismatch { reason },
704 }
705 }
706
707 fn is_match(self) -> bool {
708 match self {
709 Self::Definite | Self::Possible => true,
710 Self::Mismatch { .. } => false,
711 }
712 }
713
714 fn logic_or(self, other: Self) -> Self {
715 match (self, other) {
716 (Self::Definite, _) | (_, Self::Definite) => Self::Definite,
717 (Self::Possible, _) | (_, Self::Possible) => Self::Possible,
718 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => Self::Mismatch {
719 reason: r1.prefer_expression(r2),
720 },
721 }
722 }
723
724 fn logic_and(self, other: Self) -> Self {
725 match (self, other) {
726 (Self::Definite, Self::Definite) => Self::Definite,
727 (Self::Definite, Self::Possible)
728 | (Self::Possible, Self::Definite)
729 | (Self::Possible, Self::Possible) => Self::Possible,
730 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => {
731 Self::Mismatch {
734 reason: r1.prefer_expression(r2),
735 }
736 }
737 (Self::Mismatch { reason }, _) | (_, Self::Mismatch { reason }) => {
738 Self::Mismatch { reason }
739 }
740 }
741 }
742}
743
744#[derive(Copy, Clone, Debug, Eq, PartialEq)]
748pub enum BinaryMismatchReason {
749 Expression,
751
752 DefaultSet,
754}
755
756impl BinaryMismatchReason {
757 fn prefer_expression(self, other: Self) -> Self {
758 match (self, other) {
759 (Self::Expression, _) | (_, Self::Expression) => Self::Expression,
760 (Self::DefaultSet, Self::DefaultSet) => Self::DefaultSet,
761 }
762 }
763}
764
765impl fmt::Display for BinaryMismatchReason {
766 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
767 match self {
768 Self::Expression => write!(f, "didn't match filtersets"),
769 Self::DefaultSet => write!(f, "didn't match the default set"),
770 }
771 }
772}
773
774#[derive(Clone, Debug, Eq, PartialEq)]
775enum FilterNameMatch {
776 MatchEmptyPatterns,
778 MatchWithPatterns,
780 Mismatch(MismatchReason),
782}
783
784impl FilterNameMatch {
785 #[cfg(test)]
786 fn is_match(&self) -> bool {
787 match self {
788 Self::MatchEmptyPatterns | Self::MatchWithPatterns => true,
789 Self::Mismatch(_) => false,
790 }
791 }
792}
793
794#[cfg(test)]
795mod tests {
796 use super::*;
797 use proptest::{collection::vec, prelude::*};
798 use test_strategy::proptest;
799
800 #[proptest(cases = 50)]
801 fn proptest_empty(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
802 let patterns = TestFilterPatterns::default();
803 let test_filter = TestFilter::new(
804 NextestRunMode::Test,
805 RunIgnored::Default,
806 patterns,
807 Vec::new(),
808 )
809 .unwrap();
810 for test_name in test_names {
811 let test_name = TestCaseName::new(&test_name);
812 prop_assert!(test_filter.filter_name_match(&test_name).is_match());
813 }
814 }
815
816 #[proptest(cases = 50)]
818 fn proptest_exact(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
819 let patterns = TestFilterPatterns::new(test_names.clone());
821 let test_filter = TestFilter::new(
822 NextestRunMode::Test,
823 RunIgnored::Default,
824 patterns,
825 Vec::new(),
826 )
827 .unwrap();
828 for test_name in &test_names {
829 let test_name = TestCaseName::new(test_name);
830 prop_assert!(test_filter.filter_name_match(&test_name).is_match());
831 }
832
833 let mut patterns = TestFilterPatterns::default();
835 for test_name in &test_names {
836 patterns.add_exact_pattern(test_name.clone());
837 }
838 let test_filter = TestFilter::new(
839 NextestRunMode::Test,
840 RunIgnored::Default,
841 patterns,
842 Vec::new(),
843 )
844 .unwrap();
845 for test_name in &test_names {
846 let test_name = TestCaseName::new(test_name);
847 prop_assert!(test_filter.filter_name_match(&test_name).is_match());
848 }
849 }
850
851 #[proptest(cases = 50)]
853 fn proptest_substring(
854 #[strategy(vec([any::<String>(); 3], 0..16))] substring_prefix_suffixes: Vec<[String; 3]>,
855 ) {
856 let mut patterns = TestFilterPatterns::default();
857 let mut test_names = Vec::with_capacity(substring_prefix_suffixes.len());
858 for [substring, prefix, suffix] in substring_prefix_suffixes {
859 test_names.push(prefix + &substring + &suffix);
860 patterns.add_substring_pattern(substring);
861 }
862
863 let test_filter = TestFilter::new(
864 NextestRunMode::Test,
865 RunIgnored::Default,
866 patterns,
867 Vec::new(),
868 )
869 .unwrap();
870 for test_name in test_names {
871 let test_name = TestCaseName::new(&test_name);
872 prop_assert!(test_filter.filter_name_match(&test_name).is_match());
873 }
874 }
875
876 #[proptest(cases = 50)]
878 fn proptest_no_match(substring: String, prefix: String, suffix: String) {
879 prop_assume!(!substring.is_empty() && !prefix.is_empty() && !suffix.is_empty());
880 let pattern = prefix + &substring + &suffix;
881 let patterns = TestFilterPatterns::new(vec![pattern]);
882 let test_filter = TestFilter::new(
883 NextestRunMode::Test,
884 RunIgnored::Default,
885 patterns,
886 Vec::new(),
887 )
888 .unwrap();
889 let substring = TestCaseName::new(&substring);
890 prop_assert!(!test_filter.filter_name_match(&substring).is_match());
891 }
892
893 fn test_name(s: &str) -> TestCaseName {
894 TestCaseName::new(s)
895 }
896
897 #[test]
898 fn pattern_examples() {
899 let mut patterns = TestFilterPatterns::new(vec!["foo".to_string()]);
900 patterns.add_substring_pattern("bar".to_string());
901 patterns.add_exact_pattern("baz".to_string());
902 patterns.add_skip_pattern("quux".to_string());
903 patterns.add_skip_exact_pattern("quuz".to_string());
904
905 let resolved = patterns.clone().resolve().unwrap();
906
907 assert_eq!(
909 resolved.name_match(&test_name("foo")),
910 FilterNameMatch::MatchWithPatterns,
911 );
912 assert_eq!(
913 resolved.name_match(&test_name("1foo2")),
914 FilterNameMatch::MatchWithPatterns,
915 );
916 assert_eq!(
917 resolved.name_match(&test_name("bar")),
918 FilterNameMatch::MatchWithPatterns,
919 );
920 assert_eq!(
921 resolved.name_match(&test_name("x_bar_y")),
922 FilterNameMatch::MatchWithPatterns,
923 );
924
925 assert_eq!(
927 resolved.name_match(&test_name("baz")),
928 FilterNameMatch::MatchWithPatterns,
929 );
930 assert_eq!(
931 resolved.name_match(&test_name("abazb")),
932 FilterNameMatch::Mismatch(MismatchReason::String),
933 );
934
935 assert_eq!(
937 resolved.name_match(&test_name("bazfoo")),
938 FilterNameMatch::MatchWithPatterns,
939 );
940
941 assert_eq!(
943 resolved.name_match(&test_name("quux")),
944 FilterNameMatch::Mismatch(MismatchReason::String),
945 );
946 assert_eq!(
947 resolved.name_match(&test_name("1quux2")),
948 FilterNameMatch::Mismatch(MismatchReason::String),
949 );
950
951 assert_eq!(
953 resolved.name_match(&test_name("quuxbar")),
954 FilterNameMatch::Mismatch(MismatchReason::String),
955 );
956
957 assert_eq!(
959 resolved.name_match(&test_name("quuz")),
960 FilterNameMatch::Mismatch(MismatchReason::String),
961 );
962
963 patterns.add_skip_pattern("baz".to_string());
965 let resolved = patterns.resolve().unwrap();
966 assert_eq!(
967 resolved.name_match(&test_name("quuxbaz")),
968 FilterNameMatch::Mismatch(MismatchReason::String),
969 );
970 }
971
972 #[test]
973 fn skip_only_pattern_examples() {
974 let mut patterns = TestFilterPatterns::default();
975 patterns.add_skip_pattern("foo".to_string());
976 patterns.add_skip_pattern("bar".to_string());
977 patterns.add_skip_exact_pattern("baz".to_string());
978
979 let resolved = patterns.clone().resolve().unwrap();
980
981 assert_eq!(
983 resolved.name_match(&test_name("foo")),
984 FilterNameMatch::Mismatch(MismatchReason::String),
985 );
986 assert_eq!(
987 resolved.name_match(&test_name("1foo2")),
988 FilterNameMatch::Mismatch(MismatchReason::String),
989 );
990 assert_eq!(
991 resolved.name_match(&test_name("bar")),
992 FilterNameMatch::Mismatch(MismatchReason::String),
993 );
994 assert_eq!(
995 resolved.name_match(&test_name("x_bar_y")),
996 FilterNameMatch::Mismatch(MismatchReason::String),
997 );
998
999 assert_eq!(
1001 resolved.name_match(&test_name("baz")),
1002 FilterNameMatch::Mismatch(MismatchReason::String),
1003 );
1004 assert_eq!(
1005 resolved.name_match(&test_name("abazb")),
1006 FilterNameMatch::MatchWithPatterns,
1007 );
1008
1009 assert_eq!(
1011 resolved.name_match(&test_name("quux")),
1012 FilterNameMatch::MatchWithPatterns,
1013 );
1014 }
1015
1016 #[test]
1017 fn empty_pattern_examples() {
1018 let patterns = TestFilterPatterns::default();
1019 let resolved = patterns.resolve().unwrap();
1020 assert_eq!(resolved, ResolvedFilterPatterns::All);
1021
1022 assert_eq!(
1024 resolved.name_match(&test_name("foo")),
1025 FilterNameMatch::MatchEmptyPatterns,
1026 );
1027 assert_eq!(
1028 resolved.name_match(&test_name("1foo2")),
1029 FilterNameMatch::MatchEmptyPatterns,
1030 );
1031 assert_eq!(
1032 resolved.name_match(&test_name("bar")),
1033 FilterNameMatch::MatchEmptyPatterns,
1034 );
1035 assert_eq!(
1036 resolved.name_match(&test_name("x_bar_y")),
1037 FilterNameMatch::MatchEmptyPatterns,
1038 );
1039 assert_eq!(
1040 resolved.name_match(&test_name("baz")),
1041 FilterNameMatch::MatchEmptyPatterns,
1042 );
1043 assert_eq!(
1044 resolved.name_match(&test_name("abazb")),
1045 FilterNameMatch::MatchEmptyPatterns,
1046 );
1047 }
1048}