hdf5/test/tid.c
jhendersonHDF 34d6ef545a
Refactor h5test.c, testframe.c and testpar.h testing frameworks (#4891)
Added new testframe.h header to document testing framework functions and
split them away from h5test.h and from test programs that don't
integrate with the testframe.c testing framework

Added new test setup callback to testframe.c testing framework

Added parameters to AddTest() to specify size of test parameters so they
can be copied for later use

Enabled HDF5 error stacks in testframe.c framework by default and added
some error stack suppressions to some testhdf5 tests

Added new maxthreads option to testframe.c framework to allow specifying
the maximum number of threads a multi-threaded test can use

Moved TestExpress functionality out of testframe.c and into more general
h5test.c for wider use by tests through getter and setter

Updated some tests to not mix and match functionality between h5test.c/h
and testframe.c/h

Moved some functionality from testphdf5.h into testpar.h for parallel
tests that aren't part of testphdf5

Added new parallel test library that contains common shared
functionality for parallel tests (similar to h5test library)
2024-10-01 16:10:03 -05:00

1520 lines
47 KiB
C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 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://www.hdfgroup.org/licenses. *
* If you do not have access to either file, you may request a copy from *
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Test user-created identifiers (hid_t's) and identifier types. */
#include "testhdf5.h"
/* Include H5Ipkg.h to calculate max number of groups */
#define H5I_FRIEND /*suppress error about including H5Ipkg */
#include "H5Ipkg.h"
/* Defines used in test_appropriate_ids */
#define FILE_NAME "tid.h5"
#define DSET_NAME "Dataset 1"
static herr_t
free_wrapper(void *p, void H5_ATTR_UNUSED **_ctx)
{
free(p);
return SUCCEED;
}
/* Test basic functionality of registering and deleting types and IDs */
static int
basic_id_test(void)
{
H5I_type_t myType = H5I_BADID;
hid_t arrayID = H5I_INVALID_HID;
void *testObj = NULL;
void *testPtr = NULL;
char nameString[10];
hid_t testID;
ssize_t testSize = -1;
herr_t err;
int num_ref;
hsize_t num_members;
/* Try to register an ID with fictitious types */
H5E_BEGIN_TRY
arrayID = H5Iregister((H5I_type_t)420, testObj);
H5E_END_TRY
VERIFY(arrayID, H5I_INVALID_HID, "H5Iregister");
if (arrayID != H5I_INVALID_HID)
goto out;
H5E_BEGIN_TRY
arrayID = H5Iregister((H5I_type_t)-1, testObj);
H5E_END_TRY
VERIFY(arrayID, H5I_INVALID_HID, "H5Iregister");
if (arrayID != H5I_INVALID_HID)
goto out;
/* Try to access IDs with fictitious types */
H5E_BEGIN_TRY
testPtr = H5Iobject_verify((hid_t)100, (H5I_type_t)0);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
if (testPtr != NULL)
goto out;
H5E_BEGIN_TRY
testPtr = H5Iobject_verify((hid_t)700, (H5I_type_t)700);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
if (testPtr != NULL)
goto out;
/* Register a type */
myType = H5Iregister_type((size_t)64, 0, free_wrapper);
CHECK(myType, H5I_BADID, "H5Iregister_type");
if (myType == H5I_BADID)
goto out;
/* Register an ID and retrieve the object it points to.
* Once the ID has been registered, testObj will be freed when
* its ID type is destroyed.
*/
testObj = malloc(7 * sizeof(int));
arrayID = H5Iregister(myType, testObj);
CHECK(arrayID, H5I_INVALID_HID, "H5Iregister");
if (arrayID == H5I_INVALID_HID) {
free(testObj);
goto out;
}
testPtr = (int *)H5Iobject_verify(arrayID, myType);
CHECK_PTR_EQ(testPtr, testObj, "H5Iobject_verify");
if (testPtr != testObj)
goto out;
/* Ensure that H5Iget_file_id and H5Iget_name() fail, since this
* is an hid_t for the wrong kind of object
*/
H5E_BEGIN_TRY
testID = H5Iget_file_id(arrayID);
H5E_END_TRY
VERIFY(testID, H5I_INVALID_HID, "H5Iget_file_id");
if (testID != H5I_INVALID_HID)
goto out;
H5E_BEGIN_TRY
testSize = H5Iget_name(arrayID, nameString, (size_t)9);
H5E_END_TRY
VERIFY(testSize, -1, "H5Iget_name");
if (testSize != -1)
goto out;
/* Make sure H5Iremove_verify catches objects of the wrong type */
H5E_BEGIN_TRY
testPtr = (int *)H5Iremove_verify(arrayID, (H5I_type_t)0);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iremove_verify");
if (testPtr != NULL)
goto out;
H5E_BEGIN_TRY
testPtr = (int *)H5Iremove_verify(arrayID, (H5I_type_t)((int)myType - 1));
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iremove_verify");
if (testPtr != NULL)
goto out;
/* Remove an ID and make sure we can't access it */
testPtr = (int *)H5Iremove_verify(arrayID, myType);
CHECK_PTR(testPtr, "H5Iremove_verify");
if (testPtr == NULL)
goto out;
H5E_BEGIN_TRY
testPtr = (int *)H5Iobject_verify(arrayID, myType);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
if (testPtr != NULL)
goto out;
/* Delete the type and make sure we can't access objects within it */
arrayID = H5Iregister(myType, testObj);
err = H5Idestroy_type(myType);
VERIFY(err, 0, "H5Idestroy_type");
if (err != 0)
goto out;
VERIFY(H5Itype_exists(myType), 0, "H5Itype_exists");
if (H5Itype_exists(myType) != 0)
goto out;
H5E_BEGIN_TRY
VERIFY(H5Inmembers(myType, NULL), -1, "H5Inmembers");
if (H5Inmembers(myType, NULL) != -1)
goto out;
H5E_END_TRY
/* Register another type and another object in that type */
myType = H5Iregister_type((size_t)64, 0, free_wrapper);
CHECK(myType, H5I_BADID, "H5Iregister_type");
if (myType == H5I_BADID)
goto out;
/* The memory that testObj pointed to should already have been
* freed when the previous type was destroyed. Allocate new
* memory for it.
*/
testObj = malloc(7 * sizeof(int));
arrayID = H5Iregister(myType, testObj);
CHECK(arrayID, H5I_INVALID_HID, "H5Iregister");
if (arrayID == H5I_INVALID_HID) {
free(testObj);
goto out;
}
err = H5Inmembers(myType, &num_members);
CHECK(err, -1, "H5Inmembers");
if (err < 0)
goto out;
VERIFY(num_members, 1, "H5Inmembers");
if (num_members != 1)
goto out;
/* Increment references to type and ensure that dec_type_ref
* doesn't destroy the type
*/
num_ref = H5Iinc_type_ref(myType);
VERIFY(num_ref, 2, "H5Iinc_type_ref");
if (num_ref != 2)
goto out;
num_ref = H5Idec_type_ref(myType);
VERIFY(num_ref, 1, "H5Idec_type_ref");
if (num_ref != 1)
goto out;
err = H5Inmembers(myType, &num_members);
CHECK(err, -1, "H5Inmembers");
if (err < 0)
goto out;
VERIFY(num_members, 1, "H5Inmembers");
if (num_members != 1)
goto out;
/* This call to dec_type_ref should destroy the type */
num_ref = H5Idec_type_ref(myType);
VERIFY(num_ref, 0, "H5Idec_type_ref");
if (num_ref != 0)
goto out;
VERIFY(H5Itype_exists(myType), 0, "H5Itype_exists");
if (H5Itype_exists(myType) != 0)
goto out;
H5E_BEGIN_TRY
err = H5Inmembers(myType, &num_members);
if (err >= 0)
goto out;
H5E_END_TRY
return 0;
out:
/* Clean up type if it has been allocated and free memory used
* by testObj
*/
if (myType >= 0)
H5Idestroy_type(myType);
return -1;
}
/* A dummy search function for the next test */
static int
test_search_func(void H5_ATTR_UNUSED *ptr1, hid_t H5_ATTR_UNUSED id, void H5_ATTR_UNUSED *ptr2)
{
return 0;
}
/* Ensure that public functions cannot access "predefined" ID types */
static int
id_predefined_test(void)
{
void *testObj;
hid_t testID;
hid_t typeID = H5I_INVALID_HID;
void *testPtr;
herr_t testErr;
testObj = malloc(sizeof(int));
/*
* Attempt to perform public functions on various library types
*/
H5E_BEGIN_TRY
testID = H5Iregister(H5I_FILE, testObj);
H5E_END_TRY
VERIFY(testID, H5I_INVALID_HID, "H5Iregister");
if (testID != H5I_INVALID_HID)
goto out;
H5E_BEGIN_TRY
testPtr = H5Isearch(H5I_GENPROP_LST, test_search_func, testObj);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Isearch");
if (testPtr != NULL)
goto out;
H5E_BEGIN_TRY
testErr = H5Inmembers(H5I_ERROR_STACK, NULL);
H5E_END_TRY
VERIFY(testErr, -1, "H5Inmembers");
if (testErr != -1)
goto out;
H5E_BEGIN_TRY
testErr = H5Iclear_type(H5I_FILE, 0);
H5E_END_TRY
VERIFY((testErr >= 0), 0, "H5Iclear_type");
if (testErr >= 0)
goto out;
H5E_BEGIN_TRY
testErr = H5Idestroy_type(H5I_DATASET);
H5E_END_TRY
VERIFY((testErr >= 0), 0, "H5Idestroy_type");
if (testErr >= 0)
goto out;
H5E_BEGIN_TRY
testErr = H5Itype_exists(H5I_GROUP);
H5E_END_TRY
VERIFY(testErr, -1, "H5Itype_exists");
if (testErr != -1)
goto out;
H5E_BEGIN_TRY
testErr = H5Itype_exists(H5I_ATTR);
H5E_END_TRY
VERIFY(testErr, -1, "H5Itype_exists");
if (testErr != -1)
goto out;
/*
* Create a datatype ID and try to perform illegal functions on it
*/
typeID = H5Tcreate(H5T_OPAQUE, (size_t)42);
CHECK(typeID, H5I_INVALID_HID, "H5Tcreate");
if (typeID == H5I_INVALID_HID)
goto out;
H5E_BEGIN_TRY
testPtr = H5Iremove_verify(typeID, H5I_DATATYPE);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iremove_verify");
if (testPtr != NULL)
goto out;
H5E_BEGIN_TRY
testPtr = H5Iobject_verify(typeID, H5I_DATATYPE);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
if (testPtr != NULL)
goto out;
H5Tclose(typeID);
/* testObj was never registered as an atom, so it will not be
* automatically freed. */
free(testObj);
return 0;
out:
if (typeID != H5I_INVALID_HID)
H5Tclose(typeID);
if (testObj != NULL)
free(testObj);
return -1;
}
/* Test the H5Iis_valid function */
static int
test_is_valid(void)
{
hid_t dtype; /* datatype id */
int64_t nmembs1; /* number of type memnbers */
int64_t nmembs2;
htri_t tri_ret; /* htri_t return value */
herr_t ret; /* return value */
/* Create a datatype id */
dtype = H5Tcopy(H5T_NATIVE_INT);
CHECK(dtype, FAIL, "H5Tcopy");
if (dtype < 0)
goto out;
/* Check that the ID is valid */
tri_ret = H5Iis_valid(dtype);
VERIFY(tri_ret, true, "H5Iis_valid");
if (tri_ret != true)
goto out;
/* Artificially manipulate the reference counts so app_count is 0, and dtype
* appears to be an internal id. This takes advantage of the fact that
* H5Ipkg is included.
*/
ret = H5I_inc_ref(dtype, false);
CHECK(ret, FAIL, "H5I_inc_ref");
if (ret < 0)
goto out;
ret = H5I_dec_app_ref(dtype);
CHECK(ret, FAIL, "H5I_dec_ref");
if (ret < 0)
goto out;
/* Check that dtype is invalid */
tri_ret = H5Iis_valid(dtype);
VERIFY(tri_ret, false, "H5Iis_valid");
if (tri_ret != false)
goto out;
/* Close dtype and verify that it has been closed */
nmembs1 = H5I_nmembers(H5I_DATATYPE);
CHECK(nmembs1, FAIL, "H5I_nmembers");
if (nmembs1 < 0)
goto out;
ret = H5I_dec_ref(dtype);
CHECK(ret, FAIL, "H5I_dec_ref");
if (ret < 0)
goto out;
nmembs2 = H5I_nmembers(H5I_DATATYPE);
VERIFY(nmembs2, nmembs1 - 1, "H5I_nmembers");
if (nmembs2 != nmembs1 - 1)
goto out;
/* Check that dtype is invalid */
tri_ret = H5Iis_valid(dtype);
VERIFY(tri_ret, false, "H5Iis_valid");
if (tri_ret != false)
goto out;
/* Check that an id of -1 is invalid */
tri_ret = H5Iis_valid((hid_t)H5I_INVALID_HID);
VERIFY(tri_ret, false, "H4Iis_valid");
if (tri_ret != false)
goto out;
return 0;
out:
/* Don't attempt to close dtype as we don't know the exact state of the
* reference counts. Every state in this function will be automatically
* closed at library exit anyways, as internal count is never > 1.
*/
return -1;
}
/* Test the H5Iget_type function */
static int
test_get_type(void)
{
hid_t dtype; /* datatype id */
H5I_type_t type_ret; /* return value */
/* Create a datatype id */
dtype = H5Tcopy(H5T_NATIVE_INT);
CHECK(dtype, FAIL, "H5Tcopy");
if (dtype < 0)
goto out;
/* Check that the ID is correct */
type_ret = H5Iget_type(dtype);
VERIFY(type_ret, H5I_DATATYPE, "H5Iget_type");
if (type_ret == H5I_BADID)
goto out;
/* Check that the ID is correct */
type_ret = H5Iget_type((hid_t)H5T_STRING);
VERIFY(type_ret, H5I_BADID, "H5Iget_type");
if (type_ret != H5I_BADID)
goto out;
/* Check that the ID is correct */
type_ret = H5Iget_type((hid_t)H5I_INVALID_HID);
VERIFY(type_ret, H5I_BADID, "H5Iget_type");
if (type_ret != H5I_BADID)
goto out;
H5Tclose(dtype);
return 0;
out:
if (dtype != H5I_INVALID_HID)
H5Tclose(dtype);
return -1;
}
/* Test boundary cases with lots of types */
/* Type IDs range from H5I_NTYPES to H5I_MAX_NUM_TYPES. The system will assign */
/* IDs in sequential order until H5I_MAX_NUM_TYPES IDs have been given out, at which */
/* point it will search for type IDs that were allocated but have since been */
/* deleted. */
/* This test will allocate IDs up to H5I_MAX_NUM_TYPES, ensure that IDs wrap around */
/* to low values successfully, ensure that an error is thrown when all possible */
/* type IDs are taken, then ensure that deleting types frees up their IDs. */
/* Note that this test depends on the implementation of IDs, so may break */
/* if the implementation changes. */
/* Also note that if someone else registered a user-defined type and forgot to */
/* destroy it, this test will mysteriously fail (because it will expect there to */
/* be one more "free" type ID than there is). */
/* H5I_NTYPES is defined in h5public.h, H5I_MAX_NUM_TYPES is defined in h5pkg.h */
static int
test_id_type_list(void)
{
H5I_type_t startType; /* The first type ID we were assigned in this test */
H5I_type_t currentType;
H5I_type_t testType;
int i; /* Just a counter variable */
startType = H5Iregister_type((size_t)8, 0, free_wrapper);
CHECK(startType, H5I_BADID, "H5Iregister_type");
if (startType == H5I_BADID)
goto out;
/* Sanity check */
if ((int)startType >= H5I_MAX_NUM_TYPES || startType < H5I_NTYPES) {
/* Error condition, throw an error */
ERROR("H5Iregister_type");
goto out;
}
/* Create types up to H5I_MAX_NUM_TYPES */
for (i = startType + 1; i < H5I_MAX_NUM_TYPES; i++) {
currentType = H5Iregister_type((size_t)8, 0, free_wrapper);
CHECK(currentType, H5I_BADID, "H5Iregister_type");
if (currentType == H5I_BADID)
goto out;
}
/* Wrap around to low type ID numbers */
for (i = H5I_NTYPES; i < startType; i++) {
currentType = H5Iregister_type((size_t)8, 0, free_wrapper);
CHECK(currentType, H5I_BADID, "H5Iregister_type");
if (currentType == H5I_BADID)
goto out;
}
/* There should be no room at the inn for a new ID type*/
H5E_BEGIN_TRY
testType = H5Iregister_type((size_t)8, 0, free_wrapper);
H5E_END_TRY
VERIFY(testType, H5I_BADID, "H5Iregister_type");
if (testType != H5I_BADID)
goto out;
/* Now delete a type and try to insert again */
H5Idestroy_type(H5I_NTYPES);
testType = H5Iregister_type((size_t)8, 0, free_wrapper);
VERIFY(testType, H5I_NTYPES, "H5Iregister_type");
if (testType != H5I_NTYPES)
goto out;
/* Cleanup. Destroy all types. */
for (i = H5I_NTYPES; i < H5I_MAX_NUM_TYPES; i++)
H5Idestroy_type((H5I_type_t)i);
return 0;
out:
/* Cleanup. For simplicity, just destroy all types and ignore errors. */
H5E_BEGIN_TRY
for (i = H5I_NTYPES; i < H5I_MAX_NUM_TYPES; i++)
H5Idestroy_type((H5I_type_t)i);
H5E_END_TRY
return -1;
}
/* Test removing ids in callback for H5Iclear_type */
/* There was a rare bug where, if an id free callback being called by
* H5I_clear_type() removed another id in that type, a segfault could occur.
* This test tests for that error (and freeing ids "out of order" within
* H5Iclear_type() in general).
*
* NB: RCT = "remove clear type"
*/
/* Macro definitions */
#define RCT_MAX_NOBJS 25 /* Maximum number of objects in the list */
#define RCT_MIN_NOBJS 5
#define RCT_NITER 50 /* Number of times we cycle through object creation and deletion */
/* Structure to hold the master list of objects */
typedef struct rct_obj_list_t {
/* Pointer to the objects */
struct rct_obj_t *objects;
/* The number of objects in the list */
long count;
/* The number of objects in the list that have not been freed */
long remaining;
} rct_obj_list_t;
/* Structure for an object */
typedef struct rct_obj_t {
/* The ID for this object */
hid_t id;
/* The number of times this object has been freed */
int nfrees;
/* Whether we are currently freeing this object directly
* through H5Idec_ref().
*/
bool freeing;
/* Pointer to the master list of all objects */
rct_obj_list_t *list;
} rct_obj_t;
/* Free callback passed to H5Iclear_type()
*
* When invoked on a closing object, frees a random unfreed ID in the
* master list of objects.
*/
static herr_t
rct_free_cb(void *_obj, void H5_ATTR_UNUSED **_ctx)
{
rct_obj_t *obj = (rct_obj_t *)_obj;
long remove_nth;
long i;
herr_t ret;
/* Mark this object as freed */
obj->nfrees++;
/* Decrement the number of objects in the list that have not been freed */
obj->list->remaining--;
/* If this object isn't already being freed by a callback free call and
* the master object list still contains objects to free, pick another
* object and free it.
*/
if (!obj->freeing && (obj->list->remaining > 0)) {
/* Pick a random object from the list. This is done by picking a
* random number between 0 and the # of remaining unfreed objects
* and then scanning through the list to find that nth unfreed
* object.
*/
remove_nth = rand() % obj->list->remaining;
for (i = 0; i < obj->list->count; i++)
if (obj->list->objects[i].nfrees == 0) {
if (remove_nth == 0)
break;
else
remove_nth--;
}
/* Badness if we scanned through the list and didn't manage to
* select one to delete (the list stats were probably updated
* incorrectly).
*/
if (i == obj->list->count) {
ERROR("invalid obj_list");
goto error;
}
/* Mark the object we're about to free so its own callback does
* not free another object. We don't want to recursively free the
* entire list when we free the first ID.
*/
obj->list->objects[i].freeing = true;
/* Decrement the reference count on the object */
ret = H5Idec_ref(obj->list->objects[i].id);
CHECK(ret, FAIL, "H5Idec_ref");
if (ret == FAIL)
goto error;
/* Unset the "freeing" flag */
obj->list->objects[i].freeing = false;
}
/* Verify the number of objects remaining in the master list is non-negative */
if (obj->list->remaining < 0) {
ERROR("invalid number of objects remaining");
goto error;
}
return 0;
error:
return -1;
} /* end rct_free_cb() */
/* Test function */
static int
test_remove_clear_type(void)
{
H5I_type_t obj_type;
rct_obj_list_t obj_list;
rct_obj_t *objects = NULL; /* Convenience pointer to objects stored in master list */
size_t list_size;
long i, j;
herr_t ret; /* return value */
/* Register a user-defined type with our custom ID-deleting callback */
obj_type = H5Iregister_type((size_t)8, 0, rct_free_cb);
CHECK(obj_type, H5I_BADID, "H5Iregister_type");
if (obj_type == H5I_BADID)
goto error;
/* Create an array to hold the objects in the master list */
list_size = RCT_MAX_NOBJS * sizeof(rct_obj_t);
obj_list.objects = malloc(list_size);
CHECK_PTR(obj_list.objects, "calloc");
if (NULL == obj_list.objects)
goto error;
/* Set a convenience pointer to the object array */
objects = obj_list.objects;
for (i = 0; i < RCT_NITER; i++) {
/* The number of members in the type, according to the HDF5 library */
hsize_t nmembers = 1234567; /* (init to fake number) */
/* The number of objects found while scanning through the object list */
int found;
/*********************
* Build object list *
*********************/
memset(obj_list.objects, 0, list_size);
/* The number of objects used is a random number between the min and max */
obj_list.count = obj_list.remaining =
RCT_MIN_NOBJS + (rand() % (long)(RCT_MAX_NOBJS - RCT_MIN_NOBJS + 1));
/* Create the actual objects */
for (j = 0; j < obj_list.count; j++) {
/* Object setup */
objects[j].nfrees = 0;
objects[j].freeing = false;
objects[j].list = &obj_list;
/* Register an ID for it */
objects[j].id = H5Iregister(obj_type, &objects[j]);
CHECK(objects[j].id, FAIL, "H5Iregister");
if (objects[j].id == FAIL)
goto error;
/* Bump the reference count by 1 (to 2) 50% of the time */
if (rand() % 2) {
ret = H5Iinc_ref(objects[j].id);
CHECK(ret, FAIL, "H5Iinc_ref");
if (ret == FAIL)
goto error;
}
}
/******************************************
* Clear the type with force set to false *
******************************************/
/* Clear the type. Since force is false, only
* IDs with a reference count of 1 will be cleared.
*/
ret = H5Iclear_type(obj_type, false);
CHECK(ret, FAIL, "H5Iclear_type");
if (ret == FAIL)
goto error;
/* Verify that the object struct fields are sane and count the
* number of unfreed objects
*/
found = 0;
for (j = 0; j < obj_list.count; j++) {
if (objects[j].nfrees == 0) {
/* Count unfreed objects */
found++;
}
else {
/* Every freed object should have been freed exactly once */
VERIFY(objects[j].nfrees, 1, "object freed more than once");
if (objects[j].nfrees != 1)
goto error;
}
/* No object should still be marked as "freeing" */
VERIFY(objects[j].freeing, false, "object marked as freeing");
if (objects[j].freeing != false)
goto error;
}
/* Verify the number of unfreed objects we found during our scan
* matches the number stored in the list
*/
VERIFY(obj_list.remaining, found, "incorrect number of objects remaining");
if (obj_list.remaining != found)
goto error;
/* Make sure the HDF5 library confirms our count */
ret = H5Inmembers(obj_type, &nmembers);
CHECK(ret, FAIL, "H5Inmembers");
if (ret == FAIL)
goto error;
VERIFY(nmembers, found, "The number of members remaining in the type did not match our count");
if (nmembers != (hsize_t)found)
goto error;
/*****************************************
* Clear the type with force set to true *
*****************************************/
/* Clear the type. Since force is true, all IDs will be cleared. */
ret = H5Iclear_type(obj_type, true);
CHECK(ret, FAIL, "H5Iclear_type");
if (ret == FAIL)
goto error;
/* Verify that the object struct fields are sane */
for (j = 0; j < obj_list.count; j++) {
/* Every object should have been freed exactly once */
VERIFY(objects[j].nfrees, 1, "object freed more than once");
if (objects[j].nfrees != 1)
goto error;
/* No object should still be marked as "freeing" */
VERIFY(objects[j].freeing, false, "object marked as freeing");
if (objects[j].freeing != false)
goto error;
}
/* Verify the number of objects is 0 */
VERIFY(obj_list.remaining, 0, "objects remaining was not zero");
if (obj_list.remaining != 0)
goto error;
/* Make sure the HDF5 library confirms zero members in the type */
ret = H5Inmembers(obj_type, &nmembers);
CHECK(ret, FAIL, "H5Inmembers");
if (ret == FAIL)
goto error;
VERIFY(nmembers, 0, "The number of members remaining in the type was not zero");
if (nmembers != 0)
goto error;
}
/* Destroy the type */
ret = H5Idestroy_type(obj_type);
CHECK(ret, FAIL, "H5Idestroy_type");
if (ret == FAIL)
goto error;
/* Free the object array */
free(obj_list.objects);
return 0;
error:
/* Cleanup. For simplicity, just destroy the types and ignore errors. */
H5E_BEGIN_TRY
{
H5Idestroy_type(obj_type);
}
H5E_END_TRY
free(obj_list.objects);
return -1;
} /* end test_remove_clear_type() */
/* Typedef for future objects */
typedef struct {
H5I_type_t obj_type; /* ID type for actual object */
} future_obj_t;
/* Global (static) future ID object type */
static H5I_type_t future_obj_type_g = H5I_BADID;
/* Callback to free the actual object for future object test */
static herr_t
free_actual_object(void *_p, void H5_ATTR_UNUSED **_ctx)
{
int *p = (int *)_p;
if (7 != *p)
return FAIL;
free(p);
return SUCCEED;
}
/* Callback to realize a future object */
static herr_t
realize_future_cb(void *_future_obj, hid_t *actual_id)
{
future_obj_t *future_obj = (future_obj_t *)_future_obj; /* Future object */
int *actual_obj; /* Pointer to the actual object */
/* Check for bad future object */
if (NULL == future_obj)
return FAIL;
/* Determine type of object to realize */
if (H5I_DATASPACE == future_obj->obj_type) {
hsize_t dims = 13;
if ((*actual_id = H5Screate_simple(1, &dims, NULL)) < 0)
return FAIL;
}
else if (H5I_DATATYPE == future_obj->obj_type) {
if ((*actual_id = H5Tcopy(H5T_NATIVE_INT)) < 0)
return FAIL;
}
else if (H5I_GENPROP_LST == future_obj->obj_type) {
if ((*actual_id = H5Pcreate(H5P_DATASET_XFER)) < 0)
return FAIL;
}
else {
/* Create a new object (the 'actual object') of the correct type */
if (NULL == (actual_obj = malloc(sizeof(int))))
return FAIL;
*actual_obj = 7;
/* Register actual object of the user-defined type */
*actual_id = H5Iregister(future_obj->obj_type, actual_obj);
CHECK(*actual_id, FAIL, "H5Iregister");
if (*actual_id == FAIL)
return FAIL;
}
return SUCCEED;
}
/* Callback to discard a future object */
static herr_t
discard_future_cb(void *future_obj)
{
if (NULL == future_obj)
return FAIL;
free(future_obj);
return SUCCEED;
}
/* Callback to realize a future object when future objects are NULL*/
static herr_t
realize_future_generate_cb(void *_future_obj, hid_t *actual_id)
{
future_obj_t *future_obj = (future_obj_t *)_future_obj; /* Future object */
int *actual_obj; /* Pointer to the actual object */
if (NULL != future_obj)
return FAIL;
/* Create a new object (the 'actual object') of the correct type */
if (NULL == (actual_obj = malloc(sizeof(int))))
return FAIL;
*actual_obj = 7;
/* Register actual object without using future object info */
*actual_id = H5Iregister(future_obj_type_g, actual_obj);
CHECK(*actual_id, FAIL, "H5Iregister");
if (*actual_id == FAIL)
return FAIL;
return SUCCEED;
}
/* Callback to discard a future object when future objects are NULL */
static herr_t
discard_future_generate_cb(void *future_obj)
{
if (NULL != future_obj)
return FAIL;
return SUCCEED;
}
/* Test function */
static int
test_future_ids(void)
{
H5I_type_t obj_type; /* New user-defined ID type */
hid_t future_id; /* ID for future object */
int fake_future_obj; /* "Fake" future object for tests */
future_obj_t *future_obj; /* Future object */
int *actual_obj; /* Actual object */
int *actual_obj2; /* Another actual object */
H5I_type_t id_type; /* Type of ID */
H5T_class_t type_class; /* Datatype class */
herr_t ret; /* Return value */
/* Register a user-defined type with our custom ID-deleting callback */
obj_type = H5Iregister_type((size_t)15, 0, free_actual_object);
CHECK(obj_type, H5I_BADID, "H5Iregister_type");
if (H5I_BADID == obj_type)
goto error;
/* Test basic error conditions */
fake_future_obj = 0;
H5E_BEGIN_TRY
{
future_id = H5Iregister_future(obj_type, &fake_future_obj, NULL, NULL);
}
H5E_END_TRY
VERIFY(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID != future_id)
goto error;
H5E_BEGIN_TRY
{
future_id = H5Iregister_future(obj_type, &fake_future_obj, realize_future_cb, NULL);
}
H5E_END_TRY
VERIFY(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID != future_id)
goto error;
H5E_BEGIN_TRY
{
future_id = H5Iregister_future(obj_type, &fake_future_obj, NULL, discard_future_cb);
}
H5E_END_TRY
VERIFY(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID != future_id)
goto error;
H5E_BEGIN_TRY
{
future_id = H5Iregister_future(H5I_BADID, &fake_future_obj, realize_future_cb, discard_future_cb);
}
H5E_END_TRY
VERIFY(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID != future_id)
goto error;
/* Test base use-case: create a future object and destroy type without
* realizing the future object.
*/
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = obj_type;
future_id = H5Iregister_future(obj_type, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Destroy the type */
ret = H5Idestroy_type(obj_type);
CHECK(ret, FAIL, "H5Idestroy_type");
if (FAIL == ret)
goto error;
/* Re-register a user-defined type with our custom ID-deleting callback */
obj_type = H5Iregister_type((size_t)15, 0, free_actual_object);
CHECK(obj_type, H5I_BADID, "H5Iregister_type");
if (H5I_BADID == obj_type)
goto error;
/* Test base use-case: create a future object and realize the actual object. */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = obj_type;
future_id = H5Iregister_future(obj_type, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
actual_obj = H5Iobject_verify(future_id, obj_type);
CHECK_PTR(actual_obj, "H5Iobject_verify");
if (NULL == actual_obj)
goto error;
VERIFY(*actual_obj, 7, "H5Iobject_verify");
if (7 != *actual_obj)
goto error;
/* Retrieve the object again and verify that it's the same actual object */
actual_obj2 = H5Iobject_verify(future_id, obj_type);
CHECK_PTR(actual_obj2, "H5Iobject_verify");
if (NULL == actual_obj2)
goto error;
VERIFY(*actual_obj2, 7, "H5Iobject_verify");
if (7 != *actual_obj2)
goto error;
CHECK_PTR_EQ(actual_obj, actual_obj2, "H5Iobject_verify");
if (actual_obj != actual_obj2)
goto error;
/* Destroy the type */
ret = H5Idestroy_type(obj_type);
CHECK(ret, FAIL, "H5Idestroy_type");
if (FAIL == ret)
goto error;
/* Re-register a user-defined type with our custom ID-deleting callback */
obj_type = H5Iregister_type((size_t)15, 0, free_actual_object);
CHECK(obj_type, H5I_BADID, "H5Iregister_type");
if (H5I_BADID == obj_type)
goto error;
/* Set the global future object type */
future_obj_type_g = obj_type;
/* Test "actual object generator" use-case: create a future object with
* NULL object pointer, to create new object of predefined type when
* future object is realized.
*/
future_id = H5Iregister_future(obj_type, NULL, realize_future_generate_cb, discard_future_generate_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Realize the actual object, with will be dynamically allocated within
* the 'realize' callback.
*/
actual_obj = H5Iobject_verify(future_id, obj_type);
CHECK_PTR(actual_obj, "H5Iobject_verify");
if (NULL == actual_obj)
goto error;
VERIFY(*actual_obj, 7, "H5Iobject_verify");
if (7 != *actual_obj)
goto error;
/* Reset the global future object type */
future_obj_type_g = H5I_BADID;
/* Retrieve the object again and verify that it's the same actual object */
/* (Will fail if global future object type used) */
actual_obj2 = H5Iobject_verify(future_id, obj_type);
CHECK_PTR(actual_obj2, "H5Iobject_verify");
if (NULL == actual_obj2)
goto error;
VERIFY(*actual_obj2, 7, "H5Iobject_verify");
if (7 != *actual_obj2)
goto error;
CHECK_PTR_EQ(actual_obj, actual_obj2, "H5Iobject_verify");
if (actual_obj != actual_obj2)
goto error;
/* Destroy the type */
ret = H5Idestroy_type(obj_type);
CHECK(ret, FAIL, "H5Idestroy_type");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
/* (DATASPACE) */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATASPACE;
future_id = H5Iregister_future(H5I_DATASPACE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* (Can't verify the type of the future ID, because the library's current
* implementation realizes the object during sanity checks on the ID)
*/
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATASPACE;
future_id = H5Iregister_future(H5I_DATASPACE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Verify that the application believes the future ID is a dataspace */
/* (Currently realizes the object "implicitly" during a sanity check) */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_DATASPACE != id_type)
goto error;
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATASPACE;
future_id = H5Iregister_future(H5I_DATASPACE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Realize future dataspace by requesting its rank */
ret = H5Sget_simple_extent_ndims(future_id);
CHECK(ret, FAIL, "H5Sget_simple_extent_ndims");
if (FAIL == ret)
goto error;
if (1 != ret)
goto error;
/* Verify that the application believes the ID is still a dataspace */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_DATASPACE != id_type)
goto error;
/* Close future object for pre-defined type after realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
/* (DATATYPE) */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATATYPE;
future_id = H5Iregister_future(H5I_DATATYPE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* (Can't verify the type of the future ID, because the library's current
* implementation realizes the object during sanity checks on the ID)
*/
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATATYPE;
future_id = H5Iregister_future(H5I_DATATYPE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Verify that the application believes the future ID is a datatype */
/* (Currently realizes the object "implicitly" during a sanity check) */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_DATATYPE != id_type)
goto error;
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATATYPE;
future_id = H5Iregister_future(H5I_DATATYPE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Realize future datatype by requesting its class */
type_class = H5Tget_class(future_id);
CHECK(ret, FAIL, "H5Tget_class");
if (FAIL == ret)
goto error;
if (H5T_INTEGER != type_class)
goto error;
/* Verify that the application believes the ID is still a datatype */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_DATATYPE != id_type)
goto error;
/* Close future object for pre-defined type after realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
/* (PROPERTY LIST) */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_GENPROP_LST;
future_id = H5Iregister_future(H5I_GENPROP_LST, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* (Can't verify the type of the future ID, because the library's current
* implementation realizes the object during sanity checks on the ID)
*/
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_GENPROP_LST;
future_id = H5Iregister_future(H5I_GENPROP_LST, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Verify that the application believes the future ID is a property list */
/* (Currently realizes the object "implicitly" during a sanity check) */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_GENPROP_LST != id_type)
goto error;
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_GENPROP_LST;
future_id = H5Iregister_future(H5I_GENPROP_LST, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Realize future property list by verifying its class */
ret = H5Pisa_class(future_id, H5P_DATASET_XFER);
CHECK(ret, FAIL, "H5Pisa_class");
if (FAIL == ret)
goto error;
if (true != ret)
goto error;
/* Verify that the application believes the ID is still a property list */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_GENPROP_LST != id_type)
goto error;
/* Close future object for pre-defined type after realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
return 0;
error:
/* Cleanup. For simplicity, just destroy the types and ignore errors. */
H5E_BEGIN_TRY
{
H5Idestroy_type(obj_type);
}
H5E_END_TRY
return -1;
} /* end test_future_ids() */
/*-------------------------------------------------------------------------
* Function: test_appropriate_ids
*
* Purpose: Tests several API functions on detecting inappropriate ID.
*
* Return: Success: 0
* Failure: number of errors
*
*-------------------------------------------------------------------------
*/
static int
test_appropriate_ids(void)
{
hid_t file_id = H5I_INVALID_HID;
hid_t fapl_id = H5I_INVALID_HID;
hid_t fcpl_id = H5I_INVALID_HID;
hid_t plist = H5I_INVALID_HID;
hid_t dset_id = H5I_INVALID_HID;
hid_t space_id = H5I_INVALID_HID;
hsize_t dims = 2;
hssize_t free_space;
herr_t ret = SUCCEED; /* Generic return value */
/* Create file create property list */
fcpl_id = H5Pcreate(H5P_FILE_CREATE);
CHECK(fcpl_id, H5I_INVALID_HID, "H5Pcreate");
file_id = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, fcpl_id, H5P_DEFAULT);
CHECK(file_id, H5I_INVALID_HID, "H5Fcreate");
/* Create a dataset in the file */
space_id = H5Screate_simple(1, &dims, NULL);
CHECK(space_id, H5I_INVALID_HID, "H5Screate_simple");
dset_id = H5Dcreate2(file_id, DSET_NAME, H5T_NATIVE_INT, space_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
CHECK(dset_id, H5I_INVALID_HID, "H5Dcreate2");
/* Close IDs */
ret = H5Pclose(fcpl_id);
CHECK(ret, FAIL, "H5Pclose");
ret = H5Sclose(space_id);
CHECK(ret, FAIL, "H5Sclose");
ret = H5Dclose(dset_id);
CHECK(ret, FAIL, "H5Dclose");
ret = H5Fclose(file_id);
CHECK(ret, FAIL, "H5Fclose");
file_id = H5Fopen(FILE_NAME, H5F_ACC_RDONLY, H5P_DEFAULT);
CHECK(file_id, H5I_INVALID_HID, "H5Fopen");
/* Get the file create property */
fcpl_id = H5Fget_create_plist(file_id);
CHECK(fcpl_id, H5I_INVALID_HID, "H5Fget_create_plist");
/* Get the file access property */
fapl_id = H5Fget_access_plist(file_id);
CHECK(fapl_id, H5I_INVALID_HID, "H5Fget_access_plist");
dset_id = H5Dopen2(file_id, DSET_NAME, H5P_DEFAULT);
CHECK(dset_id, H5I_INVALID_HID, "H5Dopen2");
/*-------------------------------------------------------------
* Try to call functions passing in a wrong ID
*-----------------------------------------------------------*/
H5E_BEGIN_TRY
{
plist = H5Fget_create_plist(dset_id); /* dset_id is not file ID */
}
H5E_END_TRY
VERIFY(plist, H5I_INVALID_HID, "H5Fget_create_plist");
H5E_BEGIN_TRY
{
plist = H5Fget_access_plist(fapl_id); /* fapl_id is not file ID */
}
H5E_END_TRY
VERIFY(plist, H5I_INVALID_HID, "H5Fget_access_plist");
H5E_BEGIN_TRY
{
unsigned intent; /* File access flags */
ret = H5Fget_intent(dset_id, &intent); /* dset_id is not file ID */
}
H5E_END_TRY
VERIFY(ret, FAIL, "H5Fget_intent");
H5E_BEGIN_TRY
{
unsigned long fileno = 0;
ret = H5Fget_fileno(dset_id, &fileno); /* dset_id is not file ID */
}
H5E_END_TRY
VERIFY(ret, FAIL, "H5Fget_fileno");
H5E_BEGIN_TRY
{
free_space = H5Fget_freespace(dset_id); /* dset_id is not file ID */
}
H5E_END_TRY
VERIFY(free_space, FAIL, "H5Fget_freespace");
H5E_BEGIN_TRY
{
void *os_file_handle = NULL; /* OS file handle */
ret = H5Fget_vfd_handle(fapl_id, H5P_DEFAULT, &os_file_handle); /* fapl_id is not file ID */
}
H5E_END_TRY
VERIFY(ret, FAIL, "H5Fget_vfd_handle");
/* Close IDs */
ret = H5Pclose(fapl_id);
CHECK(ret, FAIL, "H5Pclose");
ret = H5Pclose(fcpl_id);
CHECK(ret, FAIL, "H5Pclose");
ret = H5Dclose(dset_id);
CHECK(ret, FAIL, "H5Dclose");
ret = H5Fclose(file_id);
CHECK(ret, FAIL, "H5Fclose");
return 0;
}
void
test_ids(const void H5_ATTR_UNUSED *params)
{
/* Set the random # seed */
srand((unsigned)time(NULL));
if (basic_id_test() < 0)
TestErrPrintf("Basic ID test failed\n");
if (id_predefined_test() < 0)
TestErrPrintf("Predefined ID type test failed\n");
if (test_is_valid() < 0)
TestErrPrintf("H5Iis_valid test failed\n");
if (test_get_type() < 0)
TestErrPrintf("H5Iget_type test failed\n");
if (test_id_type_list() < 0)
TestErrPrintf("ID type list test failed\n");
if (test_remove_clear_type() < 0)
TestErrPrintf("ID remove during H5Iclear_type test failed\n");
if (test_future_ids() < 0)
TestErrPrintf("Future ID test failed\n");
if (test_appropriate_ids() < 0)
TestErrPrintf("Detection of inappropriate ID test failed\n");
}