| /* |
| * 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); |
| |
| } |