/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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 LICENSE 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.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * This file is modified based on earray.c.
 */
#include "h5test.h"

/*
 * This file needs to access private datatypes from the H5FA package.
 * This file also needs to access the fixed array testing code.
 */
#define H5FA_FRIEND /*suppress error about including H5FApkg	  */
#define H5FA_TESTING
#include "H5FApkg.h" /* Fixed arrays			*/

/* Other private headers that this test requires */
#include "H5CXprivate.h" /* API Contexts                             */
#include "H5Iprivate.h"  /* IDs                                      */
#include "H5VMprivate.h" /* Vectors and arrays                       */
#include "H5VLprivate.h" /* Virtual Object Layer                     */

/* Local macros */

/* Max. testfile name length */
#define FARRAY_FILENAME_LEN 1024

/* Fixed array creation values */
#define ELMT_SIZE                   sizeof(uint64_t)
#define MAX_DBLOCK_PAGE_NELMTS_BITS 10 /* 2^10 = 1024 elements per data block page */

/* Testing # of elements in the fixed array */
#define TEST_NELMTS 20000

/* Convenience macros for computing earray state */
#define FA_HDR_SIZE   28 /* hard-coded */
#define DBLOCK_PREFIX 18 /* hard-coded */

/* 4 giga-elements: max chunk size */
#define MAX_NELMTS ((unsigned long long)4 * 1024 * 1024 * 1024) /* 4 giga-elements */

/* Iterator parameter values */
#define FA_CYC_COUNT 4

/* Local typedefs */

/* Types of tests to perform */
typedef enum {
    FARRAY_TEST_NORMAL, /* "Normal" test, with no testing parameters set */
    FARRAY_TEST_REOPEN, /* Set the reopen_array flag */
    FARRAY_TEST_NTESTS  /* The number of test types, must be last */
} farray_test_type_t;

/* Types of iteration to perform */
typedef enum {
    FARRAY_ITER_FW,    /* "Forward" iteration */
    FARRAY_ITER_RV,    /* "Reverse" iteration */
    FARRAY_ITER_RND,   /* "Random" iteration */
    FARRAY_ITER_CYC,   /* "Cyclic" iteration */
    FARRAY_ITER_NITERS /* The number of iteration types, must be last */
} farray_iter_type_t;

/* Fixed array state information */
typedef struct farray_state_t {
    hsize_t hdr_size;  /* Size of header */
    hsize_t dblk_size; /* Size of data block */
    hsize_t nelmts;    /* # of elements */
} farray_state_t;

/* Forward decl. */
typedef struct farray_test_param_t farray_test_param_t;

/* Fixed array iterator class */
typedef struct farray_iter_t {
    void *(*init)(const H5FA_create_t *cparam, const farray_test_param_t *tparam,
                  hsize_t cnt);   /* Initialize/allocate iterator private info */
    hssize_t (*next)(void *info); /* Get the next element to test */
    herr_t (*term)(void *info);   /* Shutdown/free iterator private info */
} farray_iter_t;

/* Testing parameters */
struct farray_test_param_t {
    farray_test_type_t   reopen_array; /* Whether to re-open the array during the test */
    hsize_t              nelmts;       /* # of elements to set for the fixed array */
    const farray_iter_t *fiter;        /* Iterator to use for this test */
};

/* Local variables */
static const char *FILENAME[] = {"farray", "farray_tmp", NULL};

/* Filename to use for all tests */
char filename_g[FARRAY_FILENAME_LEN];

/* Empty file size */
h5_stat_size_t empty_size_g;

/*-------------------------------------------------------------------------
 * Function:    init_cparam
 *
 * Purpose:     Initialize array creation parameter structure
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
init_cparam(H5FA_create_t *cparam, farray_test_param_t *tparam)
{
    /* Wipe out background */
    memset(cparam, 0, sizeof(*cparam));

    cparam->cls                       = H5FA_CLS_TEST;
    cparam->raw_elmt_size             = ELMT_SIZE;
    cparam->max_dblk_page_nelmts_bits = MAX_DBLOCK_PAGE_NELMTS_BITS;
    cparam->nelmts                    = tparam->nelmts;

    return SUCCEED;
} /* init_cparam() */

/*-------------------------------------------------------------------------
 * Function:    create_file
 *
 * Purpose:     Create file and retrieve pointer to internal file object
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
create_file(hid_t fapl_id, hid_t *fid, H5F_t **f)
{
    /* Create the file to work on */
    if ((*fid = H5Fcreate(filename_g, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id)) < 0)
        FAIL_STACK_ERROR;

    /* Get a pointer to the internal file object */
    if (NULL == (*f = (H5F_t *)H5VL_object(*fid)))
        FAIL_STACK_ERROR;

    /* Ignore metadata tags in the file's cache */
    if (H5AC_ignore_tags(*f) < 0)
        FAIL_STACK_ERROR;

    /* Success */
    return SUCCEED;

error:
    return FAIL;
} /* create_file() */

/*-------------------------------------------------------------------------
 * Function:    check_stats
 *
 * Purpose:     Verify stats for a fixed array
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
check_stats(const H5FA_t *fa, const farray_state_t *state)
{
    H5FA_stat_t farray_stats; /* Statistics about the array */

    /* Get statistics for fixed array and verify they are correct */
    if (H5FA_get_stats(fa, &farray_stats) < 0)
        FAIL_STACK_ERROR;

    /* Compare information */
    if (farray_stats.hdr_size != state->hdr_size) {
        fprintf(stdout, "farray_stats.hdr_size = %" PRIuHSIZE ", state->hdr_size = %" PRIuHSIZE "\n",
                farray_stats.hdr_size, state->hdr_size);
        TEST_ERROR;
    }

    if (farray_stats.dblk_size != state->dblk_size) {
        fprintf(stdout, "farray_stats.dblk_size = %" PRIuHSIZE ", state->dblk_size = %" PRIuHSIZE "\n",
                farray_stats.dblk_size, state->dblk_size);
        TEST_ERROR;
    }

    if (farray_stats.nelmts != state->nelmts) {
        fprintf(stdout, "farray_stats.nelmts = %" PRIuHSIZE ", state->nelmts = %" PRIuHSIZE "\n",
                farray_stats.nelmts, state->nelmts);
        TEST_ERROR;
    }

    /* Success */
    return SUCCEED;

error:
    return FAIL;
} /* check_stats() */

