1use crate::{
9 errors::TestFilterBuilderError,
10 list::RustTestArtifact,
11 partition::{Partitioner, PartitionerBuilder},
12};
13use aho_corasick::AhoCorasick;
14use nextest_filtering::{EvalContext, Filterset, TestQuery};
15use nextest_metadata::{FilterMatch, MismatchReason};
16use std::{collections::HashSet, fmt, mem};
17
18#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
20pub enum RunIgnored {
21 #[default]
25 Default,
26
27 Only,
29
30 All,
32}
33
34#[derive(Clone, Copy, Debug)]
36pub enum FilterBound {
37 DefaultSet,
39
40 All,
42}
43
44#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct BinaryFilter {
47 exprs: TestFilterExprs,
48}
49
50impl BinaryFilter {
51 pub fn new(exprs: Vec<Filterset>) -> Self {
55 let exprs = if exprs.is_empty() {
56 TestFilterExprs::All
57 } else {
58 TestFilterExprs::Sets(exprs)
59 };
60 Self { exprs }
61 }
62
63 pub fn check_match(
66 &self,
67 test_binary: &RustTestArtifact<'_>,
68 ecx: &EvalContext<'_>,
69 bound: FilterBound,
70 ) -> FilterBinaryMatch {
71 let query = test_binary.to_binary_query();
72 let expr_result = match &self.exprs {
73 TestFilterExprs::All => FilterBinaryMatch::Definite,
74 TestFilterExprs::Sets(exprs) => exprs.iter().fold(
75 FilterBinaryMatch::Mismatch {
76 reason: BinaryMismatchReason::Expression,
78 },
79 |acc, expr| {
80 acc.logic_or(FilterBinaryMatch::from_result(
81 expr.matches_binary(&query, ecx),
82 BinaryMismatchReason::Expression,
83 ))
84 },
85 ),
86 };
87
88 if !expr_result.is_match() {
90 return expr_result;
91 }
92
93 match bound {
94 FilterBound::All => expr_result,
95 FilterBound::DefaultSet => expr_result.logic_and(FilterBinaryMatch::from_result(
96 ecx.default_filter.matches_binary(&query, ecx),
97 BinaryMismatchReason::DefaultSet,
98 )),
99 }
100 }
101}
102
103#[derive(Clone, Debug, Eq, PartialEq)]
105pub struct TestFilterBuilder {
106 run_ignored: RunIgnored,
107 partitioner_builder: Option<PartitionerBuilder>,
108 patterns: ResolvedFilterPatterns,
109 binary_filter: BinaryFilter,
110}
111
112#[derive(Clone, Debug, Eq, PartialEq)]
113enum TestFilterExprs {
114 All,
116
117 Sets(Vec<Filterset>),
119}
120
121#[derive(Clone, Debug, Eq, PartialEq)]
123pub enum TestFilterPatterns {
124 SkipOnly {
127 skip_patterns: Vec<String>,
129
130 skip_exact_patterns: HashSet<String>,
132 },
133
134 Patterns {
141 patterns: Vec<String>,
143
144 exact_patterns: HashSet<String>,
146
147 skip_patterns: Vec<String>,
149
150 skip_exact_patterns: HashSet<String>,
152 },
153}
154
155impl Default for TestFilterPatterns {
156 fn default() -> Self {
157 Self::SkipOnly {
158 skip_patterns: Vec::new(),
159 skip_exact_patterns: HashSet::new(),
160 }
161 }
162}
163
164impl TestFilterPatterns {
165 pub fn new(substring_patterns: Vec<String>) -> Self {
170 if substring_patterns.is_empty() {
171 Self::default()
172 } else {
173 Self::Patterns {
174 patterns: substring_patterns,
175 exact_patterns: HashSet::new(),
176 skip_patterns: Vec::new(),
177 skip_exact_patterns: HashSet::new(),
178 }
179 }
180 }
181
182 pub fn add_substring_pattern(&mut self, pattern: String) {
184 match self {
185 Self::SkipOnly {
186 skip_patterns,
187 skip_exact_patterns,
188 } => {
189 *self = Self::Patterns {
190 patterns: vec![pattern],
191 exact_patterns: HashSet::new(),
192 skip_patterns: mem::take(skip_patterns),
193 skip_exact_patterns: mem::take(skip_exact_patterns),
194 };
195 }
196 Self::Patterns { patterns, .. } => {
197 patterns.push(pattern);
198 }
199 }
200 }
201
202 pub fn add_exact_pattern(&mut self, pattern: String) {
204 match self {
205 Self::SkipOnly {
206 skip_patterns,
207 skip_exact_patterns,
208 } => {
209 *self = Self::Patterns {
210 patterns: Vec::new(),
211 exact_patterns: [pattern].into_iter().collect(),
212 skip_patterns: mem::take(skip_patterns),
213 skip_exact_patterns: mem::take(skip_exact_patterns),
214 };
215 }
216 Self::Patterns { exact_patterns, .. } => {
217 exact_patterns.insert(pattern);
218 }
219 }
220 }
221
222 pub fn add_skip_pattern(&mut self, pattern: String) {
224 match self {
225 Self::SkipOnly { skip_patterns, .. } => {
226 skip_patterns.push(pattern);
227 }
228 Self::Patterns { skip_patterns, .. } => {
229 skip_patterns.push(pattern);
230 }
231 }
232 }
233
234 pub fn add_skip_exact_pattern(&mut self, pattern: String) {
236 match self {
237 Self::SkipOnly {
238 skip_exact_patterns,
239 ..
240 } => {
241 skip_exact_patterns.insert(pattern);
242 }
243 Self::Patterns {
244 skip_exact_patterns,
245 ..
246 } => {
247 skip_exact_patterns.insert(pattern);
248 }
249 }
250 }
251
252 fn resolve(self) -> Result<ResolvedFilterPatterns, TestFilterBuilderError> {
253 match self {
254 Self::SkipOnly {
255 mut skip_patterns,
256 skip_exact_patterns,
257 } => {
258 if skip_patterns.is_empty() {
259 Ok(ResolvedFilterPatterns::All)
260 } else {
261 skip_patterns.sort_unstable();
263 let skip_pattern_matcher = Box::new(AhoCorasick::new(&skip_patterns)?);
264 Ok(ResolvedFilterPatterns::SkipOnly {
265 skip_patterns,
266 skip_pattern_matcher,
267 skip_exact_patterns,
268 })
269 }
270 }
271 Self::Patterns {
272 mut patterns,
273 exact_patterns,
274 mut skip_patterns,
275 skip_exact_patterns,
276 } => {
277 patterns.sort_unstable();
279 skip_patterns.sort_unstable();
280
281 let pattern_matcher = Box::new(AhoCorasick::new(&patterns)?);
282 let skip_pattern_matcher = Box::new(AhoCorasick::new(&skip_patterns)?);
283
284 Ok(ResolvedFilterPatterns::Patterns {
285 patterns,
286 exact_patterns,
287 skip_patterns,
288 skip_exact_patterns,
289 pattern_matcher,
290 skip_pattern_matcher,
291 })
292 }
293 }
294 }
295}
296
297#[derive(Clone, Debug)]
298enum ResolvedFilterPatterns {
299 All,
304
305 SkipOnly {
307 skip_patterns: Vec<String>,
308 skip_pattern_matcher: Box<AhoCorasick>,
309 skip_exact_patterns: HashSet<String>,
310 },
311
312 Patterns {
314 patterns: Vec<String>,
315 exact_patterns: HashSet<String>,
316 skip_patterns: Vec<String>,
317 skip_exact_patterns: HashSet<String>,
318 pattern_matcher: Box<AhoCorasick>,
319 skip_pattern_matcher: Box<AhoCorasick>,
320 },
321}
322
323impl Default for ResolvedFilterPatterns {
324 fn default() -> Self {
325 Self::All
326 }
327}
328
329impl ResolvedFilterPatterns {
330 fn name_match(&self, test_name: &str) -> FilterNameMatch {
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 run_ignored: RunIgnored,
431 partitioner_builder: Option<PartitionerBuilder>,
432 patterns: TestFilterPatterns,
433 exprs: Vec<Filterset>,
434 ) -> Result<Self, TestFilterBuilderError> {
435 let patterns = patterns.resolve()?;
436
437 let binary_filter = BinaryFilter::new(exprs);
438
439 Ok(Self {
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(run_ignored: RunIgnored) -> Self {
463 let binary_filter = BinaryFilter::new(Vec::new());
464 Self {
465 run_ignored,
466 partitioner_builder: None,
467 patterns: ResolvedFilterPatterns::default(),
468 binary_filter,
469 }
470 }
471
472 pub fn build(&self) -> TestFilter<'_> {
476 let partitioner = self
477 .partitioner_builder
478 .as_ref()
479 .map(|partitioner_builder| partitioner_builder.build());
480 TestFilter {
481 builder: self,
482 partitioner,
483 }
484 }
485}
486
487#[derive(Copy, Clone, Debug)]
491pub enum FilterBinaryMatch {
492 Definite,
494
495 Possible,
497
498 Mismatch {
500 reason: BinaryMismatchReason,
502 },
503}
504
505impl FilterBinaryMatch {
506 fn from_result(result: Option<bool>, reason: BinaryMismatchReason) -> Self {
507 match result {
508 Some(true) => Self::Definite,
509 None => Self::Possible,
510 Some(false) => Self::Mismatch { reason },
511 }
512 }
513
514 fn is_match(self) -> bool {
515 match self {
516 Self::Definite | Self::Possible => true,
517 Self::Mismatch { .. } => false,
518 }
519 }
520
521 fn logic_or(self, other: Self) -> Self {
522 match (self, other) {
523 (Self::Definite, _) | (_, Self::Definite) => Self::Definite,
524 (Self::Possible, _) | (_, Self::Possible) => Self::Possible,
525 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => Self::Mismatch {
526 reason: r1.prefer_expression(r2),
527 },
528 }
529 }
530
531 fn logic_and(self, other: Self) -> Self {
532 match (self, other) {
533 (Self::Definite, Self::Definite) => Self::Definite,
534 (Self::Definite, Self::Possible)
535 | (Self::Possible, Self::Definite)
536 | (Self::Possible, Self::Possible) => Self::Possible,
537 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => {
538 Self::Mismatch {
541 reason: r1.prefer_expression(r2),
542 }
543 }
544 (Self::Mismatch { reason }, _) | (_, Self::Mismatch { reason }) => {
545 Self::Mismatch { reason }
546 }
547 }
548 }
549}
550
551#[derive(Copy, Clone, Debug, Eq, PartialEq)]
555pub enum BinaryMismatchReason {
556 Expression,
558
559 DefaultSet,
561}
562
563impl BinaryMismatchReason {
564 fn prefer_expression(self, other: Self) -> Self {
565 match (self, other) {
566 (Self::Expression, _) | (_, Self::Expression) => Self::Expression,
567 (Self::DefaultSet, Self::DefaultSet) => Self::DefaultSet,
568 }
569 }
570}
571
572impl fmt::Display for BinaryMismatchReason {
573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574 match self {
575 Self::Expression => write!(f, "didn't match filtersets"),
576 Self::DefaultSet => write!(f, "didn't match the default set"),
577 }
578 }
579}
580
581#[derive(Debug)]
583pub struct TestFilter<'builder> {
584 builder: &'builder TestFilterBuilder,
585 partitioner: Option<Box<dyn Partitioner>>,
586}
587
588impl TestFilter<'_> {
589 pub fn filter_match(
591 &mut self,
592 test_binary: &RustTestArtifact<'_>,
593 test_name: &str,
594 ecx: &EvalContext<'_>,
595 bound: FilterBound,
596 ignored: bool,
597 ) -> FilterMatch {
598 self.filter_ignored_mismatch(ignored)
599 .or_else(|| {
600 use FilterNameMatch::*;
621 match (
622 self.filter_name_match(test_name),
623 self.filter_expression_match(test_binary, test_name, ecx, bound),
624 ) {
625 (
627 MatchEmptyPatterns | MatchWithPatterns,
628 MatchEmptyPatterns | MatchWithPatterns,
629 ) => None,
630 (Mismatch(reason), _) | (_, Mismatch(reason)) => {
636 Some(FilterMatch::Mismatch { reason })
637 }
638 }
639 })
640 .or_else(|| self.filter_partition_mismatch(test_name))
644 .unwrap_or(FilterMatch::Matches)
645 }
646
647 fn filter_ignored_mismatch(&self, ignored: bool) -> Option<FilterMatch> {
648 match self.builder.run_ignored {
649 RunIgnored::Only => {
650 if !ignored {
651 return Some(FilterMatch::Mismatch {
652 reason: MismatchReason::Ignored,
653 });
654 }
655 }
656 RunIgnored::Default => {
657 if ignored {
658 return Some(FilterMatch::Mismatch {
659 reason: MismatchReason::Ignored,
660 });
661 }
662 }
663 _ => {}
664 }
665 None
666 }
667
668 fn filter_name_match(&self, test_name: &str) -> FilterNameMatch {
669 self.builder.patterns.name_match(test_name)
670 }
671
672 fn filter_expression_match(
673 &self,
674 test_binary: &RustTestArtifact<'_>,
675 test_name: &str,
676 ecx: &EvalContext<'_>,
677 bound: FilterBound,
678 ) -> FilterNameMatch {
679 let query = TestQuery {
680 binary_query: test_binary.to_binary_query(),
681 test_name,
682 };
683
684 let expr_result = match &self.builder.binary_filter.exprs {
685 TestFilterExprs::All => FilterNameMatch::MatchEmptyPatterns,
686 TestFilterExprs::Sets(exprs) => {
687 if exprs.iter().any(|expr| expr.matches_test(&query, ecx)) {
688 FilterNameMatch::MatchWithPatterns
689 } else {
690 return FilterNameMatch::Mismatch(MismatchReason::Expression);
691 }
692 }
693 };
694
695 match bound {
696 FilterBound::All => expr_result,
697 FilterBound::DefaultSet => {
698 if ecx.default_filter.matches_test(&query, ecx) {
699 expr_result
700 } else {
701 FilterNameMatch::Mismatch(MismatchReason::DefaultFilter)
702 }
703 }
704 }
705 }
706
707 fn filter_partition_mismatch(&mut self, test_name: &str) -> Option<FilterMatch> {
708 let partition_match = match &mut self.partitioner {
709 Some(partitioner) => partitioner.test_matches(test_name),
710 None => true,
711 };
712 if partition_match {
713 None
714 } else {
715 Some(FilterMatch::Mismatch {
716 reason: MismatchReason::Partition,
717 })
718 }
719 }
720}
721
722#[derive(Clone, Debug, Eq, PartialEq)]
723enum FilterNameMatch {
724 MatchEmptyPatterns,
726 MatchWithPatterns,
728 Mismatch(MismatchReason),
730}
731
732impl FilterNameMatch {
733 #[cfg(test)]
734 fn is_match(&self) -> bool {
735 match self {
736 Self::MatchEmptyPatterns | Self::MatchWithPatterns => true,
737 Self::Mismatch(_) => false,
738 }
739 }
740}
741
742#[cfg(test)]
743mod tests {
744 use super::*;
745 use proptest::{collection::vec, prelude::*};
746 use test_strategy::proptest;
747
748 #[proptest(cases = 50)]
749 fn proptest_empty(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
750 let patterns = TestFilterPatterns::default();
751 let test_filter =
752 TestFilterBuilder::new(RunIgnored::Default, None, patterns, Vec::new()).unwrap();
753 let single_filter = test_filter.build();
754 for test_name in test_names {
755 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
756 }
757 }
758
759 #[proptest(cases = 50)]
761 fn proptest_exact(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
762 let patterns = TestFilterPatterns::new(test_names.clone());
764 let test_filter =
765 TestFilterBuilder::new(RunIgnored::Default, None, patterns, Vec::new()).unwrap();
766 let single_filter = test_filter.build();
767 for test_name in &test_names {
768 prop_assert!(single_filter.filter_name_match(test_name).is_match());
769 }
770
771 let mut patterns = TestFilterPatterns::default();
773 for test_name in &test_names {
774 patterns.add_exact_pattern(test_name.clone());
775 }
776 let test_filter =
777 TestFilterBuilder::new(RunIgnored::Default, None, patterns, Vec::new()).unwrap();
778 let single_filter = test_filter.build();
779 for test_name in &test_names {
780 prop_assert!(single_filter.filter_name_match(test_name).is_match());
781 }
782 }
783
784 #[proptest(cases = 50)]
786 fn proptest_substring(
787 #[strategy(vec([any::<String>(); 3], 0..16))] substring_prefix_suffixes: Vec<[String; 3]>,
788 ) {
789 let mut patterns = TestFilterPatterns::default();
790 let mut test_names = Vec::with_capacity(substring_prefix_suffixes.len());
791 for [substring, prefix, suffix] in substring_prefix_suffixes {
792 test_names.push(prefix + &substring + &suffix);
793 patterns.add_substring_pattern(substring);
794 }
795
796 let test_filter =
797 TestFilterBuilder::new(RunIgnored::Default, None, patterns, Vec::new()).unwrap();
798 let single_filter = test_filter.build();
799 for test_name in test_names {
800 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
801 }
802 }
803
804 #[proptest(cases = 50)]
806 fn proptest_no_match(substring: String, prefix: String, suffix: String) {
807 prop_assume!(!substring.is_empty() && !prefix.is_empty() && !suffix.is_empty());
808 let pattern = prefix + &substring + &suffix;
809 let patterns = TestFilterPatterns::new(vec![pattern]);
810 let test_filter =
811 TestFilterBuilder::new(RunIgnored::Default, None, patterns, Vec::new()).unwrap();
812 let single_filter = test_filter.build();
813 prop_assert!(!single_filter.filter_name_match(&substring).is_match());
814 }
815
816 #[test]
817 fn pattern_examples() {
818 let mut patterns = TestFilterPatterns::new(vec!["foo".to_string()]);
819 patterns.add_substring_pattern("bar".to_string());
820 patterns.add_exact_pattern("baz".to_string());
821 patterns.add_skip_pattern("quux".to_string());
822 patterns.add_skip_exact_pattern("quuz".to_string());
823
824 let resolved = patterns.clone().resolve().unwrap();
825
826 assert_eq!(
828 resolved.name_match("foo"),
829 FilterNameMatch::MatchWithPatterns,
830 );
831 assert_eq!(
832 resolved.name_match("1foo2"),
833 FilterNameMatch::MatchWithPatterns,
834 );
835 assert_eq!(
836 resolved.name_match("bar"),
837 FilterNameMatch::MatchWithPatterns,
838 );
839 assert_eq!(
840 resolved.name_match("x_bar_y"),
841 FilterNameMatch::MatchWithPatterns,
842 );
843
844 assert_eq!(
846 resolved.name_match("baz"),
847 FilterNameMatch::MatchWithPatterns,
848 );
849 assert_eq!(
850 resolved.name_match("abazb"),
851 FilterNameMatch::Mismatch(MismatchReason::String),
852 );
853
854 assert_eq!(
856 resolved.name_match("bazfoo"),
857 FilterNameMatch::MatchWithPatterns,
858 );
859
860 assert_eq!(
862 resolved.name_match("quux"),
863 FilterNameMatch::Mismatch(MismatchReason::String),
864 );
865 assert_eq!(
866 resolved.name_match("1quux2"),
867 FilterNameMatch::Mismatch(MismatchReason::String),
868 );
869
870 assert_eq!(
872 resolved.name_match("quuxbar"),
873 FilterNameMatch::Mismatch(MismatchReason::String),
874 );
875
876 assert_eq!(
878 resolved.name_match("quuz"),
879 FilterNameMatch::Mismatch(MismatchReason::String),
880 );
881
882 patterns.add_skip_pattern("baz".to_string());
884 let resolved = patterns.resolve().unwrap();
885 assert_eq!(
886 resolved.name_match("quuxbaz"),
887 FilterNameMatch::Mismatch(MismatchReason::String),
888 );
889 }
890
891 #[test]
892 fn skip_only_pattern_examples() {
893 let mut patterns = TestFilterPatterns::default();
894 patterns.add_skip_pattern("foo".to_string());
895 patterns.add_skip_pattern("bar".to_string());
896 patterns.add_skip_exact_pattern("baz".to_string());
897
898 let resolved = patterns.clone().resolve().unwrap();
899
900 assert_eq!(
902 resolved.name_match("foo"),
903 FilterNameMatch::Mismatch(MismatchReason::String),
904 );
905 assert_eq!(
906 resolved.name_match("1foo2"),
907 FilterNameMatch::Mismatch(MismatchReason::String),
908 );
909 assert_eq!(
910 resolved.name_match("bar"),
911 FilterNameMatch::Mismatch(MismatchReason::String),
912 );
913 assert_eq!(
914 resolved.name_match("x_bar_y"),
915 FilterNameMatch::Mismatch(MismatchReason::String),
916 );
917
918 assert_eq!(
920 resolved.name_match("baz"),
921 FilterNameMatch::Mismatch(MismatchReason::String),
922 );
923 assert_eq!(
924 resolved.name_match("abazb"),
925 FilterNameMatch::MatchWithPatterns,
926 );
927
928 assert_eq!(
930 resolved.name_match("quux"),
931 FilterNameMatch::MatchWithPatterns,
932 );
933 }
934
935 #[test]
936 fn empty_pattern_examples() {
937 let patterns = TestFilterPatterns::default();
938 let resolved = patterns.resolve().unwrap();
939 assert_eq!(resolved, ResolvedFilterPatterns::All);
940
941 assert_eq!(
943 resolved.name_match("foo"),
944 FilterNameMatch::MatchEmptyPatterns,
945 );
946 assert_eq!(
947 resolved.name_match("1foo2"),
948 FilterNameMatch::MatchEmptyPatterns,
949 );
950 assert_eq!(
951 resolved.name_match("bar"),
952 FilterNameMatch::MatchEmptyPatterns,
953 );
954 assert_eq!(
955 resolved.name_match("x_bar_y"),
956 FilterNameMatch::MatchEmptyPatterns,
957 );
958 assert_eq!(
959 resolved.name_match("baz"),
960 FilterNameMatch::MatchEmptyPatterns,
961 );
962 assert_eq!(
963 resolved.name_match("abazb"),
964 FilterNameMatch::MatchEmptyPatterns,
965 );
966 }
967}