From eefddf281366e8327dd7397db4a2f2e7edabcf17 Mon Sep 17 00:00:00 2001 From: Quincey Koziol Date: Mon, 19 Dec 2016 20:44:54 -0800 Subject: Bring new/updated tests from revise+chunks branch: flush1/flush2 update and flushrefresh test. (Also refactor of message send / wait code) --- MANIFEST | 2 + configure.ac | 1 + hl/tools/h5watch/Makefile.am | 8 + hl/tools/h5watch/extend_dset.c | 62 +-- test/CMakeLists.txt | 1 + test/CMakeTests.cmake | 17 + test/Makefile.am | 22 +- test/flush1.c | 61 ++- test/flush2.c | 173 +++++- test/flushrefresh.c | 1151 ++++++++++++++++++++++++++++++++++++++++ test/h5test.c | 121 +++++ test/h5test.h | 3 + test/testflushrefresh.sh.in | 200 +++++++ 13 files changed, 1737 insertions(+), 85 deletions(-) create mode 100644 test/flushrefresh.c create mode 100644 test/testflushrefresh.sh.in diff --git a/MANIFEST b/MANIFEST index 3141519..0c0c4fd 100644 --- a/MANIFEST +++ b/MANIFEST @@ -943,6 +943,7 @@ ./test/filter_fail.c ./test/flush1.c ./test/flush2.c +./test/flushrefresh.c ./test/gen_bad_ohdr.c ./test/gen_bad_compound.c ./test/gen_bogus.c @@ -1010,6 +1011,7 @@ ./test/testcheck_version.sh.in ./test/testerror.sh.in ./test/testlinks_env.sh.in +./test/testflushrefresh.sh.in ./test/testframe.c ./test/testhdf5.c ./test/testhdf5.h diff --git a/configure.ac b/configure.ac index f4c5855..82ad020 100644 --- a/configure.ac +++ b/configure.ac @@ -3317,6 +3317,7 @@ AC_CONFIG_FILES([src/libhdf5.settings test/Makefile test/testcheck_version.sh test/testerror.sh + test/testflushrefresh.sh test/H5srcdir_str.h test/testlibinfo.sh test/testlinks_env.sh diff --git a/hl/tools/h5watch/Makefile.am b/hl/tools/h5watch/Makefile.am index a5891ef..35a08e6 100644 --- a/hl/tools/h5watch/Makefile.am +++ b/hl/tools/h5watch/Makefile.am @@ -37,6 +37,14 @@ h5watch_LDFLAGS = $(LT_STATIC_EXEC) $(AM_LDFLAGS) # Programs all depend on the hdf5 library, the tools library, and the HL # library. LDADD=$(LIBH5_HL) $(LIBH5TOOLS) $(LIBHDF5) + +# Add extend_dset specific preprocessor flags here +# (add the main test subdirectory to the include file path) +extend_dset_CPPFLAGS=$(AM_CPPFLAGS) -I$(top_srcdir)/test +# Add extend_dset specific library flags here +# (add the main test library to the list of libraries) +extend_dset_LDADD=$(LDADD) $(LIBH5TEST) + # CHECK_CLEANFILES+=*.h5 DISTCLEANFILES=testh5watch.sh diff --git a/hl/tools/h5watch/extend_dset.c b/hl/tools/h5watch/extend_dset.c index 023e201..4038a4f 100644 --- a/hl/tools/h5watch/extend_dset.c +++ b/hl/tools/h5watch/extend_dset.c @@ -14,6 +14,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "H5HLprivate2.h" +#include "h5test.h" /* * Extending datasets in WATCH.h5 generated by h5watchgentest.c @@ -30,8 +31,6 @@ #define WRITER_MESSAGE "writer_message" /* The message received from the test script to start extending dataset */ #define READER_MESSAGE "reader_message" -/* Message timeout in seconds */ -#define MESSAGE_TIMEOUT 300 /* Size of data buffer */ #define TEST_BUF_SIZE 100 @@ -57,8 +56,6 @@ int two_tests[TWO_NTESTS][2] = { {2, 2}, {2, -11}, {2, 0}, static herr_t extend_dset_two(const char *file, char *dname); static herr_t extend_dset_one(const char *file, char *dname); -void send_message(const char *file); -static herr_t wait_message(const char *file); /* Data structures for datasets with compound data type */ @@ -87,55 +84,6 @@ typedef struct set_t { } set_t; /* - * To send a message by creating the file. - */ -void -send_message(const char *file) -{ - FILE *id; - - id = HDfopen(file, "w+"); - HDfclose(id); -} /* end send_message() */ - -/* - * - * Repeatedly check for the message file. - * It will stop when the file exists or exceeds the timeout limit. - */ -static herr_t -wait_message(const char *file) -{ - FILE *id; /* File pointer */ - time_t t0, t1; /* Time info */ - - /* Start timer */ - HDtime(&t0); - - /* Wait for message from test script to start work */ - while((id = HDfopen(file, "r")) == NULL) { - /* Get current time */ - HDtime(&t1); - /* - * Determine time difference-- - * if waiting too long for the message, then it is - * unlikely the message will get sent, then fail rather - * than loop forever. - */ - if(HDdifftime(t1, t0) > MESSAGE_TIMEOUT) - goto error; - } /* end while */ - - HDfclose(id); - HDunlink(file); - - return SUCCEED; - -error: - return FAIL; -} /* end wait_message() */ - -/* *********************************************************************** * * Extending a two-dimensional dataset: @@ -198,7 +146,7 @@ extend_dset_two(const char *file, char *dname) goto error; /* Send message to the test script to start "h5watch" */ - send_message(WRITER_MESSAGE); + h5_send_message(WRITER_MESSAGE, NULL, NULL); if((sid = H5Dget_space(did)) < 0) goto error; @@ -215,7 +163,7 @@ extend_dset_two(const char *file, char *dname) goto error; /* Wait for message from the test script to start extending dataset */ - if(wait_message(READER_MESSAGE) < 0) + if(h5_wait_message(READER_MESSAGE) < 0) goto error; /* Loop through different variations of extending the dataset */ @@ -355,7 +303,7 @@ extend_dset_one(const char *file, char *dname) goto error; /* Send message to the test script to start "h5watch" */ - send_message(WRITER_MESSAGE); + h5_send_message(WRITER_MESSAGE, NULL, NULL); if((did = H5Dopen2(fid, dname, H5P_DEFAULT)) < 0) goto error; @@ -369,7 +317,7 @@ extend_dset_one(const char *file, char *dname) goto error; /* Wait for message from the test script to start extending dataset */ - if(wait_message(READER_MESSAGE) < 0) + if(h5_wait_message(READER_MESSAGE) < 0) goto error; /* Loop through different variations of extending the dataset */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0197524..35884d0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -319,6 +319,7 @@ set (H5_CHECK_TESTS tcheck_version testmeta links_env + flushrefresh ) foreach (test ${H5_CHECK_TESTS}) diff --git a/test/CMakeTests.cmake b/test/CMakeTests.cmake index 0ccb557..5b3d0bf 100644 --- a/test/CMakeTests.cmake +++ b/test/CMakeTests.cmake @@ -440,6 +440,13 @@ set (test_CLEANFILES tvlstr.h5 tvlstr2.h5 flush.h5 + flush-swmr.h5 + noflush.h5 + noflush-swmr.h5 + flush_extend.h5 + flush_extend-swmr.h5 + noflush_extend.h5 + noflush_extend-swmr.h5 enum1.h5 titerate.h5 ttsafe.h5 @@ -508,6 +515,11 @@ set (test_CLEANFILES vds_src_0.h5 vds_src_1.h5 tbogus.h5.copy + flushrefresh.h5 + flushrefresh_VERIFICATION_START + flushrefresh_VERIFICATION_CHECKPOINT1 + flushrefresh_VERIFICATION_CHECKPOINT2 + flushrefresh_VERIFICATION_DONE accum_swmr_big.h5 ohdr_swmr.h5 cache_logging.h5 @@ -871,6 +883,11 @@ set_tests_properties (H5PLUGIN-plugin PROPERTIES ) ############################################################################## +### S W M R T E S T S +############################################################################## +# testflushrefresh.sh: flushrefresh + +############################################################################## ############################################################################## ### V F D T E S T S ### ############################################################################## diff --git a/test/Makefile.am b/test/Makefile.am index 5a63c89..da89b22 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -28,8 +28,11 @@ AM_CPPFLAGS+=-I$(top_srcdir)/src -I$(top_builddir)/src # testlibinfo.sh: # testcheck_version.sh: tcheck_version # tetlinks_env.sh: links_env -TEST_SCRIPT = testerror.sh testlibinfo.sh testcheck_version.sh testlinks_env.sh -SCRIPT_DEPEND = error_test$(EXEEXT) err_compat$(EXEEXT) links_env$(EXEEXT) +# testflushrefresh.sh: flushrefresh +TEST_SCRIPT = testerror.sh testlibinfo.sh testcheck_version.sh testlinks_env.sh \ + testflushrefresh.sh +SCRIPT_DEPEND = error_test$(EXEEXT) err_compat$(EXEEXT) links_env$(EXEEXT) \ + testflushrefresh.sh if HAVE_SHARED_CONDITIONAL TEST_SCRIPT += test_plugin.sh SCRIPT_DEPEND += plugin$(EXEEXT) @@ -55,12 +58,13 @@ TEST_PROG= testhdf5 cache cache_api cache_tagging lheap ohdr stab gheap \ # tcheck_version is used by testcheck_version.sh. # accum_swmr_reader is used by accum.c. # links_env is used by testlinks_env.sh +# flushrefresh is used by testflushrefresh.sh. # 'make check' doesn't run them directly, so they are not included in TEST_PROG. # Also build testmeta, which is used for timings test. It builds quickly, # and this lets automake keep all its test programs in one place. check_PROGRAMS=$(TEST_PROG) error_test err_compat tcheck_version \ testmeta accum_swmr_reader \ - links_env + links_env flushrefresh if HAVE_SHARED_CONDITIONAL check_PROGRAMS+= plugin endif @@ -148,7 +152,9 @@ CHECK_CLEANFILES+=accum.h5 cmpd_dset.h5 compact_dataset.h5 dataset.h5 dset_offse stdio.h5 sec2.h5 dtypes[0-9].h5 dtypes1[0].h5 dt_arith[1-2].h5 tattr.h5 \ tselect.h5 mtime.h5 unlink.h5 unicode.h5 coord.h5 \ fillval_[0-9].h5 fillval.raw mount_[0-9].h5 testmeta.h5 ttime.h5 \ - trefer[1-3].h5 tvltypes.h5 tvlstr.h5 tvlstr2.h5 flush.h5 \ + trefer[1-3].h5 tvltypes.h5 tvlstr.h5 tvlstr2.h5 \ + flush.h5 flush-swmr.h5 noflush.h5 noflush-swmr.h5 flush_extend.h5 \ + flush_extend-swmr.h5 noflush_extend.h5 noflush_extend-swmr.h5 \ enum1.h5 titerate.h5 ttsafe.h5 tarray1.h5 tgenprop.h5 \ tmisc[0-9]*.h5 set_extent[1-5].h5 ext[12].bin \ getname.h5 getname[1-3].h5 sec2_file.h5 direct_file.h5 \ @@ -163,7 +169,9 @@ CHECK_CLEANFILES+=accum.h5 cmpd_dset.h5 compact_dataset.h5 dataset.h5 dset_offse split_get_file_image_test-m.h5 split_get_file_image_test-r.h5 \ file_image_core_test.h5.copy unregister_filter_1.h5 unregister_filter_2.h5 \ vds_virt.h5 vds_dapl.h5 vds_src_[0-1].h5 \ - accum_swmr_big.h5 ohdr_swmr.h5 \ + flushrefresh.h5 flushrefresh_VERIFICATION_START \ + flushrefresh_VERIFICATION_CHECKPOINT1 flushrefresh_VERIFICATION_CHECKPOINT2 \ + flushrefresh_VERIFICATION_DONE accum_swmr_big.h5 ohdr_swmr.h5 \ cache_logging.h5 cache_logging.out \ swmr[0-2].h5 # Sources for testhdf5 executable @@ -173,5 +181,7 @@ testhdf5_SOURCES=testhdf5.c tarray.c tattr.c tchecksum.c tconfig.c tfile.c \ tvlstr.c tvltypes.c # Temporary files. -DISTCLEANFILES=testerror.sh testlibinfo.sh testcheck_version.sh testlinks_env.sh test_plugin.sh +DISTCLEANFILES=testerror.sh testlibinfo.sh testcheck_version.sh testlinks_env.sh test_plugin.sh \ + testflushrefresh.sh + include $(top_srcdir)/config/conclude.am diff --git a/test/flush1.c b/test/flush1.c index 64d072a..0e53f2b 100644 --- a/test/flush1.c +++ b/test/flush1.c @@ -27,8 +27,13 @@ const char *FILENAME[] = { "flush", + "flush-swmr", "noflush", + "noflush-swmr", + "flush_extend", + "flush_extend-swmr", "noflush_extend", + "noflush_extend-swmr", NULL }; @@ -51,14 +56,17 @@ static double the_data[100][100]; *------------------------------------------------------------------------- */ static hid_t -create_file(char* name, hid_t fapl) +create_file(char* name, hid_t fapl, hbool_t swmr) { hid_t file, dcpl, space, dset, groups, grp; hsize_t ds_size[2] = {100, 100}; hsize_t ch_size[2] = {5, 5}; + unsigned flags; size_t i, j; - if((file = H5Fcreate(name, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0) FAIL_STACK_ERROR + flags = H5F_ACC_TRUNC | (swmr ? H5F_ACC_SWMR_WRITE : 0); + + if((file = H5Fcreate(name, flags, H5P_DEFAULT, fapl)) < 0) FAIL_STACK_ERROR /* Create a chunked dataset */ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) FAIL_STACK_ERROR @@ -156,30 +164,43 @@ main(void) { hid_t file, fapl; char name[1024]; + unsigned swmr; h5_reset(); fapl = h5_fileaccess(); TESTING("H5Fflush (part1)"); - /* Create the file */ - h5_fixname(FILENAME[0], fapl, name, sizeof name); - file = create_file(name, fapl); - /* Flush and exit without closing the library */ - if (H5Fflush(file, H5F_SCOPE_GLOBAL) < 0) goto error; - - /* Create the file */ - h5_fixname(FILENAME[2], fapl, name, sizeof name); - file = create_file(name, fapl); - /* Flush and exit without closing the library */ - if(H5Fflush(file, H5F_SCOPE_GLOBAL) < 0) goto error; - /* Add a bit to the file and don't flush the new part */ - extend_file(file); - - /* Create the other file which will not be flushed */ - h5_fixname(FILENAME[1], fapl, name, sizeof name); - file = create_file(name, fapl); - + /* Loop over SWMR & non-SWMR opens for the file */ + for(swmr = 0; swmr <= 1; swmr++) { + /* Create the file */ + h5_fixname(FILENAME[0 + swmr], fapl, name, sizeof name); + file = create_file(name, fapl, swmr); + /* Flush and exit without closing the library */ + if (H5Fflush(file, H5F_SCOPE_GLOBAL) < 0) goto error; + + /* Create the other file which will not be flushed */ + h5_fixname(FILENAME[2 + swmr], fapl, name, sizeof name); + file = create_file(name, fapl, swmr); + + /* Create the file */ + h5_fixname(FILENAME[4 + swmr], fapl, name, sizeof name); + file = create_file(name, fapl, swmr); + /* Flush and exit without closing the library */ + if(H5Fflush(file, H5F_SCOPE_GLOBAL) < 0) goto error; + /* Add a bit to the file and don't flush the new part */ + extend_file(file); + /* Flush and exit without closing the library */ + if (H5Fflush(file, H5F_SCOPE_GLOBAL) < 0) goto error; + + /* Create the file */ + h5_fixname(FILENAME[6 + swmr], fapl, name, sizeof name); + file = create_file(name, fapl, swmr); + /* Flush and exit without closing the library */ + if(H5Fflush(file, H5F_SCOPE_GLOBAL) < 0) goto error; + /* Add a bit to the file and don't flush the new part */ + extend_file(file); + } /* end for */ PASSED(); fflush(stdout); diff --git a/test/flush2.c b/test/flush2.c index 7dc45be..3f4328a 100644 --- a/test/flush2.c +++ b/test/flush2.c @@ -24,10 +24,20 @@ */ #include "h5test.h" +/* Make this private property (defined in H5Fprivate.h) available */ +/* This is used in the helper routine clear_status_flags() */ +#define H5F_ACS_CLEAR_STATUS_FLAGS_NAME "clear_status_flags" + + const char *FILENAME[] = { "flush", + "flush-swmr", "noflush", + "noflush-swmr", + "flush_extend", + "flush_extend-swmr", "noflush_extend", + "noflush_extend-swmr", NULL }; @@ -134,6 +144,53 @@ error: return 1; } /* end check_file() */ +/*------------------------------------------------------------------------- + * Function: clear_status_flags + * + * Purpose: To clear the status_flags in the superblock of the file. + * It is smilar to the tool "h5clear". + * + * Return: Success: 0 + * Failure: 1 + * + * Programmer: Vailin Choi + * July 2013 + * + *------------------------------------------------------------------------- + */ +static int +clear_status_flags(char *name, hid_t fapl) +{ + hid_t new_fapl = -1; + hid_t fid = -1; + hbool_t clear = TRUE; + + /* Get a copy of fapl */ + if((new_fapl = H5Pcopy(fapl)) < 0) + FAIL_STACK_ERROR; + + /* Set this private property */ + if(H5Pset(new_fapl, H5F_ACS_CLEAR_STATUS_FLAGS_NAME, &clear) < 0) + FAIL_STACK_ERROR; + + /* Has to open rw */ + if((fid = H5Fopen(name, H5F_ACC_RDWR, new_fapl)) < 0) + FAIL_STACK_ERROR; + + /* Close the file */ + if(H5Fclose(fid) < 0) + FAIL_STACK_ERROR; + + /* CLose the property list */ + if(H5Pclose(new_fapl) < 0) + FAIL_STACK_ERROR; + + return 0; + +error: + return 1; +} /* clear_status_flags() */ + /*------------------------------------------------------------------------- * Function: main @@ -166,6 +223,26 @@ main(void) /* Check the case where the file was flushed */ h5_fixname(FILENAME[0], fapl, name, sizeof name); + + if(check_file(name, fapl, FALSE)) { + H5_FAILED() + goto error; + } + else + PASSED(); + + + TESTING("H5Fflush (part2 with flush + SWMR)"); + + /* Check the case where the file was flushed w/SWMR */ + h5_fixname(FILENAME[1], fapl, name, sizeof name); + + /* Clear the status_flags of the file which is flushed and exited in flush1.c */ + if(clear_status_flags(name, fapl) < 0) { + H5_FAILED() + goto error; + } + if(check_file(name, fapl, FALSE)) { H5_FAILED() goto error; @@ -180,7 +257,31 @@ main(void) H5Eget_auto2(H5E_DEFAULT,&func,NULL); H5Eset_auto2(H5E_DEFAULT, NULL, NULL); - h5_fixname(FILENAME[1], fapl, name, sizeof name); + h5_fixname(FILENAME[2], fapl, name, sizeof name); + /* No need to clear the status_flags because this file is not flushed in flush1.c */ + /* H5Fopen() in check_file() will just return error */ + if(check_file(name, fapl, FALSE)) + PASSED() + else { +#if defined H5_HAVE_WIN32_API && !defined (hdf5_EXPORTS) + SKIPPED(); + puts(" DLL will flush the file even when calling _exit, skip this test temporarily"); +#else + H5_FAILED() + goto error; +#endif + } + H5Eset_auto2(H5E_DEFAULT, func, NULL); + + /* Check the case where the file was not flushed. This should give an error + * so we turn off the error stack temporarily */ + TESTING("H5Fflush (part2 without flush + SWMR)"); + H5Eget_auto2(H5E_DEFAULT,&func,NULL); + H5Eset_auto2(H5E_DEFAULT, NULL, NULL); + + h5_fixname(FILENAME[3], fapl, name, sizeof name); + /* No need to clear the status_flags because this file is not flushed in flush1.c */ + /* H5Fopen() in check_file() will just return error */ if(check_file(name, fapl, FALSE)) PASSED() else { @@ -196,11 +297,79 @@ main(void) /* Check the case where the file was flushed, but more data was added afterward. This should give an error * so we turn off the error stack temporarily */ + TESTING("H5Fflush (part2 with flush and later addition and another flush)"); + H5Eget_auto2(H5E_DEFAULT,&func,NULL); + H5Eset_auto2(H5E_DEFAULT, NULL, NULL); + + h5_fixname(FILENAME[4], fapl, name, sizeof name); + + /* Clear the status_flags of the file which is flushed and exited in flush1.c */ + if(clear_status_flags(name, fapl) < 0) { + H5_FAILED() + goto error; + } + + if(check_file(name, fapl, TRUE)) { + H5_FAILED() + goto error; + } + else + PASSED(); + + H5Eset_auto2(H5E_DEFAULT, func, NULL); + + /* Check the case where the file was flushed, but more data was added afterward. This should give an error + * so we turn off the error stack temporarily */ + TESTING("H5Fflush (part2 with flush and later addition and another flush + SWMR)"); + H5Eget_auto2(H5E_DEFAULT,&func,NULL); + H5Eset_auto2(H5E_DEFAULT, NULL, NULL); + + h5_fixname(FILENAME[5], fapl, name, sizeof name); + + if(check_file(name, fapl, TRUE)) + PASSED() + else { +#if defined H5_HAVE_WIN32_API && !defined (hdf5_EXPORTS) + SKIPPED(); + puts(" DLL will flush the file even when calling _exit, skip this test temporarily"); +#else + H5_FAILED() + goto error; +#endif + } + + H5Eset_auto2(H5E_DEFAULT, func, NULL); + + /* Check the case where the file was flushed, but more data was added afterward. This should give an error + * so we turn off the error stack temporarily */ TESTING("H5Fflush (part2 with flush and later addition)"); H5Eget_auto2(H5E_DEFAULT,&func,NULL); H5Eset_auto2(H5E_DEFAULT, NULL, NULL); - h5_fixname(FILENAME[2], fapl, name, sizeof name); + h5_fixname(FILENAME[6], fapl, name, sizeof name); + + if(check_file(name, fapl, TRUE)) + PASSED() + else { +#if defined H5_HAVE_WIN32_API && !defined (hdf5_EXPORTS) + SKIPPED(); + puts(" DLL will flush the file even when calling _exit, skip this test temporarily"); +#else + H5_FAILED() + goto error; +#endif + } + + H5Eset_auto2(H5E_DEFAULT, func, NULL); + + /* Check the case where the file was flushed, but more data was added afterward. This should give an error + * so we turn off the error stack temporarily */ + TESTING("H5Fflush (part2 with flush and later addition + SWMR)"); + H5Eget_auto2(H5E_DEFAULT,&func,NULL); + H5Eset_auto2(H5E_DEFAULT, NULL, NULL); + + h5_fixname(FILENAME[7], fapl, name, sizeof name); + if(check_file(name, fapl, TRUE)) PASSED() else { diff --git a/test/flushrefresh.c b/test/flushrefresh.c new file mode 100644 index 0000000..e0a7484 --- /dev/null +++ b/test/flushrefresh.c @@ -0,0 +1,1151 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 files COPYING and Copyright.html. COPYING can be found at the root * + * of the source code distribution tree; Copyright.html can be found at the * + * root level of an installed copy of the electronic HDF5 document set and * + * is linked from the top-level documents page. It can also be found at * + * access to either file, you may request a copy from help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Programmer: Mike McGreevy + * June 30, 2010 + * + * Purpose: This test file contains routines used to test flushing and + * refreshing individual objects' metadata from the cache. + * + * Note: This file should NOT be run manually. Instead, invoke it + * via its associated test script, testflushrefresh.sh + * + */ + +#define H5FD_FRIEND /*suppress error about including H5FDpkg */ +#define H5FD_TESTING + +/* ======== */ +/* Includes */ +/* ======== */ + +#include "testhdf5.h" +#include "H5FDpkg.h" /* File Drivers */ + +/* ======= */ +/* Defines */ +/* ======= */ + +/* Name of Test File */ +#define FILENAME "flushrefresh.h5" + +/* Names of Signal Files */ +#define SIGNAL_TO_SCRIPT "flushrefresh_VERIFICATION_START" +#define SIGNAL_BETWEEN_PROCESSES_1 "flushrefresh_VERIFICATION_CHECKPOINT1" +#define SIGNAL_BETWEEN_PROCESSES_2 "flushrefresh_VERIFICATION_CHECKPOINT2" +#define SIGNAL_FROM_SCRIPT "flushrefresh_VERIFICATION_DONE" + +/* Paths to Various Objects in the Testfile */ +#define RG "/" +#define D1 "/Dataset1" +#define D2 "/Group1/Dataset2" +#define D3 "/Group3/Dataset3" +#define G1 "/Group1" +#define G2 "/Group1/Group2" +#define G3 "/Group3" +#define T1 "/CommittedDatatype1" +#define T2 "/Group1/Group2/CommittedDatatype2" +#define T3 "/Group3/CommittedDatatype3" + +/* Flushed States */ +#define FLUSHED "FLUSHED" +#define NOT_FLUSHED "NOT_FLUSHED" + +/* Error Handling */ +/* For errors occuring in the main process, use the standard TEST_ERROR macro. + For errors occurring in the spawned process (from the test script), use + the PROCESS_ERROR macro, which will send a signal to the main process so the + main process can propogate errors correctly. */ +FILE * errorfile; +#define ERRFILE "flushrefresh_ERROR" +#define PROCESS_ERROR \ + { \ + errorfile = HDfopen(ERRFILE, "w+"); \ + HDfprintf(errorfile, "Error occurred in flushrefresh.\n"); \ + HDfflush(errorfile); \ + HDfclose(errorfile); \ + TEST_ERROR; \ + } + +#define CLEANUP_FILES \ + { \ + HDremove(ERRFILE); \ + HDremove(FILENAME); \ + HDremove(SIGNAL_TO_SCRIPT); \ + HDremove(SIGNAL_BETWEEN_PROCESSES_1); \ + HDremove(SIGNAL_BETWEEN_PROCESSES_2); \ + HDremove(SIGNAL_FROM_SCRIPT); \ + } \ + +/* ===================== */ +/* Function Declarations */ +/* ===================== */ + +/* Main */ +int main(int argc, const char *argv[]); + +/* Flush Test Framework */ +herr_t test_flush(void); +herr_t flush_verification(const char * obj_pathname, const char * expected); +herr_t run_flush_verification_process(const char * obj_pathname, const char * expected); + +/* Refresh Test Framework */ +herr_t test_refresh(void); +herr_t refresh_verification(const char * obj_pathname); +herr_t start_refresh_verification_process(const char * obj_pathname); +herr_t end_refresh_verification_process(void); + +/* Other Helper Functions */ +herr_t check_for_errors(void); +herr_t end_verification(void); + +/* ========= */ +/* Functions */ +/* ========= */ + + +/*------------------------------------------------------------------------- + * Function: main + * + * Purpose: This function coordinates the test of flush/refresh + * functionality verification. It accepts either one, two or + * no command line parameters. The main test routine runs + * with no command line parameters specified, while verification + * routines run with one or two command line parameters. + * + * Note: This program should not be run manually, as the + * test is controlled by the testflushrefresh.sh script. Running + * the flushrefresh program manually will result in failure, as + * it will time out waiting for a signal from the test script + * which will never come. + * + * Return: 0 on Success, 1 on Failure + * + * Programmer: Mike McGreevy + * July 1, 2010 + * + *------------------------------------------------------------------------- + */ +int main(int argc, const char *argv[]) +{ + /* Variables */ + const char *envval = NULL; + + /* Initialize library */ + if(H5open() < 0) + TEST_ERROR; + + /* Parse command line options */ + if(argc == 1) { + /* No arguments supplied. Run main test routines if + * using sec2 or stdio driver, otherwise don't run + * anything. */ + + /* Determine driver being used */ + envval = HDgetenv("HDF5_DRIVER"); + + if(envval == NULL || H5FD_supports_swmr_test(envval)) { + if(test_flush() != SUCCEED) TEST_ERROR; + if(test_refresh() != SUCCEED) TEST_ERROR; + } /* end if */ + else { + HDfprintf(stdout, "Skipping all flush/refresh tests (only run with SWMR-enabled file drivers).\n"); + + /* Test script is expecting some signals, so send them out to end it. */ + if(end_verification() < 0) TEST_ERROR; + if(end_verification() < 0) TEST_ERROR; + } /* end else */ + } else if(argc == 3) { + /* Two arguments supplied. Pass them to flush verification routine. */ + if(flush_verification(argv[1], argv[2]) != 0) PROCESS_ERROR; + } else if(argc == 2) { + /* One argument supplied. Pass it to refresh verification routine. */ + if(refresh_verification(argv[1]) != 0) PROCESS_ERROR; + } else { + /* Illegal number of arguments supplied. Error. */ + HDfprintf(stderr, "Error. %d is an Invalid number of arguments to main().\n", argc); + PROCESS_ERROR + } /* end if */ + + return SUCCEED; + +error: + /* Return */ + return FAIL; +} /* main */ + + +/*------------------------------------------------------------------------- + * Function: test_flush + * + * Purpose: This function tests flushing individual objects' metadata + * from the metadata cache. + * + * Return: 0 on Success, 1 on Failure + * + * Programmer: Mike McGreevy + * July 1, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t test_flush(void) +{ + /************************************************************************** + * + * Test Description: + * + * This test will build an HDF5 file with several objects in a varying + * hierarchical layout. It will then attempt to flush the objects + * in the file one by one, individually, using the four H5*flush + * routines (D,G,T, and O). After each call to either create or flush an + * object, a series of verifications will occur on each object in the file. + * + * Each verification consists of spawning off a new process and determining + * if the object can be opened and its information retreived in said + * alternate process. It reports the results, which are compared to an + * expected value (either that the object can be found on disk, or that it + * cannot). + * + * Note that to spawn a verification, this program sends a signal (by creating + * a file on disk) to the test script controlling it, indicating how to + * run the verification. + * + * Implementation is funky, but basically, an example: + * + * Step 1. Dataset is created. + * Step 2. Verify that dataset can't be opened by separate process, as + * it should not have been flushed to disk yet. + * Step 3. Group is created. + * Step 4. Verify that group can't be opened by separate process. + * Step 5. H5Gflush is called on the group. + * Step 6. Verify that group CAN be opened, but dataset still has + * yet to hit disk, and CANNOT be opened. Success! Only the group + * was flushed. + * + **************************************************************************/ + + /************************************************************************** + * Generated Test File will look like this: + * + * GROUP "/" + * DATASET "Dataset1" + * GROUP "Group1" { + * DATASET "Dataset2" + * GROUP "Group2" { + * DATATYPE "CommittedDatatype3" + * } + * } + * GROUP "Group3" { + * DATASET "Dataset3" + * DATATYPE "CommittedDatatype2" + * } + * DATATYPE "CommittedDatatype1" + **************************************************************************/ + + /* Variables */ + hid_t fid,gid,gid2,gid3,sid,tid1,tid2,tid3,did,did2,did3,rid,fapl,status = 0; + hsize_t dims[2] = {3,5}; + + /* Testing Message */ + HDfprintf(stdout, "Testing individual object flush behavior:\n"); + + /* Cleanup any old error or signal files */ + CLEANUP_FILES; + + /* ================ */ + /* CREATE TEST FILE */ + /* ================ */ + + /* Create file, open root group - have to use latest file format for SWMR */ + if((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0) TEST_ERROR; + if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) TEST_ERROR; + if((fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC|H5F_ACC_SWMR_WRITE, H5P_DEFAULT, fapl)) < 0) TEST_ERROR; + if((rid = H5Gopen2(fid, "/", H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create data space and types */ + if((sid = H5Screate_simple(2, dims, dims)) < 0) TEST_ERROR; + if((tid1 = H5Tcopy(H5T_NATIVE_INT)) < 0) TEST_ERROR; + if((tid2 = H5Tcopy(H5T_NATIVE_CHAR)) < 0) TEST_ERROR; + if((tid3 = H5Tcopy(H5T_NATIVE_LONG)) < 0) TEST_ERROR; + + /* Create Group1 */ + if((gid = H5Gcreate2(fid, "Group1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create Group2 */ + if((gid2 = H5Gcreate2(gid, "Group2", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create Group3 */ + if((gid3 = H5Gcreate2(fid, "Group3", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create Dataset1 */ + if((did = H5Dcreate2(fid, "Dataset1", tid1, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create Dataset2 */ + if((did2 = H5Dcreate2(gid, "Dataset2", tid3, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create Dataset3 */ + if((did3 = H5Dcreate2(gid3, "Dataset3", tid2, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create CommittedDatatype1 */ + if((status = H5Tcommit2(fid, "CommittedDatatype1", tid1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create CommittedDatatype2 */ + if((status = H5Tcommit2(gid2, "CommittedDatatype2", tid2, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create CommittedDatatype3 */ + if((status = H5Tcommit2(gid3, "CommittedDatatype3", tid3, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* ============ */ + /* FLUSH GROUPS */ + /* ============ */ + + /* Test */ + TESTING("to ensure H5Gflush correctly flushes single groups"); + + /* First, let's verify that nothing is currently flushed. */ + if(run_flush_verification_process(RG, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T3, NOT_FLUSHED) != 0) TEST_ERROR; + + /* Then, flush the root group and verify it's the only thing on disk */ + if((status = H5Gflush(rid)) < 0) TEST_ERROR; + if(run_flush_verification_process(RG, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T3, NOT_FLUSHED) != 0) TEST_ERROR; + + /* Flush Group1 and Verify it is recently flushed, and nothing + * else has changed. */ + if((status = H5Gflush(gid)) < 0) TEST_ERROR; + if(run_flush_verification_process(RG, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T3, NOT_FLUSHED) != 0) TEST_ERROR; + + /* Flush Group2 and Verify it is recently flushed, and nothing + * else has changed. */ + if((status = H5Gflush(gid2)) < 0) TEST_ERROR; + if(run_flush_verification_process(RG, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T3, NOT_FLUSHED) != 0) TEST_ERROR; + + PASSED(); + + /* ============== */ + /* FLUSH DATASETS */ + /* ============== */ + + /* Test */ + TESTING("to ensure H5Dflush correctly flushes single datasets"); + + /* Flush Dataset1 and verify it's the only thing that hits disk. */ + if((status = H5Dflush(did)) < 0) TEST_ERROR; + if(run_flush_verification_process(RG, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T3, NOT_FLUSHED) != 0) TEST_ERROR; + + /* Flush Dataset2 and verify it's the only thing that hits disk. */ + if((status = H5Dflush(did2)) < 0) TEST_ERROR; + if(run_flush_verification_process(RG, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T1, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T3, NOT_FLUSHED) != 0) TEST_ERROR; + + PASSED(); + + /* =============== */ + /* FLUSH DATATYPES */ + /* =============== */ + + /* Test */ + TESTING("to ensure H5Tflush correctly flushes single datatypes"); + + /* Flush Datatype 1 and verify it's the only thing that hits disk. */ + if((status = H5Tflush(tid1)) < 0) TEST_ERROR; + if(run_flush_verification_process(RG, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T2, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T3, NOT_FLUSHED) != 0) TEST_ERROR; + + /* Flush Datatype 2 and verify it's the only thing that hits disk. */ + if((status = H5Tflush(tid2)) < 0) TEST_ERROR; + if(run_flush_verification_process(RG, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T3, NOT_FLUSHED) != 0) TEST_ERROR; + + PASSED(); + + /* ============= */ + /* FLUSH OBJECTS */ + /* ============= */ + + /* Test */ + TESTING("to ensure H5Oflush correctly flushes single objects"); + + /* Flush Group3 and verify it's the only thing that hits disk. */ + if((status = H5Oflush(gid3)) < 0) TEST_ERROR; + if(run_flush_verification_process(RG, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G3, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D3, NOT_FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T3, NOT_FLUSHED) != 0) TEST_ERROR; + + /* Flush Dataset3 and verify it's the only thing that hits disk. */ + if((status = H5Oflush(did3)) < 0) TEST_ERROR; + if(run_flush_verification_process(RG, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G3, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D3, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T3, NOT_FLUSHED) != 0) TEST_ERROR; + + /* Flush CommittedDatatype3 and verify it's the only thing that hits disk. */ + if((status = H5Oflush(tid3)) < 0) TEST_ERROR; + if(run_flush_verification_process(RG, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(G3, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(D3, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T1, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T2, FLUSHED) != 0) TEST_ERROR; + if(run_flush_verification_process(T3, FLUSHED) != 0) TEST_ERROR; + + PASSED(); + + /* ================== */ + /* Cleanup and Return */ + /* ================== */ + if(H5Pclose(fapl) < 0) TEST_ERROR; + if(H5Gclose(gid) < 0) TEST_ERROR; + if(H5Gclose(gid2) < 0) TEST_ERROR; + if(H5Dclose(did) < 0) TEST_ERROR; + if(H5Dclose(did2) < 0) TEST_ERROR; + if(H5Gclose(rid) < 0) TEST_ERROR; + if(H5Fclose(fid) < 0) TEST_ERROR; + + /* Delete test file */ + HDremove(FILENAME); + + if(end_verification() < 0) TEST_ERROR; + + return SUCCEED; + +error: + return FAIL; +} /* end test_flush */ + + +/*------------------------------------------------------------------------- + * Function: test_refresh + * + * Purpose: This function tests refresh (evict/reload) of individual + * objects' metadata from the metadata cache. + * + * Return: 0 on Success, 1 on Failure + * + * Programmer: Mike McGreevy + * August 17, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t test_refresh(void) +{ + /************************************************************************** + * + * Test Description: + * + * This test will build an HDF5 file with several objects in a varying + * hierarchical layout. It will then flush the entire file to disk. Then, + * an attribute will be added to each object in the file. + * + * One by one, this process will flush each object to disk, individually. + * It will also be coordinating with another process, which will open + * the object before it is flushed by this process, and then refresh the + * object after it's been flushed, comparing the before and after object + * information to ensure that they are as expected. (i.e., most notably, + * that an attribute has been added, and is only visible after a + * successful call to a H5*refresh function). + * + * As with the flush case, the implemention is a bit tricky as it's + * dealing with signals going back and forth between the two processes + * to ensure the timing is correct, but basically, an example: + * + * Step 1. Dataset is created. + * Step 2. Dataset is flushed. + * Step 3. Attribute on Dataset is created. + * Step 4. Another process opens the dataset and verifies that it does + * not see an attribute (as the attribute hasn't been flushed yet). + * Step 5. This process flushes the dataset again (with Attribute attached). + * Step 6. The other process calls H5Drefresh, which should evict/reload + * the object's metadata, and thus pick up the attribute that's + * attached to it. Most other before/after object information is + * compared for sanity as well. + * Step 7. Rinse and Repeat for each object in the file. + * + **************************************************************************/ + + /************************************************************************** + * Generated Test File will look like this: + * + * GROUP "/" + * DATASET "Dataset1" + * GROUP "Group1" { + * DATASET "Dataset2" + * GROUP "Group2" { + * DATATYPE "CommittedDatatype3" + * } + * } + * GROUP "Group3" { + * DATASET "Dataset3" + * DATATYPE "CommittedDatatype2" + * } + * DATATYPE "CommittedDatatype1" + **************************************************************************/ + + /* Variables */ + hid_t aid,fid,sid,tid1,did,dcpl,fapl = 0; + hid_t gid,gid2,gid3,tid2,tid3,did2,did3; + herr_t status = 0; + hsize_t dims[2] = {50,50}; + hsize_t cdims[2] = {1,1}; + int fillval = 2; + + /* Testing Message */ + HDfprintf(stdout, "Testing individual object refresh behavior:\n"); + + /* Cleanup any old error or signal files */ + CLEANUP_FILES; + + /* ================ */ + /* CREATE TEST FILE */ + /* ================ */ + + /* Create File */ + if((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0) TEST_ERROR; + if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) TEST_ERROR; + if((fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC|H5F_ACC_SWMR_WRITE, H5P_DEFAULT, fapl)) < 0) TEST_ERROR; + + /* Create data space and types */ + if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) TEST_ERROR; + if(H5Pset_chunk(dcpl, 2, cdims) < 0) TEST_ERROR; + if(H5Pset_fill_value(dcpl, H5T_NATIVE_INT, &fillval) < 0) TEST_ERROR; + if((sid = H5Screate_simple(2, dims, dims)) < 0) TEST_ERROR; + if((tid1 = H5Tcopy(H5T_NATIVE_INT)) < 0) TEST_ERROR; + if((tid2 = H5Tcopy(H5T_NATIVE_CHAR)) < 0) TEST_ERROR; + if((tid3 = H5Tcopy(H5T_NATIVE_LONG)) < 0) TEST_ERROR; + + /* Create Group1 */ + if((gid = H5Gcreate2(fid, "Group1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create Group2 */ + if((gid2 = H5Gcreate2(gid, "Group2", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create Group3 */ + if((gid3 = H5Gcreate2(fid, "Group3", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create Dataset1 */ + if((did = H5Dcreate2(fid, "Dataset1", tid1, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create Dataset2 */ + if((did2 = H5Dcreate2(gid, "Dataset2", tid3, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create Dataset3 */ + if((did3 = H5Dcreate2(gid3, "Dataset3", tid2, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create CommittedDatatype1 */ + if((status = H5Tcommit2(fid, "CommittedDatatype1", tid1, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create CommittedDatatype2 */ + if((status = H5Tcommit2(gid2, "CommittedDatatype2", tid2, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Create CommittedDatatype3 */ + if((status = H5Tcommit2(gid3, "CommittedDatatype3", tid3, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + + /* Flush File to Disk */ + if(H5Fflush(fid, H5F_SCOPE_GLOBAL) < 0) TEST_ERROR; + + /* ================ */ + /* Refresh Datasets */ + /* ================ */ + + TESTING("to ensure that H5Drefresh correctly refreshes single datasets"); + + /* Create an attribute on each object before flush. */ + + /* Verify First Dataset can be refreshed with H5Drefresh */ + if(start_refresh_verification_process(D1) != 0) TEST_ERROR; + + if((aid = H5Acreate2(did, "Attribute", tid1, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + if(H5Aclose(aid) < 0) TEST_ERROR; + if(H5Oflush(did) < 0) TEST_ERROR; + + if(end_refresh_verification_process() != 0) TEST_ERROR; + + /* Verify Second Dataset can be refreshed with H5Drefresh */ + if(start_refresh_verification_process(D2) != 0) TEST_ERROR; + + if((aid = H5Acreate2(did2, "Attribute", tid1, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + if(H5Aclose(aid) < 0) TEST_ERROR; + if(H5Oflush(did2) < 0) TEST_ERROR; + + if(end_refresh_verification_process() != 0) TEST_ERROR; + + PASSED(); + + /* ============== */ + /* Refresh Groups */ + /* ============== */ + + TESTING("to ensure that H5Grefresh correctly refreshes single groups"); + + /* Verify First Group can be refreshed with H5Grefresh */ + if(start_refresh_verification_process(G1) != 0) TEST_ERROR; + + if((aid = H5Acreate2(gid, "Attribute", tid1, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + if(H5Aclose(aid) < 0) TEST_ERROR; + if(H5Oflush(gid) < 0) TEST_ERROR; + + if(end_refresh_verification_process() != 0) TEST_ERROR; + + /* Verify Second Group can be refreshed with H5Grefresh */ + if(start_refresh_verification_process(G2) != 0) TEST_ERROR; + + if((aid = H5Acreate2(gid2, "Attribute", tid1, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + if(H5Aclose(aid) < 0) TEST_ERROR; + if(H5Oflush(gid2) < 0) TEST_ERROR; + + if(end_refresh_verification_process() != 0) TEST_ERROR; + + PASSED(); + + /* ================= */ + /* Refresh Datatypes */ + /* ================= */ + + TESTING("to ensure that H5Trefresh correctly refreshes single datatypes"); + + /* Verify First Committed Datatype can be refreshed with H5Trefresh */ + if(start_refresh_verification_process(T1) != 0) TEST_ERROR; + + if((aid = H5Acreate2(tid1, "Attribute", tid1, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + if(H5Aclose(aid) < 0) TEST_ERROR; + if(H5Oflush(tid1) < 0) TEST_ERROR; + + if(end_refresh_verification_process() != 0) TEST_ERROR; + + /* Verify Second Committed Datatype can be refreshed with H5Trefresh */ + if(start_refresh_verification_process(T2) != 0) TEST_ERROR; + + if((aid = H5Acreate2(tid2, "Attribute", tid1, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + if(H5Aclose(aid) < 0) TEST_ERROR; + if(H5Oflush(tid2) < 0) TEST_ERROR; + + if(end_refresh_verification_process() != 0) TEST_ERROR; + + PASSED(); + + /* =============== */ + /* Refresh Objects */ + /* =============== */ + + TESTING("to ensure that H5Orefresh correctly refreshes single objects"); + + /* Verify Third Dataset can be refreshed with H5Orefresh */ + if(start_refresh_verification_process(D3) != 0) TEST_ERROR; + + if((aid = H5Acreate2(did3, "Attribute", tid1, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + if(H5Aclose(aid) < 0) TEST_ERROR; + if(H5Oflush(did3) < 0) TEST_ERROR; + + if(end_refresh_verification_process() != 0) TEST_ERROR; + + /* Verify Third Group can be refreshed with H5Orefresh */ + if(start_refresh_verification_process(G3) != 0) TEST_ERROR; + + if((aid = H5Acreate2(gid3, "Attribute", tid1, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + if(H5Aclose(aid) < 0) TEST_ERROR; + if(H5Oflush(gid3) < 0) TEST_ERROR; + + if(end_refresh_verification_process() != 0) TEST_ERROR; + + /* Verify Third Committed Datatype can be refreshed with H5Orefresh */ + if(start_refresh_verification_process(T3) != 0) TEST_ERROR; + + if((aid = H5Acreate2(tid3, "Attribute", tid1, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0) TEST_ERROR; + if(H5Aclose(aid) < 0) TEST_ERROR; + if(H5Oflush(tid3) < 0) TEST_ERROR; + + if(end_refresh_verification_process() != 0) TEST_ERROR; + + PASSED(); + + /* ================== */ + /* Cleanup and Return */ + /* ================== */ + + /* Close Stuff */ + if(H5Pclose(fapl) < 0) TEST_ERROR; + if(H5Pclose(dcpl) < 0) TEST_ERROR; + if(H5Tclose(tid1) < 0) TEST_ERROR; + if(H5Tclose(tid2) < 0) TEST_ERROR; + if(H5Tclose(tid3) < 0) TEST_ERROR; + if(H5Dclose(did) < 0) TEST_ERROR; + if(H5Dclose(did2) < 0) TEST_ERROR; + if(H5Dclose(did3) < 0) TEST_ERROR; + if(H5Gclose(gid) < 0) TEST_ERROR; + if(H5Gclose(gid2) < 0) TEST_ERROR; + if(H5Gclose(gid3) < 0) TEST_ERROR; + if(H5Sclose(sid) < 0) TEST_ERROR; + if(H5Fclose(fid) < 0) TEST_ERROR; + + /* Delete Test File */ + HDremove(FILENAME); + + if(end_verification() < 0) TEST_ERROR; + + return SUCCEED; + +error: + /* Return */ + return FAIL; +} /* test_refresh() */ + + +/*------------------------------------------------------------------------- + * Function: run_flush_verification_process + * + * Purpose: This function is used to communicate with the test script + * in order to spawn off a process to verify that a flush + * of an individual object was successful. + * + * Return: 0 on Success, 1 on Failure + * + * Programmer: Mike McGreevy + * July 16, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t run_flush_verification_process(const char * obj_pathname, const char * expected) +{ + HDremove(SIGNAL_FROM_SCRIPT); + + /* Send Signal to SCRIPT indicating that it should kick off a verification process. */ + h5_send_message(SIGNAL_TO_SCRIPT, obj_pathname, expected); + + /* Wait for Signal from SCRIPT indicating that verification process has completed. */ + if(h5_wait_message(SIGNAL_FROM_SCRIPT) < 0) TEST_ERROR; + + /* Check to see if any errors occurred */ + if(check_for_errors() < 0) TEST_ERROR; + + /* Return */ + return SUCCEED; + +error: + return FAIL; +} /* run_flush_verification_process */ + + +/*------------------------------------------------------------------------- + * Function: flush_verification + * + * Purpose: This function tries to open target object in the test file. + * It compares the success of the open function to the expected + * value, and succeeds if they are equal and fails if they differ. + * + * Note that full path to the object must be provided as the + * obj_pathname argument. + * + * Return: 0 on Success, 1 on Failure + * + * Programmer: Mike McGreevy + * July 16, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t flush_verification(const char * obj_pathname, const char * expected) +{ + /* Variables */ + hid_t oid = -1, fid = -1; + herr_t status = 0; + H5O_info_t oinfo; + + /* Try to open the testfile and then obj_pathname within the file */ + H5E_BEGIN_TRY { + fid = H5Fopen(FILENAME, H5F_ACC_SWMR_READ, H5P_DEFAULT); + oid = H5Oopen(fid, obj_pathname, H5P_DEFAULT); + status = H5Oget_info(oid, &oinfo); + } H5E_END_TRY; + + /* Compare to expected result */ + if(HDstrcmp(expected, FLUSHED) == 0) { + if((oid < 0) || (status < 0)) { + HDfprintf(stderr, "Error! %s should be on disk, but was NOT!\n", obj_pathname); + PROCESS_ERROR; + } /* end if */ + } else if(HDstrcmp(expected, NOT_FLUSHED) == 0) { + if((oid > 0) || (status > 0)) { + HDfprintf(stderr, "Error! %s not expected to be flushed, but it was found on disk!\n", obj_pathname); + PROCESS_ERROR; + } /* end if */ + } else { + HDfprintf(stderr, "Error! Bad verification parameters. %s is an invalid expected outcome.\n", expected); + PROCESS_ERROR; + } /* end if */ + + /* Cleanup */ + H5E_BEGIN_TRY { + H5Oclose(oid); + H5Fclose(fid); + } H5E_END_TRY; + + return SUCCEED; + +error: + return FAIL; +} /* flush_verification */ + + +/*------------------------------------------------------------------------- + * Function: start_refresh_verification_process + * + * Purpose: This function is used to communicate with the test script + * in order to spawn off a process which will test the + * H5*refresh routine. + * + * Return: 0 on Success, 1 on Failure + * + * Programmer: Mike McGreevy + * July 16, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t start_refresh_verification_process(const char * obj_pathname) +{ + HDremove(SIGNAL_BETWEEN_PROCESSES_1); + + /* Send Signal to SCRIPT indicating that it should kick off a refresh + verification process */ + h5_send_message(SIGNAL_TO_SCRIPT, obj_pathname, NULL); + + /* Wait for Signal from VERIFICATION PROCESS indicating that it's opened the + target object and ready for MAIN PROCESS to modify it */ + if(h5_wait_message(SIGNAL_BETWEEN_PROCESSES_1) < 0) TEST_ERROR; + + /* Check to see if any errors occurred */ + if(check_for_errors() < 0) TEST_ERROR; + + /* Return */ + return SUCCEED; + +error: + return FAIL; +} /* start_refresh_verification_process */ + + +/*------------------------------------------------------------------------- + * Function: end_refresh_verification_process + * + * Purpose: This function is used to communicate with the verification + * process spawned by the start_refresh_verification_process + * function. It gives it the go-ahead to call H5*refresh + * on an object and conlcude the refresh verification. + * + * Return: 0 on Success, 1 on Failure + * + * Programmer: Mike McGreevy + * July 16, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t end_refresh_verification_process(void) +{ + HDremove(SIGNAL_FROM_SCRIPT); + + /* Send Signal to REFRESH VERIFICATION PROCESS indicating that the object + has been modified and it should now attempt to refresh its metadata, + and verify the results. */ + h5_send_message(SIGNAL_BETWEEN_PROCESSES_2, NULL, NULL); + + /* Wait for Signal from SCRIPT indicating that the refresh verification + process has completed. */ + if(h5_wait_message(SIGNAL_FROM_SCRIPT) < 0) TEST_ERROR; + + /* Check to see if any errors occurred */ + if(check_for_errors() < 0) TEST_ERROR; + + /* Return */ + return SUCCEED; + +error: + return FAIL; +} /* end_refresh_verification_process */ + + +/*------------------------------------------------------------------------- + * Function: refresh_verification + * + * Purpose: This function opens the specified object, and checks to see + * that is does not have any attributes on it. It then sends + * a signal to the main process, which will flush the object + * (putting an attribute on the object on disk). This function + * will then refresh the object, and verify that it has picked + * up the new metadata reflective of the added attribute. + * + * Return: 0 on Success, 1 on Failure + * + * Programmer: Mike McGreevy + * July 16, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t refresh_verification(const char * obj_pathname) +{ + /* Variables */ + hid_t oid,fid,status = 0; + H5O_info_t flushed_oinfo; + H5O_info_t refreshed_oinfo; + int tries = 800, sleep_tries = 400; + hbool_t ok = FALSE; + + HDremove(SIGNAL_BETWEEN_PROCESSES_2); + + /* Open Object */ + if((fid = H5Fopen(FILENAME, H5F_ACC_SWMR_READ, H5P_DEFAULT)) < 0) PROCESS_ERROR; + if((oid = H5Oopen(fid, obj_pathname, H5P_DEFAULT)) < 0) PROCESS_ERROR; + + /* Get Object info */ + if((status = H5Oget_info(oid, &flushed_oinfo)) < 0) PROCESS_ERROR; + + /* Make sure there are no attributes on the object. This is just a sanity + check to ensure we didn't erroneously flush the attribute before + starting the verification. */ + if(flushed_oinfo.num_attrs != 0) + PROCESS_ERROR; + + /* Send Signal to MAIN PROCESS indicating that it can go ahead and modify the + object. */ + h5_send_message(SIGNAL_BETWEEN_PROCESSES_1, obj_pathname, NULL); + + /* Wait for Signal from MAIN PROCESS indicating that it's modified the + object and we can run verification now. */ + if(h5_wait_message(SIGNAL_BETWEEN_PROCESSES_2) < 0) PROCESS_ERROR; + + /* Get object info again. This will NOT reflect what's on disk, only what's + in the cache. Thus, all values will be unchanged from above, despite + newer information being on disk. */ + if((status = H5Oget_info(oid, &refreshed_oinfo)) < 0) PROCESS_ERROR; + + /* Verify that before doing a refresh, getting the object info returns stale + information. (i.e., unchanged from above, despite new info on disk). */ + if(flushed_oinfo.addr != refreshed_oinfo.addr) PROCESS_ERROR; + if(flushed_oinfo.type != refreshed_oinfo.type) PROCESS_ERROR; + if(flushed_oinfo.hdr.version != refreshed_oinfo.hdr.version) PROCESS_ERROR; + if(flushed_oinfo.hdr.flags != refreshed_oinfo.hdr.flags) PROCESS_ERROR; + if(flushed_oinfo.num_attrs != refreshed_oinfo.num_attrs) PROCESS_ERROR; + if(flushed_oinfo.hdr.nmesgs != refreshed_oinfo.hdr.nmesgs) PROCESS_ERROR; + if(flushed_oinfo.hdr.nchunks != refreshed_oinfo.hdr.nchunks) PROCESS_ERROR; + if(flushed_oinfo.hdr.space.total != refreshed_oinfo.hdr.space.total) PROCESS_ERROR; + + /* Refresh object */ + /* The H5*refresh function called depends on which object we are trying + * to refresh. (MIKE: add desired refresh call as parameter so adding new + * test cases is easy). */ + do { + + if((HDstrcmp(obj_pathname, D1) == 0) || (HDstrcmp(obj_pathname, D2) == 0)) { + if(H5Drefresh(oid) < 0) PROCESS_ERROR; + } /* end if */ + else if((HDstrcmp(obj_pathname, G1) == 0) || (HDstrcmp(obj_pathname, G2) == 0)) { + if(H5Grefresh(oid) < 0) PROCESS_ERROR; + } /* end if */ + else if((HDstrcmp(obj_pathname, T1) == 0) || (HDstrcmp(obj_pathname, T2) == 0)) { + if(H5Trefresh(oid) < 0) PROCESS_ERROR; + } /* end if */ + else if((HDstrcmp(obj_pathname, D3) == 0) || (HDstrcmp(obj_pathname, G3) == 0) || + (HDstrcmp(obj_pathname, T3) == 0)) { + if(H5Orefresh(oid) < 0) PROCESS_ERROR; + } /* end if */ + else { + HDfprintf(stdout, "Error. %s is an unrecognized object.\n", obj_pathname); + PROCESS_ERROR; + } /* end else */ + + /* Get object info. This should now accurately reflect the refreshed object on disk. */ + if((status = H5Oget_info(oid, &refreshed_oinfo)) < 0) PROCESS_ERROR; + + /* Confirm following (first 4) attributes are the same: */ + /* Confirm following (last 4) attributes are different */ + if( (flushed_oinfo.addr == refreshed_oinfo.addr) && + (flushed_oinfo.type == refreshed_oinfo.type) && + (flushed_oinfo.hdr.version == refreshed_oinfo.hdr.version) && + (flushed_oinfo.hdr.flags == refreshed_oinfo.hdr.flags) && + (flushed_oinfo.num_attrs != refreshed_oinfo.num_attrs) && + (flushed_oinfo.hdr.nmesgs != refreshed_oinfo.hdr.nmesgs) && + (flushed_oinfo.hdr.nchunks != refreshed_oinfo.hdr.nchunks) && + (flushed_oinfo.hdr.space.total != refreshed_oinfo.hdr.space.total) ) { + ok = TRUE; + break; + } + if(tries == sleep_tries) + HDsleep(1); + + } while(--tries); + + if(!ok) { + printf("FLUSHED: num_attrs=%d, nmesgs=%d, nchunks=%d, total=%d\n", + (int)flushed_oinfo.num_attrs, (int)flushed_oinfo.hdr.nmesgs, + (int)flushed_oinfo.hdr.nchunks, (int)flushed_oinfo.hdr.space.total); + printf("REFRESHED: num_attrs=%d, nmesgs=%d, nchunks=%d, total=%d\n", + (int)refreshed_oinfo.num_attrs, (int)refreshed_oinfo.hdr.nmesgs, + (int)refreshed_oinfo.hdr.nchunks, (int)refreshed_oinfo.hdr.space.total); + PROCESS_ERROR; + } + + /* Close objects */ + if(H5Oclose(oid) < 0) PROCESS_ERROR; + if(H5Fclose(fid) < 0) PROCESS_ERROR; + + /* Return */ + return SUCCEED; + +error: + return FAIL; +} /* refresh_verification */ + + +/*------------------------------------------------------------------------- + * Function: check_for_errors() + * + * Purpose: This function checks the status of external verification + * processes to see if they've succeeded. It checks for the + * existance of flushrefresh_ERROR file. If present, that indicates + * an external verification process has failed, and this function + * thus fails as well. If not present, then nothing else has + * failed, and this function succeeds. + * + * Return: 0 on Success, 1 on Failure + * + * Programmer: Mike McGreevy + * July 1, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t check_for_errors(void) +{ + FILE * file; + + if((file = HDfopen(ERRFILE, "r"))) { + HDfclose(file); + HDremove(ERRFILE); + return FAIL; + } /* end if */ + + return SUCCEED; +} /* check_for_errors */ + + +/*------------------------------------------------------------------------- + * Function: end_verification + * + * Purpose: Tells test script that verification routines are completed and + * that the test can wrap up. + * + * Return: void + * + * Programmer: Mike McGreevy + * July 16, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t end_verification(void) +{ + HDremove(SIGNAL_FROM_SCRIPT); + + /* Send Signal to SCRIPT to indicate that we're done with verification. */ + h5_send_message(SIGNAL_TO_SCRIPT, "VERIFICATION_DONE", "VERIFICATION_DONE"); + + /* Wait for Signal from SCRIPT indicating that we can continue. */ + if(h5_wait_message(SIGNAL_FROM_SCRIPT) < 0) TEST_ERROR; + + return SUCCEED; + +error: + return FAIL; +} /* end_verification */ + diff --git a/test/h5test.c b/test/h5test.c index f85e96c..9759fa8 100644 --- a/test/h5test.c +++ b/test/h5test.c @@ -93,6 +93,12 @@ static const char *multi_letters = "msbrglo"; /* Length of multi-file VFD filename buffers */ #define H5TEST_MULTI_FILENAME_LEN 1024 +/* Temporary file for sending signal messages */ +#define TMP_SIGNAL_FILE "tmp_signal_file" + +/* The # of seconds to wait for the message file--used by h5_wait_message() */ +#define MESSAGE_TIMEOUT 300 /* Timeout in seconds */ + /* Previous error reporting function */ static H5E_auto2_t err_func = NULL; @@ -1711,3 +1717,118 @@ error: return -1; } +/*------------------------------------------------------------------------- + * Function: h5_send_message + * + * Purpose: Sends the specified signal. + * + * In terms of this test framework, a signal consists of a file + * on disk. Since there are multiple processes that need to + * communicate with each other, they do so by writing and + * reading signal files on disk, the names and contents of + * which are used to inform a process about when it can + * proceed and what it should do next. + * + * This function writes a signal file. The first argument is + * the name of the signal file, and the second and third + * arguments are the contents of the first two lines of the + * signal file. The last two arguments may be NULL. + * + * Return: void + * + * Programmer: Mike McGreevy + * August 18, 2010 + * + *------------------------------------------------------------------------- + */ +void +h5_send_message(const char *send, const char *arg1, const char *arg2) +{ + FILE *signalfile; + + HDremove(TMP_SIGNAL_FILE); + + /* Create signal file (which will send signal to some other process) */ + signalfile = HDfopen(TMP_SIGNAL_FILE, "w+"); + + /* Write messages to signal file, if provided */ + if(arg2 != NULL) { + HDassert(arg1); + HDfprintf(signalfile, "%s\n%s\n", arg1, arg2); + } /* end if */ + else if(arg1 != NULL) { + HDassert(arg2 == NULL); + HDfprintf(signalfile, "%s\n", arg1); + } /* end if */ + else { + HDassert(arg1 == NULL); + HDassert(arg2 == NULL); + }/* end else */ + + HDfclose(signalfile); + + HDrename(TMP_SIGNAL_FILE, send); +} /* h5_send_message() */ + +/*------------------------------------------------------------------------- + * Function: h5_wait_message + * + * Purpose: Waits for the specified signal. + * + * In terms of this test framework, a signal consists of a file + * on disk. Since there are multiple processes that need to + * communicate with each other, they do so by writing and + * reading signal files on disk, the names and contents of + * which are used to inform a process about when it can + * proceed and what it should do next. + * + * This function continuously attempts to read the specified + * signal file from disk, and only continues once it has + * successfully done so (i.e., only after another process has + * called the "h5_send_message" function to write the signal file). + * This functon will then immediately remove the file (i.e., + * to indicate that it has been received and can be reused), + * and then exits, allowing the calling function to continue. + * + * Return: void + * + * Programmer: Mike McGreevy + * August 18, 2010 + * + *------------------------------------------------------------------------- + */ +herr_t +h5_wait_message(const char *waitfor) +{ + FILE *returnfile; + time_t t0,t1; + + /* Start timer. If this function runs for too long (i.e., + expected signal is never received), it will + return failure */ + HDtime(&t0); + + /* Wait for return signal from some other process */ + while ((returnfile = HDfopen(waitfor, "r")) == NULL) { + + /* make note of current time. */ + HDtime(&t1); + + /* If we've been waiting for a signal for too long, then + it was likely never sent and we should fail rather + than loop infinitely */ + if(HDdifftime(t1, t0) > MESSAGE_TIMEOUT) { + HDfprintf(stdout, "Error communicating between processes. Make sure test script is running.\n"); + TEST_ERROR; + } /* end if */ + } /* end while */ + + HDfclose(returnfile); + HDunlink(waitfor); + + return SUCCEED; + +error: + return FAIL; +} /* h5_wait_message() */ + diff --git a/test/h5test.h b/test/h5test.h index 6806e8f..e79f2b1 100644 --- a/test/h5test.h +++ b/test/h5test.h @@ -197,6 +197,9 @@ H5TEST_DLL char* getenv_all(MPI_Comm comm, int root, const char* name); /* Extern global variables */ H5TEST_DLLVAR int TestVerbosity; +H5TEST_DLL void h5_send_message(const char *file, const char *arg1, const char *arg2); +H5TEST_DLL herr_t h5_wait_message(const char *file); + #ifdef __cplusplus } #endif diff --git a/test/testflushrefresh.sh.in b/test/testflushrefresh.sh.in new file mode 100644 index 0000000..7bfeb60 --- /dev/null +++ b/test/testflushrefresh.sh.in @@ -0,0 +1,200 @@ +#! /bin/sh +# +# 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 files COPYING and Copyright.html. COPYING can be found at the root +# of the source code distribution tree; Copyright.html can be found at the +# root level of an installed copy of the electronic HDF5 document set and +# is linked from the top-level documents page. It can also be found at +# http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have +# access to either file, you may request a copy from help@hdfgroup.org. +# +# +# Test script for the flush/evict single objects feature. +# +# This test file doesn't actually perform any tests, rather, it acts +# as a process manager for the 'flushrefresh' test file, which is where +# the tests are actually housed. The reason this script exists is because +# the verification of this feature needs to occur in separate processes +# from the one in which the file is being manipulated in. (i.e., we have +# a single writer process, and various reader processes spawning off +# and doing the verification that individual objects are being +# correctly flushed). +# +# Programmer: +# Mike McGreevy +# Tuesday, July 20, 2010 + +############################################################################### +## test variables +############################################################################### + +# Number of errors encountered during test run. +nerrors=0 + +# Set up a function to check the current time since the epoch - ideally, we'd +# like to use Perl. If it wasn't detected by configure, then use date, though +# this is less portable and might cause problems on machines that don't +# recognize the +%s option (like Solaris). +# +# Note that PERL will resolve to true or false, not a path. +PERL=@PERL@ +if test -n "$PERL"; then + TimeStamp() + { + time=`perl -e 'print int(time)'` + echo "$time" + } +else + TimeStamp() + { + time=`date +%s` + echo "$time" + } +fi + +############################################################################### +## Main +############################################################################### +# The build (current) directory might be different than the source directory. +if test -z "$srcdir"; then + srcdir=. +fi + +# Check to see if the VFD specified by the HDF5_DRIVER environment variable +# supports SWMR. +./swmr_check_compat_vfd +rc=$? +if [ $rc -ne 0 ] ; then + echo + echo "The VFD specified by the HDF5_DRIVER environment variable" + echo "does not support SWMR." + echo + echo "flush/refresh tests skipped" + echo + exit 0 +fi + +# ======================== +# Launch the Test Program. +# ======================== +./flushrefresh & +pid_main=$! + +# ================================================= +# Set up/initialize some variables to be used later +# ================================================= +testfile=flushrefresh.h5 +startsignal=flushrefresh_VERIFICATION_START +endsignal=flushrefresh_VERIFICATION_DONE +timeout_length=300 +timedout=0 +verification_done=0 +if [ -e $testfile ]; then + rm $testfile +fi + +# ======================================= +# Run flush verification on test program. +# ======================================= + +until [ $verification_done -eq 1 ]; do + + # Wait for signal from test program that verification routine can run. + before=`TimeStamp` + until [ -s $startsignal ]; do + after=`TimeStamp` + timediff=`expr $after - $before` + if [ $timediff -gt $timeout_length ]; then + nerrors=`expr $nerrors + 1` + timedout=1 + break + fi + done + + # Check to see if we timed out looking for the signal before continuing. + if [ $timedout -gt 0 ]; then + echo timed out waiting for signal from test program. + break + fi + + # Read in test routine parameters from signal file, then delete signal file. + param1=`head -1 $startsignal` + param2=`tail -1 $startsignal` + rm $startsignal + + # Check if we're done with verifications, otherwise run the specified verification. + if [ "$param1" = "VERIFICATION_DONE" ]; then + verification_done=1 + echo "all flush verification complete" > $endsignal + else + ./flushrefresh $param1 $param2 + echo "verification flush process done" > $endsignal + fi + +done + +# ========================================= +# Run refresh verification on test program. +# ========================================= +if [ $timedout -eq 0 ]; then + until [ $verification_done -eq 2 ]; do + + # Wait for signal from test program that verification routine can run. + before=`TimeStamp` + until [ -s $startsignal ]; do + after=`TimeStamp` + timediff=`expr $after - $before` + if [ $timediff -gt $timeout_length ]; then + nerrors=`expr $nerrors + 1` + timedout=1 + break + fi + done + + # Check to see if we timed out looking for the signal before continuing. + if [ $timedout -gt 0 ]; then + echo timed out waiting for signal from test program. + break + fi + + # Read in test routine parameter from signal file, then delete signal file. + param1=`head -n 1 $startsignal` + rm $startsignal + + # Check if we're done with verifications, otherwise run the specified verification. + if [ "$param1" = "VERIFICATION_DONE" ]; then + verification_done=2 + echo "all refresh verification complete" > $endsignal + else + ./flushrefresh $param1 + echo "refresh verifiction process done" > $endsignal + fi + + done +fi + +# ============================================ +# Wait for main to finish up, and end testing. +# ============================================ +wait $pid_main +if test $? -ne 0; then + echo flushrefresh had error + nerrors=`expr $nerrors + 1` +fi + +############################################################################### +## Report and exit +############################################################################### + +if test $nerrors -eq 0 ; then + echo "flush/refresh objects tests passed." + exit 0 +else + echo "flush/refresh objects tests failed with $nerrors errors." + exit 1 +fi -- cgit v0.12