/*-------------------------------------------------------------------------
 * Function:    set_fa_state
 *
 * Purpose:     Set the state of the fixed array
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
set_fa_state(const H5FA_create_t *cparam, farray_state_t *state)
{
    size_t dblk_page_nelmts; /* # of elements per page */

    /* Sanity check */
    assert(cparam);
    assert(state);

    /* Compute the state of the fixed array */
    state->hdr_size = FA_HDR_SIZE;
    state->nelmts   = cparam->nelmts;

    dblk_page_nelmts = (size_t)1 << cparam->max_dblk_page_nelmts_bits;
    if (state->nelmts > dblk_page_nelmts) {
        size_t  npages              = (size_t)(((state->nelmts + dblk_page_nelmts) - 1) / dblk_page_nelmts);
        size_t  dblk_page_init_size = (npages + 7) / 8;
        hsize_t checksum_size       = npages * 4;

        state->dblk_size =
            DBLOCK_PREFIX + dblk_page_init_size + checksum_size + state->nelmts * cparam->raw_elmt_size;
    }
    else
        state->dblk_size = DBLOCK_PREFIX + state->nelmts * cparam->raw_elmt_size;

    return SUCCEED;
} /* end set_fa_state() */

/*-------------------------------------------------------------------------
 * Function:    reopen_file
 *
 * Purpose:     Perform common "re-open" operations on file & array for testing
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static int
reopen_file(hid_t *fid, H5F_t **f, hid_t fapl_id, H5FA_t **fa, haddr_t fa_addr,
            const farray_test_param_t *tparam)
{
    /* Check for closing & re-opening the array */
    /* (actually will close & re-open the file as well) */
    if (tparam->reopen_array) {

        /* Close array, if given */
        if (fa && *fa) {
            if (H5FA_close(*fa) < 0)
                FAIL_STACK_ERROR;
            *fa = NULL;
        }

        /* Close file */
        if (*fid) {
            if (H5Fclose(*fid) < 0)
                FAIL_STACK_ERROR;
            *fid = H5I_INVALID_HID;
            *f   = NULL;
        }

        /* Re-open the file */
        if ((*fid = H5Fopen(filename_g, H5F_ACC_RDWR, fapl_id)) < 0)
            FAIL_STACK_ERROR;

        /* Get a pointer to the internal file object */
        if (NULL == (*f = (H5F_t *)H5VL_object(*fid)))
            FAIL_STACK_ERROR;

        /* Ignore metadata tags in the file's cache */
        if (H5AC_ignore_tags(*f) < 0)
            FAIL_STACK_ERROR;

        /* Re-open array, if given */
        if (fa)
            if (NULL == (*fa = H5FA_open(*f, fa_addr, NULL)))
                FAIL_STACK_ERROR;
    }

    /* Success */
    return SUCCEED;

error:
    return FAIL;
} /* reopen_file() */

/*-------------------------------------------------------------------------
 * Function:    create_array
 *
 * Purpose:     Create a fixed array and perform initial checks
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
create_array(H5F_t *f, const H5FA_create_t *cparam, H5FA_t **fa, haddr_t *fa_addr)
{
    farray_state_t state; /* State of extensible array */

    /* Create array */
    if (NULL == (*fa = H5FA_create(f, cparam, NULL)))
        FAIL_STACK_ERROR;

    /* Check status of array */
    if (H5FA_get_addr(*fa, fa_addr) < 0)
        FAIL_STACK_ERROR;
    if (!H5_addr_defined(*fa_addr))
        TEST_ERROR;

    /* Check array stats */
    memset(&state, 0, sizeof(state));
    state.hdr_size = FA_HDR_SIZE;
    state.nelmts   = cparam->nelmts;
    if (check_stats(*fa, &state))
        TEST_ERROR;

    /* Success */
    return SUCCEED;

error:
    return FAIL;
} /* create_array() */

/*-------------------------------------------------------------------------
 * Function:    verify_cparam
 *
 * Purpose:     Verify creation parameters are correct
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
verify_cparam(const H5FA_t *fa, const H5FA_create_t *cparam)
{
    H5FA_create_t test_cparam; /* Creation parameters for array */

    /* Retrieve creation parameters */
    memset(&test_cparam, 0, sizeof(H5FA_create_t));
    if (H5FA__get_cparam_test(fa, &test_cparam) < 0)
        FAIL_STACK_ERROR;

    /* Verify creation parameters */
    if (H5FA__cmp_cparam_test(cparam, &test_cparam))
        TEST_ERROR;

    /* Success */
    return SUCCEED;

error:
    return FAIL;
} /* verify_cparam() */

/*-------------------------------------------------------------------------
 * Function:    finish
 *
 * Purpose:     Close array, delete array, close file and verify that file
 *              is empty size
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
static herr_t
finish(hid_t fid, hid_t fapl_id, H5F_t *f, H5FA_t *fa, haddr_t fa_addr)
{
    h5_stat_size_t file_size; /* File size, after deleting array */

    /* Close the fixed array */
    if (H5FA_close(fa) < 0)
        FAIL_STACK_ERROR;

    /* Delete array */
    if (H5FA_delete(f, fa_addr, NULL) < 0)
        FAIL_STACK_ERROR;

    /* Close the file */
    if (H5Fclose(fid) < 0)
        FAIL_STACK_ERROR;

    /* Get the size of the file */
    if ((file_size = h5_get_file_size(filename_g, fapl_id)) < 0)
        TEST_ERROR;

    /* Verify the file is correct size */
    if (file_size != empty_size_g)
        TEST_ERROR;

    /* Success */
    return SUCCEED;

error:
    return FAIL;
} /* finish() */

/*-------------------------------------------------------------------------
 * Function:	test_create
 *
 * Purpose:	Test creating fixed array
 *
 * Return:	Success: 0
 *		Failure: 1
 *
 *-------------------------------------------------------------------------
 */
static unsigned
test_create(hid_t fapl, H5FA_create_t *cparam, farray_test_param_t H5_ATTR_UNUSED *tparam)
{
    hid_t   file    = H5I_INVALID_HID; /* File ID */
    H5F_t  *f       = NULL;            /* Internal file object pointer */
    H5FA_t *fa      = NULL;            /* Fixed array wrapper */
    haddr_t fa_addr = HADDR_UNDEF;     /* Array address in file */

    /* Create file & retrieve pointer to internal file object */
    if (create_file(fapl, &file, &f) < 0)
        TEST_ERROR;

    /*
     * Display testing message
     */
    TESTING("invalid fixed array creation parameters");

#ifndef NDEBUG
    {
        H5FA_create_t test_cparam; /* Creation parameters for array */

        /* Set invalid element size */
        memcpy(&test_cparam, cparam, sizeof(test_cparam));
        test_cparam.raw_elmt_size = 0;
        H5E_BEGIN_TRY
        {
            fa = H5FA_create(f, &test_cparam, NULL);
        }
        H5E_END_TRY
        if (fa) {
            /* Close opened fixed array */
            H5FA_close(fa);
            fa = NULL;

            /* Indicate error */
            TEST_ERROR;
        } /* end if */

        /* Set invalid max. # of elements bits */
        memcpy(&test_cparam, cparam, sizeof(test_cparam));
        test_cparam.max_dblk_page_nelmts_bits = 0;
        H5E_BEGIN_TRY
        {
            fa = H5FA_create(f, &test_cparam, NULL);
        }
        H5E_END_TRY
        if (fa) {
            /* Close opened fixed array */
            H5FA_close(fa);
            fa = NULL;

            /* Indicate error */
            TEST_ERROR;
        } /* end if */

        /* Set invalid max. # of elements */
        memcpy(&test_cparam, cparam, sizeof(test_cparam));
        test_cparam.nelmts = 0;
        H5E_BEGIN_TRY
        {
            fa = H5FA_create(f, &test_cparam, NULL);
        }
        H5E_END_TRY
        if (fa) {
            /* Close opened fixed array */
            H5FA_close(fa);
            fa = NULL;

            /* Indicate error */
            TEST_ERROR;
        } /* end if */

        PASSED();
    }
#else  /* NDEBUG */
    SKIPPED();
    puts("    Not tested when assertions are disabled");
#endif /* NDEBUG */

    /*
     * Display testing message
     */
    TESTING("fixed array creation");

    /* Create array */
    if (create_array(f, cparam, &fa, &fa_addr) < 0)
        TEST_ERROR;

    PASSED();

    /* Verify the creation parameters */
    TESTING("verify array creation parameters");

    /* Verify the creation parameters */
    if (verify_cparam(fa, cparam) < 0)
        TEST_ERROR;

    /* Close array, delete array, close file & verify file is empty */
    if (finish(file, fapl, f, fa, fa_addr) < 0)
        TEST_ERROR;

    /* All tests passed */
    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (fa)
            H5FA_close(fa);
        H5Fclose(file);
    }
    H5E_END_TRY

    return 1;
} /* end test_create() */

