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, TestCaseName};
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: &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 TestFilterBuilder {
424 pub fn new(
428 mode: NextestRunMode,
429 run_ignored: RunIgnored,
430 partitioner_builder: Option<PartitionerBuilder>,
431 patterns: TestFilterPatterns,
432 exprs: Vec<Filterset>,
433 ) -> Result<Self, TestFilterBuilderError> {
434 let patterns = patterns.resolve()?;
435
436 let binary_filter = BinaryFilter::new(exprs);
437
438 Ok(Self {
439 mode,
440 run_ignored,
441 partitioner_builder,
442 patterns,
443 binary_filter,
444 })
445 }
446
447 pub fn filter_binary_match(
453 &self,
454 test_binary: &RustTestArtifact<'_>,
455 ecx: &EvalContext<'_>,
456 bound: FilterBound,
457 ) -> FilterBinaryMatch {
458 self.binary_filter.check_match(test_binary, ecx, bound)
459 }
460
461 pub fn default_set(mode: NextestRunMode, run_ignored: RunIgnored) -> Self {
463 let binary_filter = BinaryFilter::new(Vec::new());
464 Self {
465 mode,
466 run_ignored,
467 partitioner_builder: None,
468 patterns: ResolvedFilterPatterns::default(),
469 binary_filter,
470 }
471 }
472
473 pub fn mode(&self) -> NextestRunMode {
475 self.mode
476 }
477
478 pub fn build(&self) -> TestFilter<'_> {
482 let partitioner = self
483 .partitioner_builder
484 .as_ref()
485 .map(|partitioner_builder| partitioner_builder.build());
486 TestFilter {
487 builder: self,
488 partitioner,
489 }
490 }
491}
492
493#[derive(Copy, Clone, Debug)]
497pub enum FilterBinaryMatch {
498 Definite,
500
501 Possible,
503
504 Mismatch {
506 reason: BinaryMismatchReason,
508 },
509}
510
511impl FilterBinaryMatch {
512 fn from_result(result: Option<bool>, reason: BinaryMismatchReason) -> Self {
513 match result {
514 Some(true) => Self::Definite,
515 None => Self::Possible,
516 Some(false) => Self::Mismatch { reason },
517 }
518 }
519
520 fn is_match(self) -> bool {
521 match self {
522 Self::Definite | Self::Possible => true,
523 Self::Mismatch { .. } => false,
524 }
525 }
526
527 fn logic_or(self, other: Self) -> Self {
528 match (self, other) {
529 (Self::Definite, _) | (_, Self::Definite) => Self::Definite,
530 (Self::Possible, _) | (_, Self::Possible) => Self::Possible,
531 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => Self::Mismatch {
532 reason: r1.prefer_expression(r2),
533 },
534 }
535 }
536
537 fn logic_and(self, other: Self) -> Self {
538 match (self, other) {
539 (Self::Definite, Self::Definite) => Self::Definite,
540 (Self::Definite, Self::Possible)
541 | (Self::Possible, Self::Definite)
542 | (Self::Possible, Self::Possible) => Self::Possible,
543 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => {
544 Self::Mismatch {
547 reason: r1.prefer_expression(r2),
548 }
549 }
550 (Self::Mismatch { reason }, _) | (_, Self::Mismatch { reason }) => {
551 Self::Mismatch { reason }
552 }
553 }
554 }
555}
556
557#[derive(Copy, Clone, Debug, Eq, PartialEq)]
561pub enum BinaryMismatchReason {
562 Expression,
564
565 DefaultSet,
567}
568
569impl BinaryMismatchReason {
570 fn prefer_expression(self, other: Self) -> Self {
571 match (self, other) {
572 (Self::Expression, _) | (_, Self::Expression) => Self::Expression,
573 (Self::DefaultSet, Self::DefaultSet) => Self::DefaultSet,
574 }
575 }
576}
577
578impl fmt::Display for BinaryMismatchReason {
579 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
580 match self {
581 Self::Expression => write!(f, "didn't match filtersets"),
582 Self::DefaultSet => write!(f, "didn't match the default set"),
583 }
584 }
585}
586
587#[derive(Debug)]
589pub struct TestFilter<'builder> {
590 builder: &'builder TestFilterBuilder,
591 partitioner: Option<Box<dyn Partitioner>>,
592}
593
594impl TestFilter<'_> {
595 pub fn filter_match(
597 &mut self,
598 test_binary: &RustTestArtifact<'_>,
599 test_name: &TestCaseName,
600 test_kind: &RustTestKind,
601 ecx: &EvalContext<'_>,
602 bound: FilterBound,
603 ignored: bool,
604 ) -> FilterMatch {
605 if let Some(mismatch) = self.filter_benchmark_mismatch(test_kind) {
606 return mismatch;
607 }
608
609 if let Some(mismatch) = self.filter_ignored_mismatch(ignored) {
610 return mismatch;
611 }
612
613 {
614 use FilterNameMatch::*;
635 match (
636 self.filter_name_match(test_name),
637 self.filter_expression_match(test_binary, test_name, ecx, bound),
638 ) {
639 (
641 MatchEmptyPatterns | MatchWithPatterns,
642 MatchEmptyPatterns | MatchWithPatterns,
643 ) => {}
644 (Mismatch(reason), _) | (_, Mismatch(reason)) => {
650 return FilterMatch::Mismatch { reason };
651 }
652 }
653 }
654
655 if let Some(mismatch) = self.filter_partition_mismatch(test_name) {
660 return mismatch;
661 }
662
663 FilterMatch::Matches
664 }
665
666 fn filter_benchmark_mismatch(&self, test_kind: &RustTestKind) -> Option<FilterMatch> {
667 if self.builder.mode == NextestRunMode::Benchmark && test_kind != &RustTestKind::BENCH {
668 Some(FilterMatch::Mismatch {
669 reason: MismatchReason::NotBenchmark,
670 })
671 } else {
672 None
673 }
674 }
675
676 fn filter_ignored_mismatch(&self, ignored: bool) -> Option<FilterMatch> {
677 match self.builder.run_ignored {
678 RunIgnored::Only => {
679 if !ignored {
680 return Some(FilterMatch::Mismatch {
681 reason: MismatchReason::Ignored,
682 });
683 }
684 }
685 RunIgnored::Default => {
686 if ignored {
687 return Some(FilterMatch::Mismatch {
688 reason: MismatchReason::Ignored,
689 });
690 }
691 }
692 _ => {}
693 }
694 None
695 }
696
697 fn filter_name_match(&self, test_name: &TestCaseName) -> FilterNameMatch {
698 self.builder.patterns.name_match(test_name)
699 }
700
701 fn filter_expression_match(
702 &self,
703 test_binary: &RustTestArtifact<'_>,
704 test_name: &TestCaseName,
705 ecx: &EvalContext<'_>,
706 bound: FilterBound,
707 ) -> FilterNameMatch {
708 let query = TestQuery {
709 binary_query: test_binary.to_binary_query(),
710 test_name,
711 };
712
713 let expr_result = match &self.builder.binary_filter.exprs {
714 TestFilterExprs::All => FilterNameMatch::MatchEmptyPatterns,
715 TestFilterExprs::Sets(exprs) => {
716 if exprs.iter().any(|expr| expr.matches_test(&query, ecx)) {
717 FilterNameMatch::MatchWithPatterns
718 } else {
719 return FilterNameMatch::Mismatch(MismatchReason::Expression);
720 }
721 }
722 };
723
724 match bound {
725 FilterBound::All => expr_result,
726 FilterBound::DefaultSet => {
727 if ecx.default_filter.matches_test(&query, ecx) {
728 expr_result
729 } else {
730 FilterNameMatch::Mismatch(MismatchReason::DefaultFilter)
731 }
732 }
733 }
734 }
735
736 fn filter_partition_mismatch(&mut self, test_name: &TestCaseName) -> Option<FilterMatch> {
737 let partition_match = match &mut self.partitioner {
738 Some(partitioner) => partitioner.test_matches(test_name.as_str()),
739 None => true,
740 };
741 if partition_match {
742 None
743 } else {
744 Some(FilterMatch::Mismatch {
745 reason: MismatchReason::Partition,
746 })
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 = TestFilterBuilder::new(
781 NextestRunMode::Test,
782 RunIgnored::Default,
783 None,
784 patterns,
785 Vec::new(),
786 )
787 .unwrap();
788 let single_filter = test_filter.build();
789 for test_name in test_names {
790 let test_name = TestCaseName::new(&test_name);
791 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
792 }
793 }
794
795 #[proptest(cases = 50)]
797 fn proptest_exact(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
798 let patterns = TestFilterPatterns::new(test_names.clone());
800 let test_filter = TestFilterBuilder::new(
801 NextestRunMode::Test,
802 RunIgnored::Default,
803 None,
804 patterns,
805 Vec::new(),
806 )
807 .unwrap();
808 let single_filter = test_filter.build();
809 for test_name in &test_names {
810 let test_name = TestCaseName::new(test_name);
811 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
812 }
813
814 let mut patterns = TestFilterPatterns::default();
816 for test_name in &test_names {
817 patterns.add_exact_pattern(test_name.clone());
818 }
819 let test_filter = TestFilterBuilder::new(
820 NextestRunMode::Test,
821 RunIgnored::Default,
822 None,
823 patterns,
824 Vec::new(),
825 )
826 .unwrap();
827 let single_filter = test_filter.build();
828 for test_name in &test_names {
829 let test_name = TestCaseName::new(test_name);
830 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
831 }
832 }
833
834 #[proptest(cases = 50)]
836 fn proptest_substring(
837 #[strategy(vec([any::<String>(); 3], 0..16))] substring_prefix_suffixes: Vec<[String; 3]>,
838 ) {
839 let mut patterns = TestFilterPatterns::default();
840 let mut test_names = Vec::with_capacity(substring_prefix_suffixes.len());
841 for [substring, prefix, suffix] in substring_prefix_suffixes {
842 test_names.push(prefix + &substring + &suffix);
843 patterns.add_substring_pattern(substring);
844 }
845
846 let test_filter = TestFilterBuilder::new(
847 NextestRunMode::Test,
848 RunIgnored::Default,
849 None,
850 patterns,
851 Vec::new(),
852 )
853 .unwrap();
854 let single_filter = test_filter.build();
855 for test_name in test_names {
856 let test_name = TestCaseName::new(&test_name);
857 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
858 }
859 }
860
861 #[proptest(cases = 50)]
863 fn proptest_no_match(substring: String, prefix: String, suffix: String) {
864 prop_assume!(!substring.is_empty() && !prefix.is_empty() && !suffix.is_empty());
865 let pattern = prefix + &substring + &suffix;
866 let patterns = TestFilterPatterns::new(vec![pattern]);
867 let test_filter = TestFilterBuilder::new(
868 NextestRunMode::Test,
869 RunIgnored::Default,
870 None,
871 patterns,
872 Vec::new(),
873 )
874 .unwrap();
875 let single_filter = test_filter.build();
876 let substring = TestCaseName::new(&substring);
877 prop_assert!(!single_filter.filter_name_match(&substring).is_match());
878 }
879
880 fn test_name(s: &str) -> TestCaseName {
881 TestCaseName::new(s)
882 }
883
884 #[test]
885 fn pattern_examples() {
886 let mut patterns = TestFilterPatterns::new(vec!["foo".to_string()]);
887 patterns.add_substring_pattern("bar".to_string());
888 patterns.add_exact_pattern("baz".to_string());
889 patterns.add_skip_pattern("quux".to_string());
890 patterns.add_skip_exact_pattern("quuz".to_string());
891
892 let resolved = patterns.clone().resolve().unwrap();
893
894 assert_eq!(
896 resolved.name_match(&test_name("foo")),
897 FilterNameMatch::MatchWithPatterns,
898 );
899 assert_eq!(
900 resolved.name_match(&test_name("1foo2")),
901 FilterNameMatch::MatchWithPatterns,
902 );
903 assert_eq!(
904 resolved.name_match(&test_name("bar")),
905 FilterNameMatch::MatchWithPatterns,
906 );
907 assert_eq!(
908 resolved.name_match(&test_name("x_bar_y")),
909 FilterNameMatch::MatchWithPatterns,
910 );
911
912 assert_eq!(
914 resolved.name_match(&test_name("baz")),
915 FilterNameMatch::MatchWithPatterns,
916 );
917 assert_eq!(
918 resolved.name_match(&test_name("abazb")),
919 FilterNameMatch::Mismatch(MismatchReason::String),
920 );
921
922 assert_eq!(
924 resolved.name_match(&test_name("bazfoo")),
925 FilterNameMatch::MatchWithPatterns,
926 );
927
928 assert_eq!(
930 resolved.name_match(&test_name("quux")),
931 FilterNameMatch::Mismatch(MismatchReason::String),
932 );
933 assert_eq!(
934 resolved.name_match(&test_name("1quux2")),
935 FilterNameMatch::Mismatch(MismatchReason::String),
936 );
937
938 assert_eq!(
940 resolved.name_match(&test_name("quuxbar")),
941 FilterNameMatch::Mismatch(MismatchReason::String),
942 );
943
944 assert_eq!(
946 resolved.name_match(&test_name("quuz")),
947 FilterNameMatch::Mismatch(MismatchReason::String),
948 );
949
950 patterns.add_skip_pattern("baz".to_string());
952 let resolved = patterns.resolve().unwrap();
953 assert_eq!(
954 resolved.name_match(&test_name("quuxbaz")),
955 FilterNameMatch::Mismatch(MismatchReason::String),
956 );
957 }
958
959 #[test]
960 fn skip_only_pattern_examples() {
961 let mut patterns = TestFilterPatterns::default();
962 patterns.add_skip_pattern("foo".to_string());
963 patterns.add_skip_pattern("bar".to_string());
964 patterns.add_skip_exact_pattern("baz".to_string());
965
966 let resolved = patterns.clone().resolve().unwrap();
967
968 assert_eq!(
970 resolved.name_match(&test_name("foo")),
971 FilterNameMatch::Mismatch(MismatchReason::String),
972 );
973 assert_eq!(
974 resolved.name_match(&test_name("1foo2")),
975 FilterNameMatch::Mismatch(MismatchReason::String),
976 );
977 assert_eq!(
978 resolved.name_match(&test_name("bar")),
979 FilterNameMatch::Mismatch(MismatchReason::String),
980 );
981 assert_eq!(
982 resolved.name_match(&test_name("x_bar_y")),
983 FilterNameMatch::Mismatch(MismatchReason::String),
984 );
985
986 assert_eq!(
988 resolved.name_match(&test_name("baz")),
989 FilterNameMatch::Mismatch(MismatchReason::String),
990 );
991 assert_eq!(
992 resolved.name_match(&test_name("abazb")),
993 FilterNameMatch::MatchWithPatterns,
994 );
995
996 assert_eq!(
998 resolved.name_match(&test_name("quux")),
999 FilterNameMatch::MatchWithPatterns,
1000 );
1001 }
1002
1003 #[test]
1004 fn empty_pattern_examples() {
1005 let patterns = TestFilterPatterns::default();
1006 let resolved = patterns.resolve().unwrap();
1007 assert_eq!(resolved, ResolvedFilterPatterns::All);
1008
1009 assert_eq!(
1011 resolved.name_match(&test_name("foo")),
1012 FilterNameMatch::MatchEmptyPatterns,
1013 );
1014 assert_eq!(
1015 resolved.name_match(&test_name("1foo2")),
1016 FilterNameMatch::MatchEmptyPatterns,
1017 );
1018 assert_eq!(
1019 resolved.name_match(&test_name("bar")),
1020 FilterNameMatch::MatchEmptyPatterns,
1021 );
1022 assert_eq!(
1023 resolved.name_match(&test_name("x_bar_y")),
1024 FilterNameMatch::MatchEmptyPatterns,
1025 );
1026 assert_eq!(
1027 resolved.name_match(&test_name("baz")),
1028 FilterNameMatch::MatchEmptyPatterns,
1029 );
1030 assert_eq!(
1031 resolved.name_match(&test_name("abazb")),
1032 FilterNameMatch::MatchEmptyPatterns,
1033 );
1034 }
1035}