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