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