diff options
author | jhendersonHDF <jhenderson@hdfgroup.org> | 2023-05-02 19:52:39 (GMT) |
---|---|---|
committer | Jordan Henderson <jhenderson@hdfgroup.org> | 2023-05-03 18:26:57 (GMT) |
commit | c75b4af1a2630ace445da1ec661191601583f79a (patch) | |
tree | a345b94dc09dd1ea1c38c1136a133b00939ba395 /test/API/H5_api_test_util.c | |
parent | 75d64819b050bb30b2a2751d9ba55651f9a1af79 (diff) | |
download | hdf5-c75b4af1a2630ace445da1ec661191601583f79a.zip hdf5-c75b4af1a2630ace445da1ec661191601583f79a.tar.gz hdf5-c75b4af1a2630ace445da1ec661191601583f79a.tar.bz2 |
Add initial version of HDF5 API tests (#2877)
Diffstat (limited to 'test/API/H5_api_test_util.c')
-rw-r--r-- | test/API/H5_api_test_util.c | 819 |
1 files changed, 819 insertions, 0 deletions
diff --git a/test/API/H5_api_test_util.c b/test/API/H5_api_test_util.c new file mode 100644 index 0000000..7fec2b6 --- /dev/null +++ b/test/API/H5_api_test_util.c @@ -0,0 +1,819 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "H5_api_test.h" +#include "H5_api_test_util.h" + +/* + * The maximum allowable size of a generated datatype. + * + * NOTE: HDF5 currently has limits on the maximum size of + * a datatype of an object, as this information is stored + * in the object header. In order to provide maximum + * compatibility between the native VOL connector and others + * for this test suite, we limit the size of a datatype here. + * This value should be adjusted as future HDF5 development + * allows. + */ +#define GENERATED_DATATYPE_MAX_SIZE 65536 + +/* + * The maximum size of a datatype for compact objects that + * must fit within the size of a native HDF5 object header message. + * This is typically used for attributes and compact datasets. + */ +#define COMPACT_DATATYPE_MAX_SIZE 1024 + +/* The maximum level of recursion that the generate_random_datatype() + * function should go down to, before being forced to choose a base type + * in order to not cause a stack overflow. + */ +#define TYPE_GEN_RECURSION_MAX_DEPTH 3 + +/* The maximum number of members allowed in an HDF5 compound type, as + * generated by the generate_random_datatype() function, for ease of + * development. + */ +#define COMPOUND_TYPE_MAX_MEMBERS 4 + +/* The maximum number and size of the dimensions of an HDF5 array + * datatype, as generated by the generate_random_datatype() function. + */ +#define ARRAY_TYPE_MAX_DIMS 4 + +/* The maximum number of members and the maximum size of those + * members' names for an HDF5 enum type, as generated by the + * generate_random_datatype() function. + */ +#define ENUM_TYPE_MAX_MEMBER_NAME_LENGTH 256 +#define ENUM_TYPE_MAX_MEMBERS 16 + +/* The maximum size of an HDF5 string datatype, as created by the + * generate_random_datatype() function. + */ +#define STRING_TYPE_MAX_SIZE 1024 + +/* + * The maximum dimensionality and dimension size of a dataspace + * generated for an attribute or compact dataset. + */ +#define COMPACT_SPACE_MAX_DIM_SIZE 4 +#define COMPACT_SPACE_MAX_DIMS 3 + +/* + * Helper function to generate a random HDF5 datatype in order to thoroughly + * test support for datatypes. The parent_class parameter is to support + * recursive generation of datatypes. In most cases, this function should be + * called with H5T_NO_CLASS for the parent_class parameter. + */ +/* + * XXX: limit size of datatype generated + */ +hid_t +generate_random_datatype(H5T_class_t parent_class, hbool_t is_compact) +{ + static int depth = 0; + hsize_t *array_dims = NULL; + size_t i; + hid_t compound_members[COMPOUND_TYPE_MAX_MEMBERS]; + hid_t datatype = H5I_INVALID_HID; + + depth++; + + for (i = 0; i < COMPOUND_TYPE_MAX_MEMBERS; i++) + compound_members[i] = H5I_INVALID_HID; + + switch (rand() % H5T_NCLASSES) { +case_integer: + case H5T_INTEGER: { + switch (rand() % 16) { + case 0: + if ((datatype = H5Tcopy(H5T_STD_I8BE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 1: + if ((datatype = H5Tcopy(H5T_STD_I8LE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 2: + if ((datatype = H5Tcopy(H5T_STD_I16BE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 3: + if ((datatype = H5Tcopy(H5T_STD_I16LE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 4: + if ((datatype = H5Tcopy(H5T_STD_I32BE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 5: + if ((datatype = H5Tcopy(H5T_STD_I32LE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 6: + if ((datatype = H5Tcopy(H5T_STD_I64BE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 7: + if ((datatype = H5Tcopy(H5T_STD_I64LE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 8: + if ((datatype = H5Tcopy(H5T_STD_U8BE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 9: + if ((datatype = H5Tcopy(H5T_STD_U8LE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 10: + if ((datatype = H5Tcopy(H5T_STD_U16BE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 11: + if ((datatype = H5Tcopy(H5T_STD_U16LE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 12: + if ((datatype = H5Tcopy(H5T_STD_U32BE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 13: + if ((datatype = H5Tcopy(H5T_STD_U32LE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 14: + if ((datatype = H5Tcopy(H5T_STD_U64BE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + case 15: + if ((datatype = H5Tcopy(H5T_STD_U64LE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined integer type\n"); + goto done; + } + + break; + + default: + H5_FAILED(); + HDprintf(" invalid value for predefined integer type; should not happen\n"); + goto done; + } + + break; + } + +case_float: + case H5T_FLOAT: { + switch (rand() % 4) { + case 0: + if ((datatype = H5Tcopy(H5T_IEEE_F32BE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined floating-point type\n"); + goto done; + } + + break; + + case 1: + if ((datatype = H5Tcopy(H5T_IEEE_F32LE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined floating-point type\n"); + goto done; + } + + break; + + case 2: + if ((datatype = H5Tcopy(H5T_IEEE_F64BE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined floating-point type\n"); + goto done; + } + + break; + + case 3: + if ((datatype = H5Tcopy(H5T_IEEE_F64LE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy predefined floating-point type\n"); + goto done; + } + + break; + + default: + H5_FAILED(); + HDprintf(" invalid value for floating point type; should not happen\n"); + goto done; + } + + break; + } + +case_time: + case H5T_TIME: { + /* Time datatype is unsupported, try again */ + goto reroll; + break; + } + +case_string: + case H5T_STRING: { + /* Note: currently only H5T_CSET_ASCII is supported for the character set and + * only H5T_STR_NULLTERM is supported for string padding for variable-length + * strings and only H5T_STR_NULLPAD is supported for string padding for + * fixed-length strings, but these may change in the future. + */ + if (0 == (rand() % 2)) { + if ((datatype = H5Tcreate(H5T_STRING, (size_t)(rand() % STRING_TYPE_MAX_SIZE) + 1)) < 0) { + H5_FAILED(); + HDprintf(" couldn't create fixed-length string datatype\n"); + goto done; + } + + if (H5Tset_strpad(datatype, H5T_STR_NULLPAD) < 0) { + H5_FAILED(); + HDprintf(" couldn't set H5T_STR_NULLPAD for fixed-length string type\n"); + goto done; + } + } + else { + /* + * Currently, all VL datatypes are disabled. + */ + goto reroll; + +#if 0 + if ((datatype = H5Tcreate(H5T_STRING, H5T_VARIABLE)) < 0) { + H5_FAILED(); + HDprintf(" couldn't create variable-length string datatype\n"); + goto done; + } + + if (H5Tset_strpad(datatype, H5T_STR_NULLTERM) < 0) { + H5_FAILED(); + HDprintf(" couldn't set H5T_STR_NULLTERM for variable-length string type\n"); + goto done; + } +#endif + } + + if (H5Tset_cset(datatype, H5T_CSET_ASCII) < 0) { + H5_FAILED(); + HDprintf(" couldn't set string datatype character set\n"); + goto done; + } + + break; + } + +case_bitfield: + case H5T_BITFIELD: { + /* Bitfield datatype is unsupported, try again */ + goto reroll; + break; + } + +case_opaque: + case H5T_OPAQUE: { + /* Opaque datatype is unsupported, try again */ + goto reroll; + break; + } + +case_compound: + case H5T_COMPOUND: { + size_t num_members; + size_t next_offset = 0; + size_t compound_size = 0; + + /* Currently only allows arrays of integer, float or string. Pick another type if we + * are creating an array of something other than these. Also don't allow recursion + * to go too deep. Pick another type that doesn't recursively call this function. */ + if (H5T_ARRAY == parent_class || depth > TYPE_GEN_RECURSION_MAX_DEPTH) + goto reroll; + + if ((datatype = H5Tcreate(H5T_COMPOUND, 1)) < 0) { + H5_FAILED(); + HDprintf(" couldn't create compound datatype\n"); + goto done; + } + + num_members = (size_t)(rand() % COMPOUND_TYPE_MAX_MEMBERS + 1); + + for (i = 0; i < num_members; i++) { + size_t member_size; + char member_name[256]; + + HDsnprintf(member_name, 256, "compound_member%zu", i); + + if ((compound_members[i] = generate_random_datatype(H5T_NO_CLASS, is_compact)) < 0) { + H5_FAILED(); + HDprintf(" couldn't create compound datatype member %zu\n", i); + goto done; + } + + if (!(member_size = H5Tget_size(compound_members[i]))) { + H5_FAILED(); + HDprintf(" couldn't get compound member %zu size\n", i); + goto done; + } + + compound_size += member_size; + + if (H5Tset_size(datatype, compound_size) < 0) { + H5_FAILED(); + HDprintf(" couldn't set size for compound datatype\n"); + goto done; + } + + if (H5Tinsert(datatype, member_name, next_offset, compound_members[i]) < 0) { + H5_FAILED(); + HDprintf(" couldn't insert compound datatype member %zu\n", i); + goto done; + } + + next_offset += member_size; + } + + break; + } + +case_reference: + case H5T_REFERENCE: { + /* Temporarily disable generation of reference datatypes */ + goto reroll; + + /* Currently only allows arrays of integer, float or string. Pick another type if we + * are creating an array of something other than these. */ + if (H5T_ARRAY == parent_class) + goto reroll; + + if (0 == (rand() % 2)) { + if ((datatype = H5Tcopy(H5T_STD_REF_OBJ)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy object reference datatype\n"); + goto done; + } + } + else { + /* Region references are currently unsupported */ + goto reroll; + + if ((datatype = H5Tcopy(H5T_STD_REF_DSETREG)) < 0) { + H5_FAILED(); + HDprintf(" couldn't copy region reference datatype\n"); + goto done; + } + } + + break; + } + +case_enum: + case H5T_ENUM: { + /* Currently doesn't currently support ARRAY of ENUM, so try another type + * if this happens. */ + if (H5T_ARRAY == parent_class) + goto reroll; + + if ((datatype = H5Tenum_create(H5T_NATIVE_INT)) < 0) { + H5_FAILED(); + HDprintf(" couldn't create enum datatype\n"); + goto done; + } + + for (i = 0; i < (size_t)(rand() % ENUM_TYPE_MAX_MEMBERS + 1); i++) { + char name[ENUM_TYPE_MAX_MEMBER_NAME_LENGTH]; + int value = rand(); + + HDsnprintf(name, ENUM_TYPE_MAX_MEMBER_NAME_LENGTH, "enum_val%zu", i); + + if (H5Tenum_insert(datatype, name, &value) < 0) { + H5_FAILED(); + HDprintf(" couldn't insert member into enum datatype\n"); + goto done; + } + } + + break; + } + +case_vlen: + case H5T_VLEN: { + /* Variable-length datatypes are unsupported, try again */ + goto reroll; + break; + } + +case_array: + case H5T_ARRAY: { + unsigned ndims; + hid_t base_datatype = H5I_INVALID_HID; + + /* Currently doesn't currently support ARRAY of ARRAY, so try another type + * if this happens. Also check for too much recursion. */ + if (H5T_ARRAY == parent_class || depth > TYPE_GEN_RECURSION_MAX_DEPTH) + goto reroll; + + ndims = (unsigned)(rand() % ARRAY_TYPE_MAX_DIMS + 1); + + if (NULL == (array_dims = (hsize_t *)HDmalloc(ndims * sizeof(*array_dims)))) + goto done; + + for (i = 0; i < ndims; i++) + array_dims[i] = (hsize_t)(rand() % MAX_DIM_SIZE + 1); + + if ((base_datatype = generate_random_datatype(H5T_ARRAY, is_compact)) < 0) { + H5_FAILED(); + HDprintf(" couldn't create array base datatype\n"); + goto done; + } + + if ((datatype = H5Tarray_create2(base_datatype, ndims, array_dims)) < 0) { + H5_FAILED(); + HDprintf(" couldn't create array datatype\n"); + goto done; + } + + break; + } + + default: + H5_FAILED(); + HDprintf(" invalid datatype class\n"); + break; + } /* end if */ + +done: + if (depth > 0) + depth--; + + if (datatype < 0) { + for (i = 0; i < COMPOUND_TYPE_MAX_MEMBERS; i++) { + if (compound_members[i] > 0 && H5Tclose(compound_members[i]) < 0) { + H5_FAILED(); + HDprintf(" couldn't close compound member %zu\n", i); + } + } + } + + if (array_dims) { + HDfree(array_dims); + array_dims = NULL; + } + + if (is_compact && (depth == 0)) { + size_t type_size; + + /* + * Check to make sure that the generated datatype does + * not exceed the maximum compact datatype size if a + * compact datatype was requested. + */ + if (0 == (type_size = H5Tget_size(datatype))) { + H5_FAILED(); + HDprintf(" failed to retrieve datatype's size\n"); + H5Tclose(datatype); + datatype = H5I_INVALID_HID; + } + else { + if (type_size > COMPACT_DATATYPE_MAX_SIZE) { + /* + * Generate a new datatype. + */ + H5Tclose(datatype); + datatype = H5I_INVALID_HID; + goto reroll; + } + } + } + + return datatype; + +reroll: + if (depth > 0) + depth--; + + /* + * The datatype generation resulted in a datatype that is currently invalid + * for these tests, try again. + */ + switch (rand() % H5T_NCLASSES) { + case H5T_INTEGER: + goto case_integer; + case H5T_FLOAT: + goto case_float; + case H5T_TIME: + goto case_time; + case H5T_STRING: + goto case_string; + case H5T_BITFIELD: + goto case_bitfield; + case H5T_OPAQUE: + goto case_opaque; + case H5T_COMPOUND: + goto case_compound; + case H5T_REFERENCE: + goto case_reference; + case H5T_ENUM: + goto case_enum; + case H5T_VLEN: + goto case_vlen; + case H5T_ARRAY: + goto case_array; + default: + H5_FAILED(); + HDprintf(" invalid value for goto\n"); + break; + } + + return H5I_INVALID_HID; +} + +/* + * Helper function to generate a random HDF5 dataspace in order to thoroughly + * test support for dataspaces. + */ +hid_t +generate_random_dataspace(int rank, const hsize_t *max_dims, hsize_t *dims_out, hbool_t is_compact) +{ + hsize_t dataspace_dims[H5S_MAX_RANK]; + size_t i; + hid_t dataspace_id = H5I_INVALID_HID; + + if (rank < 0) + TEST_ERROR; + if (is_compact && (rank > COMPACT_SPACE_MAX_DIMS)) { + HDprintf(" current rank of compact dataspace (%lld) exceeds maximum dimensionality (%lld)\n", + (long long)rank, (long long)COMPACT_SPACE_MAX_DIMS); + TEST_ERROR; + } + + /* + * XXX: if max_dims is specified, make sure that the dimensions generated + * are not larger than this. + */ + for (i = 0; i < (size_t)rank; i++) { + if (is_compact) + dataspace_dims[i] = (hsize_t)(rand() % COMPACT_SPACE_MAX_DIM_SIZE + 1); + else + dataspace_dims[i] = (hsize_t)(rand() % MAX_DIM_SIZE + 1); + + if (dims_out) + dims_out[i] = dataspace_dims[i]; + } + + if ((dataspace_id = H5Screate_simple(rank, dataspace_dims, max_dims)) < 0) + TEST_ERROR; + + return dataspace_id; + +error: + return H5I_INVALID_HID; +} + +int +create_test_container(char *filename, uint64_t vol_cap_flags) +{ + hid_t file_id = H5I_INVALID_HID; + hid_t group_id = H5I_INVALID_HID; + + if (!(vol_cap_flags & H5VL_CAP_FLAG_FILE_BASIC)) { + HDprintf(" VOL connector doesn't support file creation\n"); + goto error; + } + + if ((file_id = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) { + HDprintf(" couldn't create testing container file '%s'\n", filename); + goto error; + } + + if (vol_cap_flags & H5VL_CAP_FLAG_GROUP_BASIC) { + /* Create container groups for each of the test interfaces + * (group, attribute, dataset, etc.). + */ + if ((group_id = H5Gcreate2(file_id, GROUP_TEST_GROUP_NAME, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) >= + 0) { + HDprintf(" created container group for Group tests\n"); + H5Gclose(group_id); + } + + if ((group_id = H5Gcreate2(file_id, ATTRIBUTE_TEST_GROUP_NAME, H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT)) >= 0) { + HDprintf(" created container group for Attribute tests\n"); + H5Gclose(group_id); + } + + if ((group_id = + H5Gcreate2(file_id, DATASET_TEST_GROUP_NAME, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) >= 0) { + HDprintf(" created container group for Dataset tests\n"); + H5Gclose(group_id); + } + + if ((group_id = + H5Gcreate2(file_id, DATATYPE_TEST_GROUP_NAME, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) >= 0) { + HDprintf(" created container group for Datatype tests\n"); + H5Gclose(group_id); + } + + if ((group_id = H5Gcreate2(file_id, LINK_TEST_GROUP_NAME, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) >= + 0) { + HDprintf(" created container group for Link tests\n"); + H5Gclose(group_id); + } + + if ((group_id = H5Gcreate2(file_id, OBJECT_TEST_GROUP_NAME, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) >= + 0) { + HDprintf(" created container group for Object tests\n"); + H5Gclose(group_id); + } + + if ((group_id = H5Gcreate2(file_id, MISCELLANEOUS_TEST_GROUP_NAME, H5P_DEFAULT, H5P_DEFAULT, + H5P_DEFAULT)) >= 0) { + HDprintf(" created container group for Miscellaneous tests\n"); + H5Gclose(group_id); + } + } + + if (H5Fclose(file_id) < 0) { + HDprintf(" failed to close testing container\n"); + goto error; + } + + return 0; + +error: + H5E_BEGIN_TRY + { + H5Gclose(group_id); + H5Fclose(file_id); + } + H5E_END_TRY; + + return -1; +} + +/* + * Add a prefix to the given filename. The caller + * is responsible for freeing the returned filename + * pointer with HDfree(). + */ +herr_t +prefix_filename(const char *prefix, const char *filename, char **filename_out) +{ + char *out_buf = NULL; + herr_t ret_value = SUCCEED; + + if (!prefix) { + HDprintf(" invalid file prefix\n"); + ret_value = FAIL; + goto done; + } + if (!filename || (*filename == '\0')) { + HDprintf(" invalid filename\n"); + ret_value = FAIL; + goto done; + } + if (!filename_out) { + HDprintf(" invalid filename_out buffer\n"); + ret_value = FAIL; + goto done; + } + + if (NULL == (out_buf = HDmalloc(H5_API_TEST_FILENAME_MAX_LENGTH))) { + HDprintf(" couldn't allocated filename buffer\n"); + ret_value = FAIL; + goto done; + } + + HDsnprintf(out_buf, H5_API_TEST_FILENAME_MAX_LENGTH, "%s%s", prefix, filename); + + *filename_out = out_buf; + +done: + return ret_value; +} + +/* + * Calls H5Fdelete on the given filename. If a prefix string + * is given, adds that prefix string to the filename before + * calling H5Fdelete + */ +herr_t +remove_test_file(const char *prefix, const char *filename) +{ + const char *test_file; + char *prefixed_filename = NULL; + herr_t ret_value = SUCCEED; + + if (prefix) { + if (prefix_filename(prefix, filename, &prefixed_filename) < 0) { + HDprintf(" couldn't prefix filename\n"); + ret_value = FAIL; + goto done; + } + + test_file = prefixed_filename; + } + else + test_file = filename; + + if (H5Fdelete(test_file, H5P_DEFAULT) < 0) { + HDprintf(" couldn't remove file '%s'\n", test_file); + ret_value = FAIL; + goto done; + } + +done: + HDfree(prefixed_filename); + + return ret_value; +} |