19
19
#define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure"
20
20
#define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success"
21
21
#define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg="
22
- #define TEST_TAG_EXPECT_REGEX_PFX "comment:test_expect_regex="
23
22
#define TEST_TAG_EXPECT_XLATED_PFX "comment:test_expect_xlated="
24
23
#define TEST_TAG_EXPECT_FAILURE_UNPRIV "comment:test_expect_failure_unpriv"
25
24
#define TEST_TAG_EXPECT_SUCCESS_UNPRIV "comment:test_expect_success_unpriv"
26
25
#define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "comment:test_expect_msg_unpriv="
27
- #define TEST_TAG_EXPECT_REGEX_PFX_UNPRIV "comment:test_expect_regex_unpriv="
28
26
#define TEST_TAG_EXPECT_XLATED_PFX_UNPRIV "comment:test_expect_xlated_unpriv="
29
27
#define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level="
30
28
#define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags="
@@ -55,8 +53,9 @@ enum mode {
55
53
56
54
struct expect_msg {
57
55
const char * substr ; /* substring match */
58
- const char * regex_str ; /* regex-based match */
59
56
regex_t regex ;
57
+ bool is_regex ;
58
+ bool on_next_line ;
60
59
};
61
60
62
61
struct expected_msgs {
@@ -111,7 +110,7 @@ static void free_msgs(struct expected_msgs *msgs)
111
110
int i ;
112
111
113
112
for (i = 0 ; i < msgs -> cnt ; i ++ )
114
- if (msgs -> patterns [i ].regex_str )
113
+ if (msgs -> patterns [i ].is_regex )
115
114
regfree (& msgs -> patterns [i ].regex );
116
115
free (msgs -> patterns );
117
116
msgs -> patterns = NULL ;
@@ -132,12 +131,71 @@ static void free_test_spec(struct test_spec *spec)
132
131
spec -> unpriv .name = NULL ;
133
132
}
134
133
135
- static int push_msg (const char * substr , const char * regex_str , struct expected_msgs * msgs )
134
+ /* Compiles regular expression matching pattern.
135
+ * Pattern has a special syntax:
136
+ *
137
+ * pattern := (<verbatim text> | regex)*
138
+ * regex := "{{" <posix extended regular expression> "}}"
139
+ *
140
+ * In other words, pattern is a verbatim text with inclusion
141
+ * of regular expressions enclosed in "{{" "}}" pairs.
142
+ * For example, pattern "foo{{[0-9]+}}" matches strings like
143
+ * "foo0", "foo007", etc.
144
+ */
145
+ static int compile_regex (const char * pattern , regex_t * regex )
146
+ {
147
+ char err_buf [256 ], buf [256 ] = {}, * ptr , * buf_end ;
148
+ const char * original_pattern = pattern ;
149
+ bool in_regex = false;
150
+ int err ;
151
+
152
+ buf_end = buf + sizeof (buf );
153
+ ptr = buf ;
154
+ while (* pattern && ptr < buf_end - 2 ) {
155
+ if (!in_regex && str_has_pfx (pattern , "{{" )) {
156
+ in_regex = true;
157
+ pattern += 2 ;
158
+ continue ;
159
+ }
160
+ if (in_regex && str_has_pfx (pattern , "}}" )) {
161
+ in_regex = false;
162
+ pattern += 2 ;
163
+ continue ;
164
+ }
165
+ if (in_regex ) {
166
+ * ptr ++ = * pattern ++ ;
167
+ continue ;
168
+ }
169
+ /* list of characters that need escaping for extended posix regex */
170
+ if (strchr (".[]\\()*+?{}|^$" , * pattern )) {
171
+ * ptr ++ = '\\' ;
172
+ * ptr ++ = * pattern ++ ;
173
+ continue ;
174
+ }
175
+ * ptr ++ = * pattern ++ ;
176
+ }
177
+ if (* pattern ) {
178
+ PRINT_FAIL ("Regexp too long: '%s'\n" , original_pattern );
179
+ return - EINVAL ;
180
+ }
181
+ if (in_regex ) {
182
+ PRINT_FAIL ("Regexp has open '{{' but no closing '}}': '%s'\n" , original_pattern );
183
+ return - EINVAL ;
184
+ }
185
+ err = regcomp (regex , buf , REG_EXTENDED | REG_NEWLINE );
186
+ if (err != 0 ) {
187
+ regerror (err , regex , err_buf , sizeof (err_buf ));
188
+ PRINT_FAIL ("Regexp compilation error in '%s': '%s'\n" , buf , err_buf );
189
+ return - EINVAL ;
190
+ }
191
+ return 0 ;
192
+ }
193
+
194
+ static int __push_msg (const char * pattern , bool on_next_line , struct expected_msgs * msgs )
136
195
{
137
- void * tmp ;
138
- int regcomp_res ;
139
- char error_msg [100 ];
140
196
struct expect_msg * msg ;
197
+ void * tmp ;
198
+ int err ;
141
199
142
200
tmp = realloc (msgs -> patterns ,
143
201
(1 + msgs -> cnt ) * sizeof (struct expect_msg ));
@@ -147,26 +205,38 @@ static int push_msg(const char *substr, const char *regex_str, struct expected_m
147
205
}
148
206
msgs -> patterns = tmp ;
149
207
msg = & msgs -> patterns [msgs -> cnt ];
150
-
151
- if (substr ) {
152
- msg -> substr = substr ;
153
- msg -> regex_str = NULL ;
154
- } else {
155
- msg -> regex_str = regex_str ;
156
- msg -> substr = NULL ;
157
- regcomp_res = regcomp (& msg -> regex , regex_str , REG_EXTENDED |REG_NEWLINE );
158
- if (regcomp_res != 0 ) {
159
- regerror (regcomp_res , & msg -> regex , error_msg , sizeof (error_msg ));
160
- PRINT_FAIL ("Regexp compilation error in '%s': '%s'\n" ,
161
- regex_str , error_msg );
162
- return - EINVAL ;
163
- }
208
+ msg -> on_next_line = on_next_line ;
209
+ msg -> substr = pattern ;
210
+ msg -> is_regex = false;
211
+ if (strstr (pattern , "{{" )) {
212
+ err = compile_regex (pattern , & msg -> regex );
213
+ if (err )
214
+ return err ;
215
+ msg -> is_regex = true;
164
216
}
165
-
166
217
msgs -> cnt += 1 ;
167
218
return 0 ;
168
219
}
169
220
221
+ static int clone_msgs (struct expected_msgs * from , struct expected_msgs * to )
222
+ {
223
+ struct expect_msg * msg ;
224
+ int i , err ;
225
+
226
+ for (i = 0 ; i < from -> cnt ; i ++ ) {
227
+ msg = & from -> patterns [i ];
228
+ err = __push_msg (msg -> substr , msg -> on_next_line , to );
229
+ if (err )
230
+ return err ;
231
+ }
232
+ return 0 ;
233
+ }
234
+
235
+ static int push_msg (const char * substr , struct expected_msgs * msgs )
236
+ {
237
+ return __push_msg (substr , false, msgs );
238
+ }
239
+
170
240
static int parse_int (const char * str , int * val , const char * name )
171
241
{
172
242
char * end ;
@@ -320,32 +390,22 @@ static int parse_test_spec(struct test_loader *tester,
320
390
spec -> auxiliary = true;
321
391
spec -> mode_mask |= UNPRIV ;
322
392
} else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_MSG_PFX ))) {
323
- err = push_msg (msg , NULL , & spec -> priv .expect_msgs );
393
+ err = push_msg (msg , & spec -> priv .expect_msgs );
324
394
if (err )
325
395
goto cleanup ;
326
396
spec -> mode_mask |= PRIV ;
327
397
} else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_MSG_PFX_UNPRIV ))) {
328
- err = push_msg (msg , NULL , & spec -> unpriv .expect_msgs );
329
- if (err )
330
- goto cleanup ;
331
- spec -> mode_mask |= UNPRIV ;
332
- } else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_REGEX_PFX ))) {
333
- err = push_msg (NULL , msg , & spec -> priv .expect_msgs );
334
- if (err )
335
- goto cleanup ;
336
- spec -> mode_mask |= PRIV ;
337
- } else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_REGEX_PFX_UNPRIV ))) {
338
- err = push_msg (NULL , msg , & spec -> unpriv .expect_msgs );
398
+ err = push_msg (msg , & spec -> unpriv .expect_msgs );
339
399
if (err )
340
400
goto cleanup ;
341
401
spec -> mode_mask |= UNPRIV ;
342
402
} else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_XLATED_PFX ))) {
343
- err = push_msg (msg , NULL , & spec -> priv .expect_xlated );
403
+ err = push_msg (msg , & spec -> priv .expect_xlated );
344
404
if (err )
345
405
goto cleanup ;
346
406
spec -> mode_mask |= PRIV ;
347
407
} else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_XLATED_PFX_UNPRIV ))) {
348
- err = push_msg (msg , NULL , & spec -> unpriv .expect_xlated );
408
+ err = push_msg (msg , & spec -> unpriv .expect_xlated );
349
409
if (err )
350
410
goto cleanup ;
351
411
spec -> mode_mask |= UNPRIV ;
@@ -457,26 +517,10 @@ static int parse_test_spec(struct test_loader *tester,
457
517
spec -> unpriv .execute = spec -> priv .execute ;
458
518
}
459
519
460
- if (spec -> unpriv .expect_msgs .cnt == 0 ) {
461
- for (i = 0 ; i < spec -> priv .expect_msgs .cnt ; i ++ ) {
462
- struct expect_msg * msg = & spec -> priv .expect_msgs .patterns [i ];
463
-
464
- err = push_msg (msg -> substr , msg -> regex_str ,
465
- & spec -> unpriv .expect_msgs );
466
- if (err )
467
- goto cleanup ;
468
- }
469
- }
470
- if (spec -> unpriv .expect_xlated .cnt == 0 ) {
471
- for (i = 0 ; i < spec -> priv .expect_xlated .cnt ; i ++ ) {
472
- struct expect_msg * msg = & spec -> priv .expect_xlated .patterns [i ];
473
-
474
- err = push_msg (msg -> substr , msg -> regex_str ,
475
- & spec -> unpriv .expect_xlated );
476
- if (err )
477
- goto cleanup ;
478
- }
479
- }
520
+ if (spec -> unpriv .expect_msgs .cnt == 0 )
521
+ clone_msgs (& spec -> priv .expect_msgs , & spec -> unpriv .expect_msgs );
522
+ if (spec -> unpriv .expect_xlated .cnt == 0 )
523
+ clone_msgs (& spec -> priv .expect_xlated , & spec -> unpriv .expect_xlated );
480
524
}
481
525
482
526
spec -> valid = true;
@@ -542,7 +586,7 @@ static void validate_msgs(char *log_buf, struct expected_msgs *msgs,
542
586
struct expect_msg * msg = & msgs -> patterns [i ];
543
587
const char * match = NULL ;
544
588
545
- if (msg -> substr ) {
589
+ if (! msg -> is_regex ) {
546
590
match = strstr (log , msg -> substr );
547
591
if (match )
548
592
log = match + strlen (msg -> substr );
@@ -562,8 +606,8 @@ static void validate_msgs(char *log_buf, struct expected_msgs *msgs,
562
606
msg = & msgs -> patterns [j ];
563
607
fprintf (stderr , "%s %s: '%s'\n" ,
564
608
j < i ? "MATCHED " : "EXPECTED" ,
565
- msg -> substr ? "SUBSTR " : " REGEX " ,
566
- msg -> substr ?: msg -> regex_str );
609
+ msg -> is_regex ? " REGEX " : "SUBSTR " ,
610
+ msg -> substr );
567
611
}
568
612
return ;
569
613
}
0 commit comments