1use crate::{
9 errors::TestFilterBuilderError,
10 list::RustTestArtifact,
11 partition::{Partitioner, PartitionerBuilder},
12 run_mode::NextestRunMode,
13};
14use aho_corasick::AhoCorasick;
15use nextest_filtering::{EvalContext, Filterset, TestQuery};
16use nextest_metadata::{FilterMatch, MismatchReason, RustTestKind};
17use std::{collections::HashSet, fmt, mem};
18
19#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
21pub enum RunIgnored {
22 #[default]
26 Default,
27
28 Only,
30
31 All,
33}
34
35#[derive(Clone, Copy, Debug)]
37pub enum FilterBound {
38 DefaultSet,
40
41 All,
43}
44
45#[derive(Clone, Debug, Eq, PartialEq)]
46pub struct BinaryFilter {
48 exprs: TestFilterExprs,
49}
50
51impl BinaryFilter {
52 pub fn new(exprs: Vec<Filterset>) -> Self {
56 let exprs = if exprs.is_empty() {
57 TestFilterExprs::All
58 } else {
59 TestFilterExprs::Sets(exprs)
60 };
61 Self { exprs }
62 }
63
64 pub fn check_match(
67 &self,
68 test_binary: &RustTestArtifact<'_>,
69 ecx: &EvalContext<'_>,
70 bound: FilterBound,
71 ) -> FilterBinaryMatch {
72 let query = test_binary.to_binary_query();
73 let expr_result = match &self.exprs {
74 TestFilterExprs::All => FilterBinaryMatch::Definite,
75 TestFilterExprs::Sets(exprs) => exprs.iter().fold(
76 FilterBinaryMatch::Mismatch {
77 reason: BinaryMismatchReason::Expression,
79 },
80 |acc, expr| {
81 acc.logic_or(FilterBinaryMatch::from_result(
82 expr.matches_binary(&query, ecx),
83 BinaryMismatchReason::Expression,
84 ))
85 },
86 ),
87 };
88
89 if !expr_result.is_match() {
91 return expr_result;
92 }
93
94 match bound {
95 FilterBound::All => expr_result,
96 FilterBound::DefaultSet => expr_result.logic_and(FilterBinaryMatch::from_result(
97 ecx.default_filter.matches_binary(&query, ecx),
98 BinaryMismatchReason::DefaultSet,
99 )),
100 }
101 }
102}
103
104#[derive(Clone, Debug, Eq, PartialEq)]
106pub struct TestFilterBuilder {
107 mode: NextestRunMode,
108 run_ignored: RunIgnored,
109 partitioner_builder: Option<PartitionerBuilder>,
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, TestFilterBuilderError> {
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: &str) -> FilterNameMatch {
328 match self {
329 Self::All => FilterNameMatch::MatchEmptyPatterns,
330 Self::SkipOnly {
331 skip_patterns: _,
333 skip_exact_patterns,
334 skip_pattern_matcher,
335 } => {
336 if skip_exact_patterns.contains(test_name)
337 || skip_pattern_matcher.is_match(test_name)
338 {
339 FilterNameMatch::Mismatch(MismatchReason::String)
340 } else {
341 FilterNameMatch::MatchWithPatterns
342 }
343 }
344 Self::Patterns {
345 patterns: _,
347 exact_patterns,
348 skip_patterns: _,
350 skip_exact_patterns,
351 pattern_matcher,
352 skip_pattern_matcher,
353 } => {
354 if skip_exact_patterns.contains(test_name)
356 || skip_pattern_matcher.is_match(test_name)
357 {
358 FilterNameMatch::Mismatch(MismatchReason::String)
359 } else if exact_patterns.contains(test_name) || pattern_matcher.is_match(test_name)
360 {
361 FilterNameMatch::MatchWithPatterns
362 } else {
363 FilterNameMatch::Mismatch(MismatchReason::String)
364 }
365 }
366 }
367 }
368}
369
370impl PartialEq for ResolvedFilterPatterns {
371 fn eq(&self, other: &Self) -> bool {
372 match (self, other) {
373 (Self::All, Self::All) => true,
374 (
375 Self::SkipOnly {
376 skip_patterns,
377 skip_exact_patterns,
378 skip_pattern_matcher: _,
380 },
381 Self::SkipOnly {
382 skip_patterns: other_skip_patterns,
383 skip_exact_patterns: other_skip_exact_patterns,
384 skip_pattern_matcher: _,
385 },
386 ) => {
387 skip_patterns == other_skip_patterns
388 && skip_exact_patterns == other_skip_exact_patterns
389 }
390 (
391 Self::Patterns {
392 patterns,
393 exact_patterns,
394 skip_patterns,
395 skip_exact_patterns,
396 pattern_matcher: _,
399 skip_pattern_matcher: _,
400 },
401 Self::Patterns {
402 patterns: other_patterns,
403 exact_patterns: other_exact_patterns,
404 skip_patterns: other_skip_patterns,
405 skip_exact_patterns: other_skip_exact_patterns,
406 pattern_matcher: _,
407 skip_pattern_matcher: _,
408 },
409 ) => {
410 patterns == other_patterns
411 && exact_patterns == other_exact_patterns
412 && skip_patterns == other_skip_patterns
413 && skip_exact_patterns == other_skip_exact_patterns
414 }
415 _ => false,
416 }
417 }
418}
419
420impl Eq for ResolvedFilterPatterns {}
421
422impl TestFilterBuilder {
423 pub fn new(
427 mode: NextestRunMode,
428 run_ignored: RunIgnored,
429 partitioner_builder: Option<PartitionerBuilder>,
430 patterns: TestFilterPatterns,
431 exprs: Vec<Filterset>,
432 ) -> Result<Self, TestFilterBuilderError> {
433 let patterns = patterns.resolve()?;
434
435 let binary_filter = BinaryFilter::new(exprs);
436
437 Ok(Self {
438 mode,
439 run_ignored,
440 partitioner_builder,
441 patterns,
442 binary_filter,
443 })
444 }
445
446 pub fn filter_binary_match(
452 &self,
453 test_binary: &RustTestArtifact<'_>,
454 ecx: &EvalContext<'_>,
455 bound: FilterBound,
456 ) -> FilterBinaryMatch {
457 self.binary_filter.check_match(test_binary, ecx, bound)
458 }
459
460 pub fn default_set(mode: NextestRunMode, run_ignored: RunIgnored) -> Self {
462 let binary_filter = BinaryFilter::new(Vec::new());
463 Self {
464 mode,
465 run_ignored,
466 partitioner_builder: None,
467 patterns: ResolvedFilterPatterns::default(),
468 binary_filter,
469 }
470 }
471
472 pub fn mode(&self) -> NextestRunMode {
474 self.mode
475 }
476
477 pub fn build(&self) -> TestFilter<'_> {
481 let partitioner = self
482 .partitioner_builder
483 .as_ref()
484 .map(|partitioner_builder| partitioner_builder.build());
485 TestFilter {
486 builder: self,
487 partitioner,
488 }
489 }
490}
491
492#[derive(Copy, Clone, Debug)]
496pub enum FilterBinaryMatch {
497 Definite,
499
500 Possible,
502
503 Mismatch {
505 reason: BinaryMismatchReason,
507 },
508}
509
510impl FilterBinaryMatch {
511 fn from_result(result: Option<bool>, reason: BinaryMismatchReason) -> Self {
512 match result {
513 Some(true) => Self::Definite,
514 None => Self::Possible,
515 Some(false) => Self::Mismatch { reason },
516 }
517 }
518
519 fn is_match(self) -> bool {
520 match self {
521 Self::Definite | Self::Possible => true,
522 Self::Mismatch { .. } => false,
523 }
524 }
525
526 fn logic_or(self, other: Self) -> Self {
527 match (self, other) {
528 (Self::Definite, _) | (_, Self::Definite) => Self::Definite,
529 (Self::Possible, _) | (_, Self::Possible) => Self::Possible,
530 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => Self::Mismatch {
531 reason: r1.prefer_expression(r2),
532 },
533 }
534 }
535
536 fn logic_and(self, other: Self) -> Self {
537 match (self, other) {
538 (Self::Definite, Self::Definite) => Self::Definite,
539 (Self::Definite, Self::Possible)
540 | (Self::Possible, Self::Definite)
541 | (Self::Possible, Self::Possible) => Self::Possible,
542 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => {
543 Self::Mismatch {
546 reason: r1.prefer_expression(r2),
547 }
548 }
549 (Self::Mismatch { reason }, _) | (_, Self::Mismatch { reason }) => {
550 Self::Mismatch { reason }
551 }
552 }
553 }
554}
555
556#[derive(Copy, Clone, Debug, Eq, PartialEq)]
560pub enum BinaryMismatchReason {
561 Expression,
563
564 DefaultSet,
566}
567
568impl BinaryMismatchReason {
569 fn prefer_expression(self, other: Self) -> Self {
570 match (self, other) {
571 (Self::Expression, _) | (_, Self::Expression) => Self::Expression,
572 (Self::DefaultSet, Self::DefaultSet) => Self::DefaultSet,
573 }
574 }
575}
576
577impl fmt::Display for BinaryMismatchReason {
578 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
579 match self {
580 Self::Expression => write!(f, "didn't match filtersets"),
581 Self::DefaultSet => write!(f, "didn't match the default set"),
582 }
583 }
584}
585
586#[derive(Debug)]
588pub struct TestFilter<'builder> {
589 builder: &'builder TestFilterBuilder,
590 partitioner: Option<Box<dyn Partitioner>>,
591}
592
593impl TestFilter<'_> {
594 pub fn filter_match(
596 &mut self,
597 test_binary: &RustTestArtifact<'_>,
598 test_name: &str,
599 test_kind: &RustTestKind,
600 ecx: &EvalContext<'_>,
601 bound: FilterBound,
602 ignored: bool,
603 ) -> FilterMatch {
604 if let Some(mismatch) = self.filter_benchmark_mismatch(test_kind) {
605 return mismatch;
606 }
607
608 if let Some(mismatch) = self.filter_ignored_mismatch(ignored) {
609 return mismatch;
610 }
611
612 {
613 use FilterNameMatch::*;
634 match (
635 self.filter_name_match(test_name),
636 self.filter_expression_match(test_binary, test_name, ecx, bound),
637 ) {
638 (
640 MatchEmptyPatterns | MatchWithPatterns,
641 MatchEmptyPatterns | MatchWithPatterns,
642 ) => {}
643 (Mismatch(reason), _) | (_, Mismatch(reason)) => {
649 return FilterMatch::Mismatch { reason };
650 }
651 }
652 }
653
654 if let Some(mismatch) = self.filter_partition_mismatch(test_name) {
659 return mismatch;
660 }
661
662 FilterMatch::Matches
663 }
664
665 fn filter_benchmark_mismatch(&self, test_kind: &RustTestKind) -> Option<FilterMatch> {
666 if self.builder.mode == NextestRunMode::Benchmark && test_kind != &RustTestKind::BENCH {
667 Some(FilterMatch::Mismatch {
668 reason: MismatchReason::NotBenchmark,
669 })
670 } else {
671 None
672 }
673 }
674
675 fn filter_ignored_mismatch(&self, ignored: bool) -> Option<FilterMatch> {
676 match self.builder.run_ignored {
677 RunIgnored::Only => {
678 if !ignored {
679 return Some(FilterMatch::Mismatch {
680 reason: MismatchReason::Ignored,
681 });
682 }
683 }
684 RunIgnored::Default => {
685 if ignored {
686 return Some(FilterMatch::Mismatch {
687 reason: MismatchReason::Ignored,
688 });
689 }
690 }
691 _ => {}
692 }
693 None
694 }
695
696 fn filter_name_match(&self, test_name: &str) -> FilterNameMatch {
697 self.builder.patterns.name_match(test_name)
698 }
699
700 fn filter_expression_match(
701 &self,
702 test_binary: &RustTestArtifact<'_>,
703 test_name: &str,
704 ecx: &EvalContext<'_>,
705 bound: FilterBound,
706 ) -> FilterNameMatch {
707 let query = TestQuery {
708 binary_query: test_binary.to_binary_query(),
709 test_name,
710 };
711
712 let expr_result = match &self.builder.binary_filter.exprs {
713 TestFilterExprs::All => FilterNameMatch::MatchEmptyPatterns,
714 TestFilterExprs::Sets(exprs) => {
715 if exprs.iter().any(|expr| expr.matches_test(&query, ecx)) {
716 FilterNameMatch::MatchWithPatterns
717 } else {
718 return FilterNameMatch::Mismatch(MismatchReason::Expression);
719 }
720 }
721 };
722
723 match bound {
724 FilterBound::All => expr_result,
725 FilterBound::DefaultSet => {
726 if ecx.default_filter.matches_test(&query, ecx) {
727 expr_result
728 } else {
729 FilterNameMatch::Mismatch(MismatchReason::DefaultFilter)
730 }
731 }
732 }
733 }
734
735 fn filter_partition_mismatch(&mut self, test_name: &str) -> Option<FilterMatch> {
736 let partition_match = match &mut self.partitioner {
737 Some(partitioner) => partitioner.test_matches(test_name),
738 None => true,
739 };
740 if partition_match {
741 None
742 } else {
743 Some(FilterMatch::Mismatch {
744 reason: MismatchReason::Partition,
745 })
746 }
747 }
748}
749
750#[derive(Clone, Debug, Eq, PartialEq)]
751enum FilterNameMatch {
752 MatchEmptyPatterns,
754 MatchWithPatterns,
756 Mismatch(MismatchReason),
758}
759
760impl FilterNameMatch {
761 #[cfg(test)]
762 fn is_match(&self) -> bool {
763 match self {
764 Self::MatchEmptyPatterns | Self::MatchWithPatterns => true,
765 Self::Mismatch(_) => false,
766 }
767 }
768}
769
770#[cfg(test)]
771mod tests {
772 use super::*;
773 use proptest::{collection::vec, prelude::*};
774 use test_strategy::proptest;
775
776 #[proptest(cases = 50)]
777 fn proptest_empty(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
778 let patterns = TestFilterPatterns::default();
779 let test_filter = TestFilterBuilder::new(
780 NextestRunMode::Test,
781 RunIgnored::Default,
782 None,
783 patterns,
784 Vec::new(),
785 )
786 .unwrap();
787 let single_filter = test_filter.build();
788 for test_name in test_names {
789 prop_assert!(single_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 = TestFilterBuilder::new(
799 NextestRunMode::Test,
800 RunIgnored::Default,
801 None,
802 patterns,
803 Vec::new(),
804 )
805 .unwrap();
806 let single_filter = test_filter.build();
807 for test_name in &test_names {
808 prop_assert!(single_filter.filter_name_match(test_name).is_match());
809 }
810
811 let mut patterns = TestFilterPatterns::default();
813 for test_name in &test_names {
814 patterns.add_exact_pattern(test_name.clone());
815 }
816 let test_filter = TestFilterBuilder::new(
817 NextestRunMode::Test,
818 RunIgnored::Default,
819 None,
820 patterns,
821 Vec::new(),
822 )
823 .unwrap();
824 let single_filter = test_filter.build();
825 for test_name in &test_names {
826 prop_assert!(single_filter.filter_name_match(test_name).is_match());
827 }
828 }
829
830 #[proptest(cases = 50)]
832 fn proptest_substring(
833 #[strategy(vec([any::<String>(); 3], 0..16))] substring_prefix_suffixes: Vec<[String; 3]>,
834 ) {
835 let mut patterns = TestFilterPatterns::default();
836 let mut test_names = Vec::with_capacity(substring_prefix_suffixes.len());
837 for [substring, prefix, suffix] in substring_prefix_suffixes {
838 test_names.push(prefix + &substring + &suffix);
839 patterns.add_substring_pattern(substring);
840 }
841
842 let test_filter = TestFilterBuilder::new(
843 NextestRunMode::Test,
844 RunIgnored::Default,
845 None,
846 patterns,
847 Vec::new(),
848 )
849 .unwrap();
850 let single_filter = test_filter.build();
851 for test_name in test_names {
852 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
853 }
854 }
855
856 #[proptest(cases = 50)]
858 fn proptest_no_match(substring: String, prefix: String, suffix: String) {
859 prop_assume!(!substring.is_empty() && !prefix.is_empty() && !suffix.is_empty());
860 let pattern = prefix + &substring + &suffix;
861 let patterns = TestFilterPatterns::new(vec![pattern]);
862 let test_filter = TestFilterBuilder::new(
863 NextestRunMode::Test,
864 RunIgnored::Default,
865 None,
866 patterns,
867 Vec::new(),
868 )
869 .unwrap();
870 let single_filter = test_filter.build();
871 prop_assert!(!single_filter.filter_name_match(&substring).is_match());
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("foo"),
887 FilterNameMatch::MatchWithPatterns,
888 );
889 assert_eq!(
890 resolved.name_match("1foo2"),
891 FilterNameMatch::MatchWithPatterns,
892 );
893 assert_eq!(
894 resolved.name_match("bar"),
895 FilterNameMatch::MatchWithPatterns,
896 );
897 assert_eq!(
898 resolved.name_match("x_bar_y"),
899 FilterNameMatch::MatchWithPatterns,
900 );
901
902 assert_eq!(
904 resolved.name_match("baz"),
905 FilterNameMatch::MatchWithPatterns,
906 );
907 assert_eq!(
908 resolved.name_match("abazb"),
909 FilterNameMatch::Mismatch(MismatchReason::String),
910 );
911
912 assert_eq!(
914 resolved.name_match("bazfoo"),
915 FilterNameMatch::MatchWithPatterns,
916 );
917
918 assert_eq!(
920 resolved.name_match("quux"),
921 FilterNameMatch::Mismatch(MismatchReason::String),
922 );
923 assert_eq!(
924 resolved.name_match("1quux2"),
925 FilterNameMatch::Mismatch(MismatchReason::String),
926 );
927
928 assert_eq!(
930 resolved.name_match("quuxbar"),
931 FilterNameMatch::Mismatch(MismatchReason::String),
932 );
933
934 assert_eq!(
936 resolved.name_match("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("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("foo"),
961 FilterNameMatch::Mismatch(MismatchReason::String),
962 );
963 assert_eq!(
964 resolved.name_match("1foo2"),
965 FilterNameMatch::Mismatch(MismatchReason::String),
966 );
967 assert_eq!(
968 resolved.name_match("bar"),
969 FilterNameMatch::Mismatch(MismatchReason::String),
970 );
971 assert_eq!(
972 resolved.name_match("x_bar_y"),
973 FilterNameMatch::Mismatch(MismatchReason::String),
974 );
975
976 assert_eq!(
978 resolved.name_match("baz"),
979 FilterNameMatch::Mismatch(MismatchReason::String),
980 );
981 assert_eq!(
982 resolved.name_match("abazb"),
983 FilterNameMatch::MatchWithPatterns,
984 );
985
986 assert_eq!(
988 resolved.name_match("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("foo"),
1002 FilterNameMatch::MatchEmptyPatterns,
1003 );
1004 assert_eq!(
1005 resolved.name_match("1foo2"),
1006 FilterNameMatch::MatchEmptyPatterns,
1007 );
1008 assert_eq!(
1009 resolved.name_match("bar"),
1010 FilterNameMatch::MatchEmptyPatterns,
1011 );
1012 assert_eq!(
1013 resolved.name_match("x_bar_y"),
1014 FilterNameMatch::MatchEmptyPatterns,
1015 );
1016 assert_eq!(
1017 resolved.name_match("baz"),
1018 FilterNameMatch::MatchEmptyPatterns,
1019 );
1020 assert_eq!(
1021 resolved.name_match("abazb"),
1022 FilterNameMatch::MatchEmptyPatterns,
1023 );
1024 }
1025}