/*-------------------------------------------------------------------------
 * Function:	test_reopen
 *
 * Purpose:	Create & reopen a fixed array
 *
 * Return:	Success:	0
 *		Failure:	1
 *
 *-------------------------------------------------------------------------
 */
static unsigned
test_reopen(hid_t fapl, H5FA_create_t *cparam, farray_test_param_t *tparam)
{
    hid_t   file    = H5I_INVALID_HID; /* File ID */
    H5F_t  *f       = NULL;            /* Internal file object pointer */
    H5FA_t *fa      = NULL;            /* Fixed array wrapper */
    haddr_t fa_addr = HADDR_UNDEF;     /* Array address in file */

    /* Create file & retrieve pointer to internal file object */
    if (create_file(fapl, &file, &f) < 0)
        TEST_ERROR;

    /*
     * Display testing message
     */
    TESTING("create, close & reopen fixed array");

    /* Create array */
    if (create_array(f, cparam, &fa, &fa_addr) < 0)
        TEST_ERROR;

    /* Close the fixed array */
    if (H5FA_close(fa) < 0)
        FAIL_STACK_ERROR;

    /* Check for closing & re-opening the file */
    if (reopen_file(&file, &f, fapl, NULL, HADDR_UNDEF, tparam) < 0)
        TEST_ERROR;

    /* Re-open the array */
    if (NULL == (fa = H5FA_open(f, fa_addr, NULL)))
        FAIL_STACK_ERROR;

    /* Verify the creation parameters */
    if (verify_cparam(fa, cparam) < 0)
        TEST_ERROR;

    /* Close array, delete array, close file & verify file is empty */
    if (finish(file, fapl, f, fa, fa_addr) < 0)
        TEST_ERROR;

    /* All tests passed */
    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (fa)
            H5FA_close(fa);
        H5Fclose(file);
    }
    H5E_END_TRY

    return 1;
} /* test_reopen() */

/*-------------------------------------------------------------------------
 * Function:	test_open_twice
 *
 * Purpose:	Open an extensible array twice
 *
 * Return:	Success:	0
 *		Failure:	1
 *
 *-------------------------------------------------------------------------
 */
static unsigned
test_open_twice(hid_t fapl_id, H5FA_create_t *cparam, farray_test_param_t *tparam)
{
    hid_t   fid     = H5I_INVALID_HID; /* File ID                      */
    hid_t   fid2    = H5I_INVALID_HID; /* File ID                      */
    H5F_t  *f       = NULL;            /* Internal file object pointer */
    H5F_t  *f2      = NULL;            /* Internal file object pointer */
    H5FA_t *fa      = NULL;            /* Fixed array wrapper          */
    H5FA_t *fa2     = NULL;            /* Fixed array wrapper          */
    haddr_t fa_addr = HADDR_UNDEF;     /* Array address in file        */

    /* Create file & retrieve pointer to internal file object */
    if (create_file(fapl_id, &fid, &f) < 0)
        TEST_ERROR;

    /* Display testing message */
    TESTING("open fixed array twice");

    /* Create array */
    if (create_array(f, cparam, &fa, &fa_addr) < 0)
        TEST_ERROR;

    /* Open the array again, through the first file handle */
    if (NULL == (fa2 = H5FA_open(f, fa_addr, NULL)))
        FAIL_STACK_ERROR;

    /* Verify the creation parameters */
    if (verify_cparam(fa, cparam) < 0)
        TEST_ERROR;
    if (verify_cparam(fa2, cparam) < 0)
        TEST_ERROR;

    /* Close the second fixed array wrapper */
    if (H5FA_close(fa2) < 0)
        FAIL_STACK_ERROR;
    fa2 = NULL;

    /* Check for closing & re-opening the file */
    if (reopen_file(&fid, &f, fapl_id, &fa, fa_addr, tparam) < 0)
        TEST_ERROR;

    /* Re-open the file */
    if ((fid2 = H5Freopen(fid)) < 0)
        FAIL_STACK_ERROR;

    /* Get a pointer to the internal file object */
    if (NULL == (f2 = (H5F_t *)H5VL_object(fid2)))
        FAIL_STACK_ERROR;

    /* Open the fixed array through the second file handle */
    if (NULL == (fa2 = H5FA_open(f2, fa_addr, NULL)))
        FAIL_STACK_ERROR;

    /* Verify the creation parameters */
    if (verify_cparam(fa, cparam) < 0)
        TEST_ERROR;

    /* Close the first extensible array wrapper */
    if (H5FA_close(fa) < 0)
        FAIL_STACK_ERROR;
    fa = NULL;

    /* Close the first file */
    /* (close before second file, to detect error on internal array header's
     *  shared file information)
     */
    if (H5Fclose(fid) < 0)
        FAIL_STACK_ERROR;

    /* Close array, delete array, close file & verify file is empty */
    if (finish(fid2, fapl_id, f2, fa2, fa_addr) < 0)
        TEST_ERROR;

    /* All tests passed */
    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (fa)
            H5FA_close(fa);
        if (fa2)
            H5FA_close(fa2);
        H5Fclose(fid);
        H5Fclose(fid2);
    }
    H5E_END_TRY

    return 1;
} /* test_open_twice() */

/*-------------------------------------------------------------------------
 * Function:	test_open_twice_diff
 *
 * Purpose:	Open a fixed array twice, through different "top" file
 *              handles, with an intermediate file open that takes the "shared"
 *              file handle from the first fixed array's file pointer.
 *
 * Return:	Success:	0
 *		Failure:	1
 *
 *-------------------------------------------------------------------------
 */
