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, Default)]
298enum ResolvedFilterPatterns {
299 #[default]
304 All,
305
306 SkipOnly {
308 skip_patterns: Vec<String>,
309 skip_pattern_matcher: Box<AhoCorasick>,
310 skip_exact_patterns: HashSet<String>,
311 },
312
313 Patterns {
315 patterns: Vec<String>,
316 exact_patterns: HashSet<String>,
317 skip_patterns: Vec<String>,
318 skip_exact_patterns: HashSet<String>,
319 pattern_matcher: Box<AhoCorasick>,
320 skip_pattern_matcher: Box<AhoCorasick>,
321 },
322}
323
324impl ResolvedFilterPatterns {
325 fn name_match(&self, test_name: &str) -> FilterNameMatch {
326 match self {
327 Self::All => FilterNameMatch::MatchEmptyPatterns,
328 Self::SkipOnly {
329 skip_patterns: _,
331 skip_exact_patterns,
332 skip_pattern_matcher,
333 } => {
334 if skip_exact_patterns.contains(test_name)
335 || skip_pattern_matcher.is_match(test_name)
336 {
337 FilterNameMatch::Mismatch(MismatchReason::String)
338 } else {
339 FilterNameMatch::MatchWithPatterns
340 }
341 }
342 Self::Patterns {
343 patterns: _,
345 exact_patterns,
346 skip_patterns: _,
348 skip_exact_patterns,
349 pattern_matcher,
350 skip_pattern_matcher,
351 } => {
352 if skip_exact_patterns.contains(test_name)
354 || skip_pattern_matcher.is_match(test_name)
355 {
356 FilterNameMatch::Mismatch(MismatchReason::String)
357 } else if exact_patterns.contains(test_name) || pattern_matcher.is_match(test_name)
358 {
359 FilterNameMatch::MatchWithPatterns
360 } else {
361 FilterNameMatch::Mismatch(MismatchReason::String)
362 }
363 }
364 }
365 }
366}
367
368impl PartialEq for ResolvedFilterPatterns {
369 fn eq(&self, other: &Self) -> bool {
370 match (self, other) {
371 (Self::All, Self::All) => true,
372 (
373 Self::SkipOnly {
374 skip_patterns,
375 skip_exact_patterns,
376 skip_pattern_matcher: _,
378 },
379 Self::SkipOnly {
380 skip_patterns: other_skip_patterns,
381 skip_exact_patterns: other_skip_exact_patterns,
382 skip_pattern_matcher: _,
383 },
384 ) => {
385 skip_patterns == other_skip_patterns
386 && skip_exact_patterns == other_skip_exact_patterns
387 }
388 (
389 Self::Patterns {
390 patterns,
391 exact_patterns,
392 skip_patterns,
393 skip_exact_patterns,
394 pattern_matcher: _,
397 skip_pattern_matcher: _,
398 },
399 Self::Patterns {
400 patterns: other_patterns,
401 exact_patterns: other_exact_patterns,
402 skip_patterns: other_skip_patterns,
403 skip_exact_patterns: other_skip_exact_patterns,
404 pattern_matcher: _,
405 skip_pattern_matcher: _,
406 },
407 ) => {
408 patterns == other_patterns
409 && exact_patterns == other_exact_patterns
410 && skip_patterns == other_skip_patterns
411 && skip_exact_patterns == other_skip_exact_patterns
412 }
413 _ => false,
414 }
415 }
416}
417
418impl Eq for ResolvedFilterPatterns {}
419
420impl TestFilterBuilder {
421 pub fn new(
425 run_ignored: RunIgnored,
426 partitioner_builder: Option<PartitionerBuilder>,
427 patterns: TestFilterPatterns,
428 exprs: Vec<Filterset>,
429 ) -> Result<Self, TestFilterBuilderError> {
430 let patterns = patterns.resolve()?;
431
432 let binary_filter = BinaryFilter::new(exprs);
433
434 Ok(Self {
435 run_ignored,
436 partitioner_builder,
437 patterns,
438 binary_filter,
439 })
440 }
441
442 pub fn filter_binary_match(
448 &self,
449 test_binary: &RustTestArtifact<'_>,
450 ecx: &EvalContext<'_>,
451 bound: FilterBound,
452 ) -> FilterBinaryMatch {
453 self.binary_filter.check_match(test_binary, ecx, bound)
454 }
455
456 pub fn default_set(run_ignored: RunIgnored) -> Self {
458 let binary_filter = BinaryFilter::new(Vec::new());
459 Self {
460 run_ignored,
461 partitioner_builder: None,
462 patterns: ResolvedFilterPatterns::default(),
463 binary_filter,
464 }
465 }
466
467 pub fn build(&self) -> TestFilter<'_> {
471 let partitioner = self
472 .partitioner_builder
473 .as_ref()
474 .map(|partitioner_builder| partitioner_builder.build());
475 TestFilter {
476 builder: self,
477 partitioner,
478 }
479 }
480}
481
482#[derive(Copy, Clone, Debug)]
486pub enum FilterBinaryMatch {
487 Definite,
489
490 Possible,
492
493 Mismatch {
495 reason: BinaryMismatchReason,
497 },
498}
499
500impl FilterBinaryMatch {
501 fn from_result(result: Option<bool>, reason: BinaryMismatchReason) -> Self {
502 match result {
503 Some(true) => Self::Definite,
504 None => Self::Possible,
505 Some(false) => Self::Mismatch { reason },
506 }
507 }
508
509 fn is_match(self) -> bool {
510 match self {
511 Self::Definite | Self::Possible => true,
512 Self::Mismatch { .. } => false,
513 }
514 }
515
516 fn logic_or(self, other: Self) -> Self {
517 match (self, other) {
518 (Self::Definite, _) | (_, Self::Definite) => Self::Definite,
519 (Self::Possible, _) | (_, Self::Possible) => Self::Possible,
520 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => Self::Mismatch {
521 reason: r1.prefer_expression(r2),
522 },
523 }
524 }
525
526 fn logic_and(self, other: Self) -> Self {
527 match (self, other) {
528 (Self::Definite, Self::Definite) => Self::Definite,
529 (Self::Definite, Self::Possible)
530 | (Self::Possible, Self::Definite)
531 | (Self::Possible, Self::Possible) => Self::Possible,
532 (Self::Mismatch { reason: r1 }, Self::Mismatch { reason: r2 }) => {
533 Self::Mismatch {
536 reason: r1.prefer_expression(r2),
537 }
538 }
539 (Self::Mismatch { reason }, _) | (_, Self::Mismatch { reason }) => {
540 Self::Mismatch { reason }
541 }
542 }
543 }
544}
545
546#[derive(Copy, Clone, Debug, Eq, PartialEq)]
550pub enum BinaryMismatchReason {
551 Expression,
553
554 DefaultSet,
556}
557
558impl BinaryMismatchReason {
559 fn prefer_expression(self, other: Self) -> Self {
560 match (self, other) {
561 (Self::Expression, _) | (_, Self::Expression) => Self::Expression,
562 (Self::DefaultSet, Self::DefaultSet) => Self::DefaultSet,
563 }
564 }
565}
566
567impl fmt::Display for BinaryMismatchReason {
568 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569 match self {
570 Self::Expression => write!(f, "didn't match filtersets"),
571 Self::DefaultSet => write!(f, "didn't match the default set"),
572 }
573 }
574}
575
576#[derive(Debug)]
578pub struct TestFilter<'builder> {
579 builder: &'builder TestFilterBuilder,
580 partitioner: Option<Box<dyn Partitioner>>,
581}
582
583impl TestFilter<'_> {
584 pub fn filter_match(
586 &mut self,
587 test_binary: &RustTestArtifact<'_>,
588 test_name: &str,
589 ecx: &EvalContext<'_>,
590 bound: FilterBound,
591 ignored: bool,
592 ) -> FilterMatch {
593 self.filter_ignored_mismatch(ignored)
594 .or_else(|| {
595 use FilterNameMatch::*;
616 match (
617 self.filter_name_match(test_name),
618 self.filter_expression_match(test_binary, test_name, ecx, bound),
619 ) {
620 (
622 MatchEmptyPatterns | MatchWithPatterns,
623 MatchEmptyPatterns | MatchWithPatterns,
624 ) => None,
625 (Mismatch(reason), _) | (_, Mismatch(reason)) => {
631 Some(FilterMatch::Mismatch { reason })
632 }
633 }
634 })
635 .or_else(|| self.filter_partition_mismatch(test_name))
639 .unwrap_or(FilterMatch::Matches)
640 }
641
642 fn filter_ignored_mismatch(&self, ignored: bool) -> Option<FilterMatch> {
643 match self.builder.run_ignored {
644 RunIgnored::Only => {
645 if !ignored {
646 return Some(FilterMatch::Mismatch {
647 reason: MismatchReason::Ignored,
648 });
649 }
650 }
651 RunIgnored::Default => {
652 if ignored {
653 return Some(FilterMatch::Mismatch {
654 reason: MismatchReason::Ignored,
655 });
656 }
657 }
658 _ => {}
659 }
660 None
661 }
662
663 fn filter_name_match(&self, test_name: &str) -> FilterNameMatch {
664 self.builder.patterns.name_match(test_name)
665 }
666
667 fn filter_expression_match(
668 &self,
669 test_binary: &RustTestArtifact<'_>,
670 test_name: &str,
671 ecx: &EvalContext<'_>,
672 bound: FilterBound,
673 ) -> FilterNameMatch {
674 let query = TestQuery {
675 binary_query: test_binary.to_binary_query(),
676 test_name,
677 };
678
679 let expr_result = match &self.builder.binary_filter.exprs {
680 TestFilterExprs::All => FilterNameMatch::MatchEmptyPatterns,
681 TestFilterExprs::Sets(exprs) => {
682 if exprs.iter().any(|expr| expr.matches_test(&query, ecx)) {
683 FilterNameMatch::MatchWithPatterns
684 } else {
685 return FilterNameMatch::Mismatch(MismatchReason::Expression);
686 }
687 }
688 };
689
690 match bound {
691 FilterBound::All => expr_result,
692 FilterBound::DefaultSet => {
693 if ecx.default_filter.matches_test(&query, ecx) {
694 expr_result
695 } else {
696 FilterNameMatch::Mismatch(MismatchReason::DefaultFilter)
697 }
698 }
699 }
700 }
701
702 fn filter_partition_mismatch(&mut self, test_name: &str) -> Option<FilterMatch> {
703 let partition_match = match &mut self.partitioner {
704 Some(partitioner) => partitioner.test_matches(test_name),
705 None => true,
706 };
707 if partition_match {
708 None
709 } else {
710 Some(FilterMatch::Mismatch {
711 reason: MismatchReason::Partition,
712 })
713 }
714 }
715}
716
717#[derive(Clone, Debug, Eq, PartialEq)]
718enum FilterNameMatch {
719 MatchEmptyPatterns,
721 MatchWithPatterns,
723 Mismatch(MismatchReason),
725}
726
727impl FilterNameMatch {
728 #[cfg(test)]
729 fn is_match(&self) -> bool {
730 match self {
731 Self::MatchEmptyPatterns | Self::MatchWithPatterns => true,
732 Self::Mismatch(_) => false,
733 }
734 }
735}
736
737#[cfg(test)]
738mod tests {
739 use super::*;
740 use proptest::{collection::vec, prelude::*};
741 use test_strategy::proptest;
742
743 #[proptest(cases = 50)]
744 fn proptest_empty(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
745 let patterns = TestFilterPatterns::default();
746 let test_filter =
747 TestFilterBuilder::new(RunIgnored::Default, None, patterns, Vec::new()).unwrap();
748 let single_filter = test_filter.build();
749 for test_name in test_names {
750 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
751 }
752 }
753
754 #[proptest(cases = 50)]
756 fn proptest_exact(#[strategy(vec(any::<String>(), 0..16))] test_names: Vec<String>) {
757 let patterns = TestFilterPatterns::new(test_names.clone());
759 let test_filter =
760 TestFilterBuilder::new(RunIgnored::Default, None, patterns, Vec::new()).unwrap();
761 let single_filter = test_filter.build();
762 for test_name in &test_names {
763 prop_assert!(single_filter.filter_name_match(test_name).is_match());
764 }
765
766 let mut patterns = TestFilterPatterns::default();
768 for test_name in &test_names {
769 patterns.add_exact_pattern(test_name.clone());
770 }
771 let test_filter =
772 TestFilterBuilder::new(RunIgnored::Default, None, patterns, Vec::new()).unwrap();
773 let single_filter = test_filter.build();
774 for test_name in &test_names {
775 prop_assert!(single_filter.filter_name_match(test_name).is_match());
776 }
777 }
778
779 #[proptest(cases = 50)]
781 fn proptest_substring(
782 #[strategy(vec([any::<String>(); 3], 0..16))] substring_prefix_suffixes: Vec<[String; 3]>,
783 ) {
784 let mut patterns = TestFilterPatterns::default();
785 let mut test_names = Vec::with_capacity(substring_prefix_suffixes.len());
786 for [substring, prefix, suffix] in substring_prefix_suffixes {
787 test_names.push(prefix + &substring + &suffix);
788 patterns.add_substring_pattern(substring);
789 }
790
791 let test_filter =
792 TestFilterBuilder::new(RunIgnored::Default, None, patterns, Vec::new()).unwrap();
793 let single_filter = test_filter.build();
794 for test_name in test_names {
795 prop_assert!(single_filter.filter_name_match(&test_name).is_match());
796 }
797 }
798
799 #[proptest(cases = 50)]
801 fn proptest_no_match(substring: String, prefix: String, suffix: String) {
802 prop_assume!(!substring.is_empty() && !prefix.is_empty() && !suffix.is_empty());
803 let pattern = prefix + &substring + &suffix;
804 let patterns = TestFilterPatterns::new(vec![pattern]);
805 let test_filter =
806 TestFilterBuilder::new(RunIgnored::Default, None, patterns, Vec::new()).unwrap();
807 let single_filter = test_filter.build();
808 prop_assert!(!single_filter.filter_name_match(&substring).is_match());
809 }
810
811 #[test]
812 fn pattern_examples() {
813 let mut patterns = TestFilterPatterns::new(vec!["foo".to_string()]);
814 patterns.add_substring_pattern("bar".to_string());
815 patterns.add_exact_pattern("baz".to_string());
816 patterns.add_skip_pattern("quux".to_string());
817 patterns.add_skip_exact_pattern("quuz".to_string());
818
819 let resolved = patterns.clone().resolve().unwrap();
820
821 assert_eq!(
823 resolved.name_match("foo"),
824 FilterNameMatch::MatchWithPatterns,
825 );
826 assert_eq!(
827 resolved.name_match("1foo2"),
828 FilterNameMatch::MatchWithPatterns,
829 );
830 assert_eq!(
831 resolved.name_match("bar"),
832 FilterNameMatch::MatchWithPatterns,
833 );
834 assert_eq!(
835 resolved.name_match("x_bar_y"),
836 FilterNameMatch::MatchWithPatterns,
837 );
838
839 assert_eq!(
841 resolved.name_match("baz"),
842 FilterNameMatch::MatchWithPatterns,
843 );
844 assert_eq!(
845 resolved.name_match("abazb"),
846 FilterNameMatch::Mismatch(MismatchReason::String),
847 );
848
849 assert_eq!(
851 resolved.name_match("bazfoo"),
852 FilterNameMatch::MatchWithPatterns,
853 );
854
855 assert_eq!(
857 resolved.name_match("quux"),
858 FilterNameMatch::Mismatch(MismatchReason::String),
859 );
860 assert_eq!(
861 resolved.name_match("1quux2"),
862 FilterNameMatch::Mismatch(MismatchReason::String),
863 );
864
865 assert_eq!(
867 resolved.name_match("quuxbar"),
868 FilterNameMatch::Mismatch(MismatchReason::String),
869 );
870
871 assert_eq!(
873 resolved.name_match("quuz"),
874 FilterNameMatch::Mismatch(MismatchReason::String),
875 );
876
877 patterns.add_skip_pattern("baz".to_string());
879 let resolved = patterns.resolve().unwrap();
880 assert_eq!(
881 resolved.name_match("quuxbaz"),
882 FilterNameMatch::Mismatch(MismatchReason::String),
883 );
884 }
885
886 #[test]
887 fn skip_only_pattern_examples() {
888 let mut patterns = TestFilterPatterns::default();
889 patterns.add_skip_pattern("foo".to_string());
890 patterns.add_skip_pattern("bar".to_string());
891 patterns.add_skip_exact_pattern("baz".to_string());
892
893 let resolved = patterns.clone().resolve().unwrap();
894
895 assert_eq!(
897 resolved.name_match("foo"),
898 FilterNameMatch::Mismatch(MismatchReason::String),
899 );
900 assert_eq!(
901 resolved.name_match("1foo2"),
902 FilterNameMatch::Mismatch(MismatchReason::String),
903 );
904 assert_eq!(
905 resolved.name_match("bar"),
906 FilterNameMatch::Mismatch(MismatchReason::String),
907 );
908 assert_eq!(
909 resolved.name_match("x_bar_y"),
910 FilterNameMatch::Mismatch(MismatchReason::String),
911 );
912
913 assert_eq!(
915 resolved.name_match("baz"),
916 FilterNameMatch::Mismatch(MismatchReason::String),
917 );
918 assert_eq!(
919 resolved.name_match("abazb"),
920 FilterNameMatch::MatchWithPatterns,
921 );
922
923 assert_eq!(
925 resolved.name_match("quux"),
926 FilterNameMatch::MatchWithPatterns,
927 );
928 }
929
930 #[test]
931 fn empty_pattern_examples() {
932 let patterns = TestFilterPatterns::default();
933 let resolved = patterns.resolve().unwrap();
934 assert_eq!(resolved, ResolvedFilterPatterns::All);
935
936 assert_eq!(
938 resolved.name_match("foo"),
939 FilterNameMatch::MatchEmptyPatterns,
940 );
941 assert_eq!(
942 resolved.name_match("1foo2"),
943 FilterNameMatch::MatchEmptyPatterns,
944 );
945 assert_eq!(
946 resolved.name_match("bar"),
947 FilterNameMatch::MatchEmptyPatterns,
948 );
949 assert_eq!(
950 resolved.name_match("x_bar_y"),
951 FilterNameMatch::MatchEmptyPatterns,
952 );
953 assert_eq!(
954 resolved.name_match("baz"),
955 FilterNameMatch::MatchEmptyPatterns,
956 );
957 assert_eq!(
958 resolved.name_match("abazb"),
959 FilterNameMatch::MatchEmptyPatterns,
960 );
961 }
962}