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 */
test_result(char * tag,char * tcid,char * tc,char * result,SYM tags)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
cuts_report(SYM tags,SYM keys,char * at,char * tag)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 */
tag_report(SYM alltags,SYM ctag,SYM keys)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 */
print_header(SYM tags)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 */
cuts_testcase(SYM tag,SYM keys)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 */
worst_case(char * t1,char * t2)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