static unsigned
test_open_twice_diff(hid_t fapl_id, H5FA_create_t *cparam, farray_test_param_t *tparam)
{
    char    filename_tmp[FARRAY_FILENAME_LEN]; /* Temporary file name          */
    hid_t   fid     = H5I_INVALID_HID;         /* File ID                      */
    hid_t   fid2    = H5I_INVALID_HID;         /* File ID                      */
    hid_t   fid0    = H5I_INVALID_HID;         /* File ID                      */
    hid_t   fid00   = H5I_INVALID_HID;         /* File ID                      */
    H5F_t  *f       = NULL;                    /* Internal file object pointer */
    H5F_t  *f2      = NULL;                    /* Internal file object pointer */
    H5FA_t *fa      = NULL;                    /* Fixed array wrapper          */
    H5FA_t *fa2     = NULL;                    /* Fixed array wrapper          */
    haddr_t fa_addr = HADDR_UNDEF;             /* Array address in file        */

    /* Display testing message */
    TESTING("open fixed array twice, through different file handles");

    /* Create file & retrieve pointer to internal file object */
    if (create_file(fapl_id, &fid, &f) < 0)
        TEST_ERROR;

    /* Create array */
    if (create_array(f, cparam, &fa, &fa_addr) < 0)
        TEST_ERROR;

    /* Open the array again, through the first file handle */
    if (NULL == (fa2 = H5FA_open(f, fa_addr, NULL)))
        FAIL_STACK_ERROR;

    /* Verify the creation parameters */
    if (verify_cparam(fa, cparam) < 0)
        TEST_ERROR;
    if (verify_cparam(fa2, cparam) < 0)
        TEST_ERROR;

    /* Close the second fixed array wrapper */
    if (H5FA_close(fa2) < 0)
        FAIL_STACK_ERROR;
    fa2 = NULL;

    /* Re-open the file */
    /* (So that there is something holding the file open when the extensible
     *  array is closed)
     */
    if ((fid0 = H5Fopen(filename_g, H5F_ACC_RDWR, fapl_id)) < 0)
        FAIL_STACK_ERROR;

    /* Check for closing & re-opening the file */
    if (reopen_file(&fid, &f, fapl_id, &fa, fa_addr, tparam) < 0)
        TEST_ERROR;

    /* Close the first fixed array wrapper */
    if (H5FA_close(fa) < 0)
        FAIL_STACK_ERROR;
    fa = NULL;

    /* Close the first file */
    /* (close before second file, to detect error on internal array header's
     *  shared file information)
     */
    if (H5Fclose(fid) < 0)
        FAIL_STACK_ERROR;
    fid = H5I_INVALID_HID;

    /* Open a different file */
    /* (This re-allocates the 'top' file pointer and assigns it a different
     *  'shared' file pointer, making the file pointer in the fixed array's
     *  header stale)
     */
    h5_fixname(FILENAME[1], fapl_id, filename_tmp, sizeof(filename_tmp));
    if ((fid00 = H5Fcreate(filename_tmp, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_id)) < 0)
        FAIL_STACK_ERROR;

    /* Re-open the file with the fixed array */
    if ((fid2 = H5Fopen(filename_g, H5F_ACC_RDWR, fapl_id)) < 0)
        FAIL_STACK_ERROR;

    /* Get a pointer to the internal file object */
    if (NULL == (f2 = (H5F_t *)H5VL_object(fid2)))
        FAIL_STACK_ERROR;

    /* Open the fixed array through the second file handle */
    if (NULL == (fa2 = H5FA_open(f2, fa_addr, NULL)))
        FAIL_STACK_ERROR;

    /* Verify the creation parameters */
    if (verify_cparam(fa2, cparam) < 0)
        TEST_ERROR;

    /* Close the extra file handles */
    if (H5Fclose(fid0) < 0)
        FAIL_STACK_ERROR;
    if (H5Fclose(fid00) < 0)
        FAIL_STACK_ERROR;

    /* Close array, delete array, close file & verify file is empty */
    if (finish(fid2, fapl_id, f2, fa2, fa_addr) < 0)
        TEST_ERROR;

    /* All tests passed */
    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (fa)
            H5FA_close(fa);
        if (fa2)
            H5FA_close(fa2);
        H5Fclose(fid);
        H5Fclose(fid2);
        H5Fclose(fid0);
        H5Fclose(fid00);
    }
    H5E_END_TRY

    return 1;
} /* test_open_twice_diff() */

/*-------------------------------------------------------------------------
 * Function:	test_delete_open
 *
 * Purpose:	Delete opened fixed array (& open deleted array)
 *
 * Return:	Success:	0
 *		Failure:	1
 *
 *-------------------------------------------------------------------------
 */
static unsigned
test_delete_open(hid_t fapl, H5FA_create_t *cparam, farray_test_param_t *tparam)
{
    hid_t          file    = H5I_INVALID_HID; /* File ID */
    H5F_t         *f       = NULL;            /* Internal file object pointer */
    H5FA_t        *fa      = NULL;            /* Fixed array wrapper */
    H5FA_t        *fa2     = NULL;            /* Fixed array wrapper */
    haddr_t        fa_addr = HADDR_UNDEF;     /* Array address in file */
    h5_stat_size_t file_size;                 /* File size, after deleting array */

    /* Create file & retrieve pointer to internal file object */
    if (create_file(fapl, &file, &f) < 0)
        TEST_ERROR;

    /*
     * Display testing message
     */
    TESTING("deleting open fixed array");

    /* Create array */
    if (create_array(f, cparam, &fa, &fa_addr) < 0)
        TEST_ERROR;

    /* Open the array again */
    if (NULL == (fa2 = H5FA_open(f, fa_addr, NULL)))
        FAIL_STACK_ERROR;

    /* Request that the array be deleted */
    if (H5FA_delete(f, fa_addr, NULL) < 0)
        FAIL_STACK_ERROR;

    /* Verify the creation parameters */
    if (verify_cparam(fa, cparam) < 0)
        TEST_ERROR;
    if (verify_cparam(fa2, cparam) < 0)
        TEST_ERROR;

    /* Close the second fixed array wrapper */
    if (H5FA_close(fa2) < 0)
        FAIL_STACK_ERROR;
    fa2 = NULL;

    /* Try re-opening the array again (should fail, as array will be deleted) */
    H5E_BEGIN_TRY
    {
        fa2 = H5FA_open(f, fa_addr, NULL);
    }
    H5E_END_TRY
    if (fa2) {
        /* Close opened array */
        H5FA_close(fa2);

        /* Indicate error */
        TEST_ERROR;
    } /* end if */

    /* Close the first fixed array wrapper */
    if (H5FA_close(fa) < 0)
        FAIL_STACK_ERROR;
    fa = NULL;

    /* Check for closing & re-opening the file */
    if (reopen_file(&file, &f, fapl, NULL, HADDR_UNDEF, tparam) < 0)
        TEST_ERROR;

    /* Try re-opening the array again (should fail, as array is now deleted) */
    H5E_BEGIN_TRY
    {
        fa = H5FA_open(f, fa_addr, NULL);
    }
    H5E_END_TRY
    if (fa) {
        /* Close opened array */
        H5FA_close(fa);

        /* Indicate error */
        TEST_ERROR;
    } /* end if */

    /* Close the file */
    if (H5Fclose(file) < 0)
        FAIL_STACK_ERROR;

    /* Get the size of the file */
    if ((file_size = h5_get_file_size(filename_g, fapl)) < 0)
        TEST_ERROR;

    /* Verify the file is correct size */
    if (file_size != empty_size_g)
        TEST_ERROR;

    /* All tests passed */
    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (fa)
            H5FA_close(fa);
        if (fa2)
            H5FA_close(fa2);
        H5Fclose(file);
    }
    H5E_END_TRY

    return 1;
} /* test_delete_open() */

