1use crate::{
9 errors::TestFilterBuilderError,
10 list::RustTestArtifact,
11 partition::{Partitioner, PartitionerBuilder},
12 record::ComputedRerunInfo,
13 run_mode::NextestRunMode,
14};
15use aho_corasick::AhoCorasick;
16use nextest_filtering::{EvalContext, Filterset, TestQuery};
17use nextest_metadata::{FilterMatch, MismatchReason, RustTestKind, TestCaseName};
18use std::{collections::HashSet, fmt, mem};
19
20#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
22pub enum RunIgnored {
23 #[default]
27 Default,
28
29 Only,
31
32 All,
34}
35
36#[derive(Clone, Copy, Debug)]
38pub enum FilterBound {
39 DefaultSet,
41
42 All,
44}
45
46#[derive(Clone, Debug, Eq, PartialEq)]
47pub struct BinaryFilter {
49 exprs: TestFilterExprs,
50}
51
52impl BinaryFilter {
53 pub fn new(exprs: Vec<Filterset>) -> Self {
57 let exprs = if exprs.is_empty() {
58 TestFilterExprs::All
59 } else {
60 TestFilterExprs::Sets(exprs)
61 };
62 Self { exprs }
63 }
64
65 pub fn check_match(
68 &self,
69 test_binary: &RustTestArtifact<'_>,
70 ecx: &EvalContext<'_>,
71 bound: FilterBound,
72 ) -> FilterBinaryMatch {
73 let query = test_binary.to_binary_query();
74 let expr_result = match &self.exprs {
75 TestFilterExprs::All => FilterBinaryMatch::Definite,
76 TestFilterExprs::Sets(exprs) => exprs.iter().fold(
77 FilterBinaryMatch::Mismatch {
78 reason: BinaryMismatchReason::Expression,
80 },
81 |acc, expr| {
82 acc.logic_or(FilterBinaryMatch::from_result(
83 expr.matches_binary(&query, ecx),
84 BinaryMismatchReason::Expression,
85 ))
86 },
87 ),
88 };
89
90 if !expr_result.is_match() {
92 return expr_result;
93 }
94
95 match bound {
96 FilterBound::All => expr_result,
97 FilterBound::DefaultSet => expr_result.logic_and(FilterBinaryMatch::from_result(
98 ecx.default_filter.matches_binary(&query, ecx),
99 BinaryMismatchReason::DefaultSet,
100 )),
101 }
102 }
103}
104
105#[derive(Clone, Debug)]
107pub struct TestFilterBuilder {
108 mode: NextestRunMode,
109 rerun_info: Option<ComputedRerunInfo>,
110 run_ignored: RunIgnored,
111 partitioner_builder: Option<PartitionerBuilder>,
112 patterns: ResolvedFilterPatterns,
113 binary_filter: BinaryFilter,
114}
115
116#[derive(Clone, Debug, Eq, PartialEq)]
117enum TestFilterExprs {
118 All,
120
121 Sets(Vec<Filterset>),
123}
124
125#[derive(Clone, Debug, Eq, PartialEq)]
127pub enum TestFilterPatterns {
128 SkipOnly {
131 skip_patterns: Vec<String>,
133
134 skip_exact_patterns: HashSet<String>,
136 },
137
138 Patterns {
145 patterns: Vec<String>,
147
148 exact_patterns: HashSet<String>,
150
151 skip_patterns: Vec<String>,
153
154 skip_exact_patterns: HashSet<String>,
156 },
157}
158
159impl Default for TestFilterPatterns {
160 fn default() -> Self {
161 Self::SkipOnly {
162 skip_patterns: Vec::new(),
163 skip_exact_patterns: HashSet::new(),
164 }
165 }
166}
167
168impl TestFilterPatterns {
169 pub fn new(substring_patterns: Vec<String>) -> Self {
174 if substring_patterns.is_empty() {
175 Self::default()
176 } else {
177 Self::Patterns {
178 patterns: substring_patterns,
179 exact_patterns: HashSet::new(),
180 skip_patterns: Vec::new(),
181 skip_exact_patterns: HashSet::new(),
182 }
183 }
184 }
185
186 pub fn add_substring_pattern(&mut self, pattern: String) {
188 match self {
189 Self::SkipOnly {
190 skip_patterns,
191 skip_exact_patterns,
192 } => {
193 *self = Self::Patterns {
194 patterns: vec![pattern],
195 exact_patterns: HashSet::new(),
196 skip_patterns: mem::take(skip_patterns),
197 skip_exact_patterns: mem::take(skip_exact_patterns),
198 };
199 }
200 Self::Patterns { patterns, .. } => {
201 patterns.push(pattern);
202 }
203 }
204 }
205
206 pub fn add_exact_pattern(&mut self, pattern: String) {
208 match self {
209 Self::SkipOnly {
210 skip_patterns,
211 skip_exact_patterns,
212 } => {
213 *self = Self::Patterns {
214 patterns: Vec::new(),
215 exact_patterns: [pattern].into_iter().collect(),
216 skip_patterns: mem::take(skip_patterns),
217 skip_exact_patterns: mem::take(skip_exact_patterns),
218 };
219 }
220 Self::Patterns { exact_patterns, .. } => {
221 exact_patterns.insert(pattern);
222 }
223 }
224 }
225
226 pub fn add_skip_pattern(&mut self, pattern: String) {
228 match self {
229 Self::SkipOnly { skip_patterns, .. } => {
230 skip_patterns.push(pattern);
231 }
232 Self::Patterns { skip_patterns, .. } => {
233 skip_patterns.push(pattern);
234 }
235 }
236 }
237
238 pub fn add_skip_exact_pattern(&mut self, pattern: String) {
240 match self {
241 Self::SkipOnly {
242 skip_exact_patterns,
243 ..
244 } => {
245 skip_exact_patterns.insert(pattern);
246 }
247 Self::Patterns {
248 skip_exact_patterns,
249 ..
250 } => {
251 skip_exact_patterns.insert(pattern);
252 }
253 }
254 }
255
256 fn resolve(self) -> Result<ResolvedFilterPatterns, TestFilterBuilderError> {
257 match self {
258 Self::SkipOnly {
259 mut skip_patterns,
260 skip_exact_patterns,
261 } => {
262 if skip_patterns.is_empty() {
263 Ok(ResolvedFilterPatterns::All)
264 } else {
265 skip_patterns.sort_unstable();
267 let skip_pattern_matcher = Box::new(AhoCorasick::new(&skip_patterns)?);
268 Ok(ResolvedFilterPatterns::SkipOnly {
269 skip_patterns,
270 skip_pattern_matcher,
271 skip_exact_patterns,
272 })
273 }
274 }
275 Self::Patterns {
276 mut patterns,
277 exact_patterns,
278 mut skip_patterns,
279 skip_exact_patterns,
280 } => {
281 patterns.sort_unstable();
283 skip_patterns.sort_unstable();
284
285 let pattern_matcher = Box::new(AhoCorasick::new(&patterns)?);
286 let skip_pattern_matcher = Box::new(AhoCorasick::new(&skip_patterns)?);
287
288 Ok(ResolvedFilterPatterns::Patterns {
289 patterns,
290 exact_patterns,
291 skip_patterns,
292 skip_exact_patterns,
293 pattern_matcher,
294 skip_pattern_matcher,
295 })
296 }
297 }
298 }
299}
300
301#[derive(Clone, Debug, Default)]
302enum ResolvedFilterPatterns {
303 #[default]
308 All,
309
310 SkipOnly {
312 skip_patterns: Vec<String>,
313 skip_pattern_matcher: Box<AhoCorasick>,
314 skip_exact_patterns: HashSet<String>,
315 },
316
317 Patterns {
319 patterns: Vec<String>,
320 exact_patterns: HashSet<String>,
321 skip_patterns: Vec<String>,
322 skip_exact_patterns: HashSet<String>,
323 pattern_matcher: Box<AhoCorasick>,
324 skip_pattern_matcher: Box<AhoCorasick>,
325 },
326}
327
328impl ResolvedFilterPatterns {
329 fn name_match(&self, test_name: &TestCaseName) -> FilterNameMatch {
330 let test_name = test_name.as_str();
331 match self {
332 Self::All => FilterNameMatch::MatchEmptyPatterns,
333 Self::SkipOnly {
334 skip_patterns: _,
336 skip_exact_patterns,
337 skip_pattern_matcher,
338 } => {
339 if skip_exact_patterns.contains(test_name)
340 || skip_pattern_matcher.is_match(test_name)
341 {
342 FilterNameMatch::Mismatch(MismatchReason::String)
343 } else {
344 FilterNameMatch::MatchWithPatterns
345 }
346 }
347 Self::Patterns {
348 patterns: _,
350 exact_patterns,
351 skip_patterns: _,
353 skip_exact_patterns,
354 pattern_matcher,
355 skip_pattern_matcher,
356 } => {
357 if skip_exact_patterns.contains(test_name)
359 || skip_pattern_matcher.is_match(test_name)
360 {
361 FilterNameMatch::Mismatch(MismatchReason::String)
362 } else if exact_patterns.contains(test_name) || pattern_matcher.is_match(test_name)
363 {
364 FilterNameMatch::MatchWithPatterns
365 } else {
366 FilterNameMatch::Mismatch(MismatchReason::String)
367 }
368 }
369 }
370 }
371}
372
373impl PartialEq for ResolvedFilterPatterns {
374 fn eq(&self, other: &Self) -> bool {
375 match (self, other) {
376 (Self::All, Self::All) => true,
377 (
378 Self::SkipOnly {
379 skip_patterns,
380 skip_exact_patterns,
381 skip_pattern_matcher: _,
383 },
384 Self::SkipOnly {
385 skip_patterns: other_skip_patterns,
386 skip_exact_patterns: other_skip_exact_patterns,
387 skip_pattern_matcher: _,
388 },
389 ) => {
390 skip_patterns == other_skip_patterns
391 && skip_exact_patterns == other_skip_exact_patterns
392 }
393 (
394 Self::Patterns {
395 patterns,
396 exact_patterns,
397 skip_patterns,
398 skip_exact_patterns,
399 pattern_matcher: _,
402 skip_pattern_matcher: _,
403 },
404 Self::Patterns {
405 patterns: other_patterns,
406 exact_patterns: other_exact_patterns,
407 skip_patterns: other_skip_patterns,
408 skip_exact_patterns: other_skip_exact_patterns,
409 pattern_matcher: _,
410 skip_pattern_matcher: _,
411 },
412 ) => {
413 patterns == other_patterns
414 && exact_patterns == other_exact_patterns
415 && skip_patterns == other_skip_patterns
416 && skip_exact_patterns == other_skip_exact_patterns
417 }
418 _ => false,
419 }
420 }
421}
422
423impl Eq for ResolvedFilterPatterns {}
424
425impl TestFilterBuilder {
426 pub fn new(
430 mode: NextestRunMode,
431 run_ignored: RunIgnored,
432 partitioner_builder: Option<PartitionerBuilder>,
433 patterns: TestFilterPatterns,
434 exprs: Vec<Filterset>,
435 ) -> Result<Self, TestFilterBuilderError> {
436 let patterns = patterns.resolve()?;
437
438 let binary_filter = BinaryFilter::new(exprs);
439
440 Ok(Self {
441 mode,
442 rerun_info: None,
443 run_ignored,
444 partitioner_builder,
445 patterns,
446 binary_filter,
447 })
448 }
449
450 pub fn filter_binary_match(
456 &self,
457 test_binary: &RustTestArtifact<'_>,
458 ecx: &EvalContext<'_>,
459 bound: FilterBound,
460 ) -> FilterBinaryMatch {
461 self.binary_filter.check_match(test_binary, ecx, bound)
462 }
463
464 pub fn default_set(mode: NextestRunMode, run_ignored: RunIgnored) -> Self {
466 let binary_filter = BinaryFilter::new(Vec::new());
467 Self {
468 mode,
469 rerun_info: None,
470 run_ignored,
471 partitioner_builder: None,
472 patterns: ResolvedFilterPatterns::default(),
473 binary_filter,
474 }
475 }
476
477 pub fn set_outstanding_tests(&mut self, rerun_info: ComputedRerunInfo) {
479 self.rerun_info = Some(rerun_info);
480 }
481
482 pub fn mode(&self) -> NextestRunMode {
484 self.mode
485 }
486
487 pub fn patterns_eq(&self, other: &Self) -> bool {
489 self.patterns == other.patterns
490 }
491
492 pub fn build(&self) -> TestFilter<'_> {
496 let partitioner = self
497 .partitioner_builder
498 .as_ref()
499 .map(|partitioner_builder| partitioner_builder.build());
500 TestFilter {
501 builder: self,
502 partitioner,
503 }
504 }
505
506 pub fn into_rerun_info(self) -> Option<ComputedRerunInfo> {
508 self.rerun_info
509 }
510}
511
512#[derive(Copy, Clone, Debug)]
516pub enum FilterBinaryMatch {
517 Definite,
519
520 Possible,
522
523 Mismatch {
525 reason: BinaryMismatchReason,
527 },
528}
529
530impl FilterBinaryMatch {
531 fn from_result(result: Option<bool>, reason: BinaryMismatchReason) -> Self {
532 match result {
533 Some(true) => Self::Definite,
534 None => Self::Possible,
535 Some(false) => Self::Mismatch { reason },
536 }
537 }
538
539 fn is_match(self) -> bool {
540 match self {
541 Self::Definite | Self::Possible => true,
542 Self::Mismatch { .. } => false,
543 }
544 }
545
546 fn logic_or(self, other: Self) -> Self {
547 match (self, other) {
548 (Self::Definite, _) | (_, Self::Definite) => Self::Definite,
549 (Self::Possible, _) | (_, Self::Possible) => Self::Possible,
550 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => Self::Mismatch {
551 reason: r1.prefer_expression(r2),
552 },
553 }
554 }
555
556 fn logic_and(self, other: Self) -> Self {
557 match (self, other) {
558 (Self::Definite, Self::Definite) => Self::Definite,
559 (Self::Definite, Self::Possible)
560 | (Self::Possible, Self::Definite)
561 | (Self::Possible, Self::Possible) => Self::Possible,
562 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => {
563 Self::Mismatch {
566 reason: r1.prefer_expression(r2),
567 }
568 }
569 (Self::Mismatch { reason }, _) | (_, Self::Mismatch { reason }) => {
570 Self::Mismatch { reason }
571 }
572 }
573 }
574}
575
576#[derive(Copy, Clone, Debug, Eq, PartialEq)]
580pub enum BinaryMismatchReason {
581 Expression,
583
584 DefaultSet,
586}
587
588impl BinaryMismatchReason {
589 fn prefer_expression(self, other: Self) -> Self {
590 match (self, other) {
591 (Self::Expression, _) | (_, Self::Expression) => Self::Expression,
592 (Self::DefaultSet, Self::DefaultSet) => Self::DefaultSet,
593 }
594 }
595}
596
597impl fmt::Display for BinaryMismatchReason {
598 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
599 match self {
600 Self::Expression => write!(f, "didn't match filtersets"),
601 Self::DefaultSet => write!(f, "didn't match the default set"),
602 }
603 }
604}
605
606#[derive(Debug)]
608pub struct TestFilter<'builder> {
609 builder: &'builder TestFilterBuilder,
610 partitioner: Option<Box<dyn Partitioner>>,
611}
612
613impl TestFilter<'_> {
614 pub fn filter_match(
616 &mut self,
617 test_binary: &RustTestArtifact<'_>,
618 test_name: &TestCaseName,
619 test_kind: &RustTestKind,
620 ecx: &EvalContext<'_>,
621 bound: FilterBound,
622 ignored: bool,
623 ) -> FilterMatch {
624 if let Some(mismatch) = self.filter_benchmark_mismatch(test_kind) {
626 return mismatch;
627 }
628
629 if self.is_rerun_already_passed(test_binary, test_name) {
641 let _ = self.filter_match_base(test_binary, test_name, ecx, bound, ignored);
643 return FilterMatch::Mismatch {
644 reason: MismatchReason::RerunAlreadyPassed,
645 };
646 }
647
648 self.filter_match_base(test_binary, test_name, ecx, bound, ignored)
649 }
650
651 fn filter_match_base(
653 &mut self,
654 test_binary: &RustTestArtifact<'_>,
655 test_name: &TestCaseName,
656 ecx: &EvalContext<'_>,
657 bound: FilterBound,
658 ignored: bool,
659 ) -> FilterMatch {
660 if let Some(mismatch) = self.filter_ignored_mismatch(ignored) {
661 return mismatch;
662 }
663
664 {
665 use FilterNameMatch::*;
686 match (
687 self.filter_name_match(test_name),
688 self.filter_expression_match(test_binary, test_name, ecx, bound),
689 ) {
690 (
692 MatchEmptyPatterns | MatchWithPatterns,
693 MatchEmptyPatterns | MatchWithPatterns,
694 ) => {}
695 (Mismatch(reason), _) | (_, Mismatch(reason)) => {
701 return FilterMatch::Mismatch { reason };
702 }
703 }
704 }
705
706 if let Some(mismatch) = self.filter_partition_mismatch(test_name) {
711 return mismatch;
712 }
713
714 FilterMatch::Matches
715 }
716
717 fn is_rerun_already_passed(
719 &self,
720 test_binary: &RustTestArtifact<'_>,
721 test_name: &TestCaseName,
722 ) -> bool {
723 if let Some(rerun_info) = &self.builder.rerun_info
724 && let Some(suite) = rerun_info.test_suites.get(&test_binary.binary_id)
725 {
726 return suite.passing.contains(test_name);
727 }
728 false
729 }
730
731 fn filter_benchmark_mismatch(&self, test_kind: &RustTestKind) -> Option<FilterMatch> {
732 if self.builder.mode == NextestRunMode::Benchmark && test_kind != &RustTestKind::BENCH {
733 Some(FilterMatch::Mismatch {
734 reason: MismatchReason::NotBenchmark,
735 })
736 } else {
737 None
738 }
739 }
740
741 fn filter_ignored_mismatch(&self, ignored: bool) -> Option<FilterMatch> {
742 match self.builder.run_ignored {
743 RunIgnored::Only => {
744 if !ignored {
745 return Some(FilterMatch::Mismatch {
746 reason: MismatchReason::Ignored,
747 });
748 }
749 }
750 RunIgnored::Default => {
751 if ignored {
752 return Some(FilterMatch::Mismatch {
753 reason: MismatchReason::Ignored,
754 });
755 }
756 }
757 _ => {}
758 }
759 None
760 }
761
762 fn filter_name_match(&self, test_name: &TestCaseName) -> FilterNameMatch {
763 self.builder.patterns.name_match(test_name)
764 }
765
766 fn filter_expression_match(
767 &self,
768 test_binary: &RustTestArtifact<'_>,
769 test_name: &TestCaseName,
770 ecx: &EvalContext<'_>,
771 bound: FilterBound,
772 ) -> FilterNameMatch {
773 let query = TestQuery {
774 binary_query: test_binary.to_binary_query(),
775 test_name,
776 };
777
778 let expr_result = match &self.builder.binary_filter.exprs {
779 TestFilterExprs::All => FilterNameMatch::MatchEmptyPatterns,
780 TestFilterExprs::Sets(exprs) => {
781 if exprs.iter().any(|expr| expr.matches_test(&query, ecx)) {
782 FilterNameMatch::MatchWithPatterns
783 } else {
784 return FilterNameMatch::Mismatch(MismatchReason::Expression);
785 }
786 }
787 };
788
789 match bound {
790 FilterBound::All => expr_result,
791 FilterBound::DefaultSet => {
792 if ecx.default_filter.matches_test(&query, ecx) {
793 expr_result
794 } else {
795 FilterNameMatch::Mismatch(MismatchReason::DefaultFilter)
796 }
797 }
798 }
799 }
800
801 fn filter_partition_mismatch(&mut self, test_name: &TestCaseName) -> Option<FilterMatch> {
802 let partition_match = match &mut self.partitioner {
803 Some(partitioner) => partitioner.test_matches(test_name.as_str()),
804 None => true,
805 };
806 if partition_match {
807 None
808 } else {
809 Some(FilterMatch::Mismatch {
810 reason: MismatchReason::Partition,
811 })
812 }
813 }
814}
815
816#[derive(Clone, Debug, Eq, PartialEq)]
817enum FilterNameMatch {
818 MatchEmptyPatterns,
820 MatchWithPatterns,
822 Mismatch(MismatchReason),
824}
825
826impl FilterNameMatch {
827 #[cfg(test)]
828 fn is_match(&self) -> bool {
829 match self {
830 Self::MatchEmptyPatterns | Self::MatchWithPatterns => true,
831 Self::Mismatch(_) => false,
832 }
833 }
834}
835
836#[cfg(test)]
837mod tests {
838 use super::*;
839 use proptest::{collection::vec, prelude::*};
840 use test_strategy::proptest;
841
842 #[proptest(cases = 50)]
843 fn proptest_empty(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
844 let patterns = TestFilterPatterns::default();
845 let test_filter = TestFilterBuilder::new(
846 NextestRunMode::Test,
847 RunIgnored::Default,
848 None,
849 patterns,
850 Vec::new(),
851 )
852 .unwrap();
853 let single_filter = test_filter.build();
854 for test_name in test_names {
855 let test_name = TestCaseName::new(&test_name);
856 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
857 }
858 }
859
860 #[proptest(cases = 50)]
862 fn proptest_exact(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
863 let patterns = TestFilterPatterns::new(test_names.clone());
865 let test_filter = TestFilterBuilder::new(
866 NextestRunMode::Test,
867 RunIgnored::Default,
868 None,
869 patterns,
870 Vec::new(),
871 )
872 .unwrap();
873 let single_filter = test_filter.build();
874 for test_name in &test_names {
875 let test_name = TestCaseName::new(test_name);
876 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
877 }
878
879 let mut patterns = TestFilterPatterns::default();
881 for test_name in &test_names {
882 patterns.add_exact_pattern(test_name.clone());
883 }
884 let test_filter = TestFilterBuilder::new(
885 NextestRunMode::Test,
886 RunIgnored::Default,
887 None,
888 patterns,
889 Vec::new(),
890 )
891 .unwrap();
892 let single_filter = test_filter.build();
893 for test_name in &test_names {
894 let test_name = TestCaseName::new(test_name);
895 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
896 }
897 }
898
899 #[proptest(cases = 50)]
901 fn proptest_substring(
902 #[strategy(vec([any::<String>(); 3], 0..16))] substring_prefix_suffixes: Vec<[String; 3]>,
903 ) {
904 let mut patterns = TestFilterPatterns::default();
905 let mut test_names = Vec::with_capacity(substring_prefix_suffixes.len());
906 for [substring, prefix, suffix] in substring_prefix_suffixes {
907 test_names.push(prefix + &substring + &suffix);
908 patterns.add_substring_pattern(substring);
909 }
910
911 let test_filter = TestFilterBuilder::new(
912 NextestRunMode::Test,
913 RunIgnored::Default,
914 None,
915 patterns,
916 Vec::new(),
917 )
918 .unwrap();
919 let single_filter = test_filter.build();
920 for test_name in test_names {
921 let test_name = TestCaseName::new(&test_name);
922 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
923 }
924 }
925
926 #[proptest(cases = 50)]
928 fn proptest_no_match(substring: String, prefix: String, suffix: String) {
929 prop_assume!(!substring.is_empty() && !prefix.is_empty() && !suffix.is_empty());
930 let pattern = prefix + &substring + &suffix;
931 let patterns = TestFilterPatterns::new(vec![pattern]);
932 let test_filter = TestFilterBuilder::new(
933 NextestRunMode::Test,
934 RunIgnored::Default,
935 None,
936 patterns,
937 Vec::new(),
938 )
939 .unwrap();
940 let single_filter = test_filter.build();
941 let substring = TestCaseName::new(&substring);
942 prop_assert!(!single_filter.filter_name_match(&substring).is_match());
943 }
944
945 fn test_name(s: &str) -> TestCaseName {
946 TestCaseName::new(s)
947 }
948
949 #[test]
950 fn pattern_examples() {
951 let mut patterns = TestFilterPatterns::new(vec!["foo".to_string()]);
952 patterns.add_substring_pattern("bar".to_string());
953 patterns.add_exact_pattern("baz".to_string());
954 patterns.add_skip_pattern("quux".to_string());
955 patterns.add_skip_exact_pattern("quuz".to_string());
956
957 let resolved = patterns.clone().resolve().unwrap();
958
959 assert_eq!(
961 resolved.name_match(&test_name("foo")),
962 FilterNameMatch::MatchWithPatterns,
963 );
964 assert_eq!(
965 resolved.name_match(&test_name("1foo2")),
966 FilterNameMatch::MatchWithPatterns,
967 );
968 assert_eq!(
969 resolved.name_match(&test_name("bar")),
970 FilterNameMatch::MatchWithPatterns,
971 );
972 assert_eq!(
973 resolved.name_match(&test_name("x_bar_y")),
974 FilterNameMatch::MatchWithPatterns,
975 );
976
977 assert_eq!(
979 resolved.name_match(&test_name("baz")),
980 FilterNameMatch::MatchWithPatterns,
981 );
982 assert_eq!(
983 resolved.name_match(&test_name("abazb")),
984 FilterNameMatch::Mismatch(MismatchReason::String),
985 );
986
987 assert_eq!(
989 resolved.name_match(&test_name("bazfoo")),
990 FilterNameMatch::MatchWithPatterns,
991 );
992
993 assert_eq!(
995 resolved.name_match(&test_name("quux")),
996 FilterNameMatch::Mismatch(MismatchReason::String),
997 );
998 assert_eq!(
999 resolved.name_match(&test_name("1quux2")),
1000 FilterNameMatch::Mismatch(MismatchReason::String),
1001 );
1002
1003 assert_eq!(
1005 resolved.name_match(&test_name("quuxbar")),
1006 FilterNameMatch::Mismatch(MismatchReason::String),
1007 );
1008
1009 assert_eq!(
1011 resolved.name_match(&test_name("quuz")),
1012 FilterNameMatch::Mismatch(MismatchReason::String),
1013 );
1014
1015 patterns.add_skip_pattern("baz".to_string());
1017 let resolved = patterns.resolve().unwrap();
1018 assert_eq!(
1019 resolved.name_match(&test_name("quuxbaz")),
1020 FilterNameMatch::Mismatch(MismatchReason::String),
1021 );
1022 }
1023
1024 #[test]
1025 fn skip_only_pattern_examples() {
1026 let mut patterns = TestFilterPatterns::default();
1027 patterns.add_skip_pattern("foo".to_string());
1028 patterns.add_skip_pattern("bar".to_string());
1029 patterns.add_skip_exact_pattern("baz".to_string());
1030
1031 let resolved = patterns.clone().resolve().unwrap();
1032
1033 assert_eq!(
1035 resolved.name_match(&test_name("foo")),
1036 FilterNameMatch::Mismatch(MismatchReason::String),
1037 );
1038 assert_eq!(
1039 resolved.name_match(&test_name("1foo2")),
1040 FilterNameMatch::Mismatch(MismatchReason::String),
1041 );
1042 assert_eq!(
1043 resolved.name_match(&test_name("bar")),
1044 FilterNameMatch::Mismatch(MismatchReason::String),
1045 );
1046 assert_eq!(
1047 resolved.name_match(&test_name("x_bar_y")),
1048 FilterNameMatch::Mismatch(MismatchReason::String),
1049 );
1050
1051 assert_eq!(
1053 resolved.name_match(&test_name("baz")),
1054 FilterNameMatch::Mismatch(MismatchReason::String),
1055 );
1056 assert_eq!(
1057 resolved.name_match(&test_name("abazb")),
1058 FilterNameMatch::MatchWithPatterns,
1059 );
1060
1061 assert_eq!(
1063 resolved.name_match(&test_name("quux")),
1064 FilterNameMatch::MatchWithPatterns,
1065 );
1066 }
1067
1068 #[test]
1069 fn empty_pattern_examples() {
1070 let patterns = TestFilterPatterns::default();
1071 let resolved = patterns.resolve().unwrap();
1072 assert_eq!(resolved, ResolvedFilterPatterns::All);
1073
1074 assert_eq!(
1076 resolved.name_match(&test_name("foo")),
1077 FilterNameMatch::MatchEmptyPatterns,
1078 );
1079 assert_eq!(
1080 resolved.name_match(&test_name("1foo2")),
1081 FilterNameMatch::MatchEmptyPatterns,
1082 );
1083 assert_eq!(
1084 resolved.name_match(&test_name("bar")),
1085 FilterNameMatch::MatchEmptyPatterns,
1086 );
1087 assert_eq!(
1088 resolved.name_match(&test_name("x_bar_y")),
1089 FilterNameMatch::MatchEmptyPatterns,
1090 );
1091 assert_eq!(
1092 resolved.name_match(&test_name("baz")),
1093 FilterNameMatch::MatchEmptyPatterns,
1094 );
1095 assert_eq!(
1096 resolved.name_match(&test_name("abazb")),
1097 FilterNameMatch::MatchEmptyPatterns,
1098 );
1099 }
1100}