/* * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public License along * with this program; if not, write the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ * */ /* $Id: tag_report.c,v 1.2 2006/12/13 22:55:22 vapier Exp $ */ #include "tag_report.h" #include "debug.h" #include "reporter.h" #include "splitstr.h" static char *worst_case(char *, char *); /************************************************************************ * Report Generation * ************************************************************************/ /* * printf format statement for standard reports * 5 fields with max/min widths */ #define FORMAT "%-20.20s %-15.15s %10.10s %-20.20s %s\n" /* * This is the central results reporting function. All standard report * format results are printed thru test_result. */ int test_result(char *tag, char *tcid, char *tc, char *result, SYM tags) { char *expert, expkey[KEYSIZE]; register char *c; char **cont; const char **cont_save; if (tcid == NULL) tcid = "-"; if (tc == NULL) tc = "-"; if (tag == NULL) tag = "test_result: no tag"; if (result == NULL) result = "(RESULT IS NULL)"; strcpy(expkey, "contacts"); /* note: the sym_get here does _not_ change the "cursor" */ if ((expert = (char *)sym_get(tags, expkey)) == NULL) { expert = "UNKNOWN"; } /* ' tr " " "_" ' */ for (c = result; *c; c++) { if (*c == ' ') { *c = '_'; } } if (*result == '\0') result = "?"; /* split contacts on "," and print out a line for each */ cont_save = splitstr(expert, ",", NULL); for (cont = (char **)cont_save; *cont != NULL; cont++) { printf(FORMAT, tag, tcid, tc, result, *cont); } splitstr_free(cont_save); return 0; } /* * CUTS test reporting. * * (1) make a list (2d char array) of all TCIDs (see above for why) * (2) look thru the list: * (a) keep track of the "worst case" in this *TAG* * (b) report each testcase's results * (c) if the testcase number is != 0, count it * (3) report tag's results * (4) check the number of expected results with the actual results, * report an error if they don't match. */ int cuts_report(SYM tags, SYM keys, char *at, char *tag) { DBT Key, Data; /* analysis type: count of CUTS test cases */ const char **ant; char *dat; /* strdup(at) */ int tccount; /* expected count of testcases */ int tcnum; /* seen count of testcases */ /* a list of tcids */ char **taglist, **tl; int ntags, tagcount; char key_get[255]; char *result = "", *worst_case(); /* overall result */ /* parse analysis type: cuts:tc-count */ ant = splitstr((dat = strdup(at)), ":", NULL); if (ant[1] != NULL) tccount = atoi(ant[1]); else tccount = 0; free(dat); splitstr_free(ant); /* extract tcids */ ntags = NTCID_START; taglist = (char **)malloc(sizeof(char *) * ntags); tagcount = 0; tl = taglist; sym_seq(tags, &Key, &Data, R_FIRST); do { if (tagcount == ntags) { /* exceeded tag array size -- realloc */ ntags += NTCID_START; taglist = (char **)realloc(taglist, sizeof(char *) * ntags); tl = taglist + tagcount; } if (strcmp((char *)Key.data, "_keys") == 0) continue; DEBUG(D_REPORT, 10) printf("cuts_report: tcid %s\n", (char *)Key.data); *tl++ = Key.data; tagcount++; } while (sym_seq(tags, &Key, &Data, R_NEXT) == 0); if (tagcount == ntags) { /* exceeded tag array size -- realloc */ ntags++; /* need just one more */ taglist = (char **)realloc(taglist, sizeof(char *) * ntags); tl = taglist + tagcount; } *tl++ = NULL; ntags = tagcount; /* dump all found records */ tcnum = 0; for (tl = taglist; *tl != NULL; tl++) { strcpy(key_get, *tl); Key.data = (void *)key_get; /*sym_dump_s(sym_get(tags, key_get), 0); */ sym_seq(tags, &Key, &Data, R_CURSOR); do { DEBUG(D_REPORT, 10) printf("cuts_report: tc %s = %s\n", (char *)Key.data, (char *)Data.data); result = worst_case(result, (char *)Data.data); test_result(tag, *tl, (char *)Key.data, (char *)Data.data, keys); if (atoi((char *)Key.data)) tcnum++; } while (sym_seq(tags, &Key, &Data, R_NEXT) == 0); } test_result(tag, "*", "*", result, keys); if (tccount != 0 && tccount != tcnum) test_result(tag, "-", "-", "TC count wrong", keys); free(taglist); return 0; } /* * Do the report generation. * * A problem: I really need multiple cursors. I'd rather not look into * the depths of the current symbol table implimentation (there are the * cursors there that I could use) so that a different (faster!) symbol * table can be used in the future. * * I could get a key (tag), get it's sub-keys (TCIDs), then get the key * again to reset to the top level, _then_ get the next key. That would * be very inefficient. * * The solution I chose is to extract all tags into a list (char array), * then go thru that list with the cursor free for other levels to use. * * (1) make a list (2d char array) of all Tags * (2) search for the first tag that has a "stime" record, and use that as * the date (MMDDYY) that the tests were run. * (3) print the report header * (4) go thru all tags and report each as described at the beginning of * this file */ int tag_report(SYM alltags, SYM ctag, SYM keys) { extern int extended; char key_get[KEYSIZE]; char *info; /* retrieved _keys values: initation status, start time, duration, * termination type, termination id, start line, end line. */ char *tag, *contact, *is, *mystime, *duration, *tt, *ti, *sl, *el; /* Check all driver-level status first */ strcpy(key_get, "tag"); if ((tag = (char *)sym_get(keys, key_get)) == NULL) { return -1; } /* Check all driver-level status first */ strcpy(key_get, "initiation_status"); if ((is = (char *)sym_get(keys, key_get)) == NULL) { test_result(tag, NULL, NULL, "no init status", keys); return -1; } if (strcmp(is, "ok")) { test_result(tag, NULL, NULL, is, keys); } else { strcpy(key_get, "corefile"); if ((info = (char *)sym_get(keys, key_get)) != NULL) if (strcmp(info, "no") != 0) { test_result(tag, NULL, NULL, "coredump", keys); } strcpy(key_get, "termination_type"); if ((tt = (char *)sym_get(keys, key_get)) == NULL) { test_result(tag, NULL, NULL, "no Term Type", keys); return -1; } if (strcmp(tt, "exited")) { test_result(tag, NULL, NULL, tt, keys); } strcpy(key_get, "analysis"); if ((info = (char *)sym_get(keys, key_get)) == NULL) { test_result(tag, NULL, NULL, "no Analysis Type", keys); return -1; } /* Getting here indicates that there were no fatal driver-level * errors. Do the kind of reporting requested by the test. */ if (strncmp(info, "none", 4) == 0) { /* * If analysis is 'none', alway report the test as * a pass regardless of output or exit status. */ test_result(tag, NULL, NULL, "pass", keys); } else if (strncmp(info, "cuts", 4)) { /* * If analysis is not cuts, assume it is 'exit', thus * the termination_id is used to determine pass/fail result. */ if (strcmp(tt, "timeout")) { strcpy(key_get, "termination_id"); if ((info = (char *)sym_get(keys, key_get)) == NULL) { test_result(tag, NULL, NULL, "no_Term_Id", keys); } else { if (strcmp(info, "0")) { test_result(tag, NULL, NULL, "fail", keys); } else { test_result(tag, NULL, NULL, "pass", keys); } } } } else { cuts_report(ctag, keys, info, tag); } } /* * Extended Format: * - tcid+tc = "!" * - tab separated fields * - no field widths * - fields 6 - ~ are: * start-time (time_t) * duration * termination_id * termination_type * Start Line (of test results in output file) * End Line */ if (extended) { strcpy(key_get, "termination_id"); if ((ti = (char *)sym_get(keys, key_get)) == NULL) { ti = "No_Termination_ID"; } strcpy(key_get, "termination_type"); if ((tt = (char *)sym_get(keys, key_get)) == NULL) { tt = "No_Termination_Type"; } strcpy(key_get, "duration"); if ((duration = (char *)sym_get(keys, key_get)) == NULL) { duration = "No_Duration"; } strcpy(key_get, "_Start_line"); if ((sl = (char *)sym_get(keys, key_get)) == NULL) { sl = "No_Start_line"; } strcpy(key_get, "_End_line"); if ((el = (char *)sym_get(keys, key_get)) == NULL) { el = "No_End_line"; } strcpy(key_get, "contacts"); if ((contact = (char *)sym_get(keys, key_get)) == NULL) { contact = "No_Contacts"; } strcpy(key_get, "stime"); if ((mystime = (char *)sym_get(keys, key_get)) == NULL) { mystime = "No_stime"; } printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\n", tag, "!", "!", is, contact, mystime, duration, ti, tt, sl, el); } return 0; } /* * Print a header made up of the RTS keywords * In "extended" mode, print the header to stderr. */ int print_header(SYM tags) { DBT Key, Data; char key_get[255]; FILE *out; extern int extended; if (extended) out = stderr; else out = stdout; fprintf(out, "System Configuration:\n"); /* build header out of RTS keywords */ sprintf(key_get, "_RTS"); Key.data = (void *)key_get; if (sym_seq(tags, &Key, &Data, R_CURSOR) == 0) { do { if (strcmp((char *)Key.data, "PATH") == 0) continue; fprintf(out, "%-20.20s %s\n", (char *)Key.data, (char *)Data.data); } while (sym_seq(tags, &Key, &Data, R_NEXT) == 0); } fprintf(out, "\n"); fprintf(out, FORMAT, "tag", "tcid", "testcase", "status", "contact"); fprintf(out, "-------------------------------------------------------------------------------\n"); return 0; } /* * CUTS testcase record * * This is passed s SYM for the current tag and the initiation keys. * The text seen by lex is in yytext (global). */ int cuts_testcase(SYM tag, SYM keys) { char *cuts_info[6]; char key[KEYSIZE]; char *oldresult, *newresult, *worst_case(); int tok_num = 0; extern char yytext[]; cuts_info[tok_num] = strtok(yytext, "\t "); while (tok_num < 5 && (cuts_info[++tok_num] = strtok(NULL, "\t ")) != NULL) ; strcpy(key, cuts_info[0]); strcat(key, ","); strcat(key, cuts_info[1]); #ifdef DEBUGGING DEBUG(D_SCAN_CUTS, 1) { printf("cuts_testcase: TCID=%s TC=%s Result=%s\n", cuts_info[0], cuts_info[1], cuts_info[2]); printf("cuts_testcase: %d %s\n", tok_num, key); } #endif if ((oldresult = (char *)sym_get(tag, key)) != NULL) { /* Duplicate -- assume mulitple runs */ /* keep "worst case" */ newresult = worst_case(oldresult, cuts_info[2]); sym_put(tag, key, strdup(newresult), PUT_REPLACE); free(oldresult); /* remove the "data" portion of the key */ } else { sym_put(tag, key, strdup(cuts_info[2]), 0); } return 0; } /* * Determine a "worst case" status from two given statuses. */ static char *worst_case(char *t1, char *t2) { /* NULL-terminated table, ordered from worst-case to best-case */ static char *worst[] = { "FAIL", "BROK", "PASS", "CONF", "WARN", "INFO", NULL, }; char **w1, **w2; /* Search the table for each status, then use the index to determine which has a lower precedence */ for (w1 = worst; *w1 != NULL && strcmp(t1, *w1); w1++) ; for (w2 = worst; *w2 != NULL && strcmp(t2, *w2); w2++) ; if (w1 < w2) return (t1); else return (t2); }