/* Fixed array iterator info for forward iteration */
typedef struct fiter_fw_t {
    hsize_t idx; /* Index of next array location */
} fiter_fw_t;

/*-------------------------------------------------------------------------
 * Function:	fiter_fw_init
 *
 * Purpose:	Initialize element iterator (forward iteration)
 *
 * Return:	Success:	Pointer to iteration status object
 *		Failure:	NULL
 *
 *-------------------------------------------------------------------------
 */
static void *
fiter_fw_init(const H5FA_create_t H5_ATTR_UNUSED *cparam, const farray_test_param_t H5_ATTR_UNUSED *tparam,
              hsize_t H5_ATTR_UNUSED cnt)
{
    fiter_fw_t *fiter; /* Forward element iteration object */

    /* Allocate space for the element iteration object */
    fiter = (fiter_fw_t *)malloc(sizeof(fiter_fw_t));
    assert(fiter);

    /* Initialize the element iteration object */
    fiter->idx = 0;

    /* Return iteration object */
    return (fiter);
} /* end fiter_fw_init() */

/*-------------------------------------------------------------------------
 * Function:	fiter_fw_next
 *
 * Purpose:	Get next element index (forward iteration)
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 *-------------------------------------------------------------------------
 */
static hssize_t
fiter_fw_next(void *_fiter)
{
    fiter_fw_t *fiter = (fiter_fw_t *)_fiter;
    hssize_t    ret_val;

    /* Sanity check */
    assert(fiter);

    /* Get the next array index to test */
    ret_val = (hssize_t)fiter->idx++;

    return (ret_val);
} /* end fiter_fw_next() */

/*-------------------------------------------------------------------------
 * Function:	fiter_term
 *
 * Purpose:	Shut down element iterator (simple iterators)
 *
 * Return:	Success:	0
 *		Failure:	-1
 *
 *-------------------------------------------------------------------------
 */
static int
fiter_term(void *fiter)
{
    /* Sanity check */
    assert(fiter);

    /* Free iteration object */
    free(fiter);

    return (0);
} /* end fiter_term() */

/* Fixed array iterator class for forward iteration */
static const farray_iter_t fa_iter_fw = {
    fiter_fw_init, /* Iterator init */
    fiter_fw_next, /* Next array index */
    fiter_term     /* Iterator term */
};

/* Fixed array iterator info for reverse iteration */
typedef struct fiter_rv_t {
    hsize_t idx; /* Index of next array location */
} fiter_rv_t;

/*-------------------------------------------------------------------------
 * Function:	fiter_rv_init
 *
 * Purpose:	Initialize element iterator (reverse iteration)
 *
 * Return:	Success:	Pointer to iteration status object
 *		Failure:	NULL
 *
 *-------------------------------------------------------------------------
 */
static void *
fiter_rv_init(const H5FA_create_t *cparam, const farray_test_param_t H5_ATTR_UNUSED *tparam,
              hsize_t H5_ATTR_UNUSED cnt)
{
    fiter_rv_t *fiter; /* Reverse element iteration object */

    /* Allocate space for the element iteration object */
    fiter = (fiter_rv_t *)malloc(sizeof(fiter_rv_t));
    assert(fiter);

    /* Initialize reverse iteration info */
    fiter->idx = cparam->nelmts - 1;

    /* Return iteration object */
    return (fiter);
} /* end fiter_rv_init() */

/*-------------------------------------------------------------------------
 * Function:	fiter_rv_next
 *
 * Purpose:	Get next element index (reverse iteration)
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 *-------------------------------------------------------------------------
 */
static hssize_t
fiter_rv_next(void *_fiter)
{
    fiter_rv_t *fiter = (fiter_rv_t *)_fiter;
    hssize_t    ret_val;

    /* Sanity check */
    assert(fiter);

    /* Get the next array index to test */
    ret_val = (hssize_t)fiter->idx--;

    return (ret_val);
} /* end fiter_rv_next() */

/* Fixed array iterator class for reverse iteration */
static const farray_iter_t fa_iter_rv = {
    fiter_rv_init, /* Iterator init */
    fiter_rv_next, /* Next array index */
    fiter_term     /* Iterator term */
};

/* Fixed array iterator info for random iteration */
typedef struct fiter_rnd_t {
    hsize_t  pos; /* Position in shuffled array */
    hsize_t *idx; /* Array of shuffled indices */
} fiter_rnd_t;

/*-------------------------------------------------------------------------
 * Function:	fiter_rnd_init
 *
 * Purpose:	Initialize element iterator (random iteration)
 *
 * Return:	Success:	Pointer to iteration status object
 *		Failure:	NULL
 *
 *-------------------------------------------------------------------------
 */
static void *
fiter_rnd_init(const H5FA_create_t H5_ATTR_UNUSED *cparam, const farray_test_param_t H5_ATTR_UNUSED *tparam,
               hsize_t cnt)
{
    fiter_rnd_t *fiter; /* Random element iteration object */
    size_t       u;     /* Local index variable */

    /* Allocate space for the element iteration object */
    fiter = (fiter_rnd_t *)malloc(sizeof(fiter_rnd_t));
    assert(fiter);

    /* Allocate space for the array of shuffled indices */
    fiter->idx = (hsize_t *)malloc(sizeof(hsize_t) * (size_t)cnt);
    assert(fiter->idx);

    /* Initialize reverse iteration info */
    fiter->pos = 0;
    for (u = 0; u < (size_t)cnt; u++)
        fiter->idx[u] = (hsize_t)u;

    /* Randomly shuffle array indices */
    if (cnt > 1) {
        for (u = 0; u < (size_t)cnt; u++) {
            size_t  swap_idx; /* Location to swap with when shuffling */
            hsize_t temp_idx; /* Temporary index */

            swap_idx             = ((size_t)rand() % ((size_t)cnt - u)) + u;
            temp_idx             = fiter->idx[u];
            fiter->idx[u]        = fiter->idx[swap_idx];
            fiter->idx[swap_idx] = temp_idx;
        } /* end for */
    }     /* end if */

    /* Return iteration object */
    return (fiter);
} /* end fiter_rnd_init() */

