5
5
//!
6
6
//! * TESTBRIDGE_TEST_ONLY: string passed from --test_filter
7
7
//!
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"
10
19
use crate :: internal:: glob:: { is_glob_pattern, Pattern } ;
11
20
use std:: sync:: OnceLock ;
12
21
@@ -51,23 +60,56 @@ impl TestFilter for Matches {
51
60
}
52
61
53
62
struct 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 ] > ,
56
70
}
57
71
58
72
impl TestFilter for Collection {
59
73
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) ) )
62
78
}
63
79
}
64
80
65
81
fn 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
+
68
102
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 ( ) ,
71
113
}
72
114
}
73
115
@@ -86,11 +128,20 @@ mod tests {
86
128
}
87
129
88
130
#[ test]
89
- fn empty_filter_accepts_only_empty ( ) -> Result < ( ) > {
131
+ fn empty_filter_accepts_all ( ) -> Result < ( ) > {
90
132
let filter = get_test_filter ( "" ) ;
91
133
92
134
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( ) ) ?;
94
145
Ok ( ( ) )
95
146
}
96
147
@@ -134,7 +185,7 @@ mod tests {
134
185
135
186
#[ test]
136
187
fn collection ( ) -> Result < ( ) > {
137
- let filter = get_test_filter ( "a: b" ) ;
188
+ let filter = get_test_filter ( "a, b" ) ;
138
189
139
190
verify_that ! ( filter. filter( "" ) , is_false( ) ) ?;
140
191
verify_that ! ( filter. filter( "a" ) , is_true( ) ) ?;
@@ -148,12 +199,63 @@ mod tests {
148
199
149
200
#[ test]
150
201
fn collection_with_globs ( ) -> Result < ( ) > {
151
- let filter = get_test_filter ( "*test1*: *test2*" ) ;
202
+ let filter = get_test_filter ( "*test1*, *test2*" ) ;
152
203
153
204
verify_that ! ( filter. filter( "" ) , is_false( ) ) ?;
154
205
verify_that ! ( filter. filter( "this is test1" ) , is_true( ) ) ?;
155
206
verify_that ! ( filter. filter( "and test2 is it" ) , is_true( ) ) ?;
156
207
verify_that ! ( filter. filter( "but test3 is not" ) , is_false( ) ) ?;
157
208
Ok ( ( ) )
158
209
}
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
+ }
159
261
}
0 commit comments