/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright by The HDF Group. * * Copyright by the Board of Trustees of the University of Illinois. * * All rights reserved. * * * * This file is part of HDF5. The full HDF5 copyright notice, including * * terms governing use, modification, and redistribution, is contained in * * the COPYING file, which can be found at the root of the source code * * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * * If you do not have access to either file, you may request a copy from * * help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Programmer: Quincey Koziol * Tuesday, January 6, 2004 * * Purpose: Provides support functions for the testing framework. * */ #include "testhdf5.h" /* * Definitions for the testing structure. */ #define MAXTESTNAME 16 #define MAXTESTDESC 64 typedef struct TestStruct { int NumErrors; char Description[MAXTESTDESC]; int SkipFlag; char Name[MAXTESTNAME]; void (*Call)(void); void (*Cleanup)(void); const void *Parameters; } TestStruct; /* * Variables used by testing framework. */ static int enable_error_stack = 0; /* enable error stack; disable=0 enable=1 */ static int num_errs = 0; /* Total number of errors during testing */ int TestVerbosity = VERBO_DEF; /* Default Verbosity is Low */ static int Summary = 0; /* Show test summary. Default is no. */ static int CleanUp = 1; /* Do cleanup or not. Default is yes. */ static int TestExpress = -1; /* Do TestExpress or not. -1 means not set yet. */ static TestStruct *Test = NULL; /* Array of tests */ static unsigned TestAlloc = 0; /* Size of the Test array */ static unsigned Index = 0; static const void *Test_parameters = NULL; static const char *TestProgName = NULL; static void (*TestPrivateUsage)(void) = NULL; static int (*TestPrivateParser)(int ac, char *av[]) = NULL; /* * Setup a test function and add it to the list of tests. * It must have no parameters and returns void. * TheName--short test name. * If the name starts with '-', do not run it by default. * TheCall--the test routine. * Cleanup--the cleanup routine for the test. * TheDescr--Long description of the test. * Parameters--pointer to extra parameters. Use NULL if none used. * Since only the pointer is copied, the contents should not change. * Return: Void * exit EXIT_FAILURE if error is encountered. */ void AddTest(const char *TheName, void (*TheCall) (void), void (*Cleanup) (void), const char *TheDescr, const void *Parameters) { /* Sanity checking */ if (HDstrlen(TheDescr) >= MAXTESTDESC) { printf("Test description ('%s') too long, increase MAXTESTDESC(%d).\n", TheDescr, MAXTESTDESC); exit(EXIT_FAILURE); } /* end if */ if (HDstrlen(TheName) >= MAXTESTNAME) { printf("Test name too long, increase MAXTESTNAME(%d).\n", MAXTESTNAME); exit(EXIT_FAILURE); } /* end if */ /* Check for increasing the Test array size */ if(Index >= TestAlloc) { TestStruct *newTest = Test; /* New array of tests */ unsigned newAlloc = MAX(1, TestAlloc * 2); /* New array size */ /* Reallocate array */ if(NULL == (newTest = (TestStruct *)HDrealloc(Test, newAlloc * sizeof(TestStruct)))) { printf("Out of memory for tests, Index = %u, TestAlloc = %u, newAlloc = %u\n", Index, TestAlloc, newAlloc); exit(EXIT_FAILURE); } /* end if */ /* Update info */ Test = newTest; TestAlloc = newAlloc; } /* end if */ /* Set up test function */ HDstrcpy(Test[Index].Description, TheDescr); if(*TheName != '-') { HDstrcpy(Test[Index].Name, TheName); Test[Index].SkipFlag = 0; } else { /* skip test by default */ HDstrcpy(Test[Index].Name, TheName+1); Test[Index].SkipFlag = 1; } Test[Index].Call = TheCall; Test[Index].Cleanup = Cleanup; Test[Index].NumErrors = -1; Test[Index].Parameters = Parameters; /* Increment test count */ Index++; } /* * Initialize testing framework * * ProgName: Name of test program. * private_usage: Optional routine provided by test program to print the * private portion of usage page. Default to NULL which means none is * provided. * private_parser: Optional routine provided by test program to parse the * private options. Default to NULL which means none is provided. * * Modifications: * Albert Cheng 2004/08/17 * Added the ProgName, private_usage and private_parser arguments. */ void TestInit(const char *ProgName, void (*private_usage)(void), int (*private_parser)(int ac, char *av[])) { /* * Turn off automatic error reporting since we do it ourselves. Besides, * half the functions this test calls are private, so automatic error * reporting wouldn't do much good since it's triggered at the API layer. */ if (enable_error_stack == 0) H5Eset_auto2(H5E_DEFAULT, NULL, NULL); /* * Record the program name and private routines if provided. */ TestProgName = ProgName; if(NULL != private_usage) TestPrivateUsage = private_usage; if(NULL != private_parser) TestPrivateParser = private_parser; } /* * Print test usage. * First print the common test options, then the extra options if provided. * * Modification: * 2004/08/18 Albert Cheng. Add TestPrivateUsage feature. */ void TestUsage(void) { unsigned i; print_func("Usage: %s [-v[erbose] (l[ow]|m[edium]|h[igh]|0-9)] %s\n", TestProgName, (TestPrivateUsage ? "" : "")); print_func(" [-[e]x[clude] name]+ \n"); print_func(" [-o[nly] name]+ \n"); print_func(" [-b[egin] name] \n"); print_func(" [-s[ummary]] \n"); print_func(" [-c[leanoff]] \n"); print_func(" [-h[elp]] \n"); print_func("\n\n"); print_func("verbose controls the amount of information displayed\n"); print_func("exclude to exclude tests by name\n"); print_func("only to name tests which should be run\n"); print_func("begin start at the name of the test givin\n"); print_func("summary prints a summary of test results at the end\n"); print_func("cleanoff does not delete *.hdf files after execution of tests\n"); print_func("help print out this information\n"); if (TestPrivateUsage){ print_func("\nExtra options\n"); TestPrivateUsage(); } print_func("\n\n"); print_func("This program currently tests the following: \n\n"); print_func("%16s %s\n", "Name", "Description"); print_func("%16s %s\n", "----", "-----------"); for (i = 0; i < Index; i++) print_func("%16s %s\n", Test[i].Name, Test[i].Description); print_func("\n\n"); } /* * Print test info. */ void TestInfo(const char *ProgName) { unsigned major, minor, release; H5get_libversion(&major, &minor, &release); print_func("\nFor help use: %s -help\n",ProgName); print_func("Linked with hdf5 version %u.%u release %u\n", major, minor, release); } /* * Parse command line information. * argc, argv: the usual command line argument count and strings * * Return: Void * exit EXIT_FAILURE if error is encountered. * * Modification: * 2004/08/18 Albert Cheng. Add extra_parse feature. */ void TestParseCmdLine(int argc, char *argv[]) { hbool_t skipped_all = FALSE; int ret_code; while (argv++, --argc > 0){ if ((HDstrcmp(*argv, "-verbose") == 0) || (HDstrcmp(*argv, "-v") == 0)) { if (argc > 0){ --argc; ++argv; ParseTestVerbosity(*argv); }else{ TestUsage(); exit(EXIT_FAILURE); } } else if (((HDstrcmp(*argv, "-exclude") == 0) || (HDstrcmp(*argv, "-x") == 0))) { if (argc > 0){ --argc; ++argv; SetTest(*argv, SKIPTEST); }else{ TestUsage(); exit(EXIT_FAILURE); } } else if (((HDstrcmp(*argv, "-begin") == 0) || (HDstrcmp(*argv, "-b") == 0))) { if (argc > 0){ --argc; ++argv; SetTest(*argv, BEGINTEST); }else{ TestUsage(); exit(EXIT_FAILURE); } } else if (((HDstrcmp(*argv, "-only") == 0) || (HDstrcmp(*argv, "-o") == 0))) { if(argc > 0) { unsigned Loop; --argc; ++argv; /* Skip all tests, then activate only one. */ if(!skipped_all) { for(Loop = 0; Loop < Index; Loop++) Test[Loop].SkipFlag = 1; skipped_all = TRUE; } /* end if */ SetTest(*argv, ONLYTEST); } /* end if */ else { TestUsage(); exit(EXIT_FAILURE); } } else if ((HDstrcmp(*argv, "-summary") == 0) || (HDstrcmp(*argv, "-s") == 0)) Summary = 1; else if (HDstrcmp(*argv, "-enable-error-stack") == 0) enable_error_stack = 1; else if ((HDstrcmp(*argv, "-help") == 0) || (HDstrcmp(*argv, "-h") == 0)) { TestUsage(); exit(EXIT_SUCCESS); } else if ((HDstrcmp(*argv, "-cleanoff") == 0) || (HDstrcmp(*argv, "-c") == 0)) SetTestNoCleanup(); else { /* non-standard option. Break out. */ break; } } /* Call extra parsing function if provided. */ if (NULL != TestPrivateParser){ ret_code=TestPrivateParser(argc+1, argv-1); if (ret_code != 0) exit(EXIT_FAILURE); } } /* * Perform Tests. */ void PerformTests(void) { unsigned Loop; for (Loop = 0; Loop < Index; Loop++) if (Test[Loop].SkipFlag) { MESSAGE(2, ("Skipping -- %s (%s) \n", Test[Loop].Description, Test[Loop].Name)); } else { MESSAGE(2, ("Testing -- %s (%s) \n", Test[Loop].Description, Test[Loop].Name)); MESSAGE(5, ("===============================================\n")); Test[Loop].NumErrors = num_errs; Test_parameters = Test[Loop].Parameters; ALARM_ON; Test[Loop].Call(); ALARM_OFF; Test[Loop].NumErrors = num_errs - Test[Loop].NumErrors; MESSAGE(5, ("===============================================\n")); MESSAGE(5, ("There were %d errors detected.\n\n", (int)Test[Loop].NumErrors)); } Test_parameters = NULL; /* clear it. */ MESSAGE(2, ("\n\n")) if (num_errs) print_func("!!! %d Error(s) were detected !!!\n\n", (int) num_errs); else print_func("All tests were successful. \n\n"); } /* * Display test summary. */ void TestSummary(void) { unsigned Loop; print_func("Summary of Test Results:\n"); print_func("Name of Test Errors Description of Test\n"); print_func("---------------- ------ --------------------------------------\n"); for (Loop = 0; Loop < Index; Loop++) { if (Test[Loop].NumErrors == -1) print_func("%16s %6s %s\n", Test[Loop].Name, "N/A", Test[Loop].Description); else print_func("%16s %6d %s\n", Test[Loop].Name, (int)Test[Loop].NumErrors, Test[Loop].Description); } print_func("\n\n"); } /* * Cleanup files from testing */ void TestCleanup(void) { unsigned Loop; MESSAGE(2, ("\nCleaning Up temp files...\n\n")); /* call individual cleanup routines in each source module */ for (Loop = 0; Loop < Index; Loop++) if (!Test[Loop].SkipFlag && Test[Loop].Cleanup!=NULL) Test[Loop].Cleanup(); } /* * Shutdown the test infrastructure */ void TestShutdown(void) { if(Test) HDfree(Test); } /* * Retrieve the verbosity level for the testing framework */ H5_ATTR_PURE int GetTestVerbosity(void) { return(TestVerbosity); } /* * Set the verbosity level for the testing framework. * Return previous verbosity level. */ int SetTestVerbosity(int newval) { int oldval; oldval = TestVerbosity; TestVerbosity = newval; return(oldval); } /* * Retrieve the TestExpress mode for the testing framework Values: 0: Exhaustive run Tests should take as long as necessary 1: Full run. Default if HDF5TestExpress is not defined Tests should take no more than 30 minutes 2: Quick run Tests should take no more than 10 minutes 3: Smoke test. Default if HDF5TestExpress is set to a value other than 0-3 Tests should take less than 1 minute Design: If the environment variable $HDF5TestExpress is defined, then test programs should skip some tests so that they complete sooner. Terms: A "test" is a single executable, even if it contains multiple sub-tests. The standard system for test times is a Linux machine running in NFS space (to catch tests that involve a great deal of disk I/O). Implementation: I think this can be easily implemented in the test library (libh5test.a) so that all tests can just call it to check the status of $HDF5TestExpress. */ int GetTestExpress(void) { char * env_val; /* set it here for now. Should be done in something like h5test_init(). */ if(TestExpress==-1) { env_val = getenv("HDF5TestExpress"); if(env_val == NULL) SetTestExpress(1); else if(strcmp(env_val, "0") == 0) SetTestExpress(0); else if(strcmp(env_val, "1") == 0) SetTestExpress(1); else if(strcmp(env_val, "2") == 0) SetTestExpress(2); else SetTestExpress(3); } return(TestExpress); } /* * Set the TestExpress mode for the testing framework. * Return previous TestExpress mode. * Values: non-zero means TestExpress mode is on, 0 means off. */ int SetTestExpress(int newval) { int oldval; oldval = TestExpress; TestExpress = newval; return(oldval); } /* * Retrieve Summary request value. * 0 means no summary, 1 means yes. */ H5_ATTR_PURE int GetTestSummary(void) { return(Summary); } /* * Retrieve Cleanup request value. * 0 means no Cleanup, 1 means yes. */ H5_ATTR_PURE int GetTestCleanup(void) { return(CleanUp); } /* * Set cleanup to no. * Return previous cleanup value. */ int SetTestNoCleanup(void) { int oldval; oldval = CleanUp; CleanUp = 0; return(oldval); } /* * Parse an argument string for verbosity level and set it. */ void ParseTestVerbosity(char *argv) { if (*argv == 'l') SetTestVerbosity(VERBO_LO); else if (*argv == 'm') SetTestVerbosity(VERBO_MED); else if (*argv == 'h') SetTestVerbosity(VERBO_HI); else SetTestVerbosity(atoi(argv)); } /* * Retrieve the number of testing errors for the testing framework */ H5_ATTR_PURE int GetTestNumErrs(void) { return(num_errs); } /* * Increment the number of testing errors */ void IncTestNumErrs(void) { num_errs++; } /* * Retrieve the current Test Parameters pointer. */ H5_ATTR_PURE const void *GetTestParameters(void) { return(Test_parameters); } /* * This routine is designed to provide equivalent functionality to 'printf' * and also increment the error count for the testing framework. */ int TestErrPrintf(const char *format, ...) { va_list arglist; int ret_value; /* Increment the error count */ num_errs++; /* Print the requested information */ va_start(arglist, format); ret_value = vprintf(format, arglist); va_end(arglist); /* Return the length of the string produced (like printf() does) */ return ret_value; } /* * Set (control) which test will be tested. * SKIPTEST: skip this test * ONLYTEST: do only this test * BEGINETEST: skip all tests before this test * */ void SetTest(const char *testname, int action) { unsigned Loop; switch (action){ case SKIPTEST: for (Loop = 0; Loop < Index; Loop++) if (HDstrcmp(testname, Test[Loop].Name) == 0){ Test[Loop].SkipFlag = 1; break; } break; case BEGINTEST: for (Loop = 0; Loop < Index; Loop++) { if (HDstrcmp(testname, Test[Loop].Name) != 0) Test[Loop].SkipFlag = 1; else{ /* Found it. Set it to run. Done. */ Test[Loop].SkipFlag = 0; break; } } break; case ONLYTEST: for (Loop = 0; Loop < Index; Loop++) { if (HDstrcmp(testname, Test[Loop].Name) == 0) { /* Found it. Set it to run. Break to skip the rest. */ Test[Loop].SkipFlag = 0; break; } } break; default: /* error */ printf("*** ERROR: Unknown action (%d) for SetTest\n", action); break; } } /* * Enable alarm on test execution, configurable by environment variable */ void TestAlarmOn(void) { char * env_val = HDgetenv("HDF5_ALARM_SECONDS"); /* Alarm environment */ unsigned long alarm_sec = H5_ALARM_SEC; /* Number of seconds before alarm goes off */ /* Get the alarm value from the environment variable, if set */ if(env_val != NULL) alarm_sec = (unsigned)HDstrtoul(env_val, (char **)NULL, 10); /* Set the number of seconds before alarm goes off */ HDalarm((unsigned)alarm_sec); }