/*-------------------------------------------------------------------------
 * Function:	fiter_rnd_next
 *
 * Purpose:	Get next element index (random iteration)
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 *-------------------------------------------------------------------------
 */
static hssize_t
fiter_rnd_next(void *_fiter)
{
    fiter_rnd_t *fiter = (fiter_rnd_t *)_fiter;
    hssize_t     ret_val;

    /* Sanity check */
    assert(fiter);

    /* Get the next array index to test */
    ret_val = (hssize_t)fiter->idx[fiter->pos];
    fiter->pos++;

    return (ret_val);
} /* end fiter_rnd_next() */

/*-------------------------------------------------------------------------
 * Function:	fiter_rnd_term
 *
 * Purpose:	Shut down element iterator (random iteration)
 *
 * Return:	Success:	0
 *		Failure:	-1
 *
 *-------------------------------------------------------------------------
 */
static int
fiter_rnd_term(void *_fiter)
{
    fiter_rnd_t *fiter = (fiter_rnd_t *)_fiter;

    /* Sanity check */
    assert(fiter);
    assert(fiter->idx);

    /* Free shuffled index array */
    free(fiter->idx);

    /* Free iteration object */
    free(fiter);

    return (0);
} /* end fiter_rnd_term() */

/* Fixed array iterator class for random iteration */
static const farray_iter_t fa_iter_rnd = {
    fiter_rnd_init, /* Iterator init */
    fiter_rnd_next, /* Next array index */
    fiter_rnd_term  /* Iterator term */
};

/* Fixed array iterator info for cyclic iteration */
typedef struct fiter_cyc_t {
    hsize_t pos; /* Position in shuffled array */
    hsize_t cnt; /* # of elements to store */
    hsize_t cyc; /* Cycle of elements to choose from */
} fiter_cyc_t;

/*-------------------------------------------------------------------------
 * Function:	fiter_cyc_init
 *
 * Purpose:	Initialize element iterator (cyclic iteration)
 *
 * Return:	Success:	Pointer to iteration status object
 *		Failure:	NULL
 *
 *-------------------------------------------------------------------------
 */
static void *
fiter_cyc_init(const H5FA_create_t H5_ATTR_UNUSED *cparam, const farray_test_param_t H5_ATTR_UNUSED *tparam,
               hsize_t cnt)
{
    fiter_cyc_t *fiter; /* Cyclic element iteration object */

    /* Allocate space for the element iteration object */
    fiter = (fiter_cyc_t *)malloc(sizeof(fiter_cyc_t));
    assert(fiter);

    /* Initialize reverse iteration info */
    fiter->pos = 0;
    fiter->cnt = cnt;
    fiter->cyc = 0;

    /* Return iteration object */
    return (fiter);
} /* end fiter_cyc_init() */

/*-------------------------------------------------------------------------
 * Function:	fiter_cyc_next
 *
 * Purpose:	Get next element index (cyclic iteration)
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 *-------------------------------------------------------------------------
 */
static hssize_t
fiter_cyc_next(void *_fiter)
{
    fiter_cyc_t *fiter = (fiter_cyc_t *)_fiter;
    hssize_t     ret_val;

    /* Sanity check */
    assert(fiter);

    /* Get the next array index to test */
    ret_val = (hssize_t)fiter->pos;
    fiter->pos += FA_CYC_COUNT;
    if (fiter->pos >= fiter->cnt)
        fiter->pos = ++fiter->cyc;

    return (ret_val);
} /* end fiter_cyc_next() */

/* Fixed array iterator class for cyclic iteration */
static const farray_iter_t fa_iter_cyc = {
    fiter_cyc_init, /* Iterator init */
    fiter_cyc_next, /* Next array index */
    fiter_term      /* Iterator term */
};

/*-------------------------------------------------------------------------
 * Function:	check_elmt
 *
 * Purpose:	Check whether _relmt is the same as in _welmt
 *
 * Return:	Success:	0
 *		Failure:	1
 *
 *-------------------------------------------------------------------------
 */
static int
check_elmt(void *_relmt, void *_welmt)
{
    uint64_t *relmt = (uint64_t *)_relmt;
    uint64_t *welmt = (uint64_t *)_welmt;

    if (welmt == NULL) { /* check for fill value */
        if (*relmt != H5FA_TEST_FILL)
            TEST_ERROR;
    } /* end if */
    else {
        if (*relmt != *welmt)
            TEST_ERROR;
    } /* end else */

    return (0);

error:
    return (-1);
} /* end check_elmt() */

/*-------------------------------------------------------------------------
 * Function:	test_set_elmts
 *
 * Purpose:	Set all elements from 0 to ('nelmts' - 1) in fixed array
 *		("nelmts" is the # of elements to be set in the fixed array)
 *
 * Return:	Success:	0
 *		Failure:	1
 *
 *-------------------------------------------------------------------------
 */
