Home | History | Annotate | Download | only in pan
      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