55//!
66//! * TESTBRIDGE_TEST_ONLY: string passed from --test_filter
77//!
8- //! We interpret it as a colon-separated list of glob patterns. If
9- //! any pattern in the list succeeds, the filter passes.
8+ //! The format of a filter is a ‘,‘-separated list of wildcard
9+ //! patterns (called the positive patterns) optionally followed by a
10+ //! ‘-’ and another ‘,‘-separated pattern list (called the negative
11+ //! patterns). A test matches the filter if and only if it matches any
12+ //! of the positive patterns but does not match any of the negative
13+ //! patterns. (Note that this is a deliberate devation from GTest
14+ //! C++, which uses colons to separate the patterns, as colons will
15+ //! unfortunately clash with Rust's "::" namespacing operator.)
16+ //!
17+ //! As an example: "*mount*-*doom*" will accept any string that contains the
18+ //! substring "mount", as long as it also doesn't contain "doom"
1019use crate :: internal:: glob:: { is_glob_pattern, Pattern } ;
1120use std:: sync:: OnceLock ;
1221
@@ -51,23 +60,56 @@ impl TestFilter for Matches {
5160}
5261
5362struct Collection {
54- equals : Box < [ Equals ] > ,
55- matches : Box < [ Matches ] > ,
63+ // The positive portion:
64+ positive_equals : Box < [ Equals ] > ,
65+ positive_matches : Box < [ Matches ] > ,
66+
67+ // The negative portion:
68+ negative_equals : Box < [ Equals ] > ,
69+ negative_matches : Box < [ Matches ] > ,
5670}
5771
5872impl TestFilter for Collection {
5973 fn filter ( & self , test_name : & str ) -> bool {
60- self . equals . iter ( ) . any ( |f| f. filter ( test_name) )
61- || self . matches . iter ( ) . any ( |f| f. filter ( test_name) )
74+ ( self . positive_equals . iter ( ) . any ( |f| f. filter ( test_name) )
75+ || self . positive_matches . iter ( ) . any ( |f| f. filter ( test_name) ) )
76+ && ( !self . negative_equals . iter ( ) . any ( |f| f. filter ( test_name) ) )
77+ && ( !self . negative_matches . iter ( ) . any ( |f| f. filter ( test_name) ) )
6278 }
6379}
6480
6581fn get_test_filter ( testbridge_test_only : & str ) -> Collection {
66- let ( with_globs, literals) : ( Vec < _ > , Vec < _ > ) =
67- testbridge_test_only. split ( ':' ) . partition ( |s| is_glob_pattern ( s) ) ;
82+ let positive_negative: Vec < & str > = testbridge_test_only. splitn ( 2 , '-' ) . collect ( ) ;
83+
84+ let ( positive_with_globs, positive_literals) : ( Vec < _ > , Vec < _ > ) = {
85+ let positive = positive_negative[ 0 ] ;
86+ if positive. is_empty ( ) {
87+ // Forces the empty positive filter to accept everything:
88+ ( vec ! [ "*" ] , vec ! [ ] )
89+ } else {
90+ positive. split ( ',' ) . partition ( |s| is_glob_pattern ( s) )
91+ }
92+ } ;
93+
94+ let ( negative_with_globs, negative_literals) : ( Vec < _ > , Vec < _ > ) = match positive_negative. get ( 1 )
95+ {
96+ Some ( negative) if !negative. is_empty ( ) => {
97+ negative. split ( ',' ) . partition ( |s| is_glob_pattern ( s) )
98+ }
99+ _ => ( vec ! [ ] , vec ! [ ] ) ,
100+ } ;
101+
68102 Collection {
69- equals : literals. into_iter ( ) . map ( |s| Equals ( s. to_string ( ) ) ) . collect ( ) ,
70- matches : with_globs. into_iter ( ) . map ( |s| Matches ( Pattern :: new ( s. to_string ( ) ) ) ) . collect ( ) ,
103+ positive_equals : positive_literals. into_iter ( ) . map ( |s| Equals ( s. to_string ( ) ) ) . collect ( ) ,
104+ positive_matches : positive_with_globs
105+ . into_iter ( )
106+ . map ( |s| Matches ( Pattern :: new ( s. to_string ( ) ) ) )
107+ . collect ( ) ,
108+ negative_equals : negative_literals. into_iter ( ) . map ( |s| Equals ( s. to_string ( ) ) ) . collect ( ) ,
109+ negative_matches : negative_with_globs
110+ . into_iter ( )
111+ . map ( |s| Matches ( Pattern :: new ( s. to_string ( ) ) ) )
112+ . collect ( ) ,
71113 }
72114}
73115
@@ -86,11 +128,20 @@ mod tests {
86128 }
87129
88130 #[ test]
89- fn empty_filter_accepts_only_empty ( ) -> Result < ( ) > {
131+ fn empty_filter_accepts_all ( ) -> Result < ( ) > {
90132 let filter = get_test_filter ( "" ) ;
91133
92134 verify_that ! ( filter. filter( "" ) , is_true( ) ) ?;
93- verify_that ! ( filter. filter( "abcdefg" ) , is_false( ) ) ?;
135+ verify_that ! ( filter. filter( "abcdefg" ) , is_true( ) ) ?;
136+ Ok ( ( ) )
137+ }
138+
139+ #[ test]
140+ fn empty_negation_filter_accepts_all ( ) -> Result < ( ) > {
141+ let filter = get_test_filter ( "-" ) ;
142+
143+ verify_that ! ( filter. filter( "" ) , is_true( ) ) ?;
144+ verify_that ! ( filter. filter( "abcdefg" ) , is_true( ) ) ?;
94145 Ok ( ( ) )
95146 }
96147
@@ -134,7 +185,7 @@ mod tests {
134185
135186 #[ test]
136187 fn collection ( ) -> Result < ( ) > {
137- let filter = get_test_filter ( "a: b" ) ;
188+ let filter = get_test_filter ( "a, b" ) ;
138189
139190 verify_that ! ( filter. filter( "" ) , is_false( ) ) ?;
140191 verify_that ! ( filter. filter( "a" ) , is_true( ) ) ?;
@@ -148,12 +199,63 @@ mod tests {
148199
149200 #[ test]
150201 fn collection_with_globs ( ) -> Result < ( ) > {
151- let filter = get_test_filter ( "*test1*: *test2*" ) ;
202+ let filter = get_test_filter ( "*test1*, *test2*" ) ;
152203
153204 verify_that ! ( filter. filter( "" ) , is_false( ) ) ?;
154205 verify_that ! ( filter. filter( "this is test1" ) , is_true( ) ) ?;
155206 verify_that ! ( filter. filter( "and test2 is it" ) , is_true( ) ) ?;
156207 verify_that ! ( filter. filter( "but test3 is not" ) , is_false( ) ) ?;
157208 Ok ( ( ) )
158209 }
210+
211+ #[ test]
212+ fn collection_with_globs_negation ( ) -> Result < ( ) > {
213+ let filter = get_test_filter ( "*test*-*testbad" ) ;
214+
215+ verify_that ! ( filter. filter( "" ) , is_false( ) ) ?;
216+ verify_that ! ( filter. filter( "module" ) , is_false( ) ) ?;
217+ verify_that ! ( filter. filter( "module::my_test1" ) , is_true( ) ) ?;
218+ verify_that ! ( filter. filter( "module::my_test2" ) , is_true( ) ) ?;
219+ verify_that ! ( filter. filter( "module::my_testbad" ) , is_false( ) ) ?;
220+ Ok ( ( ) )
221+ }
222+
223+ #[ test]
224+ fn mount_doom ( ) -> Result < ( ) > {
225+ let filter = get_test_filter ( "*mount*-*doom*" ) ;
226+
227+ verify_that ! ( filter. filter( "" ) , is_false( ) ) ?;
228+ verify_that ! ( filter. filter( "mount rushmore" ) , is_true( ) ) ?;
229+ verify_that ! ( filter. filter( "doom mount" ) , is_false( ) ) ?;
230+ verify_that ! ( filter. filter( "dismount" ) , is_true( ) ) ?;
231+ verify_that ! ( filter. filter( "mountains of moria" ) , is_true( ) ) ?;
232+ verify_that ! ( filter. filter( "frodo and sam went to mount doom" ) , is_false( ) ) ?;
233+ Ok ( ( ) )
234+ }
235+
236+ #[ test]
237+ fn collection_with_only_negation ( ) -> Result < ( ) > {
238+ let filter = get_test_filter ( "-testbad1,testbad2" ) ;
239+
240+ verify_that ! ( filter. filter( "" ) , is_true( ) ) ?;
241+ verify_that ! ( filter. filter( "test" ) , is_true( ) ) ?;
242+ verify_that ! ( filter. filter( "testbad1" ) , is_false( ) ) ?;
243+ verify_that ! ( filter. filter( "testbad2" ) , is_false( ) ) ?;
244+ verify_that ! ( filter. filter( "testbad3" ) , is_true( ) ) ?;
245+ Ok ( ( ) )
246+ }
247+
248+ #[ test]
249+ fn magic_words_a_and_e ( ) -> Result < ( ) > {
250+ let filter = get_test_filter ( "a*,e*-abracadabra,elbereth,avada kedavra" ) ;
251+
252+ verify_that ! ( filter. filter( "alakazam" ) , is_true( ) ) ?;
253+ verify_that ! ( filter. filter( "abracadabra" ) , is_false( ) ) ?;
254+ verify_that ! ( filter. filter( "avada kedavra" ) , is_false( ) ) ?;
255+ verify_that ! ( filter. filter( "enchantment" ) , is_true( ) ) ?;
256+ verify_that ! ( filter. filter( "elbereth" ) , is_false( ) ) ?;
257+ verify_that ! ( filter. filter( "expecto patronum" ) , is_true( ) ) ?;
258+ verify_that ! ( filter. filter( "fuego" ) , is_false( ) ) ?;
259+ Ok ( ( ) )
260+ }
159261}
0 commit comments