static unsigned
test_set_elmts(hid_t fapl, H5FA_create_t *cparam, farray_test_param_t *tparam, hsize_t nelmts,
               const char *test_str)
{
    hid_t          file = H5I_INVALID_HID; /* File ID */
    H5F_t         *f    = NULL;            /* Internal file object pointer */
    H5FA_t        *fa   = NULL;            /* Fixed array wrapper */
    void          *fiter_info;             /* Fixed array iterator info */
    farray_state_t state;                  /* State of fixed array */
    uint64_t       welmt;                  /* Element to write */
    uint64_t       relmt;                  /* Element to read */
    hsize_t        cnt;                    /* Count of array indices */
    hssize_t       sidx;                   /* Index value of next element in the fixed array */
    hsize_t        idx;                    /* Index value of next element in the fixed array */
    hsize_t        fa_nelmts;              /* # of elements in fixed array */
    haddr_t        fa_addr = HADDR_UNDEF;  /* Array address in file */

    assert(nelmts);
    /*
     * Display testing message
     */
    TESTING(test_str);

    /* Create file & retrieve pointer to internal file object */
    if (create_file(fapl, &file, &f) < 0)
        TEST_ERROR;

    /* Create array */
    if (create_array(f, cparam, &fa, &fa_addr) < 0)
        TEST_ERROR;

    /* Verify the creation parameters */
    if (verify_cparam(fa, cparam) < 0)
        TEST_ERROR;

    /* Check for closing & re-opening the file */
    if (reopen_file(&file, &f, fapl, &fa, fa_addr, tparam) < 0)
        TEST_ERROR;

    if (H5FA_get_nelmts(fa, &fa_nelmts) < 0)
        FAIL_STACK_ERROR;

    if (nelmts > fa_nelmts)
        TEST_ERROR;

    /* Verify array state */
    memset(&state, 0, sizeof(state));
    state.hdr_size  = FA_HDR_SIZE;
    state.nelmts    = cparam->nelmts;
    state.dblk_size = 0;
    if (check_stats(fa, &state))
        TEST_ERROR;

    /* Get all elements from empty array */

    /* Initialize iterator */
    if (NULL == (fiter_info = tparam->fiter->init(cparam, tparam, nelmts)))
        TEST_ERROR;

    /* Get elements of array */
    for (cnt = 0; cnt < nelmts; cnt++) {
        /* Get the array index */
        if ((sidx = tparam->fiter->next(fiter_info)) < 0)
            TEST_ERROR;
        idx = (hsize_t)sidx;

        /* Retrieve element of array (not set yet) */
        relmt = (uint64_t)0;
        if (H5FA_get(fa, idx, &relmt) < 0)
            FAIL_STACK_ERROR;

        /* Verify that the retrieved is correct */
        if (check_elmt(&relmt, NULL))
            TEST_ERROR;
    } /* end for */

    /* Shutdown iterator */
    if (tparam->fiter->term(fiter_info) < 0)
        TEST_ERROR;

    /* Set (& get) all elements from empty array */

    /* Initialize iterator */
    if (NULL == (fiter_info = tparam->fiter->init(cparam, tparam, nelmts)))
        TEST_ERROR;

    /* Set elements of array */
    for (cnt = 0; cnt < nelmts; cnt++) {
        /* Get the array index */
        if ((sidx = tparam->fiter->next(fiter_info)) < 0)
            TEST_ERROR;
        idx = (hsize_t)sidx;

        relmt = (uint64_t)0;
        if (H5FA_get(fa, idx, &relmt) < 0)
            FAIL_STACK_ERROR;

        /* Verify that the retrieved element is correct */
        if (check_elmt(&relmt, NULL))
            TEST_ERROR;

        /* Set element of array */
        welmt = (uint64_t)7 + idx;
        if (H5FA_set(fa, idx, &welmt) < 0)
            FAIL_STACK_ERROR;

        /* Retrieve element of array (set now) */
        relmt = (uint64_t)0;
        if (H5FA_get(fa, idx, &relmt) < 0)
            FAIL_STACK_ERROR;

        /* Verify that the retrieved element is correct */
        if (check_elmt(&relmt, &welmt))
            TEST_ERROR;
    } /* end for */

    /* Verify array state */
    memset(&state, 0, sizeof(state));
    set_fa_state(cparam, &state);
    if (check_stats(fa, &state))
        TEST_ERROR;

    /* Shutdown iterator */
    if (tparam->fiter->term(fiter_info) < 0)
        TEST_ERROR;

    /* Close array, delete array, close file & verify file is empty */
    if (finish(file, fapl, f, fa, fa_addr) < 0)
        TEST_ERROR;

    /* All tests passed */
    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (fa)
            H5FA_close(fa);
        H5Fclose(file);
    }
    H5E_END_TRY

    return 1;
} /* test_set_elmts() */

/*-------------------------------------------------------------------------
 * Function:	test_skip_elmts
 *
 * Purpose:	Set the element "skip_elmts" in the fixed array
 *		("skip_elmts" is the index of the fixed array to be set.)
 *
 * Return:	Success:	0
 *		Failure:	1
 *
 *-------------------------------------------------------------------------
 */
static unsigned
test_skip_elmts(hid_t fapl, H5FA_create_t *cparam, farray_test_param_t *tparam, hsize_t skip_elmts,
                bool check_rest, const char *test_str)
{
    hid_t          file = H5I_INVALID_HID; /* File ID */
    H5F_t         *f    = NULL;            /* Internal file object pointer */
    H5FA_t        *fa   = NULL;            /* Extensible array wrapper */
    farray_state_t state;                  /* State of extensible array */
    uint64_t       welmt;                  /* Element to write */
    uint64_t       relmt;                  /* Element to read */
    hsize_t        idx;                    /* Index value of element to get */
    hsize_t        cnt;                    /* Count of array indices */
    hsize_t        fa_nelmts;              /* # of elements in fixed array */
    haddr_t        fa_addr = HADDR_UNDEF;  /* Array address in file */

    /*
     * Display testing message
     */
    TESTING(test_str);

    /* Create file & retrieve pointer to internal file object */
    if (create_file(fapl, &file, &f) < 0)
        TEST_ERROR;

    /* Create array */
    if (create_array(f, cparam, &fa, &fa_addr) < 0)
        TEST_ERROR;

    /* Verify the creation parameters */
    if (verify_cparam(fa, cparam) < 0)
        TEST_ERROR;

    /* Check for closing & re-opening the file */
    if (reopen_file(&file, &f, fapl, &fa, fa_addr, tparam) < 0)
        TEST_ERROR;

    if (H5FA_get_nelmts(fa, &fa_nelmts) < 0)
        FAIL_STACK_ERROR;

    if (skip_elmts >= fa_nelmts)
        TEST_ERROR;

    /* Verify array state */
    memset(&state, 0, sizeof(state));
    state.hdr_size  = FA_HDR_SIZE;
    state.nelmts    = cparam->nelmts;
    state.dblk_size = 0;
    if (check_stats(fa, &state))
        TEST_ERROR;

    /* Set (& get) element after skipping elements */
    idx = skip_elmts;

    /* Retrieve element of array (not set yet) */
    relmt = (uint64_t)0;
    if (H5FA_get(fa, idx, &relmt) < 0)
        FAIL_STACK_ERROR;

    /* Verify that the retrieved is correct */
    if (check_elmt(&relmt, NULL))
        TEST_ERROR;

    /* Set element of array */
    welmt = (uint64_t)7 + idx;
    if (H5FA_set(fa, idx, &welmt) < 0)
        FAIL_STACK_ERROR;

    /* Verify array state */
    memset(&state, 0, sizeof(state));
    set_fa_state(cparam, &state);
    if (check_stats(fa, &state))
        TEST_ERROR;

    /* Retrieve element of array (set now) */
    relmt = (uint64_t)0;
    if (H5FA_get(fa, idx, &relmt) < 0)
        FAIL_STACK_ERROR;

    /* Verify that the retrieved is correct */
    if (check_elmt(&relmt, &welmt))
        TEST_ERROR;

    if (check_rest) {
        /* Get unset elements of array */
        for (cnt = 0; cnt < skip_elmts; cnt++) {
            /* Retrieve element of array (not set yet) */
            relmt = (uint64_t)0;
            if (H5FA_get(fa, cnt, &relmt) < 0)
                FAIL_STACK_ERROR;

            /* Verify that the retrieved is correct */
            if (check_elmt(&relmt, NULL))
                TEST_ERROR;
        } /* end for */
    }     /* end if */

    /* Close array, delete array, close file & verify file is empty */
    if (finish(file, fapl, f, fa, fa_addr) < 0)
        TEST_ERROR;

    /* All tests passed */
    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (fa)
            H5FA_close(fa);
        H5Fclose(file);
    }
    H5E_END_TRY

    return 1;
} /* test_skip_elmts() */

