1 /* 2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of version 2 of the GNU General Public License as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it would be useful, but 9 * WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 11 * 12 * Further, this software is distributed without any warranty that it is 13 * free of the rightful claim of any third person regarding infringement 14 * or the like. Any license provided herein, whether implied or 15 * otherwise, applies only to this software file. Patent licenses, if 16 * any, provided herein do not apply to combinations of this program with 17 * other software, or any other product whatsoever. 18 * 19 * You should have received a copy of the GNU General Public License along 20 * with this program; if not, write the Free Software Foundation, Inc., 21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, 24 * Mountain View, CA 94043, or: 25 * 26 * http://www.sgi.com 27 * 28 * For further information regarding this notice, see: 29 * 30 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ 31 * 32 */ 33 /* $Id: tag_report.c,v 1.2 2006/12/13 22:55:22 vapier Exp $ */ 34 #include "tag_report.h" 35 #include "debug.h" 36 #include "reporter.h" 37 #include "splitstr.h" 38 39 static char *worst_case(char *, char *); 40 41 /************************************************************************ 42 * Report Generation * 43 ************************************************************************/ 44 45 /* 46 * printf format statement for standard reports 47 * 5 fields with max/min widths 48 */ 49 #define FORMAT "%-20.20s %-15.15s %10.10s %-20.20s %s\n" 50 51 /* 52 * This is the central results reporting function. All standard report 53 * format results are printed thru test_result. 54 */ 55 int test_result(char *tag, char *tcid, char *tc, char *result, SYM tags) 56 { 57 char *expert, expkey[KEYSIZE]; 58 register char *c; 59 char **cont; 60 const char **cont_save; 61 62 if (tcid == NULL) 63 tcid = "-"; 64 if (tc == NULL) 65 tc = "-"; 66 if (tag == NULL) 67 tag = "test_result: no tag"; 68 if (result == NULL) 69 result = "(RESULT IS NULL)"; 70 71 strcpy(expkey, "contacts"); 72 /* note: the sym_get here does _not_ change the "cursor" */ 73 if ((expert = (char *)sym_get(tags, expkey)) == NULL) { 74 expert = "UNKNOWN"; 75 } 76 77 /* ' tr " " "_" ' */ 78 for (c = result; *c; c++) { 79 if (*c == ' ') { 80 *c = '_'; 81 } 82 } 83 if (*result == '\0') 84 result = "?"; 85 86 /* split contacts on "," and print out a line for each */ 87 cont_save = splitstr(expert, ",", NULL); 88 for (cont = (char **)cont_save; *cont != NULL; cont++) { 89 printf(FORMAT, tag, tcid, tc, result, *cont); 90 } 91 splitstr_free(cont_save); 92 93 return 0; 94 } 95 96 /* 97 * CUTS test reporting. 98 * 99 * (1) make a list (2d char array) of all TCIDs (see above for why) 100 * (2) look thru the list: 101 * (a) keep track of the "worst case" in this *TAG* 102 * (b) report each testcase's results 103 * (c) if the testcase number is != 0, count it 104 * (3) report tag's results 105 * (4) check the number of expected results with the actual results, 106 * report an error if they don't match. 107 */ 108 109 int cuts_report(SYM tags, SYM keys, char *at, char *tag) 110 { 111 DBT Key, Data; 112 113 /* analysis type: count of CUTS test cases */ 114 const char **ant; 115 char *dat; /* strdup(at) */ 116 int tccount; /* expected count of testcases */ 117 int tcnum; /* seen count of testcases */ 118 119 /* a list of tcids */ 120 char **taglist, **tl; 121 int ntags, tagcount; 122 123 char key_get[255]; 124 125 char *result = "", *worst_case(); /* overall result */ 126 127 /* parse analysis type: cuts:tc-count */ 128 ant = splitstr((dat = strdup(at)), ":", NULL); 129 if (ant[1] != NULL) 130 tccount = atoi(ant[1]); 131 else 132 tccount = 0; 133 free(dat); 134 splitstr_free(ant); 135 136 /* extract tcids */ 137 ntags = NTCID_START; 138 taglist = (char **)malloc(sizeof(char *) * ntags); 139 tagcount = 0; 140 141 tl = taglist; 142 sym_seq(tags, &Key, &Data, R_FIRST); 143 do { 144 if (tagcount == ntags) { 145 /* exceeded tag array size -- realloc */ 146 ntags += NTCID_START; 147 taglist = 148 (char **)realloc(taglist, sizeof(char *) * ntags); 149 tl = taglist + tagcount; 150 } 151 152 if (strcmp((char *)Key.data, "_keys") == 0) 153 continue; 154 DEBUG(D_REPORT, 10) 155 printf("cuts_report: tcid %s\n", (char *)Key.data); 156 *tl++ = Key.data; 157 tagcount++; 158 } while (sym_seq(tags, &Key, &Data, R_NEXT) == 0); 159 160 if (tagcount == ntags) { 161 /* exceeded tag array size -- realloc */ 162 ntags++; /* need just one more */ 163 taglist = (char **)realloc(taglist, sizeof(char *) * ntags); 164 tl = taglist + tagcount; 165 } 166 167 *tl++ = NULL; 168 169 ntags = tagcount; 170 171 /* dump all found records */ 172 tcnum = 0; 173 for (tl = taglist; *tl != NULL; tl++) { 174 175 strcpy(key_get, *tl); 176 Key.data = (void *)key_get; 177 178 /*sym_dump_s(sym_get(tags, key_get), 0); */ 179 180 sym_seq(tags, &Key, &Data, R_CURSOR); 181 do { 182 DEBUG(D_REPORT, 10) 183 printf("cuts_report: tc %s = %s\n", 184 (char *)Key.data, (char *)Data.data); 185 result = worst_case(result, (char *)Data.data); 186 test_result(tag, *tl, (char *)Key.data, 187 (char *)Data.data, keys); 188 if (atoi((char *)Key.data)) 189 tcnum++; 190 } while (sym_seq(tags, &Key, &Data, R_NEXT) == 0); 191 } 192 193 test_result(tag, "*", "*", result, keys); 194 195 if (tccount != 0 && tccount != tcnum) 196 test_result(tag, "-", "-", "TC count wrong", keys); 197 198 free(taglist); 199 200 return 0; 201 } 202 203 /* 204 * Do the report generation. 205 * 206 * A problem: I really need multiple cursors. I'd rather not look into 207 * the depths of the current symbol table implimentation (there are the 208 * cursors there that I could use) so that a different (faster!) symbol 209 * table can be used in the future. 210 * 211 * I could get a key (tag), get it's sub-keys (TCIDs), then get the key 212 * again to reset to the top level, _then_ get the next key. That would 213 * be very inefficient. 214 * 215 * The solution I chose is to extract all tags into a list (char array), 216 * then go thru that list with the cursor free for other levels to use. 217 * 218 * (1) make a list (2d char array) of all Tags 219 * (2) search for the first tag that has a "stime" record, and use that as 220 * the date (MMDDYY) that the tests were run. 221 * (3) print the report header 222 * (4) go thru all tags and report each as described at the beginning of 223 * this file 224 */ 225 int tag_report(SYM alltags, SYM ctag, SYM keys) 226 { 227 228 extern int extended; 229 230 char key_get[KEYSIZE]; 231 char *info; 232 233 /* retrieved _keys values: initation status, start time, duration, 234 * termination type, termination id, start line, end line. */ 235 char *tag, *contact, *is, *mystime, *duration, *tt, *ti, *sl, *el; 236 237 /* Check all driver-level status first */ 238 strcpy(key_get, "tag"); 239 if ((tag = (char *)sym_get(keys, key_get)) == NULL) { 240 return -1; 241 } 242 243 /* Check all driver-level status first */ 244 strcpy(key_get, "initiation_status"); 245 if ((is = (char *)sym_get(keys, key_get)) == NULL) { 246 test_result(tag, NULL, NULL, "no init status", keys); 247 return -1; 248 } 249 250 if (strcmp(is, "ok")) { 251 test_result(tag, NULL, NULL, is, keys); 252 } else { 253 254 strcpy(key_get, "corefile"); 255 if ((info = (char *)sym_get(keys, key_get)) != NULL) 256 if (strcmp(info, "no") != 0) { 257 test_result(tag, NULL, NULL, "coredump", keys); 258 } 259 260 strcpy(key_get, "termination_type"); 261 if ((tt = (char *)sym_get(keys, key_get)) == NULL) { 262 test_result(tag, NULL, NULL, "no Term Type", keys); 263 return -1; 264 } 265 266 if (strcmp(tt, "exited")) { 267 test_result(tag, NULL, NULL, tt, keys); 268 } 269 270 strcpy(key_get, "analysis"); 271 if ((info = (char *)sym_get(keys, key_get)) == NULL) { 272 test_result(tag, NULL, NULL, "no Analysis Type", keys); 273 return -1; 274 } 275 276 /* Getting here indicates that there were no fatal driver-level 277 * errors. Do the kind of reporting requested by the test. 278 */ 279 280 if (strncmp(info, "none", 4) == 0) { 281 /* 282 * If analysis is 'none', alway report the test as 283 * a pass regardless of output or exit status. 284 */ 285 test_result(tag, NULL, NULL, "pass", keys); 286 287 } else if (strncmp(info, "cuts", 4)) { 288 289 /* 290 * If analysis is not cuts, assume it is 'exit', thus 291 * the termination_id is used to determine pass/fail result. 292 */ 293 if (strcmp(tt, "timeout")) { 294 strcpy(key_get, "termination_id"); 295 if ((info = 296 (char *)sym_get(keys, key_get)) == NULL) { 297 test_result(tag, NULL, NULL, 298 "no_Term_Id", keys); 299 } else { 300 if (strcmp(info, "0")) { 301 test_result(tag, NULL, NULL, 302 "fail", keys); 303 } else { 304 test_result(tag, NULL, NULL, 305 "pass", keys); 306 } 307 } 308 } 309 } else { 310 cuts_report(ctag, keys, info, tag); 311 } 312 } 313 314 /* 315 * Extended Format: 316 * - tcid+tc = "!" 317 * - tab separated fields 318 * - no field widths 319 * - fields 6 - ~ are: 320 * start-time (time_t) 321 * duration 322 * termination_id 323 * termination_type 324 * Start Line (of test results in output file) 325 * End Line 326 */ 327 328 if (extended) { 329 330 strcpy(key_get, "termination_id"); 331 if ((ti = (char *)sym_get(keys, key_get)) == NULL) { 332 ti = "No_Termination_ID"; 333 } 334 335 strcpy(key_get, "termination_type"); 336 if ((tt = (char *)sym_get(keys, key_get)) == NULL) { 337 tt = "No_Termination_Type"; 338 } 339 340 strcpy(key_get, "duration"); 341 if ((duration = (char *)sym_get(keys, key_get)) == NULL) { 342 duration = "No_Duration"; 343 } 344 345 strcpy(key_get, "_Start_line"); 346 if ((sl = (char *)sym_get(keys, key_get)) == NULL) { 347 sl = "No_Start_line"; 348 } 349 350 strcpy(key_get, "_End_line"); 351 if ((el = (char *)sym_get(keys, key_get)) == NULL) { 352 el = "No_End_line"; 353 } 354 355 strcpy(key_get, "contacts"); 356 if ((contact = (char *)sym_get(keys, key_get)) == NULL) { 357 contact = "No_Contacts"; 358 } 359 360 strcpy(key_get, "stime"); 361 if ((mystime = (char *)sym_get(keys, key_get)) == NULL) { 362 mystime = "No_stime"; 363 } 364 365 printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n", 366 tag, "!", "!", is, contact, mystime, duration, 367 ti, tt, sl, el); 368 } 369 370 return 0; 371 } 372 373 /* 374 * Print a header made up of the RTS keywords 375 * In "extended" mode, print the header to stderr. 376 */ 377 int print_header(SYM tags) 378 { 379 DBT Key, Data; 380 char key_get[255]; 381 382 FILE *out; 383 384 extern int extended; 385 386 if (extended) 387 out = stderr; 388 else 389 out = stdout; 390 391 fprintf(out, "System Configuration:\n"); 392 /* build header out of RTS keywords */ 393 sprintf(key_get, "_RTS"); 394 Key.data = (void *)key_get; 395 if (sym_seq(tags, &Key, &Data, R_CURSOR) == 0) { 396 do { 397 if (strcmp((char *)Key.data, "PATH") == 0) 398 continue; 399 fprintf(out, "%-20.20s %s\n", (char *)Key.data, 400 (char *)Data.data); 401 } while (sym_seq(tags, &Key, &Data, R_NEXT) == 0); 402 } 403 404 fprintf(out, "\n"); 405 fprintf(out, FORMAT, "tag", "tcid", "testcase", "status", "contact"); 406 fprintf(out, 407 "-------------------------------------------------------------------------------\n"); 408 409 return 0; 410 } 411 412 /* 413 * CUTS testcase record 414 * 415 * This is passed s SYM for the current tag and the initiation keys. 416 * The text seen by lex is in yytext (global). 417 */ 418 int cuts_testcase(SYM tag, SYM keys) 419 { 420 char *cuts_info[6]; 421 char key[KEYSIZE]; 422 char *oldresult, *newresult, *worst_case(); 423 int tok_num = 0; 424 extern char yytext[]; 425 426 cuts_info[tok_num] = strtok(yytext, "\t "); 427 while (tok_num < 5 && 428 (cuts_info[++tok_num] = strtok(NULL, "\t ")) != NULL) ; 429 430 strcpy(key, cuts_info[0]); 431 strcat(key, ","); 432 strcat(key, cuts_info[1]); 433 434 #ifdef DEBUGGING 435 DEBUG(D_SCAN_CUTS, 1) { 436 printf("cuts_testcase: TCID=%s TC=%s Result=%s\n", cuts_info[0], 437 cuts_info[1], cuts_info[2]); 438 printf("cuts_testcase: %d %s\n", tok_num, key); 439 } 440 #endif 441 442 if ((oldresult = (char *)sym_get(tag, key)) != NULL) { 443 /* Duplicate -- assume mulitple runs */ 444 /* keep "worst case" */ 445 newresult = worst_case(oldresult, cuts_info[2]); 446 sym_put(tag, key, strdup(newresult), PUT_REPLACE); 447 free(oldresult); /* remove the "data" portion of the key */ 448 } else { 449 sym_put(tag, key, strdup(cuts_info[2]), 0); 450 } 451 return 0; 452 } 453 454 /* 455 * Determine a "worst case" status from two given statuses. 456 */ 457 static char *worst_case(char *t1, char *t2) 458 { 459 /* NULL-terminated table, ordered from worst-case to best-case */ 460 static char *worst[] = { 461 "FAIL", "BROK", "PASS", "CONF", 462 "WARN", "INFO", NULL, 463 }; 464 465 char **w1, **w2; 466 467 /* Search the table for each status, then use the index to determine 468 which has a lower precedence */ 469 for (w1 = worst; *w1 != NULL && strcmp(t1, *w1); w1++) ; 470 471 for (w2 = worst; *w2 != NULL && strcmp(t2, *w2); w2++) ; 472 473 if (w1 < w2) 474 return (t1); 475 else 476 return (t2); 477 478 } 479