]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/netbsd-tests/lib/libc/regex/t_regex_att.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / netbsd-tests / lib / libc / regex / t_regex_att.c
1 /*      $NetBSD: t_regex_att.c,v 1.1 2012/08/24 20:24:40 jmmv Exp $     */
2
3 /*-
4  * Copyright (c) 2011 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Christos Zoulas.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38
39 #include <sys/cdefs.h>
40 __RCSID("$NetBSD: t_regex_att.c,v 1.1 2012/08/24 20:24:40 jmmv Exp $");
41
42 #include <sys/param.h>
43
44 #include <stdio.h>
45 #include <regex.h>
46 #include <string.h>
47 #include <stdlib.h>
48 #include <vis.h>
49 #include <ctype.h>
50 #include <atf-c.h>
51 #ifdef __FreeBSD__
52 #include <libutil.h>
53 #endif
54
55 static const char sep[] = "\r\n\t";
56 static const char delim[3] = "\\\\\0";
57
58
59 static void
60 fail(const char *pattern, const char *input, size_t lineno) {
61         fprintf(stderr,
62             "skipping failed test at line %zu (pattern=%s, input=%s)\n",
63             lineno, pattern, input);
64 }
65
66 static int
67 bug(const char *pattern, const char *input, size_t lineno) {
68         static const struct {
69                 const char *p;
70                 const char *i;
71         } b[] = {
72 #if defined(REGEX_SPENCER)
73                 /*
74                  * The default libc implementation by Henry Spencer
75                  */
76                 { "a[-]?c", "ac" },                     // basic.dat
77                 { "(a*)*", "a" },                       // categorization.dat
78                 { "(aba|a*b)*", "ababa" },              // categorization.dat
79                 { "\\(a\\(b\\)*\\)*\\2", "abab" },      // categorization.dat
80                 { "(a*)*", "aaaaaa" },                  // nullsubexpression.dat
81                 { "(a*)*", "aaaaaax" },                 // nullsubexpression.dat
82                 { "(a*)+", "a" },                       // nullsubexpression.dat
83                 { "(a*)+", "aaaaaa" },                  // nullsubexpression.dat
84                 { "(a*)+", "aaaaaax" },                 // nullsubexpression.dat
85                 { "([a]*)*", "a" },                     // nullsubexpression.dat
86                 { "([a]*)*", "aaaaaa" },                // nullsubexpression.dat
87                 { "([a]*)*", "aaaaaax" },               // nullsubexpression.dat
88                 { "([a]*)+", "a" },                     // nullsubexpression.dat
89                 { "([a]*)+", "aaaaaa" },                // nullsubexpression.dat
90                 { "([a]*)+", "aaaaaax" },               // nullsubexpression.dat
91                 { "([^b]*)*", "a" },                    // nullsubexpression.dat
92                 { "([^b]*)*", "aaaaaa" },               // nullsubexpression.dat
93                 { "([^b]*)*", "aaaaaab" },              // nullsubexpression.dat
94                 { "([ab]*)*", "a" },                    // nullsubexpression.dat
95                 { "([ab]*)*", "aaaaaa" },               // nullsubexpression.dat
96                 { "([ab]*)*", "ababab" },               // nullsubexpression.dat
97                 { "([ab]*)*", "bababa" },               // nullsubexpression.dat
98                 { "([ab]*)*", "b" },                    // nullsubexpression.dat
99                 { "([ab]*)*", "bbbbbb" },               // nullsubexpression.dat
100                 { "([ab]*)*", "aaaabcde" },             // nullsubexpression.dat
101                 { "([^a]*)*", "b" },                    // nullsubexpression.dat
102                 { "([^a]*)*", "bbbbbb" },               // nullsubexpression.dat
103                 { "([^ab]*)*", "ccccxx" },              // nullsubexpression.dat
104                 { "\\(a*\\)*\\(x\\)", "ax" },           // nullsubexpression.dat
105                 { "\\(a*\\)*\\(x\\)", "axa" },          // nullsubexpression.dat
106                 { "\\(a*\\)*\\(x\\)\\(\\1\\)", "x" },   // nullsubexpression.dat
107 /* crash! */    { "\\(a*\\)*\\(x\\)\\(\\1\\)", "ax" },  // nullsubexpression.dat
108 /* crash! */    { "\\(a*\\)*\\(x\\)\\(\\1\\)\\(x\\)", "axxa" }, // ""
109                 { "(a*)*(x)",  "ax" },                  // nullsubexpression.dat
110                 { "(a*)*(x)",  "axa" },                 // nullsubexpression.dat
111                 { "(a*)+(x)",  "ax" },                  // nullsubexpression.dat
112                 { "(a*)+(x)",  "axa" },                 // nullsubexpression.dat
113                 { "((a|ab)(c|bcd))(d*)", "abcd" },      // forcedassoc.dat
114                 { "((a|ab)(bcd|c))(d*)", "abcd" },      // forcedassoc.dat
115                 { "((ab|a)(c|bcd))(d*)", "abcd" },      // forcedassoc.dat
116                 { "((ab|a)(bcd|c))(d*)", "abcd" },      // forcedassoc.dat
117                 { "((a*)(b|abc))(c*)", "abc" },         // forcedassoc.dat
118                 { "((a*)(abc|b))(c*)", "abc" },         // forcedassoc.dat
119                 { "((..)|(.)){2}", "aaa" },             // repetition.dat
120                 { "((..)|(.)){3}", "aaa" },             // repetition.dat
121                 { "((..)|(.)){3}", "aaaa" },            // repetition.dat
122                 { "((..)|(.)){3}", "aaaaa" },           // repetition.dat
123                 { "X(.?){0,}Y", "X1234567Y" },          // repetition.dat
124                 { "X(.?){1,}Y", "X1234567Y" },          // repetition.dat
125                 { "X(.?){2,}Y", "X1234567Y" },          // repetition.dat
126                 { "X(.?){3,}Y", "X1234567Y" },          // repetition.dat
127                 { "X(.?){4,}Y", "X1234567Y" },          // repetition.dat
128                 { "X(.?){5,}Y", "X1234567Y" },          // repetition.dat
129                 { "X(.?){6,}Y", "X1234567Y" },          // repetition.dat
130                 { "X(.?){7,}Y", "X1234567Y" },          // repetition.dat
131                 { "X(.?){0,8}Y", "X1234567Y" },         // repetition.dat
132                 { "X(.?){1,8}Y", "X1234567Y" },         // repetition.dat
133                 { "X(.?){2,8}Y", "X1234567Y" },         // repetition.dat
134                 { "X(.?){3,8}Y", "X1234567Y" },         // repetition.dat
135                 { "X(.?){4,8}Y", "X1234567Y" },         // repetition.dat
136                 { "X(.?){5,8}Y", "X1234567Y" },         // repetition.dat
137                 { "X(.?){6,8}Y", "X1234567Y" },         // repetition.dat
138                 { "X(.?){7,8}Y", "X1234567Y" },         // repetition.dat
139                 { "(a|ab|c|bcd){0,}(d*)", "ababcd" },   // repetition.dat
140                 { "(a|ab|c|bcd){1,}(d*)", "ababcd" },   // repetition.dat
141                 { "(a|ab|c|bcd){2,}(d*)", "ababcd" },   // repetition.dat
142                 { "(a|ab|c|bcd){3,}(d*)", "ababcd" },   // repetition.dat
143                 { "(a|ab|c|bcd){1,10}(d*)", "ababcd" }, // repetition.dat
144                 { "(a|ab|c|bcd){2,10}(d*)", "ababcd" }, // repetition.dat
145                 { "(a|ab|c|bcd){3,10}(d*)", "ababcd" }, // repetition.dat
146                 { "(a|ab|c|bcd)*(d*)", "ababcd" },      // repetition.dat
147                 { "(a|ab|c|bcd)+(d*)", "ababcd" },      // repetition.dat
148                 { "(ab|a|c|bcd){0,}(d*)", "ababcd" },   // repetition.dat
149                 { "(ab|a|c|bcd){1,}(d*)", "ababcd" },   // repetition.dat
150                 { "(ab|a|c|bcd){2,}(d*)", "ababcd" },   // repetition.dat
151                 { "(ab|a|c|bcd){3,}(d*)", "ababcd" },   // repetition.dat
152                 { "(ab|a|c|bcd){1,10}(d*)", "ababcd" }, // repetition.dat
153                 { "(ab|a|c|bcd){2,10}(d*)", "ababcd" }, // repetition.dat
154                 { "(ab|a|c|bcd){3,10}(d*)", "ababcd" }, // repetition.dat
155                 { "(ab|a|c|bcd)*(d*)", "ababcd" },      // repetition.dat
156                 { "(ab|a|c|bcd)+(d*)", "ababcd" },      // repetition.dat
157 #elif defined(REGEX_TRE)
158                 { "a[-]?c", "ac" },                     // basic.dat
159                 { "a\\(b\\)*\\1", "a" },                // categorization.dat
160                 { "a\\(b\\)*\\1", "abab" },             // categorization.dat
161                 { "\\(a\\(b\\)*\\)*\\2", "abab" },      // categorization.dat
162                 { "\\(a*\\)*\\(x\\)\\(\\1\\)", "ax" },  // categorization.dat
163                 { "\\(a*\\)*\\(x\\)\\(\\1\\)\\(x\\)", "axxa" }, // ""
164                 { "((..)|(.))*", "aa" },                // repetition.dat
165                 { "((..)|(.))*", "aaa" },               // repetition.dat
166                 { "((..)|(.))*", "aaaaa" },             // repetition.dat
167                 { "X(.?){7,}Y", "X1234567Y" },          // repetition.dat
168 #else
169                 { "", "" }
170 #endif
171         };
172
173         for (size_t i = 0; i < __arraycount(b); i++) {
174                 if (strcmp(pattern, b[i].p) == 0 &&
175                     strcmp(input, b[i].i) == 0) {
176                         fail(pattern, input, lineno);
177                         return 1;
178                 }
179         }
180         return 0;
181 }
182
183 #ifdef REGEX_SPENCER
184 #define HAVE_BRACES     1
185 #define HAVE_MINIMAL    0
186 #endif
187 #ifndef HAVE_BRACES
188 #define HAVE_BRACES     1
189 #endif
190 #ifndef HAVE_MINIMAL
191 #define HAVE_MINIMAL    1
192 #endif
193
194 static int
195 optional(const char *s)
196 {
197         static const struct{
198                 const char *n;
199                 int v;
200         } nv[]= {
201                 { "[[<element>]] not supported", HAVE_BRACES },
202                 { "no *? +? mimimal match ops", HAVE_MINIMAL },
203         };
204
205         for (size_t i = 0; i < __arraycount(nv); i++)
206                 if (strcmp(nv[i].n, s) == 0) {
207                         if (nv[i].v)
208                                 return 0;
209                         fprintf(stderr, "skipping unsupported [%s] tests\n", s);
210                         return 1;
211                 }
212
213         ATF_REQUIRE_MSG(0, "Unknown feature: %s", s);
214         return 0;
215 }
216
217 static int
218 unsupported(const char *s)
219 {
220         static const char *we[] = {
221 #if defined(REGEX_SPENCER)
222                 "ASSOCIATIVITY=left",           // have right associativity
223                 "SUBEXPRESSION=precedence",     // have grouping subexpression
224                 "REPEAT_LONGEST=last",          // have first repeat longest
225                 "BUG=alternation-order",        // don't have it
226                 "BUG=first-match",              // don't have it
227                 "BUG=nomatch-match",            // don't have it
228                 "BUG=repeat-any",               // don't have it
229                 "BUG=range-null",               // don't have it
230                 "BUG=repeat-null-unknown",      // don't have it
231                 "BUG=repeat-null",              // don't have it
232                 "BUG=repeat-artifact",          // don't have it
233                 "BUG=subexpression-first",      // don't have it
234 #elif defined(REGEX_TRE)
235                 "ASSOCIATIVITY=right",          // have left associativity
236                 "SUBEXPRESSION=grouping",       // have precedence subexpression
237                 "REPEAT_LONGEST=first",         // have last repeat longest
238                 "LENGTH=first",                 // have last length
239                 "BUG=alternation-order",        // don't have it
240                 "BUG=first-match",              // don't have it
241                 "BUG=range-null",               // don't have it
242                 "BUG=repeat-null",              // don't have it
243                 "BUG=repeat-artifact",          // don't have it
244                 "BUG=subexpression-first",      // don't have it
245                 "BUG=repeat-short",             // don't have it
246 #endif
247         };
248
249         if (s == NULL)
250                 return 0;
251
252         while (*s == '#' || isspace((unsigned char)*s))
253                 s++;
254
255         for (size_t i = 0; i < __arraycount(we); i++)
256                 if (strcmp(we[i], s) == 0)
257                         return 1;
258         return 0;
259 }
260
261 static void
262 geterror(const char *s, int *comp, int *exec)
263 {
264         static const struct {
265                 const char *n;
266                 int v;
267                 int ce;
268         } nv[] = {
269 #define COMP 1
270 #define EXEC 2
271                 { "OK", 0, COMP|EXEC },
272 #define _DO(a, b)       { # a, REG_ ## a, b },
273                 _DO(NOMATCH, EXEC)
274                 _DO(BADPAT, COMP)
275                 _DO(ECOLLATE, COMP)
276                 _DO(ECTYPE, COMP)
277                 _DO(EESCAPE, COMP)
278                 _DO(ESUBREG, COMP)
279                 _DO(EBRACK, COMP)
280                 _DO(EPAREN, COMP)
281                 _DO(EBRACE, COMP)
282                 _DO(BADBR, COMP)
283                 _DO(ERANGE, COMP)
284                 _DO(ESPACE, EXEC)
285                 _DO(BADRPT, COMP)
286                 _DO(EMPTY, COMP)
287                 _DO(ASSERT, COMP)
288                 _DO(INVARG, COMP)
289                 _DO(ENOSYS, COMP)
290 #undef _DO
291         };
292         *comp = 0;
293         *exec = 0;
294         for (size_t i = 0; i < __arraycount(nv); i++)
295                 if (strcmp(s, nv[i].n) == 0) {
296                         if (nv[i].ce & COMP)
297                                 *comp = nv[i].v;
298                         if (nv[i].ce & EXEC)
299                                 *exec = nv[i].v;
300                         return;
301                 }
302         ATF_REQUIRE_MSG(0, "Unknown error %s", s);
303         return;
304 }
305
306 static int
307 getflags(char *s)
308 {
309         int flags = 0;
310
311         for (;; s++)
312                 switch (*s) {
313                 case '0': case '1': case '2': case '3': case '4':
314                 case '5': case '6': case '7': case '8': case '9':
315                         *s = '\0';
316                         break;
317                 case '\0':
318                         return flags;
319                 case 'B':
320                 case 'E':
321                 case 'F':
322                 case 'L':
323                         break;
324                 case 'i':
325                         flags |= REG_ICASE;
326                         *s = '\0';
327                         break;
328                 case '$':
329                         *s = '\0';
330                         break;
331                 case 'n':
332                         *s = '\0';
333                         break;
334                 default:
335                         ATF_REQUIRE_MSG(0, "Unknown char %c", *s);
336                         break;
337                 }
338 }
339
340 static size_t
341 getmatches(const char *s)
342 {
343         size_t i;
344         char *q;
345         for (i = 0; (q = strchr(s, '(')) != NULL; i++, s = q + 1)
346                 continue;
347         ATF_REQUIRE_MSG(i != 0, "No parentheses found");
348         return i;
349 }
350
351 static void
352 checkcomment(const char *s, size_t lineno)
353 {
354         if (s && strstr(s, "BUG") != NULL)
355                 fprintf(stderr, "Expected %s at line %zu\n", s, lineno);
356 }
357
358 static void
359 checkmatches(const char *matches, size_t nm, const regmatch_t *pm,
360     size_t lineno)
361 {
362         if (nm == 0)
363                 return;
364
365         char *res;
366         size_t len = strlen(matches) + 1, off = 0;
367
368         ATF_REQUIRE((res = strdup(matches)) != NULL);
369         for (size_t i = 0; i < nm; i++) {
370                 int l;
371                 if (pm[i].rm_so == -1 && pm[i].rm_eo == -1)
372                         l = snprintf(res + off, len - off, "(?,?)");
373                 else
374                         l = snprintf(res + off, len - off, "(%lld,%lld)",
375                             (long long)pm[i].rm_so, (long long)pm[i].rm_eo);
376                 ATF_REQUIRE_MSG((size_t) l < len - off, "String too long %s"
377                     " cur=%d, max=%zu", res, l, len - off);
378                 off += l;
379         }
380 #ifdef __FreeBSD__
381         ATF_CHECK_STREQ_MSG(res, matches, " at line %zu", lineno);
382 #else
383         ATF_REQUIRE_STREQ_MSG(res, matches, " at line %zu", lineno);
384 #endif
385         free(res);
386 }
387
388 static void
389 att_test(const struct atf_tc *tc, const char *data_name)
390 {
391         regex_t re;
392         char *line, *lastpattern = NULL, data_path[MAXPATHLEN];
393         size_t len, lineno = 0;
394         int skipping = 0;
395         FILE *input_file;
396
397         snprintf(data_path, sizeof(data_path), "%s/data/%s.dat",
398             atf_tc_get_config_var(tc, "srcdir"), data_name);
399
400         input_file = fopen(data_path, "r");
401         if (input_file == NULL)
402                 atf_tc_fail("Failed to open input file %s", data_path);
403
404         for (; (line = fparseln(input_file, &len, &lineno, delim, 0))
405             != NULL; free(line)) {
406                 char *name, *pattern, *input, *matches, *comment;
407                 regmatch_t *pm;
408                 size_t nm;
409 #ifdef DEBUG
410                 fprintf(stderr, "[%s]\n", line);
411 #endif
412                 if ((name = strtok(line, sep)) == NULL)
413                         continue;
414
415                 /*
416                  * We check these early so that we skip the lines quickly
417                  * in order to do more strict testing on the other arguments
418                  * The same characters are also tested in the switch below
419                  */
420                 if (*name == '}') {
421                         skipping = 0;
422                         continue;
423                 }
424                 if (skipping)
425                         continue;
426                 if (*name == ';' || *name == '#' || strcmp(name, "NOTE") == 0)
427                         continue;
428                 if (*name == ':') {
429                         /* Skip ":HA#???:" prefix */
430                         while (*++name && *name != ':')
431                                 continue;
432                         if (*name)
433                                 name++;
434                 }
435
436                 ATF_REQUIRE_MSG((pattern = strtok(NULL, sep)) != NULL,
437                         "Missing pattern at line %zu", lineno);
438                 ATF_REQUIRE_MSG((input = strtok(NULL, sep)) != NULL,
439                         "Missing input at line %zu", lineno);
440
441                 if (strchr(name, '$')) {
442                         ATF_REQUIRE(strunvis(pattern, pattern) != -1);
443                         ATF_REQUIRE(strunvis(input, input) != -1);
444                 }
445
446
447                 if (strcmp(input, "NULL") == 0)
448                         *input = '\0';
449
450                 if (strcmp(pattern, "SAME") == 0) {
451                         ATF_REQUIRE(lastpattern != NULL);
452                         pattern = lastpattern;
453                 } else {
454                         free(lastpattern);
455                         ATF_REQUIRE((lastpattern = strdup(pattern)) != NULL);
456                 }
457
458                 ATF_REQUIRE_MSG((matches = strtok(NULL, sep)) != NULL,
459                     "Missing matches at line %zu", lineno);
460
461                 comment = strtok(NULL, sep);
462                 switch (*name) {
463                 case '{':       /* Begin optional implementation */
464                         if (optional(comment)) {
465                                 skipping++;
466                                 continue;
467                         }
468                         name++; /* We have it, so ignore */
469                         break;
470                 case '}':       /* End optional implementation */
471                         skipping = 0;
472                         continue;
473                 case '?':       /* Optional */
474                 case '|':       /* Alternative */
475                         if (unsupported(comment))
476                                 continue;
477                         name++; /* We have it, so ignore */
478                         break;
479                 case '#':       /* Comment */
480                 case ';':       /* Skip */
481                         continue;
482                 default:
483                         break;
484                 }
485
486                 /* XXX: Our bug */
487                 if (bug(pattern, input, lineno))
488                         continue;
489
490                 int comp, exec;
491                 if (*matches != '(') {
492                         geterror(matches, &comp, &exec);
493                         pm = NULL;
494                         nm = 0;
495                 } else {
496                         comp = exec = 0;
497                         nm = getmatches(matches);
498                         ATF_REQUIRE((pm = calloc(nm, sizeof(*pm))) != NULL);
499                 }
500
501
502
503                 int iflags = getflags(name);
504                 for (; *name; name++) {
505                         int flags;
506                         switch (*name) {
507                         case 'B':
508                                 flags = REG_BASIC;
509                                 break;
510                         case 'E':
511                                 flags = REG_EXTENDED;
512                                 break;
513                         case 'L':
514                                 flags = REG_NOSPEC;
515                                 break;
516                         default:
517                                 ATF_REQUIRE_MSG(0, "Bad name %c", *name);
518                                 continue;
519                         }
520                         int c = regcomp(&re, pattern, flags | iflags);
521                         ATF_REQUIRE_MSG(c == comp,
522                             "regcomp returned %d for pattern %s at line %zu",
523                             c, pattern, lineno);
524                         if (c)
525                                 continue;
526                         int e = regexec(&re, input, nm, pm, 0);
527                         ATF_REQUIRE_MSG(e == exec, "Expected error %d,"
528                             " got %d at line %zu", exec, e, lineno);
529                         checkmatches(matches, nm, pm, lineno);
530                         checkcomment(comment, lineno);
531                         regfree(&re);
532                 }
533                 free(pm);
534         }
535
536         fclose(input_file);
537 }
538
539 ATF_TC(basic);
540 ATF_TC_HEAD(basic, tc)
541 {
542         atf_tc_set_md_var(tc, "descr", "Tests basic functionality");
543 }
544 ATF_TC_BODY(basic, tc)
545 {
546         att_test(tc, "basic");
547 }
548
549 ATF_TC(categorization);
550 ATF_TC_HEAD(categorization, tc)
551 {
552         atf_tc_set_md_var(tc, "descr", "Tests implementation categorization");
553 }
554 ATF_TC_BODY(categorization, tc)
555 {
556         att_test(tc, "categorization");
557 }
558
559 ATF_TC(nullsubexpr);
560 ATF_TC_HEAD(nullsubexpr, tc)
561 {
562         atf_tc_set_md_var(tc, "descr", "Tests (...)*");
563 }
564 ATF_TC_BODY(nullsubexpr, tc)
565 {
566         att_test(tc, "nullsubexpr");
567 }
568
569 ATF_TC(leftassoc);
570 ATF_TC_HEAD(leftassoc, tc)
571 {
572         atf_tc_set_md_var(tc, "descr", "Tests left-associative "
573             "implementations");
574 }
575 ATF_TC_BODY(leftassoc, tc)
576 {
577 #if SKIP_LEFTASSOC
578         /* jmmv: I converted the original shell-based tests to C and they
579          * disabled this test in a very unconventional way without giving
580          * any explation.  Mark as broken here, but I don't know why. */
581         atf_tc_expect_fail("Reason for breakage unknown");
582 #endif
583 #ifdef __FreeBSD__
584         atf_tc_expect_fail("The expected and matched groups are mismatched on FreeBSD");
585 #endif
586         att_test(tc, "leftassoc");
587 }
588
589 ATF_TC(rightassoc);
590 ATF_TC_HEAD(rightassoc, tc)
591 {
592         atf_tc_set_md_var(tc, "descr", "Tests right-associative "
593             "implementations");
594 }
595 ATF_TC_BODY(rightassoc, tc)
596 {
597 #if SKIP_RIGHTASSOC
598         /* jmmv: I converted the original shell-based tests to C and they
599          * disabled this test in a very unconventional way without giving
600          * any explation.  Mark as broken here, but I don't know why. */
601         atf_tc_expect_fail("Reason for breakage unknown");
602 #endif
603         att_test(tc, "rightassoc");
604 }
605
606 ATF_TC(forcedassoc);
607 ATF_TC_HEAD(forcedassoc, tc)
608 {
609         atf_tc_set_md_var(tc, "descr", "Tests subexpression grouping to "
610             "force association");
611 }
612 ATF_TC_BODY(forcedassoc, tc)
613 {
614         att_test(tc, "forcedassoc");
615 }
616
617 ATF_TC(repetition);
618 ATF_TC_HEAD(repetition, tc)
619 {
620         atf_tc_set_md_var(tc, "descr", "Tests implicit vs. explicit "
621             "repetition");
622 }
623 ATF_TC_BODY(repetition, tc)
624 {
625         att_test(tc, "repetition");
626 }
627
628 ATF_TP_ADD_TCS(tp)
629 {
630
631         ATF_TP_ADD_TC(tp, basic);
632         ATF_TP_ADD_TC(tp, categorization);
633         ATF_TP_ADD_TC(tp, nullsubexpr);
634         ATF_TP_ADD_TC(tp, leftassoc);
635         ATF_TP_ADD_TC(tp, rightassoc);
636         ATF_TP_ADD_TC(tp, forcedassoc);
637         ATF_TP_ADD_TC(tp, repetition);
638         return atf_no_error();
639 }