1use crate::{
9 errors::TestFilterBuildError, list::RustTestArtifact, record::ComputedRerunInfo,
10 run_mode::NextestRunMode,
11};
12use aho_corasick::AhoCorasick;
13use nextest_filtering::{EvalContext, Filterset, TestQuery};
14use nextest_metadata::{FilterMatch, MismatchReason, RustTestKind, TestCaseName};
15use std::{collections::HashSet, fmt, mem};
16
17#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
19pub enum RunIgnored {
20 #[default]
24 Default,
25
26 Only,
28
29 All,
31}
32
33#[derive(Clone, Copy, Debug)]
35pub enum FilterBound {
36 DefaultSet,
38
39 All,
41}
42
43#[derive(Clone, Debug, Eq, PartialEq)]
44pub struct BinaryFilter {
46 exprs: TestFilterExprs,
47}
48
49impl BinaryFilter {
50 pub fn new(exprs: Vec<Filterset>) -> Self {
54 let exprs = if exprs.is_empty() {
55 TestFilterExprs::All
56 } else {
57 TestFilterExprs::Sets(exprs)
58 };
59 Self { exprs }
60 }
61
62 pub fn check_match(
65 &self,
66 test_binary: &RustTestArtifact<'_>,
67 ecx: &EvalContext<'_>,
68 bound: FilterBound,
69 ) -> FilterBinaryMatch {
70 let query = test_binary.to_binary_query();
71 let expr_result = match &self.exprs {
72 TestFilterExprs::All => FilterBinaryMatch::Definite,
73 TestFilterExprs::Sets(exprs) => exprs.iter().fold(
74 FilterBinaryMatch::Mismatch {
75 reason: BinaryMismatchReason::Expression,
77 },
78 |acc, expr| {
79 acc.logic_or(FilterBinaryMatch::from_result(
80 expr.matches_binary(&query, ecx),
81 BinaryMismatchReason::Expression,
82 ))
83 },
84 ),
85 };
86
87 if !expr_result.is_match() {
89 return expr_result;
90 }
91
92 match bound {
93 FilterBound::All => expr_result,
94 FilterBound::DefaultSet => expr_result.logic_and(FilterBinaryMatch::from_result(
95 ecx.default_filter.matches_binary(&query, ecx),
96 BinaryMismatchReason::DefaultSet,
97 )),
98 }
99 }
100}
101
102#[derive(Clone, Debug)]
106pub struct TestFilter {
107 mode: NextestRunMode,
108 rerun_info: Option<ComputedRerunInfo>,
109 run_ignored: RunIgnored,
110 patterns: ResolvedFilterPatterns,
111 binary_filter: BinaryFilter,
112}
113
114#[derive(Clone, Debug, Eq, PartialEq)]
115enum TestFilterExprs {
116 All,
118
119 Sets(Vec<Filterset>),
121}
122
123#[derive(Clone, Debug, Eq, PartialEq)]
125pub enum TestFilterPatterns {
126 SkipOnly {
129 skip_patterns: Vec<String>,
131
132 skip_exact_patterns: HashSet<String>,
134 },
135
136 Patterns {
143 patterns: Vec<String>,
145
146 exact_patterns: HashSet<String>,
148
149 skip_patterns: Vec<String>,
151
152 skip_exact_patterns: HashSet<String>,
154 },
155}
156
157impl Default for TestFilterPatterns {
158 fn default() -> Self {
159 Self::SkipOnly {
160 skip_patterns: Vec::new(),
161 skip_exact_patterns: HashSet::new(),
162 }
163 }
164}
165
166impl TestFilterPatterns {
167 pub fn new(substring_patterns: Vec<String>) -> Self {
172 if substring_patterns.is_empty() {
173 Self::default()
174 } else {
175 Self::Patterns {
176 patterns: substring_patterns,
177 exact_patterns: HashSet::new(),
178 skip_patterns: Vec::new(),
179 skip_exact_patterns: HashSet::new(),
180 }
181 }
182 }
183
184 pub fn add_substring_pattern(&mut self, pattern: String) {
186 match self {
187 Self::SkipOnly {
188 skip_patterns,
189 skip_exact_patterns,
190 } => {
191 *self = Self::Patterns {
192 patterns: vec![pattern],
193 exact_patterns: HashSet::new(),
194 skip_patterns: mem::take(skip_patterns),
195 skip_exact_patterns: mem::take(skip_exact_patterns),
196 };
197 }
198 Self::Patterns { patterns, .. } => {
199 patterns.push(pattern);
200 }
201 }
202 }
203
204 pub fn add_exact_pattern(&mut self, pattern: String) {
206 match self {
207 Self::SkipOnly {
208 skip_patterns,
209 skip_exact_patterns,
210 } => {
211 *self = Self::Patterns {
212 patterns: Vec::new(),
213 exact_patterns: [pattern].into_iter().collect(),
214 skip_patterns: mem::take(skip_patterns),
215 skip_exact_patterns: mem::take(skip_exact_patterns),
216 };
217 }
218 Self::Patterns { exact_patterns, .. } => {
219 exact_patterns.insert(pattern);
220 }
221 }
222 }
223
224 pub fn add_skip_pattern(&mut self, pattern: String) {
226 match self {
227 Self::SkipOnly { skip_patterns, .. } => {
228 skip_patterns.push(pattern);
229 }
230 Self::Patterns { skip_patterns, .. } => {
231 skip_patterns.push(pattern);
232 }
233 }
234 }
235
236 pub fn add_skip_exact_pattern(&mut self, pattern: String) {
238 match self {
239 Self::SkipOnly {
240 skip_exact_patterns,
241 ..
242 } => {
243 skip_exact_patterns.insert(pattern);
244 }
245 Self::Patterns {
246 skip_exact_patterns,
247 ..
248 } => {
249 skip_exact_patterns.insert(pattern);
250 }
251 }
252 }
253
254 fn resolve(self) -> Result<ResolvedFilterPatterns, TestFilterBuildError> {
255 match self {
256 Self::SkipOnly {
257 mut skip_patterns,
258 skip_exact_patterns,
259 } => {
260 if skip_patterns.is_empty() {
261 Ok(ResolvedFilterPatterns::All)
262 } else {
263 skip_patterns.sort_unstable();
265 let skip_pattern_matcher = Box::new(AhoCorasick::new(&skip_patterns)?);
266 Ok(ResolvedFilterPatterns::SkipOnly {
267 skip_patterns,
268 skip_pattern_matcher,
269 skip_exact_patterns,
270 })
271 }
272 }
273 Self::Patterns {
274 mut patterns,
275 exact_patterns,
276 mut skip_patterns,
277 skip_exact_patterns,
278 } => {
279 patterns.sort_unstable();
281 skip_patterns.sort_unstable();
282
283 let pattern_matcher = Box::new(AhoCorasick::new(&patterns)?);
284 let skip_pattern_matcher = Box::new(AhoCorasick::new(&skip_patterns)?);
285
286 Ok(ResolvedFilterPatterns::Patterns {
287 patterns,
288 exact_patterns,
289 skip_patterns,
290 skip_exact_patterns,
291 pattern_matcher,
292 skip_pattern_matcher,
293 })
294 }
295 }
296 }
297}
298
299#[derive(Clone, Debug, Default)]
300enum ResolvedFilterPatterns {
301 #[default]
306 All,
307
308 SkipOnly {
310 skip_patterns: Vec<String>,
311 skip_pattern_matcher: Box<AhoCorasick>,
312 skip_exact_patterns: HashSet<String>,
313 },
314
315 Patterns {
317 patterns: Vec<String>,
318 exact_patterns: HashSet<String>,
319 skip_patterns: Vec<String>,
320 skip_exact_patterns: HashSet<String>,
321 pattern_matcher: Box<AhoCorasick>,
322 skip_pattern_matcher: Box<AhoCorasick>,
323 },
324}
325
326impl ResolvedFilterPatterns {
327 fn name_match(&self, test_name: &TestCaseName) -> FilterNameMatch {
328 let test_name = test_name.as_str();
329 match self {
330 Self::All => FilterNameMatch::MatchEmptyPatterns,
331 Self::SkipOnly {
332 skip_patterns: _,
334 skip_exact_patterns,
335 skip_pattern_matcher,
336 } => {
337 if skip_exact_patterns.contains(test_name)
338 || skip_pattern_matcher.is_match(test_name)
339 {
340 FilterNameMatch::Mismatch(MismatchReason::String)
341 } else {
342 FilterNameMatch::MatchWithPatterns
343 }
344 }
345 Self::Patterns {
346 patterns: _,
348 exact_patterns,
349 skip_patterns: _,
351 skip_exact_patterns,
352 pattern_matcher,
353 skip_pattern_matcher,
354 } => {
355 if skip_exact_patterns.contains(test_name)
357 || skip_pattern_matcher.is_match(test_name)
358 {
359 FilterNameMatch::Mismatch(MismatchReason::String)
360 } else if exact_patterns.contains(test_name) || pattern_matcher.is_match(test_name)
361 {
362 FilterNameMatch::MatchWithPatterns
363 } else {
364 FilterNameMatch::Mismatch(MismatchReason::String)
365 }
366 }
367 }
368 }
369}
370
371impl PartialEq for ResolvedFilterPatterns {
372 fn eq(&self, other: &Self) -> bool {
373 match (self, other) {
374 (Self::All, Self::All) => true,
375 (
376 Self::SkipOnly {
377 skip_patterns,
378 skip_exact_patterns,
379 skip_pattern_matcher: _,
381 },
382 Self::SkipOnly {
383 skip_patterns: other_skip_patterns,
384 skip_exact_patterns: other_skip_exact_patterns,
385 skip_pattern_matcher: _,
386 },
387 ) => {
388 skip_patterns == other_skip_patterns
389 && skip_exact_patterns == other_skip_exact_patterns
390 }
391 (
392 Self::Patterns {
393 patterns,
394 exact_patterns,
395 skip_patterns,
396 skip_exact_patterns,
397 pattern_matcher: _,
400 skip_pattern_matcher: _,
401 },
402 Self::Patterns {
403 patterns: other_patterns,
404 exact_patterns: other_exact_patterns,
405 skip_patterns: other_skip_patterns,
406 skip_exact_patterns: other_skip_exact_patterns,
407 pattern_matcher: _,
408 skip_pattern_matcher: _,
409 },
410 ) => {
411 patterns == other_patterns
412 && exact_patterns == other_exact_patterns
413 && skip_patterns == other_skip_patterns
414 && skip_exact_patterns == other_skip_exact_patterns
415 }
416 _ => false,
417 }
418 }
419}
420
421impl Eq for ResolvedFilterPatterns {}
422
423impl TestFilter {
424 pub fn new(
428 mode: NextestRunMode,
429 run_ignored: RunIgnored,
430 patterns: TestFilterPatterns,
431 exprs: Vec<Filterset>,
432 ) -> Result<Self, TestFilterBuildError> {
433 let patterns = patterns.resolve()?;
434
435 let binary_filter = BinaryFilter::new(exprs);
436
437 Ok(Self {
438 mode,
439 rerun_info: None,
440 run_ignored,
441 patterns,
442 binary_filter,
443 })
444 }
445
446 pub fn filter_binary_match(
449 &self,
450 test_binary: &RustTestArtifact<'_>,
451 ecx: &EvalContext<'_>,
452 bound: FilterBound,
453 ) -> FilterBinaryMatch {
454 self.binary_filter.check_match(test_binary, ecx, bound)
455 }
456
457 pub fn default_set(mode: NextestRunMode, run_ignored: RunIgnored) -> Self {
459 let binary_filter = BinaryFilter::new(Vec::new());
460 Self {
461 mode,
462 rerun_info: None,
463 run_ignored,
464 patterns: ResolvedFilterPatterns::default(),
465 binary_filter,
466 }
467 }
468
469 pub fn set_outstanding_tests(&mut self, rerun_info: ComputedRerunInfo) {
471 self.rerun_info = Some(rerun_info);
472 }
473
474 pub fn mode(&self) -> NextestRunMode {
476 self.mode
477 }
478
479 pub fn patterns_eq(&self, other: &Self) -> bool {
481 self.patterns == other.patterns
482 }
483
484 pub fn into_rerun_info(self) -> Option<ComputedRerunInfo> {
486 self.rerun_info
487 }
488
489 pub fn filter_match(
491 &self,
492 test_binary: &RustTestArtifact<'_>,
493 test_name: &TestCaseName,
494 test_kind: &RustTestKind,
495 ecx: &EvalContext<'_>,
496 bound: FilterBound,
497 ignored: bool,
498 ) -> FilterMatch {
499 if let Some(mismatch) = self.filter_benchmark_mismatch(test_kind) {
501 return mismatch;
502 }
503
504 if self.is_rerun_already_passed(test_binary, test_name) {
506 return FilterMatch::Mismatch {
507 reason: MismatchReason::RerunAlreadyPassed,
508 };
509 }
510
511 self.filter_match_base(test_binary, test_name, ecx, bound, ignored)
512 }
513
514 fn filter_match_base(
516 &self,
517 test_binary: &RustTestArtifact<'_>,
518 test_name: &TestCaseName,
519 ecx: &EvalContext<'_>,
520 bound: FilterBound,
521 ignored: bool,
522 ) -> FilterMatch {
523 if let Some(mismatch) = self.filter_ignored_mismatch(ignored) {
524 return mismatch;
525 }
526
527 {
528 use FilterNameMatch::*;
549 match (
550 self.filter_name_match(test_name),
551 self.filter_expression_match(test_binary, test_name, ecx, bound),
552 ) {
553 (
555 MatchEmptyPatterns | MatchWithPatterns,
556 MatchEmptyPatterns | MatchWithPatterns,
557 ) => {}
558 (Mismatch(reason), _) | (_, Mismatch(reason)) => {
564 return FilterMatch::Mismatch { reason };
565 }
566 }
567 }
568
569 FilterMatch::Matches
570 }
571
572 fn is_rerun_already_passed(
574 &self,
575 test_binary: &RustTestArtifact<'_>,
576 test_name: &TestCaseName,
577 ) -> bool {
578 if let Some(rerun_info) = &self.rerun_info
579 && let Some(suite) = rerun_info.test_suites.get(&test_binary.binary_id)
580 {
581 return suite.passing.contains(test_name);
582 }
583 false
584 }
585
586 fn filter_benchmark_mismatch(&self, test_kind: &RustTestKind) -> Option<FilterMatch> {
587 if self.mode == NextestRunMode::Benchmark && test_kind != &RustTestKind::BENCH {
588 Some(FilterMatch::Mismatch {
589 reason: MismatchReason::NotBenchmark,
590 })
591 } else {
592 None
593 }
594 }
595
596 fn filter_ignored_mismatch(&self, ignored: bool) -> Option<FilterMatch> {
597 match self.run_ignored {
598 RunIgnored::Only => {
599 if !ignored {
600 return Some(FilterMatch::Mismatch {
601 reason: MismatchReason::Ignored,
602 });
603 }
604 }
605 RunIgnored::Default => {
606 if ignored {
607 return Some(FilterMatch::Mismatch {
608 reason: MismatchReason::Ignored,
609 });
610 }
611 }
612 RunIgnored::All => {}
613 }
614 None
615 }
616
617 fn filter_name_match(&self, test_name: &TestCaseName) -> FilterNameMatch {
618 self.patterns.name_match(test_name)
619 }
620
621 fn filter_expression_match(
622 &self,
623 test_binary: &RustTestArtifact<'_>,
624 test_name: &TestCaseName,
625 ecx: &EvalContext<'_>,
626 bound: FilterBound,
627 ) -> FilterNameMatch {
628 let query = TestQuery {
629 binary_query: test_binary.to_binary_query(),
630 test_name,
631 };
632
633 let expr_result = match &self.binary_filter.exprs {
634 TestFilterExprs::All => FilterNameMatch::MatchEmptyPatterns,
635 TestFilterExprs::Sets(exprs) => {
636 if exprs.iter().any(|expr| expr.matches_test(&query, ecx)) {
637 FilterNameMatch::MatchWithPatterns
638 } else {
639 return FilterNameMatch::Mismatch(MismatchReason::Expression);
640 }
641 }
642 };
643
644 match bound {
645 FilterBound::All => expr_result,
646 FilterBound::DefaultSet => {
647 if ecx.default_filter.matches_test(&query, ecx) {
648 expr_result
649 } else {
650 FilterNameMatch::Mismatch(MismatchReason::DefaultFilter)
651 }
652 }
653 }
654 }
655}
656
657#[derive(Copy, Clone, Debug)]
661pub enum FilterBinaryMatch {
662 Definite,
664
665 Possible,
667
668 Mismatch {
670 reason: BinaryMismatchReason,
672 },
673}
674
675impl FilterBinaryMatch {
676 fn from_result(result: Option<bool>, reason: BinaryMismatchReason) -> Self {
677 match result {
678 Some(true) => Self::Definite,
679 None => Self::Possible,
680 Some(false) => Self::Mismatch { reason },
681 }
682 }
683
684 fn is_match(self) -> bool {
685 match self {
686 Self::Definite | Self::Possible => true,
687 Self::Mismatch { .. } => false,
688 }
689 }
690
691 fn logic_or(self, other: Self) -> Self {
692 match (self, other) {
693 (Self::Definite, _) | (_, Self::Definite) => Self::Definite,
694 (Self::Possible, _) | (_, Self::Possible) => Self::Possible,
695 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => Self::Mismatch {
696 reason: r1.prefer_expression(r2),
697 },
698 }
699 }
700
701 fn logic_and(self, other: Self) -> Self {
702 match (self, other) {
703 (Self::Definite, Self::Definite) => Self::Definite,
704 (Self::Definite, Self::Possible)
705 | (Self::Possible, Self::Definite)
706 | (Self::Possible, Self::Possible) => Self::Possible,
707 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => {
708 Self::Mismatch {
711 reason: r1.prefer_expression(r2),
712 }
713 }
714 (Self::Mismatch { reason }, _) | (_, Self::Mismatch { reason }) => {
715 Self::Mismatch { reason }
716 }
717 }
718 }
719}
720
721#[derive(Copy, Clone, Debug, Eq, PartialEq)]
725pub enum BinaryMismatchReason {
726 Expression,
728
729 DefaultSet,
731}
732
733impl BinaryMismatchReason {
734 fn prefer_expression(self, other: Self) -> Self {
735 match (self, other) {
736 (Self::Expression, _) | (_, Self::Expression) => Self::Expression,
737 (Self::DefaultSet, Self::DefaultSet) => Self::DefaultSet,
738 }
739 }
740}
741
742impl fmt::Display for BinaryMismatchReason {
743 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
744 match self {
745 Self::Expression => write!(f, "didn't match filtersets"),
746 Self::DefaultSet => write!(f, "didn't match the default set"),
747 }
748 }
749}
750
751#[derive(Clone, Debug, Eq, PartialEq)]
752enum FilterNameMatch {
753 MatchEmptyPatterns,
755 MatchWithPatterns,
757 Mismatch(MismatchReason),
759}
760
761impl FilterNameMatch {
762 #[cfg(test)]
763 fn is_match(&self) -> bool {
764 match self {
765 Self::MatchEmptyPatterns | Self::MatchWithPatterns => true,
766 Self::Mismatch(_) => false,
767 }
768 }
769}
770
771#[cfg(test)]
772mod tests {
773 use super::*;
774 use proptest::{collection::vec, prelude::*};
775 use test_strategy::proptest;
776
777 #[proptest(cases = 50)]
778 fn proptest_empty(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
779 let patterns = TestFilterPatterns::default();
780 let test_filter = TestFilter::new(
781 NextestRunMode::Test,
782 RunIgnored::Default,
783 patterns,
784 Vec::new(),
785 )
786 .unwrap();
787 for test_name in test_names {
788 let test_name = TestCaseName::new(&test_name);
789 prop_assert!(test_filter.filter_name_match(&test_name).is_match());
790 }
791 }
792
793 #[proptest(cases = 50)]
795 fn proptest_exact(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
796 let patterns = TestFilterPatterns::new(test_names.clone());
798 let test_filter = TestFilter::new(
799 NextestRunMode::Test,
800 RunIgnored::Default,
801 patterns,
802 Vec::new(),
803 )
804 .unwrap();
805 for test_name in &test_names {
806 let test_name = TestCaseName::new(test_name);
807 prop_assert!(test_filter.filter_name_match(&test_name).is_match());
808 }
809
810 let mut patterns = TestFilterPatterns::default();
812 for test_name in &test_names {
813 patterns.add_exact_pattern(test_name.clone());
814 }
815 let test_filter = TestFilter::new(
816 NextestRunMode::Test,
817 RunIgnored::Default,
818 patterns,
819 Vec::new(),
820 )
821 .unwrap();
822 for test_name in &test_names {
823 let test_name = TestCaseName::new(test_name);
824 prop_assert!(test_filter.filter_name_match(&test_name).is_match());
825 }
826 }
827
828 #[proptest(cases = 50)]
830 fn proptest_substring(
831 #[strategy(vec([any::<String>(); 3], 0..16))] substring_prefix_suffixes: Vec<[String; 3]>,
832 ) {
833 let mut patterns = TestFilterPatterns::default();
834 let mut test_names = Vec::with_capacity(substring_prefix_suffixes.len());
835 for [substring, prefix, suffix] in substring_prefix_suffixes {
836 test_names.push(prefix + &substring + &suffix);
837 patterns.add_substring_pattern(substring);
838 }
839
840 let test_filter = TestFilter::new(
841 NextestRunMode::Test,
842 RunIgnored::Default,
843 patterns,
844 Vec::new(),
845 )
846 .unwrap();
847 for test_name in test_names {
848 let test_name = TestCaseName::new(&test_name);
849 prop_assert!(test_filter.filter_name_match(&test_name).is_match());
850 }
851 }
852
853 #[proptest(cases = 50)]
855 fn proptest_no_match(substring: String, prefix: String, suffix: String) {
856 prop_assume!(!substring.is_empty() && !prefix.is_empty() && !suffix.is_empty());
857 let pattern = prefix + &substring + &suffix;
858 let patterns = TestFilterPatterns::new(vec![pattern]);
859 let test_filter = TestFilter::new(
860 NextestRunMode::Test,
861 RunIgnored::Default,
862 patterns,
863 Vec::new(),
864 )
865 .unwrap();
866 let substring = TestCaseName::new(&substring);
867 prop_assert!(!test_filter.filter_name_match(&substring).is_match());
868 }
869
870 fn test_name(s: &str) -> TestCaseName {
871 TestCaseName::new(s)
872 }
873
874 #[test]
875 fn pattern_examples() {
876 let mut patterns = TestFilterPatterns::new(vec!["foo".to_string()]);
877 patterns.add_substring_pattern("bar".to_string());
878 patterns.add_exact_pattern("baz".to_string());
879 patterns.add_skip_pattern("quux".to_string());
880 patterns.add_skip_exact_pattern("quuz".to_string());
881
882 let resolved = patterns.clone().resolve().unwrap();
883
884 assert_eq!(
886 resolved.name_match(&test_name("foo")),
887 FilterNameMatch::MatchWithPatterns,
888 );
889 assert_eq!(
890 resolved.name_match(&test_name("1foo2")),
891 FilterNameMatch::MatchWithPatterns,
892 );
893 assert_eq!(
894 resolved.name_match(&test_name("bar")),
895 FilterNameMatch::MatchWithPatterns,
896 );
897 assert_eq!(
898 resolved.name_match(&test_name("x_bar_y")),
899 FilterNameMatch::MatchWithPatterns,
900 );
901
902 assert_eq!(
904 resolved.name_match(&test_name("baz")),
905 FilterNameMatch::MatchWithPatterns,
906 );
907 assert_eq!(
908 resolved.name_match(&test_name("abazb")),
909 FilterNameMatch::Mismatch(MismatchReason::String),
910 );
911
912 assert_eq!(
914 resolved.name_match(&test_name("bazfoo")),
915 FilterNameMatch::MatchWithPatterns,
916 );
917
918 assert_eq!(
920 resolved.name_match(&test_name("quux")),
921 FilterNameMatch::Mismatch(MismatchReason::String),
922 );
923 assert_eq!(
924 resolved.name_match(&test_name("1quux2")),
925 FilterNameMatch::Mismatch(MismatchReason::String),
926 );
927
928 assert_eq!(
930 resolved.name_match(&test_name("quuxbar")),
931 FilterNameMatch::Mismatch(MismatchReason::String),
932 );
933
934 assert_eq!(
936 resolved.name_match(&test_name("quuz")),
937 FilterNameMatch::Mismatch(MismatchReason::String),
938 );
939
940 patterns.add_skip_pattern("baz".to_string());
942 let resolved = patterns.resolve().unwrap();
943 assert_eq!(
944 resolved.name_match(&test_name("quuxbaz")),
945 FilterNameMatch::Mismatch(MismatchReason::String),
946 );
947 }
948
949 #[test]
950 fn skip_only_pattern_examples() {
951 let mut patterns = TestFilterPatterns::default();
952 patterns.add_skip_pattern("foo".to_string());
953 patterns.add_skip_pattern("bar".to_string());
954 patterns.add_skip_exact_pattern("baz".to_string());
955
956 let resolved = patterns.clone().resolve().unwrap();
957
958 assert_eq!(
960 resolved.name_match(&test_name("foo")),
961 FilterNameMatch::Mismatch(MismatchReason::String),
962 );
963 assert_eq!(
964 resolved.name_match(&test_name("1foo2")),
965 FilterNameMatch::Mismatch(MismatchReason::String),
966 );
967 assert_eq!(
968 resolved.name_match(&test_name("bar")),
969 FilterNameMatch::Mismatch(MismatchReason::String),
970 );
971 assert_eq!(
972 resolved.name_match(&test_name("x_bar_y")),
973 FilterNameMatch::Mismatch(MismatchReason::String),
974 );
975
976 assert_eq!(
978 resolved.name_match(&test_name("baz")),
979 FilterNameMatch::Mismatch(MismatchReason::String),
980 );
981 assert_eq!(
982 resolved.name_match(&test_name("abazb")),
983 FilterNameMatch::MatchWithPatterns,
984 );
985
986 assert_eq!(
988 resolved.name_match(&test_name("quux")),
989 FilterNameMatch::MatchWithPatterns,
990 );
991 }
992
993 #[test]
994 fn empty_pattern_examples() {
995 let patterns = TestFilterPatterns::default();
996 let resolved = patterns.resolve().unwrap();
997 assert_eq!(resolved, ResolvedFilterPatterns::All);
998
999 assert_eq!(
1001 resolved.name_match(&test_name("foo")),
1002 FilterNameMatch::MatchEmptyPatterns,
1003 );
1004 assert_eq!(
1005 resolved.name_match(&test_name("1foo2")),
1006 FilterNameMatch::MatchEmptyPatterns,
1007 );
1008 assert_eq!(
1009 resolved.name_match(&test_name("bar")),
1010 FilterNameMatch::MatchEmptyPatterns,
1011 );
1012 assert_eq!(
1013 resolved.name_match(&test_name("x_bar_y")),
1014 FilterNameMatch::MatchEmptyPatterns,
1015 );
1016 assert_eq!(
1017 resolved.name_match(&test_name("baz")),
1018 FilterNameMatch::MatchEmptyPatterns,
1019 );
1020 assert_eq!(
1021 resolved.name_match(&test_name("abazb")),
1022 FilterNameMatch::MatchEmptyPatterns,
1023 );
1024 }
1025}