From 6e7877db5e4f05e8f64dda968eb4c69d0d83d599 Mon Sep 17 00:00:00 2001 From: Binh-Minh Ribler Date: Thu, 1 Mar 2001 13:07:25 -0500 Subject: [svn-r3532] Purpose: Adding tests to the C++ API Description: The C++ API has no formal testing yet. Solution: Added tests for file and dataset interfaces. I'm still working on other tests. Platforms tested: Linux (gcc version egcs-2.91.66) I temporarily modified the Makefile on my local Linux machine and these tests work. I need Bill to help adding them permanently before I can test on an NCSA machine. I checked the files in now so Bill can do that. --- c++/test/dsets.cpp | 1048 +++++++++++++++++++++++++++++++++++++++++++++++++ c++/test/testhdf5.cpp | 312 +++++++++++++++ c++/test/tfile.cpp | 332 ++++++++++++++++ 3 files changed, 1692 insertions(+) create mode 100644 c++/test/dsets.cpp create mode 100644 c++/test/testhdf5.cpp create mode 100644 c++/test/tfile.cpp diff --git a/c++/test/dsets.cpp b/c++/test/dsets.cpp new file mode 100644 index 0000000..df5be42 --- /dev/null +++ b/c++/test/dsets.cpp @@ -0,0 +1,1048 @@ +/**************************************************************************** + * NCSA HDF * + * Software Development Group * + * National Center for Supercomputing Applications * + * University of Illinois at Urbana-Champaign * + * 605 E. Springfield, Champaign IL 61820 * + * * + * For conditions of distribution and use, see the accompanying * + * hdf/COPYING file. * + * * + ****************************************************************************/ + +/*********************************************************** +* +* Test program: dsets +* +* Test the dataset interface (H5D) +* +*************************************************************/ + +#include +#include +#include + +#ifndef H5_NO_NAMESPACE +using namespace H5; +#endif + +const char *FILENAME[] = { + "dataset", + NULL +}; + +#define DSET_DEFAULT_NAME "default" +#define DSET_CHUNKED_NAME "chunked" +#define DSET_SIMPLE_IO_NAME "simple_io" +#define DSET_TCONV_NAME "tconv" +#define DSET_COMPRESS_NAME "compressed" +#define DSET_BOGUS_NAME "bogus" + +#define H5Z_BOGUS 305 + + +/*------------------------------------------------------------------------- + * Function: test_create + * + * Purpose: Attempts to create a dataset. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Binh-Minh Ribler + * Friday, January 5, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +test_create( H5File& file) +{ + TESTING("create, open, close"); + + try { + /* Create the data space */ + hsize_t dims[2]; + dims[0] = 256; + dims[1] = 512; + DataSpace space (2, dims, NULL); + + /* + * Create a dataset using the default dataset creation properties. + * We're not sure what they are, so we won't check. + */ + DataSet *dataset = new DataSet (file.createDataSet + (DSET_DEFAULT_NAME, PredType::NATIVE_DOUBLE, space)); + + /* Close the dataset */ + delete dataset; + + /* Add a comment to the dataset */ + file.setComment (DSET_DEFAULT_NAME, "This is a dataset"); + + /* + * Try creating a dataset that already exists. This should fail since a + * dataset can only be created once. If an exception is not thrown + * for this action by createDataSet, then display failure information + * and jump to label error: to return. + */ + try { + dataset = new DataSet (file.createDataSet + (DSET_DEFAULT_NAME, PredType::NATIVE_DOUBLE, space)); + // continuation here, that means no exception has been thrown + FAILED(); + cout << " Library allowed overwrite of existing dataset." << endl; + goto error; + } + catch (FileIException E ) // catching invalid creating dataset + { + // Exception is expected. Do nothing here. + } + /* + * Open the dataset we created above and then close it. This is how + * existing datasets are accessed. + */ + dataset = new DataSet (file.openDataSet (DSET_DEFAULT_NAME)); + delete dataset; + + /* + * Try opening a non-existent dataset. This should fail so if an + * exception is not thrown for this action by openDataSet, then + * display failure information and jump to label error: to return. + */ + try { + dataset = new DataSet (file.openDataSet( "does_not_exist" )); + // continuation here, that means no exception has been thrown + FAILED(); + cout << " Opened a non-existent dataset." << endl; + goto error; + } + catch (FileIException E ) // catching creating non-existent dataset + { + // Exception is expected. Do nothing here. + } + + /* + * Create a new dataset that uses chunked storage instead of the default + * layout. + */ + DSetCreatPropList create_parms; + hsize_t csize[2]; + csize[0] = 5; + csize[1] = 100; + create_parms.setChunk( 2, csize ); + + dataset = new DataSet (file.createDataSet + (DSET_CHUNKED_NAME, PredType::NATIVE_DOUBLE, space, create_parms)); + // Note: this one has no error message in C when failure occurs? + + /* + * Close the chunked dataset. + */ + delete dataset; + + PASSED(); + return 0; + } // outer most try block + + // catch all dataset, file, space, plist exceptions + catch (Exception E) { goto error; } + + error: + return -1; +} + +/*------------------------------------------------------------------------- + * Function: check_values + * + * Purpose: Checks a read value against the written value. If they are + * different, the function will + * print out a message and the different values. This function + * is made to reuse the code segment that is used in various + * places throughout test_compression and in test_simple_io. + * Where the C version + * of this code segment "goto error," this function will + * return -1, so that the caller can "goto error." + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Binh-Minh Ribler (using C code segment for checking values) + * Friday, February 6, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static int +check_values (hsize_t i, hsize_t j, int apoint, int acheck) +{ + if (apoint != acheck) + { + FAILED(); + cout << " Read different values than written.\n" << endl; + cout << " At index " << (unsigned long)i << "," << + (unsigned long)j << endl; + return -1; + } + return 0; +} // check_values + +/*------------------------------------------------------------------------- + * Function: test_simple_io + * + * Purpose: Tests simple I/O. That is, reading and writing a complete + * multi-dimensional array without data type or data space + * conversions, without compression, and stored contiguously. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Binh-Minh Ribler (using Robb Matzke's C version) + * Friday, January 5, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +test_simple_io( H5File& file) +{ + + TESTING("simple I/O"); + + int points[100][200]; + int check[100][200]; + int i, j, n; + + /* Initialize the dataset */ + for (i = n = 0; i < 100; i++) + { + for (j = 0; j < 200; j++) { + points[i][j] = n++; + } + } + + void* tconv_buf = new char [1000]; + try + { + /* Create the data space */ + hsize_t dims[2]; + dims[0] = 100; + dims[1] = 200; + DataSpace space (2, dims, NULL); + + /* Create a small conversion buffer to test strip mining */ + DSetMemXferPropList xfer; + + //if (H5Pset_buffer (xfer, 1000, tconv_buf, NULL)<0) goto error; + xfer.setBuffer (1000, tconv_buf, NULL); + + /* Create the dataset */ + DataSet dataset (file.createDataSet (DSET_SIMPLE_IO_NAME, PredType::NATIVE_INT, space)); + + /* Write the data to the dataset */ + dataset.write ((void*) points, PredType::NATIVE_INT, DataSpace::ALL, DataSpace::ALL, xfer); + + /* Read the dataset back */ + dataset.read ((void*) check, PredType::NATIVE_INT, DataSpace::ALL, DataSpace::ALL, xfer); + + /* Check that the values read are the same as the values written */ + for (i = 0; i < 100; i++) + for (j = 0; j < 200; j++) + { + int status = check_values (i, j, points[i][j], check[i][j]); + if (status == -1) goto error; + } + + delete [] tconv_buf; + PASSED(); + return 0; + } // end try + + // catch all dataset, space, plist exceptions + catch (Exception E) { goto error; } + + error: + // cleaning up + if (tconv_buf) + delete [] tconv_buf; + return -1; +} + +/*------------------------------------------------------------------------- + * Function: test_tconv + * + * Purpose: Test some simple data type conversion stuff. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Binh-Minh Ribler (using Robb Matzke's C version) + * Friday, January 5, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +test_tconv( H5File& file) +{ + // Prepare buffers for input/output + char *out=NULL, *in=NULL; + out = new char [4*1000000]; + // assert (out); - should use exception handler for new - BMR + in = new char [4*1000000]; + //assert (in); + + TESTING("data type conversion"); + + /* Initialize the dataset */ + for (int i = 0; i < 1000000; i++) { + out[i*4+0] = 0x11; + out[i*4+1] = 0x22; + out[i*4+2] = 0x33; + out[i*4+3] = 0x44; + } + + try + { + /* Create the data space */ + hsize_t dims[1]; + dims[0] = 1000000; + DataSpace space (1, dims, NULL); + + /* Create the data set */ + DataSet dataset (file.createDataSet (DSET_TCONV_NAME, PredType::STD_I32LE, space)); + + /* Write the data to the dataset */ + dataset.write ((void*) out, PredType::STD_I32LE); + + /* Read data with byte order conversion */ + dataset.read ((void*) in, PredType::STD_I32BE); + + /* Check */ + for (i = 0; i < 1000000; i++) { + if (in[4*i+0]!=out[4*i+3] || + in[4*i+1]!=out[4*i+2] || + in[4*i+2]!=out[4*i+1] || + in[4*i+3]!=out[4*i+0]) + { + FAILED(); + cout << " Read with byte order conversion failed." << endl; + goto error; + } + } + + delete [] out; + delete [] in; + cout << " PASSED" << endl; + return 0; + } // end try + + // catch all dataset and space exceptions + catch (Exception E) { goto error; } + + error: + delete [] out; + delete [] in; + return -1; +} + +/*------------------------------------------------------------------------- + * Function: bogus + * + * Purpose: A bogus compression method that doesn't do anything. + * + * Return: Success: Data chunk size + * + * Failure: 0 + * + * Programmer: Robb Matzke + * Tuesday, April 21, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static size_t +/*bogus(unsigned int UNUSED flags, size_t UNUSED cd_nelmts, + const unsigned int UNUSED cd_values[], size_t nbytes, + size_t UNUSED *buf_size, void UNUSED **buf) +BMR: removed UNUSED for now until asking Q. or R. to pass compilation*/ +bogus(unsigned int flags, size_t cd_nelmts, + const unsigned int cd_values[], size_t nbytes, + size_t *buf_size, void **buf) +{ + return nbytes; +} + + +/*------------------------------------------------------------------------- + * Function: test_compression + * + * Purpose: Tests dataset compression. If compression is requested when + * it hasn't been compiled into the library (such as when + * updating an existing compressed dataset) then data is sent to + * the file uncompressed but no errors are returned. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Binh-Minh Ribler (using Robb Matzke's C version) + * Friday, January 5, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ + +static herr_t +test_compression(H5File& file) +{ + const char *not_supported; + not_supported = " Deflate compression is not supported.\n" + " The zlib was not found when hdf5 was configured."; + + TESTING("compression (setup)"); + + int points[100][200]; + int check[100][200]; + hsize_t i, j, n; + + /* Initialize the dataset */ + for (i = n = 0; i < 100; i++) + { + for (j = 0; j < 200; j++) { + points[i][j] = n++; + } + } + void* tconv_buf = new char [1000]; + + try + { + const hsize_t size[2] = {100, 200}; + /* Create the data space */ + DataSpace space1(2, size, NULL); + + /* + * Create a small conversion buffer to test strip mining. We + * might as well test all we can! + */ + DSetMemXferPropList xfer; + + xfer.setBuffer (1000, tconv_buf, NULL); + + /* Use chunked storage with compression */ + DSetCreatPropList dscreatplist; + + const hsize_t chunk_size[2] = {2, 25}; + dscreatplist.setChunk (2, chunk_size); + dscreatplist.setDeflate (6); + + /* Create the dataset */ + DataSet* dataset = new DataSet (file.createDataSet + (DSET_COMPRESS_NAME, PredType::NATIVE_INT, space1, dscreatplist)); + +#ifdef H5_HAVE_COMPRESS2 + PASSED(); +#else + SKIPPED(); + cout << not_supported << endl; +#endif + + /*---------------------------------------------------------------------- + * STEP 1: Read uninitialized data. It should be zero. + *---------------------------------------------------------------------- + */ + TESTING("compression (uninitialized read)"); + + dataset->read ((void*) check, PredType::NATIVE_INT, DataSpace::ALL, DataSpace::ALL, xfer); + + for (i=0; iwrite ((void*) points, PredType::NATIVE_INT, DataSpace::ALL, DataSpace::ALL, xfer); +#ifdef H5_HAVE_COMPRESS2 + + PASSED(); +#else + SKIPPED(); + cout << not_supported << endl; +#endif + + /*---------------------------------------------------------------------- + * STEP 3: Try to read the data we just wrote. + *---------------------------------------------------------------------- + */ + TESTING("compression (read)"); + + /* Read the dataset back */ + dataset->read ((void*)check, PredType::NATIVE_INT, DataSpace::ALL, DataSpace::ALL, xfer); + + /* Check that the values read are the same as the values written */ + for (i = 0; i < size[0]; i++) + for (j = 0; j < size[1]; j++) + { + int status = check_values (i, j, points[i][j], check[i][j]); + if (status == -1) goto error; + } + +#ifdef H5_HAVE_COMPRESS2 + PASSED(); +#else + SKIPPED(); + cout << not_supported << endl; +#endif + + /*---------------------------------------------------------------------- + * STEP 4: Write new data over the top of the old data. The new data is + * random thus not very compressible, and will cause the chunks to move + * around as they grow. We only change values for the left half of the + * dataset although we rewrite the whole thing. + *---------------------------------------------------------------------- + */ + TESTING("compression (modify)"); + + for (i=0; iwrite ((void*)points, PredType::NATIVE_INT, DataSpace::ALL, DataSpace::ALL, xfer); + + /* Read the dataset back and check it */ + dataset->read ((void*)check, PredType::NATIVE_INT, DataSpace::ALL, DataSpace::ALL, xfer); + + /* Check that the values read are the same as the values written */ + for (i = 0; i < size[0]; i++) + for (j = 0; j < size[1]; j++) + { + int status = check_values (i, j, points[i][j], check[i][j]); + if (status == -1) goto error; + } + +#ifdef H5_HAVE_COMPRESS2 + PASSED(); +#else + SKIPPED(); + cout << not_supported << endl; +#endif + + /*---------------------------------------------------------------------- + * STEP 5: Close the dataset and then open it and read it again. This + * insures that the compression message is picked up properly from the + * object header. + *---------------------------------------------------------------------- + */ + TESTING("compression (re-open)"); + + delete dataset; + + //if ((dataset = H5Dopen (file, DSET_COMPRESS_NAME))<0) goto error; + dataset = new DataSet (file.openDataSet (DSET_COMPRESS_NAME)); + dataset->read ((void*)check, PredType::NATIVE_INT, DataSpace::ALL, DataSpace::ALL, xfer); + + /* Check that the values read are the same as the values written */ + for (i = 0; i < size[0]; i++) + for (j = 0; j < size[1]; j++) + { + int status = check_values (i, j, points[i][j], check[i][j]); + if (status == -1) goto error; + } + +#ifdef H5_HAVE_COMPRESS2 + PASSED(); +#else + SKIPPED(); + cout << not_supported << endl; +#endif + + + /*---------------------------------------------------------------------- + * STEP 6: Test partial I/O by writing to and then reading from a + * hyperslab of the dataset. The hyperslab does not line up on chunk + * boundaries (we know that case already works from above tests). + *---------------------------------------------------------------------- + */ + TESTING("compression (partial I/O)"); + + const hsize_t hs_size[2] = {4, 50}; + const hssize_t hs_offset[2] = {7, 30}; + for (i = 0; i < hs_size[0]; i++) { + for (j = 0; j < hs_size[1]; j++) { + points[hs_offset[0]+i][hs_offset[1]+j] = rand (); + } + } + space1.selectHyperslab( H5S_SELECT_SET, hs_size, hs_offset ); + dataset->write ((void*)points, PredType::NATIVE_INT, space1, space1, xfer); + dataset->read ((void*)check, PredType::NATIVE_INT, space1, space1, xfer); + + /* Check that the values read are the same as the values written */ + for (i=0; iwrite ((void*)points, PredType::NATIVE_INT, DataSpace::ALL, DataSpace::ALL, xfer); + dataset->read ((void*)check, PredType::NATIVE_INT, DataSpace::ALL, DataSpace::ALL, xfer); + + /* Check that the values read are the same as the values written */ + for (i = 0; i < size[0]; i++) + for (j = 0; j < size[1]; j++) + { + int status = check_values (i, j, points[i][j], check[i][j]); + if (status == -1) goto error; + } + + PASSED(); + + /*---------------------------------------------------------------------- + * Cleanup + *---------------------------------------------------------------------- + */ + delete dataset; + delete [] tconv_buf; + return 0; + } // end try + + // catch all dataset, file, space, and plist exceptions + catch (Exception E) { goto error; } + + error: + // cleaning up + if (tconv_buf) + delete [] tconv_buf; + return -1; +} + +/*------------------------------------------------------------------------- + * Function: test_multiopen + * + * Purpose: Tests that a bug no longer exists. If a dataset is opened + * twice and one of the handles is used to extend the dataset, + * then the other handle should return the new size when + * queried. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Binh-Minh Ribler (using Robb Matzke's C version) + * Saturday, February 17, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +test_multiopen (H5File& file) +{ + + TESTING("multi-open with extending"); + try { + + // Create a dataset creation property list + DSetCreatPropList dcpl; + + // Set chunk size to given size + hsize_t cur_size[1] = {10}; + dcpl.setChunk (1, cur_size); + + // Create a simple data space with unlimited size + static hsize_t max_size[1] = {H5S_UNLIMITED}; + DataSpace* space = new DataSpace (1, cur_size, max_size); + + // Create first dataset + DataSet dset1 = file.createDataSet ("multiopen", PredType::NATIVE_INT, *space, dcpl); + + //BMR: Quincey said Rob gave a tweak to have dataset being the first + // argument in this call but actually shouldn't be valid, so just + // ignore the argument dset1. Just open the first dataset again + // from the file to another DataSet object. + // if ((dset2=H5Dopen (dset1, "."))<0) goto error; + DataSet dset2 = file.openDataSet ("multiopen"); + + // Relieve the dataspace + delete space; + + // Extend the dimensionality of the first dataset + cur_size[0] = 20; + dset1.extend (cur_size); + + /* Get the size from the second handle */ + //if ((space = H5Dget_space (dset2))<0) goto error; + space = new DataSpace (dset2.getSpace()); + + hsize_t tmp_size[1]; + //if (H5Sget_simple_extent_dims (space, tmp_size, NULL)<0) goto error; + space->getSimpleExtentDims (tmp_size); + if (cur_size[0]!=tmp_size[0]) + { + FAILED(); + cout << " Got " << (int)tmp_size[0] << " instead of " + << (int)cur_size[0] << "!" << endl; + delete space; + goto error; + } + + delete space; + PASSED(); + return 0; + } // end try block + + // catch all dataset, file, space, and plist exceptions + catch (Exception E) + { goto error; } + + error: + return -1; +} + + +/*------------------------------------------------------------------------- + * Function: test_types + * + * Purpose: Make some datasets with various types so we can test h5ls. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Binh-Minh Ribler (using Robb Matzke's C version) + * February 17, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +test_types(H5File& file) +{ + size_t i; + + TESTING("various datatypes"); + try { + + // Create a group in the file that was passed in from the caller + Group grp = file.createGroup ("typetests"); + + /* bitfield_1 */ + unsigned char buf[32]; + hsize_t nelmts = sizeof(buf); + DataType type; + try { // block 1 + type.copy (PredType::STD_B8LE); + DataSpace space (1, &nelmts); + DataSet* dset = new DataSet(grp.createDataSet("bitfield_1", type, space)); + + // Fill buffer + for (i=0; iwrite (buf, type); } + catch(DataSetIException E) + { + delete dset; + goto error; + } + delete dset; + + } // end try block of bitfield_1 + + // catch exceptions thrown in try block of bitfield_1 + catch (Exception E) { goto error; } + + /* bitfield_2 */ + nelmts = sizeof(buf)/2; + try { // bitfield_2 block + + type.copy (PredType::STD_B16LE); + DataSpace space (1, &nelmts); + DataSet* dset = new DataSet(grp.createDataSet("bitfield_2", type, space)); + + // Fill buffer + for (i=0; iwrite (buf, type); } + catch(DataSetIException E) + { + delete dset; + goto error; + } + delete dset; + } // end try block of bitfield_2 + + // catch exceptions thrown in try block of bitfield_2 + catch (Exception E) { goto error; } + + /* opaque_1 */ + DataType* optype = new DataType(H5T_OPAQUE, 1); + try { // opaque_1 block + nelmts = sizeof(buf); + DataSpace space (1, &nelmts); + optype->setTag ("testing 1-byte opaque type"); + DataSet* dset = new DataSet(grp.createDataSet("opaque_1", *optype, space)); + + // Fill buffer + for (i=0; iwrite (buf, *optype); } + catch(DataSetIException E) + { + delete dset; + goto error; + } + delete dset; + delete optype; + } // end try block of opaque_1 + + // catch exceptions thrown in try block of opaque_1 + catch (DataSetIException E) { delete optype; goto error; } + catch (Exception E) { goto error; } + + /* opaque_2 */ + try { // block opaque_2 + nelmts = sizeof(buf)/4; + DataSpace space (1, &nelmts); + optype = new DataType(H5T_OPAQUE, 4); + optype->setTag ("testing 4-byte opaque type"); + DataSet* dset = new DataSet(grp.createDataSet("opaque_2", *optype, space)); + + // Fill buffer + for (i=0; iwrite (buf, *optype); } + catch(DataSetIException E) + { + delete dset; + goto error; + } + delete dset; + delete optype; + } //end try block of opaque_2 + catch (DataSetIException E) { delete optype; goto error; } + catch (Exception E) { goto error; } + + PASSED(); + return 0; + } // end top try block + catch (Exception E) { goto error; } // Group and DataType exceptions + + error: + return -1; +} + + +/*------------------------------------------------------------------------- + * Function: test_report + * + * Purpose: Prints out the number of errors for dataset tests if there + * were any failures occurred. If no failure, test_report + * prints out the "All dataset tests passed" message + * + * Return: if any failure has occurred: 1 + * + * if no failure occurs: 0 + * + * Programmer: Binh-Minh Ribler (using C code segment for reporting tests) + * Friday, February 6, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int test_report( int nerrors ) +{ + if (nerrors) + { + nerrors = MAX(1, nerrors); + if (1 == nerrors) + cout << "***** " << nerrors << " DATASET TEST" + << " FAILED! *****" << endl; + else + cout << "***** " << nerrors << " DATASET TESTS" + << " FAILED! *****" << endl; + return 1; + } + else + { + cout << "All dataset tests passed." << endl; + return 0; + } +} + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: Tests the dataset interface (H5D) + * + * Return: Success: exit(0) + * + * Failure: exit(1) + * + * Programmer: Binh-Minh Ribler (using Robb Matzke's C version) + * Friday, January 5, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +int +main(void) +{ + + h5_reset(); // in h5test.c, resets the library by closing it + + hid_t fapl_id; + fapl_id = h5_fileaccess(); // in h5test.c, returns a file access template + // should create an object from this id - BMR + +/* BMR: leave paralell stuff out! +#if 0 + { + // Turn off raw data cache + int mdc_nelmts; + (H5Pget_cache(fapl_id, &mdc_nelmts, NULL, NULL, NULL)<0) goto error; + (H5Pset_cache(fapl_id, mdc_nelmts, 0, 0, 0.0)<0) goto error; + } +#endif +*/ + + char filename[1024]; + h5_fixname(FILENAME[0], fapl_id, filename, sizeof filename); + + int nerrors=0; // keep track of number of failures occurr + try + { + // Turn of the auto-printing when failure occurs so that we can + // handle the errors appropriately since sometime failures are + // caused deliberately and expected. + Exception::dontPrint(); + + // Use the file access template id to create a file access prop. + // list object to pass in H5File::H5File + FileAccPropList* fapl = new FileAccPropList(fapl_id); + + H5File file( filename, H5F_ACC_TRUNC, FileCreatPropList::DEFAULT, *fapl ); + + /* Cause the library to emit initial messages */ + Group grp = file.createGroup( "emit diagnostics", 0); + grp.setComment( ".", "Causes diagnostic messages to be emitted"); + + nerrors += test_create(file)<0 ?1:0; + nerrors += test_simple_io(file)<0 ?1:0; + nerrors += test_tconv(file)<0 ?1:0; + nerrors += test_compression(file)<0 ?1:0; + nerrors += test_multiopen (file)<0 ?1:0; + nerrors += test_types(file)<0 ?1:0; + + // BMR: this is a very unattractive workaround approach, but I used + // it for now, since I want to use already existing utility code from + // the C tests. h5_cleanup calls H5Pclose to close the fapl_id while + // the object 'fapl' is still opened. So, I dynamically allocated + // 'fapl' then deleted it before h5_cleanup, but after incrementing + // 'fapl's ref counter so that the prop list id will not be closed + // when 'fapl' is destroyed. Why did I need to create a + // FileCreatPropList object while I already had the prop list id? + // Because I need to pass it into the constructor H5File::H5File. + // We're trying to avoid introducing id's into the C++ API. + fapl->incRefCount(); + delete fapl; + h5_cleanup(FILENAME, fapl_id); + return( test_report( nerrors )); + } + catch (FileIException error) + { + return( test_report( nerrors )); + } + catch (GroupIException error ) + { + return( test_report( nerrors )); + } +} + + diff --git a/c++/test/testhdf5.cpp b/c++/test/testhdf5.cpp new file mode 100644 index 0000000..28d6a54 --- /dev/null +++ b/c++/test/testhdf5.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** + * NCSA HDF * + * Software Development Group * + * National Center for Supercomputing Applications * + * University of Illinois at Urbana-Champaign * + * 605 E. Springfield, Champaign IL 61820 * + * * + * For conditions of distribution and use, see the accompanying * + * hdf/COPYING file. * + * * + ****************************************************************************/ + +/* + FILE + testhdf5.c - HDF5 testing framework main file. + + REMARKS + General test wrapper for HDF5 base library test programs + + DESIGN + Each test function should be implemented as function having no + parameters and returning void (i.e. no return value). They should be put + into the list of InitTest() calls in main() below. Functions which depend + on other functionality should be placed below the InitTest() call for the + base functionality testing. + Each test module should include testhdf5.h and define a unique set of + names for test files they create. + + BUGS/LIMITATIONS + + EXPORTED ROUTINES/VARIABLES: + Two variables are exported: num_errs, and Verbosity. + + */ + +#if defined __MWERKS__ +#include +#endif + +#include + +#define MAXNUMOFTESTS 30 +#define HDF5_TEST_MASTER + +/* Internal Variables */ +static int Index = 0; + +/* Global variables */ +int num_errs = 0; +int Verbosity; + +// Use C version of the header file testhdf5.h instead of re-coding it +#include +#include + +#ifndef H5_NO_NAMESPACE +using namespace H5; +#endif + +struct TestStruct { + int NumErrors; + char Description[64]; + int SkipFlag; + char Name[16]; + void (*Call) (void); + void (*Cleanup) (void); +} Test[MAXNUMOFTESTS]; + +static void InitTest(const char *TheName, void (*TheCall) (void), void (*Cleanup) (void), const char *TheDescr); +static void usage(void); + +static void +InitTest(const char *TheName, void (*TheCall) (void), void (*Cleanup) (void), const char *TheDescr) +{ + if (Index >= MAXNUMOFTESTS) { + print_func("Uh-oh, too many tests added, increase MAXNUMOFTEST!\n"); + exit(-1); + } /* end if */ + HDstrcpy(Test[Index].Description, TheDescr); + HDstrcpy(Test[Index].Name, TheName); + Test[Index].Call = TheCall; + Test[Index].Cleanup = Cleanup; + Test[Index].NumErrors = -1; + Test[Index].SkipFlag = 0; + Index++; +} + +static void +usage(void) +{ + intn i; + + print_func("Usage: testhdf5 [-v[erbose] (l[ow]|m[edium]|h[igh]|0-10)] \n"); + 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(" [-n[ocaching]] \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("nocaching do not turn on low-level DD caching\n"); + print_func("help print out this information\n"); + 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"); +} /* end usage() */ + +/* + * This routine is designed to provide equivalent functionality to 'printf' + * and allow easy replacement for environments which don't have stdin/stdout + * available. (i.e. Windows & the Mac) + */ +int +print_func(const char *format,...) +{ + va_list arglist; + int ret_value; + + va_start(arglist, format); + ret_value = vprintf(format, arglist); + va_end(arglist); + return (ret_value); +} + +void +test_tbbt(void) +{ } + +int +main(int argc, char *argv[]) +{ + int CLLoop; /* Command Line Loop */ + int Loop, Loop1; + int Summary = 0; + int CleanUp = 1; + int Cache = 1; + +#if defined __MWERKS__ + argc = ccommand(&argv); +#endif + +#if !(defined MAC || defined __MWERKS__ || defined SYMANTEC_C) + /* Un-buffer the stdout and stderr */ + setbuf(stderr, NULL); + setbuf(stdout, NULL); +#endif + + /* + * 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. + */ + Exception::dontPrint(); + + // Tests are generally arranged from least to most complexity... + //InitTest("metadata", test_metadata, cleanup_metadata, "Encode/decode metadata code"); + + // C++ API doesn't need this test */ + InitTest("tbbt", test_tbbt, NULL, "Threaded, Balanced, Binary Trees - not tested"); + + // testing file creation and opening in tfile.cpp + InitTest("file", test_file, cleanup_file, "Low-Level File I/O"); + + // Comment out tests that are not done yet. - BMR, Feb 2001 + //InitTest("h5s", test_h5s, cleanup_h5s, "Dataspaces"); + //InitTest("attr", test_attr, cleanup_attr, "Attributes"); + //InitTest("select", test_select, cleanup_select, "Selections"); + //InitTest("time", test_time, cleanup_time, "Time Datatypes"); + //InitTest("reference", test_reference, cleanup_reference, "References"); + //InitTest("vltypes", test_vltypes, cleanup_vltypes, "Variable-Length Datatypes"); + //InitTest("vlstrings", test_vlstrings, cleanup_vlstrings, "Variable-Length Strings"); + //InitTest("iterate", test_iterate, cleanup_iterate, "Group & Attribute Iteration"); + //InitTest("array", test_array, cleanup_array, "Array Datatypes"); + //InitTest("genprop", test_genprop, cleanup_genprop, "Generic Properties"); + + Verbosity = 4; /* Default Verbosity is Low */ + uintn major, minor, release; + H5Library::getLibVersion( major, minor, release); + + print_func("\nFor help use: testhdf5 -help\n"); + print_func("Linked with hdf5 version %u.%u release %u\n", + (unsigned)major, (unsigned)minor, (unsigned)release); + for (CLLoop = 1; CLLoop < argc; CLLoop++) { + if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-verbose") == 0) || + (HDstrcmp(argv[CLLoop], "-v") == 0))) { + if (argv[CLLoop + 1][0] == 'l') + Verbosity = 4; + else if (argv[CLLoop + 1][0] == 'm') + Verbosity = 6; + else if (argv[CLLoop + 1][0] == 'h') + Verbosity = 10; + else + Verbosity = atoi(argv[CLLoop + 1]); + } /* end if */ + if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-summary") == 0) || + (HDstrcmp(argv[CLLoop], "-s") == 0))) + Summary = 1; + + if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-help") == 0) || + (HDstrcmp(argv[CLLoop], "-h") == 0))) { + usage(); + exit(0); + } + if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-cleanoff") == 0) || + (HDstrcmp(argv[CLLoop], "-c") == 0))) + CleanUp = 0; + + if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-nocache") == 0) || + (HDstrcmp(argv[CLLoop], "-n") == 0))) { + Cache = 0; + printf ("Cache = %d\n", Cache); + } + + if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-exclude") == 0) || + (HDstrcmp(argv[CLLoop], "-x") == 0))) { + Loop = CLLoop + 1; + while ((Loop < argc) && (argv[Loop][0] != '-')) { + for (Loop1 = 0; Loop1 < Index; Loop1++) + if (HDstrcmp(argv[Loop], Test[Loop1].Name) == 0) + Test[Loop1].SkipFlag = 1; + Loop++; + } /* end while */ + } /* end if */ + if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-begin") == 0) || + (HDstrcmp(argv[CLLoop], "-b") == 0))) { + Loop = CLLoop + 1; + while ((Loop < argc) && (argv[Loop][0] != '-')) { + for (Loop1 = 0; Loop1 < Index; Loop1++) { + if (HDstrcmp(argv[Loop], Test[Loop1].Name) != 0) + Test[Loop1].SkipFlag = 1; + if (HDstrcmp(argv[Loop], Test[Loop1].Name) == 0) + Loop1 = Index; + } /* end for */ + Loop++; + } /* end while */ + } /* end if */ + if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-only") == 0) || + (HDstrcmp(argv[CLLoop], "-o") == 0))) { + for (Loop = 0; Loop < Index; Loop++) + Test[Loop].SkipFlag = 1; + Loop = CLLoop + 1; + while ((Loop < argc) && (argv[Loop][0] != '-')) { + for (Loop1 = 0; Loop1 < Index; Loop1++) + if (HDstrcmp(argv[Loop], Test[Loop1].Name) == 0) + Test[Loop1].SkipFlag = 0; + Loop++; + } /* end while */ + } /* end if */ + } /* end for */ + +#ifdef NOT_YET + if (Cache) /* turn on caching, unless we were instucted not to */ + Hcache(CACHE_ALL_FILES, TRUE); +#endif /* NOT_YET */ + + for (Loop = 0; Loop < Index; Loop++) { + if (Test[Loop].SkipFlag) { + MESSAGE(2, ("Skipping -- %s \n", Test[Loop].Description)); + } else { + MESSAGE(2, ("Testing -- %s (%s) \n", Test[Loop].Description, + Test[Loop].Name)); + MESSAGE(5, ("===============================================\n")); + Test[Loop].NumErrors = num_errs; + (*Test[Loop].Call) (); + Test[Loop].NumErrors = num_errs - Test[Loop].NumErrors; + MESSAGE(5, ("===============================================\n")); + MESSAGE(5, ("There were %d errors detected.\n\n", (int) Test[Loop].NumErrors)); + } /* end else */ + } /* end for */ + + 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"); + + if (Summary) { + 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); + } /* end for */ + print_func("\n\n"); + } /* end if */ + if (CleanUp && !getenv("HDF5_NOCLEANUP")) { + 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) (); + } + return (num_errs); +} /* end main() */ + diff --git a/c++/test/tfile.cpp b/c++/test/tfile.cpp new file mode 100644 index 0000000..f359eb7 --- /dev/null +++ b/c++/test/tfile.cpp @@ -0,0 +1,332 @@ +/**************************************************************************** + * NCSA HDF * + * Software Development Group * + * National Center for Supercomputing Applications * + * University of Illinois at Urbana-Champaign * + * 605 E. Springfield, Champaign IL 61820 * + * * + * For conditions of distribution and use, see the accompanying * + * hdf/COPYING file. * + * * + ****************************************************************************/ + +/*********************************************************** +* +* Test program: tfile +* +* Test the low-level file I/O features. +* +*************************************************************/ + +#include +#include + +#ifndef H5_NO_NAMESPACE +using namespace H5; +#endif + +#include +#include +#include + +#define F1_USERBLOCK_SIZE (hsize_t)0 +#define F1_OFFSET_SIZE sizeof(haddr_t) +#define F1_LENGTH_SIZE sizeof(hsize_t) +#define F1_SYM_LEAF_K 4 +#define F1_SYM_INTERN_K 16 +#define FILE1 "tfile1.h5" + +#define F2_USERBLOCK_SIZE (hsize_t)512 +#define F2_OFFSET_SIZE 8 +#define F2_LENGTH_SIZE 8 +#define F2_SYM_LEAF_K 8 +#define F2_SYM_INTERN_K 32 +#define FILE2 "tfile2.h5" + +#define F3_USERBLOCK_SIZE (hsize_t)0 +#define F3_OFFSET_SIZE F2_OFFSET_SIZE +#define F3_LENGTH_SIZE F2_LENGTH_SIZE +#define F3_SYM_LEAF_K F2_SYM_LEAF_K +#define F3_SYM_INTERN_K F2_SYM_INTERN_K +#define FILE3 "tfile3.h5" + + +/*------------------------------------------------------------------------- + * Function: test_file_create + * + * Purpose: Test file and template creations + * + * Return: None + * + * Programmer: Binh-Minh Ribler + * January, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +test_file_create(void) +{ + hid_t tmpl1, tmpl2; /*file creation templates */ + int iparm, iparm2; + herr_t ret; /*generic return value */ + + /* Output message about test being performed */ + MESSAGE(5, ("Testing Low-Level File Creation I/O\n")); + + /* Test create with various sequences of H5F_ACC_EXCL and */ + /* H5F_ACC_TRUNC flags */ + + /* Create with H5F_ACC_EXCL */ + /* First ensure the file does not exist */ + //remove(FILE1); + + try { + H5File* fid1 = new H5File (FILE1, H5F_ACC_EXCL); + + /* + * try to create the same file with H5F_ACC_TRUNC. This should fail + * because fid1 is the same file and is currently open. + */ + try { H5File fid2 (FILE1, H5F_ACC_TRUNC); } + catch( FileIException error ) { + // cannot use fid2 here (out of scope), but the exception was + // thrown only if file id was < 0, so -1 is used to verify - 1/15/01 + VERIFY(-1, FAIL, "H5File constructor"); + } + // Close file fid1 + delete fid1; + + /* + * Try again with H5F_ACC_EXCL. This should fail because the file already + * exists from the previous steps. + */ + try { fid1 = new H5File( FILE1, H5F_ACC_EXCL ); } + catch( FileIException error ){ VERIFY(-1, FAIL, "H5File constructor"); } + + // Test create with H5F_ACC_TRUNC. This will truncate the existing file. + fid1 = new H5File (FILE1, H5F_ACC_TRUNC); + + /* + * Try to truncate first file again. This should fail because fid1 is the + * same file and is currently open. + */ + try { H5File fid2 (FILE1, H5F_ACC_TRUNC); } + catch( FileIException error ) { VERIFY(-1, FAIL, "H5File constructor"); } + + /* + * Try with H5F_ACC_EXCL. This should fail too because the file already + * exists. + */ + try { H5File fid3 (FILE1, H5F_ACC_EXCL); } + catch( FileIException error ) { VERIFY(-1, FAIL, "H5File constructor"); } + + /* Get the file-creation template */ + FileCreatPropList tmpl1 = fid1->getCreatePlist(); + + hsize_t ublock = tmpl1.getUserblock(); + VERIFY(ublock, F1_USERBLOCK_SIZE, "FileCreatPropList::H5Pget_userblock"); + + size_t parm1, parm2; /*file-creation parameters */ + tmpl1.getSizes( parm1, parm2); + VERIFY(parm1, F1_OFFSET_SIZE, "FileCreatPropList::getSizes"); + VERIFY(parm2, F1_LENGTH_SIZE, "FileCreatPropList::getSizes"); + + int iparm1, iparm2; /*file-creation parameters */ + tmpl1.getSymk( iparm1, iparm2); + VERIFY(iparm1, F1_SYM_INTERN_K, "FileCreatPropList::getSymk"); + VERIFY(iparm2, F1_SYM_LEAF_K, "FileCreatPropList::getSymk"); + + // tmpl1 is automatically closed; if error occurs, it'll be + // caught in the catch block + + /* Close first file */ + delete fid1; + } + catch( PropListIException error ) { + CHECK(-1, FAIL, error.getCDetailMesg()); + } + catch( FileIException error ) { + CHECK(-1, FAIL, error.getCDetailMesg()); + } + + try + { + /* Create a new file with a non-standard file-creation template */ + FileCreatPropList* tmpl1 = new FileCreatPropList; + + /* Set the new file-creation parameters */ + tmpl1->setUserblock (F2_USERBLOCK_SIZE); + tmpl1->setSizes( F2_OFFSET_SIZE, F2_LENGTH_SIZE ); + tmpl1->setSymk( F2_SYM_INTERN_K, F2_SYM_LEAF_K ); + + /* + * Try to create second file, with non-standard file-creation template + * params. + */ + H5File fid2( FILE2, H5F_ACC_TRUNC, *tmpl1 ); + + /* Release file-creation template */ + delete tmpl1; + + /* Get the file-creation template */ + tmpl1 = new FileCreatPropList (fid2.getCreatePlist()); + + /* Get the file-creation parameters */ + hsize_t ublock = tmpl1->getUserblock(); + VERIFY(ublock, F2_USERBLOCK_SIZE, "FileCreatPropList::getUserblock"); + + size_t parm1, parm2; /*file-creation parameters */ + tmpl1->getSizes( parm1, parm2); + VERIFY(parm1, F2_OFFSET_SIZE, "FileCreatPropList::getSizes"); + VERIFY(parm2, F2_LENGTH_SIZE, "FileCreatPropList::getSizes"); + + int iparm1, iparm2; /*file-creation parameters */ + tmpl1->getSymk( iparm1, iparm2); + VERIFY(iparm1, F2_SYM_INTERN_K, "FileCreatPropList::getSymk"); + VERIFY(iparm2, F2_SYM_LEAF_K, "FileCreatPropList::getSymk"); + + /* Clone the file-creation template */ + FileCreatPropList tmpl2; + tmpl2.copy (*tmpl1); + + /* Dynamically release file-creation template */ + delete tmpl1; + + /* Set the new file-creation parameter */ + tmpl2.setUserblock( F3_USERBLOCK_SIZE ); + + /* + * Try to create second file, with non-standard file-creation template + * params + */ + H5File fid3( FILE3, H5F_ACC_TRUNC, tmpl2 ); + + /* Get the file-creation template */ + tmpl1 = new FileCreatPropList (fid3.getCreatePlist()); + + /* Get the file-creation parameters */ + ublock = tmpl1->getUserblock(); + VERIFY(ublock, F3_USERBLOCK_SIZE, "FileCreatPropList::getUserblock"); + + tmpl1->getSizes( parm1, parm2); + VERIFY(parm1, F3_OFFSET_SIZE, "FileCreatPropList::getSizes"); + VERIFY(parm2, F3_LENGTH_SIZE, "FileCreatPropList::getSizes"); + + tmpl1->getSymk( iparm1, iparm2); + VERIFY(iparm1, F3_SYM_INTERN_K, "FileCreatPropList::getSymk"); + VERIFY(iparm2, F3_SYM_LEAF_K, "FileCreatPropList::getSymk"); + + /* Dynamically release file-creation template */ + delete tmpl1; + } + catch( PropListIException error ) { + CHECK(-1, FAIL, error.getCDetailMesg()); + } + catch( FileIException error ) { + CHECK(-1, FAIL, error.getCDetailMesg()); + } +} /* test_file_create() */ + + +/*------------------------------------------------------------------------- + * Function: test_file_open + * + * Purpose: Test file accesses + * + * Return: None + * + * Programmer: Binh-Minh Ribler + * January, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static void +test_file_open(void) +{ + /* Output message about test being performed */ + MESSAGE(5, ("Testing Low-Level File Opening I/O\n")); + + try { + + /* Open first file */ + H5File fid1 (FILE2, H5F_ACC_RDWR ); + + /* Get the file-creation template */ + //FileCreatPropList tmpl1; + FileCreatPropList tmpl1 = fid1.getCreatePlist(); + + /* Get the file-creation parameters */ + hsize_t ublock = tmpl1.getUserblock(); + VERIFY(ublock, F2_USERBLOCK_SIZE, "FileCreatPropList::getUserblock"); + + size_t parm1, parm2; /*file-creation parameters */ + tmpl1.getSizes( parm1, parm2); + VERIFY(parm1, F2_OFFSET_SIZE, "FileCreatPropList::getSizes"); + VERIFY(parm2, F2_LENGTH_SIZE, "FileCreatPropList::getSizes"); + + int iparm1, iparm2; /*file-creation parameters */ + tmpl1.getSymk( iparm1, iparm2); + VERIFY(iparm1, F2_SYM_INTERN_K, "FileCreatPropList::getSymk"); + VERIFY(iparm2, F2_SYM_LEAF_K, "FileCreatPropList::getSymk"); + } // end of try block + + catch( FileIException error ) { + CHECK(FAIL, FAIL, error.getCDetailMesg()); + } + catch( PropListIException error ) { + CHECK(FAIL, FAIL, error.getCDetailMesg()); + } +} /* test_file_open() */ + + +/*------------------------------------------------------------------------- + * Function: test_file + * + * Purpose: Main program + * + * Return: None + * + * Programmer: Binh-Minh Ribler + * January, 2001 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +void +test_file(void) +{ + /* Output message about test being performed */ + MESSAGE(5, ("Testing Low-Level File I/O\n")); + + test_file_create(); /* Test file creation (also creation templates) */ + test_file_open(); /* Test file opening */ +} /* test_file() */ + + +/*------------------------------------------------------------------------- + * Function: cleanup_file + * + * Purpose: Cleanup temporary test files + * + * Return: none + * + * Programmer: Albert Cheng + * July 2, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +void +cleanup_file(void) +{ + remove(FILE1); + remove(FILE2); + remove(FILE3); +} + -- cgit v0.12