/* $Header: /plroot/cuts/1.0/.RCS/PL/src/lib/RCS/t_result.c,v 1.6 2000/03/28 18:15:48 alaffin Exp $ */ /* * (C) COPYRIGHT CRAY RESEARCH, INC. * UNPUBLISHED PROPRIETARY INFORMATION. * ALL RIGHTS RESERVED. */ /************************************************************** * * OS Testing - Cray Research, Inc. * * FUNCTION NAME : t_result, t_res, t_breakum, t_brk, t_brklook, * t_environ(), tt_exit() * * FUNCTION TITLE : Routines to report test case results, change * standard output environment, and exit a test case * with a meaningful status. * * SYNOPSIS * #include "test.h" * * extern int T_count; * * void t_result(char *tcid, int tnum, int ttype, char *tmesg[, arg]); * void t_res(int ttype, char *fname, char *tmesg[, arg]); * void t_breakum(char *tcid, int t_total, int ttype, char *tmesg, * void (*cleanup)()); * void t_brk(int ttype, char *fname, char *tmesg[, arg]); * void tt_exit(); * int t_environ(); * * AUTHOR : Bob Clark * * CO-PILOT(s) : ?? * * DATE STARTED : 8/85 * * DESIGN DESCRIPTION * None * * SPECIAL REQUIREMENTS * None * * UPDATE HISTORY * username description * ---------------------------------------------------------------- * kar Masked TCONF off from the exit value (3/98). * * BUGS/LIMITATIONS * Tests are heavily discouraged from using this library. The * tst_res(3) library should be used for new tests. * **************************************************************/ #include #include /* for string functions */ #include /* for malloc(), free(), getenv() */ #include "test.h" #define VERBOSE_S "VERBOSE" /* string values of the TOUTPUT */ #define CONDENSE_S "CONDENSE" /* environment variable */ #define NOPASS_S "NOPASS" #define VERBOSE 1 /* flag values for the T_mode variable */ #define CONDENSE 2 #define NOPASS 3 #define MAXMESG 64 /* max length of internal messages */ #define TRUE 1 #define FALSE 0 static FILE *T_out = NULL; /* t_result output file descriptor */ static int T_exval = 0; /* exit value used by tt_exit() */ int T_count = 0; /* current count of test cases executed; note: t_count is available to other programs */ static int T_range = 1; /* # of cases to be printed by t_print() */ static int T_mode = VERBOSE; /* flag indicating print mode: VERBOSE, CONDENSE, or NOPASS */ static int First_time = TRUE; /* flag FALSE after first t_result call */ static int Range_warn = FALSE; /* flag indicating possible range error due to out of sequence tnum's */ /* * Following are static variables for saving the information of the * previous test case. These are used for compression of test case * messages when NOT in verbose mode. */ static char *Last_tcid; /* test case id */ static int Last_num; /* test case number */ static int Last_type; /* test result type */ static char *Last_mesg; /* test result message */ /* * Define local function prototypes. */ static int check_env(); /* checks environment for TOUTPUT variable */ static void t_verbose(); /* handles verbose mode */ static void t_condense(); /* handles condense and nopass modes */ void t_print(); /* prints output results */ /* * t_result() * * Handle test information appropriately depending on information contents * and output display mode. Call t_verbose() or t_condense() to output * results. */ void t_result(tcid, tnum, ttype, tmesg) char *tcid; /* Test case identifier */ int tnum; /* Test case number */ int ttype; /* Test result type: TPASS, TFAIL, TBROK, TRETR, TINFO, TCONF or TWARN */ char *tmesg; /* Message explaining result of test case */ { static char warn_mesg[MAXMESG]; /* warning message */ /* * First save the test result type by ORing ttype into the current exit * value (to be used by tt_exit()). */ T_exval |= ttype; /* * Unless T_out has already been set by t_environ, make t_result * output go to standard output. */ if ( T_out == NULL ) T_out = stdout; /* * Check TOUTPUT environment variable (if first time) and set T_mode * flag. */ if ( First_time ) T_mode = check_env(); /* * Clear warning message. */ warn_mesg[0] = '\0'; /* * Make sure if this case is a WARN or INFO, that tnum is 0. */ if ( tnum == 0 && ttype != TWARN && ttype != TINFO ) { strcpy(warn_mesg, "t_result: Unexpected test case type (expected WARN or INFO)"); } else if ( (ttype == TWARN || ttype == TINFO) && tnum != 0 ) { strcpy(warn_mesg, "t_result: Unexpected test case number (expected 0)"); } /* * A negative tnum is only valid for a range of BROKs, RETRs or CONFs... */ if ( tnum < 0 && ttype != TBROK && ttype != TRETR && ttype != TCONF ) { strcpy(warn_mesg, "t_result: Invalid test case range specifier"); } /* * Set warning flag if this test case is out of order. A warning * will be printed later if a range is detected. */ if ( tnum > 0 && tnum != T_count + 1 ) { Range_warn = TRUE; } /* * Print warning if the user is requesting a range of BROKs and the * range warning flag has been set. */ if ( tnum < 0 && Range_warn == TRUE ) { strcpy(warn_mesg, "t_result: Previous test case out of sequence. Ranges may be incorrect"); } /* * Process each display type and print warning message if needed. */ switch ( T_mode ) { case VERBOSE: /* if there is a warning, print it first */ if ( warn_mesg[0] != '\0' ) { t_verbose(tcid, 0, TWARN, warn_mesg); } t_verbose(tcid, tnum, ttype, tmesg); break; case NOPASS: /* use t_condense() - t_print() strips PASSs */ case CONDENSE: /* if there is a warning, print it first */ if ( warn_mesg[0] != '\0' ) { t_condense(tcid, 0, TWARN, warn_mesg); } t_condense(tcid, tnum, ttype, tmesg); break; default: sprintf(warn_mesg, "t_result: Invalid T_mode %d", T_mode); t_print(tcid, 0, TWARN, warn_mesg); break; } /* end switch() */ /* * If we had to print any warnings because of errors in the way * t_result() was called, add TWARN to the exit value. */ if ( warn_mesg[0] != '\0' ) T_exval |= TWARN; First_time = FALSE; } /* t_result() */ /* * t_verbose() * * Handle test cases in VERBOSE mode - print all messages and expand * messages if a BROK range is given. */ static void t_verbose(char *tcid, int tnum, int ttype, char *tmesg) { /* * Check for negative tnum - range of BROKS. */ if ( tnum < 0 ) { /* * Set T_range to the absolute value of tnum and set tnum to the * current test case number based on T_count. NOTE: this is why test * cases must be received in order. */ T_range = -(tnum); tnum = T_count+1; } else { /* * A regular test case - make sure T_range is set to 1. */ T_range = 1; } /* * Update T_count (except for WARNs and INFOs). */ if ( ttype != TWARN && ttype != TINFO ) { /* * Increment T_count by the number of cases being printed (T_range). */ T_count += T_range; } /* * Print the line(s). */ t_print(tcid, tnum, ttype, tmesg); } /* t_verbose() */ /* * t_condense() * * Handle test cases in CONDENSE or NOPASS mode - save current message and * print last message if different than current. */ static void t_condense(char *tcid, int tnum, int ttype, char *tmesg) { /* * Handle WARNs and INFOs first: no counters are affected. */ if ( ttype == TWARN || ttype == TINFO ) { if ( First_time == FALSE ) { t_print(Last_tcid, Last_num, Last_type, Last_mesg); free(Last_tcid); free(Last_mesg); } /* * Save current line info for next time. */ Last_tcid = malloc(strlen(tcid) + 1); strcpy(Last_tcid, tcid); Last_num = tnum; Last_type = ttype; Last_mesg = malloc(strlen(tmesg) + 1); strcpy(Last_mesg, tmesg); T_range = 1; } else { /* * We have a PASS, FAIL, BROK, or RETR test case. Check if this * message is the same as last message - if so, just increment * T_range and T_count and return. */ if ( strcmp(Last_tcid, tcid) == 0 && Last_type == ttype && strcmp(Last_mesg, tmesg) == 0 && Range_warn == FALSE ) { /* * SAME message: increment T_range and T_count (tnum is ignored * unless less than 0). * * Check for negative tnum - range of BROKS or RETRs. */ if ( tnum < 0 ) { /* * Add the absolute value of tnum to range. */ T_range += -(tnum); T_count += -(tnum); } else { /* * Regular test case, increment T_range and T_count by 1. */ ++T_range; ++T_count; } } else { /* * NEW message: print last message (if there is one) and save * current message for next time. */ if ( First_time == FALSE ) { t_print(Last_tcid, Last_num, Last_type, Last_mesg); free(Last_tcid); free(Last_mesg); } /* * Check for negative tnum - range of BROKS or RETRs. */ if ( tnum < 0 ) { /* * Set T_range to the absolute value of tnum. Set tnum to the * current test case number based on T_count. */ T_range = -(tnum); tnum = T_count+1; } else { /* * A regular test case - make sure T_range is set to 1. */ T_range = 1; } /* * Increment T_count by the number of cases to print. */ T_count += T_range; /* * Save current line info for next time. */ Last_tcid = malloc(strlen(tcid) + 1); strcpy(Last_tcid, tcid); Last_num = tnum; Last_type = ttype; Last_mesg = malloc(strlen(tmesg) + 1); strcpy(Last_mesg, tmesg); } /* if ( we have same message as last time ) */ } /* if ( TWARN or TINFO ) */ } /* t_condense() */ /* * t_print() * * Print a line or range of lines to output stream. If T_mode is VERBOSE * use T_range to expand range of BROKS. If T_mode is not VERBOSE, use * T_range to print range value. */ void t_print(char *tcid, int tnum, int ttype, char *tmesg) { int i; int range = T_range; /* local range variable (to handle WARNs) */ char type[5]; /* storage for result type */ /* * First make sure only one case is printed for a WARN or INFO. */ if ( ttype == TWARN || ttype == TINFO ) { range = 1; } /* * Fill in type string according to ttype. */ switch ( ttype ) { case TPASS: strcpy(type, "PASS"); break; case TFAIL: strcpy(type, "FAIL"); break; case TBROK: strcpy(type, "BROK"); break; case TRETR: strcpy(type, "RETR"); break; case TCONF: strcpy(type, "CONF"); break; case TWARN: strcpy(type, "WARN"); break; case TINFO: strcpy(type, "INFO"); break; default: strcpy(type, "????"); break; } /* end switch() */ /* * Now build line and output. */ if ( T_mode == VERBOSE ) { /* * Verbose mode: print range number of lines. */ for ( i = tnum ; i < tnum + range ; ++i ) { fprintf(T_out, "%-8s %4d %s : %s\n", tcid, i, type, tmesg); } } else { /* * CONDENSE or NOPASS mode: print one condensed line (except for * PASS, RETR, or TCONF cases in NOPASS mode!). */ if ( T_mode == NOPASS && (ttype == TPASS || ttype == TRETR || ttype == TCONF || ttype == TINFO) ) { return; } if ( range > 1 ) { /* * We must print a range. */ fprintf(T_out, "%-8s %4d-%-4d %s : %s\n", tcid, tnum, tnum+range-1, type, tmesg); } else { /* * Only one test case here. */ fprintf(T_out, "%-8s %4d %s : %s\n", tcid, tnum, type, tmesg); } } /* if ( T_mode == VERBOSE ) */ fflush(T_out); } /* t_print() */ /* * check_env() * * Check the value of the environment variable TOUTPUT and set the global * variable T_mode. The TOUTPUT environment variable should be set to * "VERBOSE", "CONDENSE" or "NOPASS". If TOUTPUT does not exist or is not * set to a valid value, the default is "VERBOSE". */ static int check_env() { char *value; /* value of TOUTPUT environment variable */ if ( (value = getenv("TOUTPUT")) == NULL ) { /* TOUTPUT not defined, use default */ T_mode = VERBOSE; } else if ( strcmp(value, CONDENSE_S) == 0 ) { T_mode = CONDENSE; } else if ( strcmp(value, NOPASS_S) == 0 ) { T_mode = NOPASS; } else { /* default */ T_mode = VERBOSE; } return(T_mode); } /* check_env() */ /* * tt_exit() * * Call exit with the value T_exval set up by t_result. T_exval has a bit * set for each of the test result types (TPASS, TFAIL, TBROK, TCONF, * TWARN) passed to t_result. tt_exit masks off the TRETR and TINFO bits * before exiting. tt_exit prints the last line (if needed) before * exiting. */ void tt_exit() { void exit(); /* * Print out last line if there. */ if ( T_mode != VERBOSE && First_time == FALSE ) { t_print(Last_tcid, Last_num, Last_type, Last_mesg); } exit(T_exval & ~(TRETR | TINFO | TCONF)); } /* tt_exit() */ /* * t_environ() * * Preserve the t_result output, despite any changes to stdout. */ int t_environ() { FILE *fdopen(); if ( (T_out = fdopen(dup(fileno(stdout)), "w")) == NULL ) return(-1); else return(0); } /* t_environ() */ /* * t_breakum() * * Fail or break current test with given msg and break the remaining tests. */ void t_breakum(tcid, t_total, typ, msg, fnc) char *tcid; /* pointer to Test Case ID */ int t_total; /* Total number of test cases in this test */ int typ; /* Type of result TFAIL, TCONF, or TBROK */ char *msg; /* pointer to message to include in t_result */ void (*fnc)(); /* pointer to cleanup function */ { char mess[80]; /* message buffer */ if ( typ != TFAIL && typ != TBROK && typ != TCONF ) { sprintf(mess, "Invalid Type: %d. Using TBROK", typ); t_result("t_breakum", 0, TWARN, mess); typ=TBROK; } if ( T_count != t_total ) /* * Print error message, cleanup and exit. */ t_result(tcid, T_count+1, typ, msg); if ( T_count < t_total ) { /* * If typ is TFAIL (or TBROK), use BROK for the rest. If TCONF, use * that. */ if ( typ == TFAIL || typ == TBROK ) t_result(tcid, T_count-t_total, TBROK, "Remaining cases broken"); else t_result(tcid, T_count-t_total, TCONF, "Remaining cases not appropriate for configuration"); } if ( fnc == NULL ) { /* * Return to caller - no cleanup and exit. */ return; } else { (*fnc)(); /* call user specified function */ tt_exit(); /* if fnc function does not call tt_exit - do it! */ } } /* t_breakum() */ #ifdef UNIT /* * Unit test code: takes input from stdin and calls t_result and tt_exit. */ main() { static char tcid[MAXMESG]; /* test case identifier */ static int tnum; /* test case number */ static int ttype; /* test result */ static char tmesg[MAXMESG]; /* test result message */ printf("main: enter tcid, tnum, result, mesg : "); scanf("%8s %d %d %s", tcid, &tnum, &ttype, tmesg); while ( strcmp(tcid, "end") != 0 ) { t_result(tcid, tnum, ttype, tmesg); /* * printf("main: enter tcid, tnum, ttype, tmesg : "); */ scanf("%8s %d %d %s", tcid, &tnum, &ttype, tmesg); } /* end while */ tt_exit(); } #endif