/*-------------------------------------------------------------------------
 * Function:	main
 *
 * Purpose:	Test the fixed array code
 *
 * Return:	Success: 0
 *		Failure: 1
 *
 *-------------------------------------------------------------------------
 */
int
main(void)
{
    H5FA_create_t       cparam;                           /* Creation parameters for fixed array */
    farray_test_param_t tparam;                           /* Testing parameters */
    farray_test_type_t  curr_test;                        /* Current test being worked on */
    farray_iter_type_t  curr_iter;                        /* Current iteration type being worked on */
    hid_t               fapl           = H5I_INVALID_HID; /* File access property list for data files */
    unsigned            nerrors        = 0;               /* Cumulative error count */
    H5CX_node_t         api_ctx        = {{0}, NULL};     /* API context node to push */
    bool                api_ctx_pushed = false;           /* Whether API context pushed */

    /* Reset library */
    h5_test_init();
    fapl = h5_fileaccess();

    /* Set the filename to use for this test (dependent on fapl) */
    h5_fixname(FILENAME[0], fapl, filename_g, sizeof(filename_g));

    /* Push API context */
    if (H5CX_push(&api_ctx) < 0)
        FAIL_STACK_ERROR;
    api_ctx_pushed = true;

    /* Seed random #'s */
    srand((unsigned)time(NULL));

    /* Create an empty file to retrieve size */
    {
        hid_t file; /* File ID */

        if ((file = H5Fcreate(filename_g, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
            FAIL_STACK_ERROR;

        /* Close file */
        if (H5Fclose(file) < 0)
            FAIL_STACK_ERROR;

        /* Get the size of a file w/no array */
        if ((empty_size_g = h5_get_file_size(filename_g, fapl)) < 0)
            TEST_ERROR;
    }

    /* Iterate over the testing parameters */
    for (curr_test = FARRAY_TEST_NORMAL; curr_test < FARRAY_TEST_NTESTS; curr_test++) {

        /* Initialize the testing parameters */
        memset(&tparam, 0, sizeof(tparam));
        tparam.nelmts = TEST_NELMTS;

        /* Set appropriate testing parameters for each test */
        switch (curr_test) {
            /* "Normal" testing parameters */
            case FARRAY_TEST_NORMAL:
                puts("Testing with NORMAL PARAMETERS");
                break;

            /* "Re-open array" testing parameters */
            case FARRAY_TEST_REOPEN:
                puts("Testing with reopen array flag set");
                tparam.reopen_array = FARRAY_TEST_REOPEN;
                break;

            /* An unknown test? */
            case FARRAY_TEST_NTESTS:
            default:
                goto error;
        } /* end switch */

        /* Initialize fixed array creation parameters */
        init_cparam(&cparam, &tparam);

        /* Basic capability tests */
        nerrors += test_create(fapl, &cparam, &tparam);
        nerrors += test_reopen(fapl, &cparam, &tparam);
        nerrors += test_open_twice(fapl, &cparam, &tparam);
        nerrors += test_open_twice_diff(fapl, &cparam, &tparam);
        nerrors += test_delete_open(fapl, &cparam, &tparam);

        /* Iterate over the type of capacity tests */
        for (curr_iter = FARRAY_ITER_FW; curr_iter < FARRAY_ITER_NITERS; curr_iter++) {

            /* Set appropriate parameters for each type of iteration */
            switch (curr_iter) {
                /* "Forward" testing parameters */
                case FARRAY_ITER_FW:
                    puts("Testing with forward iteration");
                    tparam.fiter = &fa_iter_fw;
                    break;

                /* "Reverse" testing parameters */
                case FARRAY_ITER_RV:
                    puts("Testing with reverse iteration");
                    tparam.fiter = &fa_iter_rv;
                    break;

                /* "Random" testing parameters */
                case FARRAY_ITER_RND:
                    puts("Testing with random iteration");
                    tparam.fiter = &fa_iter_rnd;
                    break;

                /* "Cyclic" testing parameters */
                case FARRAY_ITER_CYC:
                    puts("Testing with cyclic iteration");
                    tparam.fiter = &fa_iter_cyc;
                    break;

                /* An unknown iteration? */
                case FARRAY_ITER_NITERS:
                default:
                    goto error;
            } /* end switch */

            /* Basic capacity tests */
            nerrors += test_set_elmts(fapl, &cparam, &tparam, (hsize_t)1, "setting 1 element of the array");
            nerrors += test_set_elmts(fapl, &cparam, &tparam, (hsize_t)(tparam.nelmts / 2),
                                      "setting half of the array's elements ");
            nerrors += test_set_elmts(fapl, &cparam, &tparam, (hsize_t)tparam.nelmts,
                                      "setting all the array elements");
        } /* end for */

        /* Check skipping elements */
        nerrors += test_skip_elmts(fapl, &cparam, &tparam, (hsize_t)1, true, "skipping to first element");
        nerrors += test_skip_elmts(fapl, &cparam, &tparam, ((hsize_t)1 << cparam.max_dblk_page_nelmts_bits),
                                   true, "skipping to first element in data block page");
        nerrors += test_skip_elmts(fapl, &cparam, &tparam, (hsize_t)(tparam.nelmts - 1), true,
                                   "skipping to last element");

        /* Create fixed array */
        /* MAX_NELMTS succeeds on some platforms but may fail on others:
         *
         *      "H5FD_sec2_truncate(): unable to extend file properly"
         *
         * and
         *
         *      "H5FD_sec2_truncate(): File too large"
         *
         * have both been seen.
         */
        tparam.nelmts = MAX_NELMTS / 17;
        init_cparam(&cparam, &tparam);

        /* Set the last element in the fixed array */
        nerrors += test_skip_elmts(fapl, &cparam, &tparam, (hsize_t)(tparam.nelmts - 1), false,
                                   "skipping to last element");
    } /* end for */

    /* Verify symbol table messages are cached */
    nerrors += (h5_verify_cached_stabs(FILENAME, fapl) < 0 ? 1 : 0);

    /* Pop API context */
    if (api_ctx_pushed && H5CX_pop(false) < 0)
        FAIL_STACK_ERROR;
    api_ctx_pushed = false;

    if (nerrors)
        goto error;
    puts("All fixed array tests passed.");

    /* Clean up file used */
    h5_cleanup(FILENAME, fapl);

    exit(EXIT_SUCCESS);

error:
    puts("*** TESTS FAILED ***");

    H5E_BEGIN_TRY
    {
        H5Pclose(fapl);
    }
    H5E_END_TRY

    if (api_ctx_pushed)
        H5CX_pop(false);

    exit(EXIT_FAILURE);
} /* end main() */
