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