/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
 * Tests for free-space manager
 */
#include "h5test.h"

#define H5FS_FRIEND /*suppress error about including H5FSpkg	  */
#define H5FS_TESTING
#include "H5FSpkg.h" /* Free space manager */

/* Other private headers that this test requires */
#define H5F_FRIEND /*suppress error about including H5Fpkg	  */
#include "H5Fpkg.h"

#include "H5CXprivate.h" /* API Contexts                             */
#include "H5Iprivate.h"
#include "H5VLprivate.h" /* Virtual Object Layer                     */
#include "H5VMprivate.h"

#define FILENAME_LEN 1024

#define TEST_FSPACE_SECT_TYPE      0
#define TEST_FSPACE_SECT_TYPE_NEW  1
#define TEST_FSPACE_SECT_TYPE_NONE 2

#define TEST_FSPACE_SHRINK 80
#define TEST_FSPACE_EXPAND 120
#define TEST_MAX_SECT_SIZE (64 * 1024)
#define TEST_MAX_INDEX     32

#define TEST_SECT_ADDR60  60
#define TEST_SECT_ADDR70  70
#define TEST_SECT_ADDR80  80
#define TEST_SECT_ADDR100 100
#define TEST_SECT_ADDR150 150
#define TEST_SECT_ADDR200 200
#define TEST_SECT_ADDR300 300

#define TEST_SECT_SIZE5  5
#define TEST_SECT_SIZE10 10
#define TEST_SECT_SIZE15 15
#define TEST_SECT_SIZE20 20
#define TEST_SECT_SIZE30 30
#define TEST_SECT_SIZE40 40
#define TEST_SECT_SIZE50 50
#define TEST_SECT_SIZE80 80

#define FSPACE_THRHD_DEF 1 /* Default: no alignment threshold */
#define FSPACE_ALIGN_DEF 1 /* Default: no alignment */

static const char *FILENAME[] = {"frspace", NULL};

typedef struct frspace_state_t {
    hsize_t tot_space;         /* Total amount of space tracked              */
    hsize_t tot_sect_count;    /* Total # of sections tracked                */
    hsize_t serial_sect_count; /* # of serializable sections tracked         */
    hsize_t ghost_sect_count;  /* # of un-serializable sections tracked      */
} frspace_state_t;

haddr_t        g_eoa = 0;
static haddr_t TEST_get_eoa(void);
static void    TEST_set_eoa(haddr_t);

/*
 * TEST client
 */
typedef struct TEST_free_section_t {
    H5FS_section_info_t sect_info; /* Free space section information (must be first in struct) */
} TEST_free_section_t;

static herr_t TEST_sect_init_cls(H5FS_section_class_t *, void *);
static herr_t TEST_sect_free(H5FS_section_info_t *_sect);
static herr_t TEST_sect_can_merge(const H5FS_section_info_t *, const H5FS_section_info_t *,
                                  void H5_ATTR_UNUSED *);
static herr_t TEST_sect_merging(H5FS_section_info_t **, H5FS_section_info_t *, void H5_ATTR_UNUSED *);
static herr_t TEST_sect_can_shrink(const H5FS_section_info_t *, void *);
static herr_t TEST_sect_shrinking(H5FS_section_info_t **, void *);

static unsigned test_fs_create(hid_t fapl);
static unsigned test_fs_sect_add(hid_t fapl);
static unsigned test_fs_sect_merge(hid_t fapl);
static unsigned test_fs_sect_shrink(hid_t fapl);
static unsigned test_fs_sect_find(hid_t fapl);
static unsigned test_fs_sect_change_class(hid_t fapl);
static unsigned test_fs_sect_extend(hid_t fapl);
static unsigned test_fs_sect_iterate(hid_t fapl);

H5FS_section_class_t TEST_FSPACE_SECT_CLS[1] = {{
    TEST_FSPACE_SECT_TYPE,                   /* Section type                 */
    0,                                       /* Extra serialized size        */
    H5FS_CLS_MERGE_SYM | H5FS_CLS_ADJUST_OK, /* Class flags  */
    NULL,                                    /* Class private info           */

    /* Class methods */
    TEST_sect_init_cls, /* Initialize section class     */
    NULL,               /* Terminate section class      */

    /* Object methods */
    NULL,                 /* Add section                  */
    NULL,                 /* Serialize section            */
    NULL,                 /* Deserialize section          */
    TEST_sect_can_merge,  /* Can sections merge?          */
    TEST_sect_merging,    /* Merge sections               */
    TEST_sect_can_shrink, /* Can section shrink container?*/
    TEST_sect_shrinking,  /* Shrink container w/section   */
    TEST_sect_free,       /* Free section                 */
    NULL,                 /* Check validity of section    */
    NULL,                 /* Split section node for alignment */
    NULL,                 /* Dump debugging for section   */
}};

H5FS_section_class_t TEST_FSPACE_SECT_CLS_NEW[1] = {{
    TEST_FSPACE_SECT_TYPE_NEW,               /* Section type                 */
    0,                                       /* Extra serialized size        */
    H5FS_CLS_MERGE_SYM | H5FS_CLS_ADJUST_OK, /* Class flags  */
    NULL,                                    /* Class private info           */

    /* Class methods */
    TEST_sect_init_cls, /* Initialize section class     */
    NULL,               /* Terminate section class      */

    /* Object methods */
    NULL,                /* Add section                  */
    NULL,                /* Serialize section            */
    NULL,                /* Deserialize section          */
    TEST_sect_can_merge, /* Can sections merge?          */
    TEST_sect_merging,   /* Merge sections               */
    NULL,                /* Can section shrink container?*/
    NULL,                /* Shrink container w/section   */
    TEST_sect_free,      /* Free section                 */
    NULL,                /* Check validity of section    */
    NULL,                /* Split section node for alignment */
    NULL,                /* Dump debugging for section   */
}};

H5FS_section_class_t TEST_FSPACE_SECT_CLS_NOINIT[1] = {{
    TEST_FSPACE_SECT_TYPE_NONE,              /* Section type                 */
    0,                                       /* Extra serialized size        */
    H5FS_CLS_MERGE_SYM | H5FS_CLS_ADJUST_OK, /* Class flags  */
    NULL,                                    /* Class private info           */

    /* Class methods */
    NULL, /* Initialize section class     */
    NULL, /* Terminate section class      */

    /* Object methods */
    NULL,                /* Add section                  */
    NULL,                /* Serialize section            */
    NULL,                /* Deserialize section          */
    TEST_sect_can_merge, /* Can sections merge?          */
    TEST_sect_merging,   /* Merge sections               */
    NULL,                /* Can section shrink container?*/
    NULL,                /* Shrink container w/section   */
    TEST_sect_free,      /* Free section                 */
    NULL,                /* Check validity of section    */
    NULL,                /* Split section node for alignment */
    NULL,                /* Dump debugging for section   */
}};

const H5FS_section_class_t *test_classes[] = {TEST_FSPACE_SECT_CLS, TEST_FSPACE_SECT_CLS_NEW,
                                              TEST_FSPACE_SECT_CLS_NOINIT};

static void init_cparam(H5FS_create_t *);
static void init_sect_node(TEST_free_section_t *, haddr_t, hsize_t, unsigned, H5FS_section_state_t);
static int  check_stats(const H5F_t *, const H5FS_t *, frspace_state_t *);

#define NUM_SECTIONS 1000

/* User data for free space section iterator callback */
typedef struct {
    hsize_t tot_size;
    hsize_t tot_sect_count;
} TEST_iter_ud_t;

static herr_t TEST_sects_cb(H5FS_section_info_t *_sect, void *_udata);

/*
 * Tests
 */

/*
 * free-space section routines for client TEST
 */
static herr_t
TEST_sect_init_cls(H5FS_section_class_t *cls, void *_udata)
{
    herr_t    ret_value = SUCCEED; /* Return value */
    unsigned *init_flags;

    /* Check arguments. */
    assert(cls);
    assert(_udata);

    init_flags = (unsigned *)_udata;
    cls->flags |= *init_flags;

    return ret_value;
} /* TEST_sect_init_cls() */

/*
 * Check if the two sections can be merged:
 * 	true if second section adjoins the first section
 */
H5_ATTR_PURE static herr_t
TEST_sect_can_merge(const H5FS_section_info_t *_sect1, const H5FS_section_info_t *_sect2,
                    void H5_ATTR_UNUSED *_udata)
{
    const TEST_free_section_t *sect1 = (const TEST_free_section_t *)_sect1;
    const TEST_free_section_t *sect2 = (const TEST_free_section_t *)_sect2;
    htri_t                     ret_value; /* Return value */

    /* Check arguments. */
    assert(sect1);
    assert(sect2);
    assert(sect1->sect_info.type == sect2->sect_info.type); /* Checks "MERGE_SYM" flag */
    assert(H5_addr_lt(sect1->sect_info.addr, sect2->sect_info.addr));

    /* Check if second section adjoins first section */
    ret_value = H5_addr_eq(sect1->sect_info.addr + sect1->sect_info.size, sect2->sect_info.addr);

    return ret_value;
} /* TEST_sect_can_merge() */

/*
 * Merge the two sections (second section is merged into the first section)
 */
static herr_t
TEST_sect_merging(H5FS_section_info_t **_sect1, H5FS_section_info_t *_sect2, void H5_ATTR_UNUSED *_udata)
{
    TEST_free_section_t **sect1     = (TEST_free_section_t **)_sect1;
    TEST_free_section_t  *sect2     = (TEST_free_section_t *)_sect2;
    herr_t                ret_value = SUCCEED; /* Return value */

    /* Check arguments. */
    assert(sect1);
    assert(((*sect1)->sect_info.type == TEST_FSPACE_SECT_TYPE) ||
           ((*sect1)->sect_info.type == TEST_FSPACE_SECT_TYPE_NEW) ||
           ((*sect1)->sect_info.type == TEST_FSPACE_SECT_TYPE_NONE));
    assert(sect2);
    assert((sect2->sect_info.type == TEST_FSPACE_SECT_TYPE) ||
           (sect2->sect_info.type == TEST_FSPACE_SECT_TYPE_NEW) ||
           (sect2->sect_info.type == TEST_FSPACE_SECT_TYPE_NONE));
    assert(H5_addr_eq((*sect1)->sect_info.addr + (*sect1)->sect_info.size, sect2->sect_info.addr));

    /* Add second section's size to first section */
    (*sect1)->sect_info.size += sect2->sect_info.size;

    /* Get rid of second section */
    if (TEST_sect_free((H5FS_section_info_t *)sect2) < 0)
        TEST_ERROR;
error:
    return ret_value;
} /* TEST_sect_merging() */

/*
 * Free the section
 */
static herr_t
TEST_sect_free(H5FS_section_info_t *sect)
{
    /* Release the section */
    free(sect);

    return 0;
} /* TEST_sect_free() */

/*
 * Determine if the section can be shrunk and set _udata accordingly
 *	if _udata passed in is NULL, return false
 *	Otherwise:
 *		if section's address+size is the end of file, return true
 *		otherwise return false
 */
static herr_t
TEST_sect_can_shrink(const H5FS_section_info_t *_sect, void *_udata)
{
    unsigned                  *can_shrink = (unsigned *)_udata;
    const TEST_free_section_t *sect       = (const TEST_free_section_t *)_sect;
    haddr_t                    end, eoa;

    if (can_shrink == NULL)
        return false;

    end = sect->sect_info.addr + sect->sect_info.size;
    eoa = TEST_get_eoa();

    if (end == eoa)
        *can_shrink = true;
    else
        *can_shrink = false;

    return (htri_t)*can_shrink;
} /* TEST_sect_can_shrink() */

/*
 * Shrink the section
 */
static herr_t
TEST_sect_shrinking(H5FS_section_info_t **_sect, void *_udata)
{
    TEST_free_section_t **sect       = (TEST_free_section_t **)_sect;
    unsigned             *can_shrink = (unsigned *)_udata;

    /* address of the section is faked, so, doesn't need to do anything */
    /* just free the section node */
    if (*can_shrink) {
        if (TEST_sect_free((H5FS_section_info_t *)*sect) < 0)
            TEST_ERROR;
        *sect = NULL;
        return true;
    } /* end if */

error:
    return false;
}

/*
 * iteration callback
 */
static herr_t
TEST_sects_cb(H5FS_section_info_t *_sect, void *_udata)
{
    TEST_free_section_t *sect      = (TEST_free_section_t *)_sect;
    TEST_iter_ud_t      *udata     = (TEST_iter_ud_t *)_udata;
    herr_t               ret_value = SUCCEED; /* Return value */

    assert(sect);
    assert(udata);

    udata->tot_size += sect->sect_info.size;
    udata->tot_sect_count += 1;

    return (ret_value);
}

/* supporting routine for shrinking */
static haddr_t
TEST_get_eoa(void)
{
    return g_eoa;
}

/* supporting routine for shrinking */
static void
TEST_set_eoa(haddr_t val)
{
    g_eoa = val;
}

/*
 * Initialize creation parameter structure for TEST client
 */
static void
init_cparam(H5FS_create_t *cparam)
{
    memset(cparam, 0, sizeof(H5FS_create_t));

    /* Set the free space creation parameters */
    cparam->shrink_percent = TEST_FSPACE_SHRINK;
    cparam->expand_percent = TEST_FSPACE_EXPAND;
    cparam->max_sect_size  = TEST_MAX_SECT_SIZE;
    cparam->max_sect_addr  = TEST_MAX_INDEX;

} /* init_cparam() */

/*
 * Initialize free space section node
 */
static void
init_sect_node(TEST_free_section_t *sect_node, haddr_t addr, hsize_t size, unsigned type,
               H5FS_section_state_t state)
{
    sect_node->sect_info.addr  = addr;
    sect_node->sect_info.size  = size;
    sect_node->sect_info.type  = type;
    sect_node->sect_info.state = state;
} /* init_sect_node() */

/*
 * Verify statistics for the free-space manager
 */
static int
check_stats(const H5F_t *f, const H5FS_t *frsp, frspace_state_t *state)
{
    H5FS_stat_t frspace_stats; /* Statistics about the heap */

    /* Get statistics for heap and verify they are correct */
    if (H5FS_stat_info(f, frsp, &frspace_stats) < 0)
        FAIL_STACK_ERROR;

    if (frspace_stats.tot_space != state->tot_space) {
        fprintf(stdout, "frspace_stats.tot_space = %" PRIuHSIZE ", state->tot_space = %" PRIuHSIZE "\n",
                frspace_stats.tot_space, state->tot_space);
        TEST_ERROR;
    } /* end if */
    if (frspace_stats.tot_sect_count != state->tot_sect_count) {
        fprintf(stdout,
                "frspace_stats.tot_sect_count = %" PRIuHSIZE ", state->tot_sect_count = %" PRIuHSIZE "\n",
                frspace_stats.tot_sect_count, state->tot_sect_count);
        TEST_ERROR;
    } /* end if */
    if (frspace_stats.serial_sect_count != state->serial_sect_count) {
        fprintf(stdout,
                "frspace_stats.serial_sect_count = %" PRIuHSIZE ", state->serial_sect_count = %" PRIuHSIZE
                "\n",
                frspace_stats.serial_sect_count, state->serial_sect_count);
        TEST_ERROR;
    } /* end if */
    if (frspace_stats.ghost_sect_count != state->ghost_sect_count) {
        fprintf(stdout,
                "frspace_stats.ghost_sect_count = %" PRIuHSIZE ", state->ghost_sect_count = %" PRIuHSIZE "\n",
                frspace_stats.ghost_sect_count, state->ghost_sect_count);
        TEST_ERROR;
    } /* end if */

    /* All tests passed */
    return 0;

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

/*
 * TESTS for free-space manager
 */

/*
 *  To verify the creation, close, reopen and deletion of the free-space manager
 */
static unsigned
test_fs_create(hid_t fapl)
{
    hid_t           file = H5I_INVALID_HID; /* File ID */
    char            filename[FILENAME_LEN]; /* Filename to use */
    H5F_t          *f    = NULL;            /* Internal file object pointer */
    H5FS_t         *frsp = NULL;            /* pointer to free space structure */
    haddr_t         fs_addr;                /* address of free space */
    h5_stat_size_t  file_size, empty_size;  /* File size */
    frspace_state_t state;                  /* State of free space*/
    H5FS_create_t   cparam, test_cparam;    /* creation parameters */
    uint16_t        nclasses;
    unsigned        init_flags = 0;

    TESTING("the creation/close/reopen/deletion of the free-space manager");

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

    /* Create the file to work on */
    if ((file = H5Fcreate(filename, 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/empty heap*/
    if ((empty_size = h5_get_file_size(filename, fapl)) < 0)
        TEST_ERROR;

    /* Re-open the file */
    if ((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    /* initialize creation parameters for free-space manager */
    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    /* Tag with the global free space tag */
    H5AC_tag(H5AC__FREESPACE_TAG, NULL);

    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;
    if (frsp->nclasses != nclasses)
        TEST_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    memset(&test_cparam, 0, sizeof(H5FS_create_t));
    if (H5FS__get_cparam_test(frsp, &test_cparam) < 0)
        FAIL_STACK_ERROR;
    if (H5FS__cmp_cparam_test(&cparam, &test_cparam))
        FAIL_STACK_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* reopen the free-space manager */
    if (NULL == (frsp = H5FS_open(f, fs_addr, nclasses, test_classes, NULL, (hsize_t)FSPACE_THRHD_DEF,
                                  (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;
    if (frsp->nclasses != nclasses)
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

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

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

    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (frsp)
            H5FS_close(f, frsp);
        H5Fclose(file);
    }
    H5E_END_TRY
    return 1;
} /* test_fs_create() */

/*
 * Test 1:
 *	Create free-space manager
 *	Add section A via H5FS_sect_add() with H5FS_ADD_RETURNED_SPACE
 *	Close the free-space manager
 *	Result: section A is serialized to the file
 *
 * Test 2:
 *	Create free-space manager with H5FS_CLS_GHOST_OBJ section class setting
 *	Add section A via H5FS_sect_add() with H5FS_ADD_RETURNED_SPACE
 *	Close the free-space manager
 *	Result: section A is not serialized to the file
 *
 * Test 3:
 *	Add section A via H5FS_sect_add() to allow shrinking with H5FS_ADD_RETURNED_SPACE
 *	Set EOF to be the ending address of section A
 *	Result: H5FS_sect_add() will shrink section A
 *
 * Test 4:
 *	Add section A via H5FS_sect_add() to allow shrinking with H5FS_ADD_DESERIALIZING
 *	Set EOF to be the ending address of section A
 *	Result: H5FS_sect_add() will not shrink section A
 *
 */
static unsigned
test_fs_sect_add(hid_t fapl)
{
    hid_t           file = H5I_INVALID_HID; /* File ID */
    char            filename[FILENAME_LEN]; /* Filename to use */
    H5F_t          *f       = NULL;         /* Internal file object pointer */
    H5FS_t         *frsp    = NULL;         /* pointer to free space structure */
    haddr_t         fs_addr = HADDR_UNDEF;  /* address of free space */
    uint16_t        nclasses;
    H5FS_create_t   cparam; /* creation parameters */
    frspace_state_t state;  /* State of free space*/

    TEST_free_section_t *sect_node  = NULL;
    unsigned             init_flags = 0;
    h5_stat_size_t       file_size = 0, tmp_file_size = 0, fr_meta_size = 0;
    unsigned             can_shrink = false;

    TESTING("adding a section via H5FS_sect_add() to free-space: test 1");

    h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));

    /* Create the file to work on */
    if ((file = H5Fcreate(filename, 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/empty heap*/
    if ((file_size = h5_get_file_size(filename, fapl)) < 0)
        TEST_ERROR;

    /* Re-open the file */
    if ((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    /* Tag with the global free space tag */
    H5AC_tag(H5AC__FREESPACE_TAG, NULL);

    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    if (NULL == (sect_node = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node, (haddr_t)TEST_SECT_ADDR80, (hsize_t)TEST_SECT_SIZE20, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    fr_meta_size = H5FS_HEADER_SIZE(f) + H5FS_SINFO_PREFIX_SIZE(f);

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

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

    if (h5_using_default_driver(NULL)) {
        /* Get the size of a file w/empty heap*/
        if ((tmp_file_size = h5_get_file_size(filename, fapl)) < 0)
            TEST_ERROR;

        if (tmp_file_size <= (file_size + fr_meta_size))
            TEST_ERROR;
    }

    PASSED();

    TESTING("adding a section via H5FS_sect_add() to free-space with H5FS_CLS_GHOST_OBJ: test 2");

    /* Get the size of a file w/empty heap*/
    if ((file_size = h5_get_file_size(filename, fapl)) < 0)
        TEST_ERROR;

    /* Re-open the file */
    if ((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    init_flags = H5FS_CLS_GHOST_OBJ;
    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /* Create free list section node */
    if (NULL == (sect_node = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node, (haddr_t)TEST_SECT_ADDR80, (hsize_t)TEST_SECT_SIZE20, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node, 0, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node->sect_info.size;
    state.tot_sect_count += 1;
    state.ghost_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    fr_meta_size = H5FS_HEADER_SIZE(f);

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;

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

    if (h5_using_default_driver(NULL)) {
        /* Get the size of a file w/empty heap*/
        if ((tmp_file_size = h5_get_file_size(filename, fapl)) < 0)
            TEST_ERROR;

        if (tmp_file_size != (file_size + fr_meta_size))
            TEST_ERROR;
    }

    PASSED();

    TESTING("adding a section via H5FS_sect_add() to free-space: test 3");

    /* Create the file to work on */
    if ((file = H5Fcreate(filename, 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/empty heap*/
    if ((file_size = h5_get_file_size(filename, fapl)) < 0)
        TEST_ERROR;

    /* Re-open the file */
    if ((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    TEST_set_eoa((haddr_t)TEST_SECT_ADDR150); /* set end of file address for shrinking */

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    init_flags = 0;
    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    if (NULL == (sect_node = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    /*
     * Add section A
     */
    init_sect_node(sect_node, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node, H5FS_ADD_RETURNED_SPACE, &can_shrink, NULL) <
        0)
        FAIL_STACK_ERROR;

    /* nothing in free-space */
    memset(&state, 0, sizeof(frspace_state_t));

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    TESTING("adding a section via H5FS_sect_add() to free-space: test 4");

    /* Create the file to work on */
    if ((file = H5Fcreate(filename, 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/empty heap*/
    if ((file_size = h5_get_file_size(filename, fapl)) < 0)
        TEST_ERROR;

    /* Re-open the file */
    if ((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    TEST_set_eoa((haddr_t)TEST_SECT_ADDR150); /* set end of file address for shrinking */

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    init_flags = 0;
    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    if (NULL == (sect_node = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    /*
     * Add section A
     */
    init_sect_node(sect_node, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node, H5FS_ADD_DESERIALIZING, &can_shrink, NULL) <
        0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    if (H5FS_sect_remove(f, frsp, (H5FS_section_info_t *)sect_node) < 0)
        FAIL_STACK_ERROR;

    /* Free the section node(s) */
    if (TEST_sect_free((H5FS_section_info_t *)sect_node) < 0)
        TEST_ERROR;
    sect_node = NULL;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (sect_node)
            TEST_sect_free((H5FS_section_info_t *)sect_node);
        if (frsp)
            H5FS_close(f, frsp);
        H5Fclose(file);
    }
    H5E_END_TRY
    return 1;
} /* test_fs_sect_add() */

/*
 * To verify the finding of a section with the requested-size from free-space
 *
 * Test 1: Free-space is empty and is not able to fulfill the requested-size
 *	Set up: free-space is started up but is empty
 *
 * Test 2: Add a section and find the section whose size is equal to the requested-size
 * 	Set up: Add section A whose size is less than requested-size
 *		Add section B whose size is the same as requested-size with addr=b
 *		Add section C whose size is the same as requested-size with addr=c > b
 *		Add section D whose size is greater than requested-size
 *
 * Test 3: Add a section and find the section whose size is > requested-size
 * 	Set up: Add section A whose size is less than requested-size
 *		Add section B whose size is greater than requested-size
 *
 * Test 4: Add a section but the section is not able to fulfill the requested-size
 * 	Set up:	Add section A whose size is less than requested-size
 *
 */
static unsigned
test_fs_sect_find(hid_t fapl)
{
    hid_t           file = H5I_INVALID_HID; /* File ID */
    char            filename[FILENAME_LEN]; /* Filename to use */
    H5F_t          *f       = NULL;         /* Internal file object pointer */
    H5FS_t         *frsp    = NULL;         /* pointer to free space structure */
    haddr_t         fs_addr = HADDR_UNDEF;  /* address of free space */
    uint16_t        nclasses;
    H5FS_create_t   cparam; /* creation parameters */
    frspace_state_t state;  /* State of free space*/

    TEST_free_section_t *sect_node1 = NULL, *sect_node2, *sect_node3 = NULL, *sect_node4 = NULL;
    TEST_free_section_t *node;
    htri_t               node_found = false;
    unsigned             init_flags = 0;

    TESTING("H5FS_sect_find(): free-space is empty");

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

    /* Create the file to work on */
    if ((file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    /* Tag with the global free space tag */
    H5AC_tag(H5AC__FREESPACE_TAG, NULL);

    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)TEST_SECT_SIZE30, (H5FS_section_info_t **)&node)) < 0)
        FAIL_STACK_ERROR;

    if (node_found)
        TEST_ERROR;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    PASSED();

    TESTING("H5FS_sect_find() a section equal to requested-size from free-space");

    /* reopen the free-space manager */
    if (NULL == (frsp = H5FS_open(f, fs_addr, nclasses, test_classes, NULL, (hsize_t)FSPACE_THRHD_DEF,
                                  (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;
    if (frsp->nclasses != nclasses)
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR60, (hsize_t)TEST_SECT_SIZE30, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node1->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section C
     */
    if (NULL == (sect_node3 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node3, (haddr_t)(TEST_SECT_ADDR200), (hsize_t)TEST_SECT_SIZE50, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node3, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    state.tot_space += sect_node3->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    state.tot_space += sect_node2->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section D
     */
    if (NULL == (sect_node4 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node4, (haddr_t)TEST_SECT_ADDR300, (hsize_t)TEST_SECT_SIZE80, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node4, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    state.tot_space += sect_node4->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)TEST_SECT_SIZE50, (H5FS_section_info_t **)&node)) < 0)
        FAIL_STACK_ERROR;

    if (!node_found)
        TEST_ERROR;

    if ((node->sect_info.addr != TEST_SECT_ADDR100) || (node->sect_info.size != TEST_SECT_SIZE50))
        TEST_ERROR;

    if (TEST_sect_free((H5FS_section_info_t *)node) < 0)
        TEST_ERROR;

    /* remove sections A, C and D */
    if (H5FS_sect_remove(f, frsp, (H5FS_section_info_t *)sect_node1) < 0)
        FAIL_STACK_ERROR;
    if (H5FS_sect_remove(f, frsp, (H5FS_section_info_t *)sect_node3) < 0)
        FAIL_STACK_ERROR;
    if (H5FS_sect_remove(f, frsp, (H5FS_section_info_t *)sect_node4) < 0)
        FAIL_STACK_ERROR;

    /* Free the section node(s) */
    if (TEST_sect_free((H5FS_section_info_t *)sect_node1) < 0)
        TEST_ERROR;
    sect_node1 = NULL;
    if (TEST_sect_free((H5FS_section_info_t *)sect_node3) < 0)
        TEST_ERROR;
    sect_node3 = NULL;
    if (TEST_sect_free((H5FS_section_info_t *)sect_node4) < 0)
        TEST_ERROR;
    sect_node4 = NULL;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    PASSED();

    TESTING("H5FS_sect_find() a section greater than requested-size from free-space");

    /* reopen the free-space manager */
    if (NULL == (frsp = H5FS_open(f, fs_addr, nclasses, test_classes, NULL, (hsize_t)FSPACE_THRHD_DEF,
                                  (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;
    if (frsp->nclasses != nclasses)
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR60, (hsize_t)TEST_SECT_SIZE30, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node1->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR200, (hsize_t)TEST_SECT_SIZE80, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    state.tot_space += sect_node2->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)TEST_SECT_SIZE50, (H5FS_section_info_t **)&node)) < 0)
        FAIL_STACK_ERROR;

    if (!node_found)
        TEST_ERROR;
    if ((node->sect_info.addr != TEST_SECT_ADDR200) || (node->sect_info.size < TEST_SECT_SIZE50))
        TEST_ERROR;

    if (TEST_sect_free((H5FS_section_info_t *)node) < 0)
        TEST_ERROR;
    node = NULL;

    /* remove sections A */
    if (H5FS_sect_remove(f, frsp, (H5FS_section_info_t *)sect_node1) < 0)
        FAIL_STACK_ERROR;

    /* Free the section node(s) */
    if (TEST_sect_free((H5FS_section_info_t *)sect_node1) < 0)
        TEST_ERROR;
    sect_node1 = NULL;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    PASSED();

    TESTING("H5FS_sect_find(): cannot find a section with requested-size from free-space");

    /* reopen the free-space manager */
    if (NULL == (frsp = H5FS_open(f, fs_addr, nclasses, test_classes, NULL, (hsize_t)FSPACE_THRHD_DEF,
                                  (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;
    if (frsp->nclasses != nclasses)
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR60, (hsize_t)TEST_SECT_SIZE30, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node1->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)TEST_SECT_SIZE50, (H5FS_section_info_t **)&node)) < 0)
        FAIL_STACK_ERROR;

    if (node_found)
        TEST_ERROR;

    /* remove sections A */
    if (H5FS_sect_remove(f, frsp, (H5FS_section_info_t *)sect_node1) < 0)
        FAIL_STACK_ERROR;

    /* Free the section node(s) */
    if (TEST_sect_free((H5FS_section_info_t *)sect_node1) < 0)
        TEST_ERROR;
    sect_node1 = NULL;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (sect_node1)
            TEST_sect_free((H5FS_section_info_t *)sect_node1);
        if (sect_node3)
            TEST_sect_free((H5FS_section_info_t *)sect_node3);
        if (sect_node4)
            TEST_sect_free((H5FS_section_info_t *)sect_node4);
        if (frsp)
            H5FS_close(f, frsp);
        H5Fclose(file);
    }
    H5E_END_TRY
    return 1;
} /* test_fs_sect_find() */

/*
 * To verify that sections are merged when adding sections to free-space
 *
 * Test 1:
 *   Set up:
 *	H5FS_CLS_SEPAR_OBJ (cls->flags) is not set
 *	H5FS_ADD_RETURNED_SPACE is passed to H5FS_sect_add()
 *
 *   Add sections C, B, A & D that can be merged together
 *
 * Test 2:
 *   Set up:
 *	H5FS_CLS_SEPAR_OBJ (cls->flags) is set
 *	H5FS_ADD_RETURNED_SPACE is passed to H5FS_sect_add()
 *
 *   Add sections A & B that can be merged together but cannot do so because H5FS_CLS_SEPAR_OBJ flag is set
 *
 * Test 3:
 *   Set up:
 *	H5FS_CLS_SEPAR_OBJ (cls->flags) is not set
 *	H5FS_ADD_RETURNED_SPACE is passed to H5FS_sect_add()
 *
 *    Add 4 sections that adjoin each other as follows:
 *	section A is of section class type A
 *	section B is of section class type B
 *	section C is of section class type B
 *	section D is of section class type A
 *    Sections B & C are merged together but not section A nor D because:
 *	sections B & C are merged because of the same section class type
 *	section A cannot be merged with the merged section of B & C because of different section class type
 *	section D cannot be merged with the merged section of B & C because of different section class type
 */
static unsigned
test_fs_sect_merge(hid_t fapl)
{
    hid_t           file = H5I_INVALID_HID; /* File ID */
    char            filename[FILENAME_LEN]; /* Filename to use */
    H5F_t          *f       = NULL;         /* Internal file object pointer */
    H5FS_t         *frsp    = NULL;         /* pointer to free space structure */
    haddr_t         fs_addr = HADDR_UNDEF;  /* address of free space */
    uint16_t        nclasses;
    H5FS_create_t   cparam; /* creation parameters */
    frspace_state_t state;  /* State of free space*/

    TEST_free_section_t *sect_node1 = NULL, *sect_node2 = NULL, *sect_node3 = NULL, *sect_node4 = NULL;
    unsigned             init_flags = 0;
    htri_t               node_found = false;
    TEST_free_section_t *node;

    TESTING("the merge of sections when H5FS_sect_add() to free-space: test 1");

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

    /* Create the file to work on */
    if ((file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    /* Tag with the global free space tag */
    H5AC_tag(H5AC__FREESPACE_TAG, NULL);

    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section C
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += TEST_SECT_SIZE50;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR70, (hsize_t)TEST_SECT_SIZE30, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    /* section B & C are merged */
    state.tot_space += TEST_SECT_SIZE30;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node3 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node3, (haddr_t)TEST_SECT_ADDR60, (hsize_t)TEST_SECT_SIZE10, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node3, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    /* section A is merged with the merged section of B & C */
    state.tot_space += TEST_SECT_SIZE10;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section D
     */
    if (NULL == (sect_node4 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node4, (haddr_t)TEST_SECT_ADDR150, (hsize_t)TEST_SECT_SIZE80, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node4, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    /* section D is merged with the merged section of A & B & C */
    state.tot_space += TEST_SECT_SIZE80;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* should be able to find the merged section of A, B, C & D */
    if ((node_found = H5FS_sect_find(
             f, frsp, (hsize_t)(TEST_SECT_SIZE10 + TEST_SECT_SIZE30 + TEST_SECT_SIZE50 + TEST_SECT_SIZE80),
             (H5FS_section_info_t **)&node)) < 0)
        FAIL_STACK_ERROR;

    if (!node_found)
        TEST_ERROR;
    if ((node->sect_info.addr != TEST_SECT_ADDR60) ||
        (node->sect_info.size != (TEST_SECT_SIZE10 + TEST_SECT_SIZE30 + TEST_SECT_SIZE50 + TEST_SECT_SIZE80)))
        TEST_ERROR;

    if (TEST_sect_free((H5FS_section_info_t *)node) < 0)
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    /*
     * TEST 2
     */
    TESTING("the merge of sections when H5FS_sect_add() to free-space: test 2");

    /* Re-open the file */
    if ((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    init_flags = H5FS_CLS_SEPAR_OBJ;
    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR70, (hsize_t)TEST_SECT_SIZE30, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += TEST_SECT_SIZE30;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    /* section A & B are not merged because H5FS_CLS_SEPAR_OBJ is set */
    state.tot_space += TEST_SECT_SIZE50;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* should not be able to find the merged section of A & B */
    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)(TEST_SECT_SIZE30 + TEST_SECT_SIZE50),
                                     (H5FS_section_info_t **)&node)) < 0)
        FAIL_STACK_ERROR;

    if (node_found)
        TEST_ERROR;

    /* remove section A from free-space */
    if (H5FS_sect_remove(f, frsp, (H5FS_section_info_t *)sect_node1) < 0)
        FAIL_STACK_ERROR;
    /* remove section B from free-space */
    if (H5FS_sect_remove(f, frsp, (H5FS_section_info_t *)sect_node2) < 0)
        FAIL_STACK_ERROR;

    /* Free the section node(s) */
    if (TEST_sect_free((H5FS_section_info_t *)sect_node1) < 0)
        TEST_ERROR;
    sect_node1 = NULL;
    if (TEST_sect_free((H5FS_section_info_t *)sect_node2) < 0)
        TEST_ERROR;
    sect_node2 = NULL;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    /*
     * TEST 3
     */
    TESTING("the merge of sections when H5FS_sect_add() to free-space: test 3");

    /* Re-open the file */
    if ((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    init_flags = 0; /* reset */
    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR60, (hsize_t)TEST_SECT_SIZE10, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += TEST_SECT_SIZE10;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR70, (hsize_t)TEST_SECT_SIZE30,
                   TEST_FSPACE_SECT_TYPE_NEW, H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    /* sections A & B are not merged because H5FS_CLS_MERGE_SYM is set & section class type is different */
    state.tot_space += TEST_SECT_SIZE30;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section C
     */
    if (NULL == (sect_node3 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node3, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50,
                   TEST_FSPACE_SECT_TYPE_NEW, H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node3, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    /* sections B & C are merged because H5FS_CLS_MERGE_SYM is set & section class type is the same */
    state.tot_space += TEST_SECT_SIZE50;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section D
     */
    if (NULL == (sect_node4 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node4, (haddr_t)TEST_SECT_ADDR150, (hsize_t)TEST_SECT_SIZE80, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node4, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    /*
     * section D is not merged with the merged section of B & C because
     * H5FS_CLS_MERGE_SYM is set and section class type is different
     */
    state.tot_space += TEST_SECT_SIZE80;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* should not be able to find a merged section of A, B, C & D */
    if ((node_found = H5FS_sect_find(
             f, frsp, (hsize_t)(TEST_SECT_SIZE10 + TEST_SECT_SIZE30 + TEST_SECT_SIZE50 + TEST_SECT_SIZE80),
             (H5FS_section_info_t **)&node)) < 0)
        FAIL_STACK_ERROR;

    if (node_found)
        TEST_ERROR;

    /* should be able to find the merged section of B & C */
    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)(TEST_SECT_SIZE30 + TEST_SECT_SIZE50),
                                     (H5FS_section_info_t **)&node)) < 0)
        FAIL_STACK_ERROR;

    if (!node_found)
        TEST_ERROR;

    if ((node->sect_info.addr != TEST_SECT_ADDR70) ||
        (node->sect_info.size != (TEST_SECT_SIZE30 + TEST_SECT_SIZE50)))
        TEST_ERROR;

    if (TEST_sect_free((H5FS_section_info_t *)node) < 0)
        TEST_ERROR;

    /* should be able to find section A */
    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)(TEST_SECT_SIZE10), (H5FS_section_info_t **)&node)) <
        0)
        FAIL_STACK_ERROR;

    if (!node_found)
        TEST_ERROR;

    if ((node->sect_info.addr != TEST_SECT_ADDR60) || (node->sect_info.size != TEST_SECT_SIZE10))
        TEST_ERROR;

    if (TEST_sect_free((H5FS_section_info_t *)node) < 0)
        TEST_ERROR;

    /* should be able to find section D */
    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)(TEST_SECT_SIZE80), (H5FS_section_info_t **)&node)) <
        0)
        FAIL_STACK_ERROR;

    if (!node_found)
        TEST_ERROR;

    if ((node->sect_info.addr != TEST_SECT_ADDR150) || (node->sect_info.size != TEST_SECT_SIZE80))
        TEST_ERROR;

    if (TEST_sect_free((H5FS_section_info_t *)node) < 0)
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (sect_node1)
            TEST_sect_free((H5FS_section_info_t *)sect_node1);
        if (sect_node2)
            TEST_sect_free((H5FS_section_info_t *)sect_node2);
        if (frsp)
            H5FS_close(f, frsp);
        H5Fclose(file);
    }
    H5E_END_TRY
    return 1;
} /* test_fs_sect_merge() */

/*
 * To verify that sections are shrunk when adding sections to free-space
 *
 *	Test 1:
 *	  Set EOF to be the ending address of section A
 *	  H5FS_CLS_SEPAR_OBJ (cls->flags) is not set when creating free-space manager
 *	  Add section A to allow shrinking but is not shrunk because its section class type
 *		TEST_FSPACE_SECT_TYPE_NEW does not define "can_shrink"
 *	  Result:section A is not shrunk and section A is still in free-space
 *
 *	  Re-add section A to allow shrinking and with section class type TEST_FSPACE_SECT_TYPE
 *		that defines "can_shrink"
 *	  Result:section A is shrunk and there is nothing in free-space
 *
 *	Test 2:
 *	  Set EOF to be greater than the ending address of section A
 *	  Set H5FS_CLS_SEPAR_OBJ (cls->flags) when creating free-space manager
 *
 *	  Add section A to allow shrinking but is not shrunk because it is not at EOF,
 *		and section A is not on the merge list due to H5FS_CLS_SEPAR_OBJ
 *	  Add section B to allow shrinking and whose ending address is the same as eof.
 *		 Section B is not merged with section A because of H5FS_CLS_SEPAR_OBJ but it is shrunk
 *	  Result: section A is still in free-space
 *
 *	Test 3:
 *	  Set EOF to be greater than the ending address of section A
 *	  H5FS_CLS_SEPAR_OBJ (cls->flags) is not set when creating free-space manager
 *
 *	  Add section A to allow shrinking but is not shrunk because it is not at EOF,
 *		and section A is on the merge list
 *	  Add section B to allow shrinking and whose ending address is the same as eof.
 *	  	Section B is merged with section A and then shrunk.
 *	  Result: free-space should be empty
 */
static unsigned
test_fs_sect_shrink(hid_t fapl)
{
    hid_t           file = H5I_INVALID_HID; /* File ID */
    char            filename[FILENAME_LEN]; /* Filename to use */
    H5F_t          *f       = NULL;         /* Internal file object pointer */
    H5FS_t         *frsp    = NULL;         /* pointer to free space structure */
    haddr_t         fs_addr = HADDR_UNDEF;  /* address of free space */
    uint16_t        nclasses;
    H5FS_create_t   cparam; /* creation parameters */
    frspace_state_t state;  /* State of free space*/

    TEST_free_section_t *sect_node1 = NULL, *sect_node2 = NULL;
    unsigned             init_flags = 0;
    unsigned             can_shrink = false;
    htri_t               node_found = false;
    TEST_free_section_t *node;

    TESTING("shrinking of sections when H5FS_sect_add() to free-space: test 1");

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

    /* Create the file to work on */
    if ((file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    TEST_set_eoa((haddr_t)TEST_SECT_ADDR150); /* set end of file address for shrinking */

    /* Tag with the global free space tag */
    H5AC_tag(H5AC__FREESPACE_TAG, NULL);

    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section A that allow shrinking but its section class type does not define "can_shrink"
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50,
                   TEST_FSPACE_SECT_TYPE_NEW, H5FS_SECT_LIVE);

    can_shrink = false;
    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, &can_shrink,
                      NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node1->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* section A should still be there in free-space */
    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)(TEST_SECT_SIZE50), (H5FS_section_info_t **)&node)) <
        0)
        FAIL_STACK_ERROR;

    if (!node_found)
        TEST_ERROR;

    if ((node->sect_info.addr != TEST_SECT_ADDR100) || (node->sect_info.size != TEST_SECT_SIZE50))
        TEST_ERROR;

    if (TEST_sect_free((H5FS_section_info_t *)node) < 0)
        TEST_ERROR;

    /*
     * Re-add section A that allow shrinking and its section class type defines "can_shrink"
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    can_shrink = false;
    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, &can_shrink,
                      NULL) < 0)
        FAIL_STACK_ERROR;

    /* should have nothing in free-space */
    memset(&state, 0, sizeof(frspace_state_t));

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* section A should not be there in free-space */
    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)(TEST_SECT_SIZE50), (H5FS_section_info_t **)&node)) <
        0)
        FAIL_STACK_ERROR;

    if (node_found)
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    TESTING("shrinking of sections when H5FS_sect_add() to free-space: test 2");

    /* Re-open the file */
    if ((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    TEST_set_eoa((haddr_t)TEST_SECT_ADDR150); /* set end of file address for shrinking */

    /* does not allow merging */
    init_flags = H5FS_CLS_SEPAR_OBJ;
    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR80, (hsize_t)TEST_SECT_SIZE20, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, &can_shrink,
                      NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node1->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, &can_shrink,
                      NULL) < 0)
        FAIL_STACK_ERROR;

    /* free-space should be the same since section B is shrunk */
    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* section B should not be there in free-space */
    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)(TEST_SECT_SIZE50), (H5FS_section_info_t **)&node)) <
        0)
        FAIL_STACK_ERROR;

    if (node_found)
        TEST_ERROR;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* section A should still be there in free-space */
    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)(TEST_SECT_SIZE20), (H5FS_section_info_t **)&node)) <
        0)
        FAIL_STACK_ERROR;

    if (!node_found)
        TEST_ERROR;

    if ((node->sect_info.addr != TEST_SECT_ADDR80) || (node->sect_info.size != TEST_SECT_SIZE20))
        TEST_ERROR;

    if (TEST_sect_free((H5FS_section_info_t *)node) < 0)
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    TESTING("shrinking of sections when H5FS_sect_add() to free-space: test 3");

    /* Re-open the file */
    if ((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    TEST_set_eoa((haddr_t)TEST_SECT_ADDR150); /* set end of file address for shrinking */

    init_flags = 0; /* reset */
    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR70, (hsize_t)TEST_SECT_SIZE30, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, &can_shrink,
                      NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node1->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, &can_shrink,
                      NULL) < 0)
        FAIL_STACK_ERROR;

    /* section A & B are merged and then strunk, so there is nothing in free-space */
    memset(&state, 0, sizeof(frspace_state_t));
    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* section B should not be there in free-space */
    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)(TEST_SECT_SIZE50), (H5FS_section_info_t **)&node)) <
        0)
        FAIL_STACK_ERROR;

    if (node_found)
        TEST_ERROR;

    /* section A should not be there in free-space */
    if ((node_found = H5FS_sect_find(f, frsp, (hsize_t)(TEST_SECT_SIZE30), (H5FS_section_info_t **)&node)) <
        0)
        FAIL_STACK_ERROR;

    if (node_found)
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (frsp)
            H5FS_close(f, frsp);
        H5Fclose(file);
    }
    H5E_END_TRY
    return 1;
} /* test_sect_shrink() */

/*
 * To verify a section's class is changed via H5FS_sect_change_class()
 *
 * Test 1:
 *	Add section A with TEST_FSPACE_SECT_TYPE class type with H5FS_CLS_GHOST_OBJ setting
 *	Add section B with TEST_FSPACE_SECT_TYPE_NONE class type without H5FS_CLS_GHOST_OBJ setting
 * 	Change section A's class to the second section's class
 *	Result: serial_sect_count is incremented by 1; ghost_sect_count is decremented by 1
 *
 * Test 2:
 *	Add section A with TEST_FSPACE_SECT_TYPE class type with H5FS_CLS_SEPAR_OBJ setting
 *	Add section B with TEST_FSPACE_SECT_TYPE_NONE class type without H5FS_CLS_SEPAR_OBJ setting
 *	Add section C with TEST_FSPACE_SECT_TYPE_NONE class type without H5FS_CLS_SEPAR_OBJ setting
 *	Sections B & C are on the merge list
 * 	Change section class of B and C to A's section class
 *	Result: the merge list should be null and the class of sections B & C should be changed
 */
static unsigned
test_fs_sect_change_class(hid_t fapl)
{
    hid_t           file = H5I_INVALID_HID; /* File ID */
    char            filename[FILENAME_LEN]; /* Filename to use */
    H5F_t          *f       = NULL;         /* Internal file object pointer */
    H5FS_t         *frsp    = NULL;         /* pointer to free space structure */
    haddr_t         fs_addr = HADDR_UNDEF;  /* address of free space */
    uint16_t        nclasses;
    H5FS_create_t   cparam; /* creation parameters */
    frspace_state_t state;  /* State of free space*/

    TEST_free_section_t *sect_node1 = NULL, *sect_node2 = NULL, *sect_node3 = NULL;
    unsigned             init_flags = 0;
    TEST_free_section_t *node;

    TESTING("the change of section class via H5FS_sect_change_class() in free-space: Test 1");

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

    /* Create the file to work on */
    if ((file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    /* Tag with the global free space tag */
    H5AC_tag(H5AC__FREESPACE_TAG, NULL);

    init_flags = H5FS_CLS_GHOST_OBJ;
    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR60, (hsize_t)TEST_SECT_SIZE30, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += TEST_SECT_SIZE30;
    state.tot_sect_count += 1;
    state.ghost_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50,
                   TEST_FSPACE_SECT_TYPE_NONE, H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    state.tot_space += TEST_SECT_SIZE50;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    if (H5FS_sect_change_class(f, frsp, (H5FS_section_info_t *)sect_node1, TEST_FSPACE_SECT_TYPE_NONE) < 0)
        TEST_ERROR;

    state.serial_sect_count += 1;
    state.ghost_sect_count -= 1;
    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    if (H5FS_sect_find(f, frsp, (hsize_t)TEST_SECT_SIZE30, (H5FS_section_info_t **)&node) < 0)
        FAIL_STACK_ERROR;

    if (node->sect_info.type != TEST_FSPACE_SECT_TYPE_NONE)
        TEST_ERROR;

    if (TEST_sect_free((H5FS_section_info_t *)node) < 0)
        TEST_ERROR;

    if (H5FS_sect_remove(f, frsp, (H5FS_section_info_t *)sect_node2) < 0)
        FAIL_STACK_ERROR;

    /* Free the section node(s) */
    if (TEST_sect_free((H5FS_section_info_t *)sect_node2) < 0)
        TEST_ERROR;
    sect_node2 = NULL;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    /*
     * TEST 2
     */
    TESTING("the merge of sections when H5FS_sect_add() to free-space: test 2");

    /* Re-open the file */
    if ((file = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    init_flags = H5FS_CLS_SEPAR_OBJ;
    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR70, (hsize_t)TEST_SECT_SIZE30, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE50,
                   TEST_FSPACE_SECT_TYPE_NONE, H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    /*
     * Add section C
     */
    if (NULL == (sect_node3 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node3, (haddr_t)TEST_SECT_ADDR200, (hsize_t)TEST_SECT_SIZE80,
                   TEST_FSPACE_SECT_TYPE_NONE, H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node3, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    /* change the class of B to A's class */
    if (H5FS_sect_change_class(f, frsp, (H5FS_section_info_t *)sect_node2, TEST_FSPACE_SECT_TYPE) < 0)
        TEST_ERROR;

    /* change the class of C to A's class */
    if (H5FS_sect_change_class(f, frsp, (H5FS_section_info_t *)sect_node3, TEST_FSPACE_SECT_TYPE) < 0)
        TEST_ERROR;

    /* the merge_list should be empty */
    if (frsp->sinfo->merge_list)
        if (H5SL_count(frsp->sinfo->merge_list))
            TEST_ERROR;

    /* verify that section B has changed class */
    if (H5FS_sect_find(f, frsp, (hsize_t)TEST_SECT_SIZE50, (H5FS_section_info_t **)&node) < 0)
        FAIL_STACK_ERROR;

    if (node->sect_info.type != TEST_FSPACE_SECT_TYPE)
        TEST_ERROR;

    if (TEST_sect_free((H5FS_section_info_t *)node) < 0)
        TEST_ERROR;

    /* verify that section C has changed class */
    if (H5FS_sect_find(f, frsp, (hsize_t)TEST_SECT_SIZE80, (H5FS_section_info_t **)&node) < 0)
        FAIL_STACK_ERROR;

    if (node->sect_info.type != TEST_FSPACE_SECT_TYPE)
        TEST_ERROR;

    if (TEST_sect_free((H5FS_section_info_t *)node) < 0)
        TEST_ERROR;

    /* remove section A from free-space */
    if (H5FS_sect_remove(f, frsp, (H5FS_section_info_t *)sect_node1) < 0)
        FAIL_STACK_ERROR;

    /* Free the section node(s) */
    if (TEST_sect_free((H5FS_section_info_t *)sect_node1) < 0)
        TEST_ERROR;
    sect_node1 = NULL;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (sect_node1)
            TEST_sect_free((H5FS_section_info_t *)sect_node1);
        if (sect_node2)
            TEST_sect_free((H5FS_section_info_t *)sect_node2);
        if (frsp)
            H5FS_close(f, frsp);
        H5Fclose(file);
    }
    H5E_END_TRY
    return 1;
} /* test_sect_change_class() */

/*
 * To verify the extension of a block using space from a section in free-space
 *
 * Test 1: Try to extend the block by requested-size, which is equal to section B's size
 *	  	Add section A (addr=70, size=5)
 *		Add section B (addr=100, size=40)
 *		Try to extend the block (addr=80, size=20) by 40, which is the same as section B's size
 *		Result: succeed in extending the block
 *
 * Test 2: Try to extend the block by requested-size, which is greater than section B's size
 *	  	Add section A (addr=70, size=5)
 *		Add section B (addr=100, size=40)
 *		Try to extend the block (addr=80, size=20) by 50, which is greater than section B's size
 *		Result: fail in extending the block
 *
 * Test 3: Try to extend the block by requested-size, which is less than section B's size
 *	  	Add section A (addr=70, size=5)
 *		Add section B (addr=100, size=40)
 *		Try to extend the block (addr=80, size=20) by 30, which is less than section B's size
 *		Result: succeed in extending the block and a section of size=10 is left in free-space
 *
 * Test 4: Try to extend the block which does not adjoin section B
 *	  	Add section A (addr=70, size=5)
 *		Add section B (addr=100, size=40)
 *		Try to extend the block (addr=80, size=15) by 40
 *		Result: fail in extending the block because:
 *			the block does not adjoin section B (80+15 != addr of section B (80))
 *			even though the requested-size is equal to section B's size
 *
 */
static unsigned
test_fs_sect_extend(hid_t fapl)
{
    hid_t                file = H5I_INVALID_HID; /* File ID */
    char                 filename[FILENAME_LEN]; /* Filename to use */
    H5F_t               *f       = NULL;         /* Internal file object pointer */
    H5FS_t              *frsp    = NULL;         /* pointer to free space structure */
    haddr_t              fs_addr = HADDR_UNDEF;  /* address of free space */
    uint16_t             nclasses;
    H5FS_create_t        cparam; /* creation parameters */
    frspace_state_t      state;  /* State of free space*/
    TEST_free_section_t *sect_node1 = NULL, *sect_node2 = NULL;
    unsigned             init_flags = 0;
    htri_t               status; /* Status of 'try' calls */

    TESTING("a block's extension by requested-size which is = adjoining free section's size: Test 1");

    h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));

    /* Create the file to work on */
    if ((file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    /* Tag with the global free space tag */
    H5AC_tag(H5AC__FREESPACE_TAG, NULL);

    /*
     * TEST 1
     */
    init_cparam(&cparam);
    nclasses = NELMTS(test_classes);

    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR70, (hsize_t)TEST_SECT_SIZE5, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node1->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE40, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    state.tot_space += sect_node2->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* Extend a block by requested-size */
    if ((status = H5FS_sect_try_extend(f, frsp, (haddr_t)TEST_SECT_SIZE80, (hsize_t)TEST_SECT_SIZE20,
                                       (hsize_t)TEST_SECT_SIZE40, 0, NULL)) < 0)
        FAIL_STACK_ERROR;
    if (false == status)
        TEST_ERROR;

    /* Succeed in extending the block: free space info is decremented accordingly */
    state.tot_space -= (hsize_t)TEST_SECT_SIZE40;
    state.tot_sect_count -= 1;
    state.serial_sect_count -= 1;
    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

    PASSED();

    /*
     * TEST 2
     */
    TESTING("a block's extension by requested-size which is > adjoining free section's size: Test 2");

    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR70, (hsize_t)TEST_SECT_SIZE5, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node1->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE40, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    state.tot_space += sect_node2->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* Extend the block by requested-size */
    if ((status = H5FS_sect_try_extend(f, frsp, (haddr_t)TEST_SECT_ADDR80, (hsize_t)TEST_SECT_SIZE20,
                                       (hsize_t)TEST_SECT_SIZE50, 0, NULL)) < 0)
        FAIL_STACK_ERROR;
    if (true == status)
        TEST_ERROR;

    /* Not able to extend the block: free space info remains the same */
    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

    PASSED();

    /*
     * Test 3
     */
    TESTING("a block's extension by requested-size which is < adjoining free section's size: Test 3");

    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR70, (hsize_t)TEST_SECT_SIZE5, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node1->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE40, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    state.tot_space += sect_node2->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* Extend the block by requested-size */
    if ((status = H5FS_sect_try_extend(f, frsp, (haddr_t)TEST_SECT_ADDR80, (hsize_t)TEST_SECT_SIZE20,
                                       (hsize_t)TEST_SECT_SIZE30, 0, NULL)) < 0)
        TEST_ERROR;
    if (false == status)
        TEST_ERROR;

    /* Succeed in extending the block: total free space is decreased but other info remains the same */
    state.tot_space -= 30;
    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

    PASSED();

    /*
     * TEST 4
     */
    TESTING("a block's extension by requested-size which does not adjoin any free section: Test 4");

    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    /*
     * Add section A
     */
    if (NULL == (sect_node1 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node1, (haddr_t)TEST_SECT_ADDR70, (hsize_t)TEST_SECT_SIZE5, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node1, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    memset(&state, 0, sizeof(frspace_state_t));
    state.tot_space += sect_node1->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /*
     * Add section B
     */
    if (NULL == (sect_node2 = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
        FAIL_STACK_ERROR;

    init_sect_node(sect_node2, (haddr_t)TEST_SECT_ADDR100, (hsize_t)TEST_SECT_SIZE40, TEST_FSPACE_SECT_TYPE,
                   H5FS_SECT_LIVE);

    if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node2, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
        FAIL_STACK_ERROR;

    state.tot_space += sect_node2->sect_info.size;
    state.tot_sect_count += 1;
    state.serial_sect_count += 1;

    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* Extend the block by requested-size */
    if ((status = H5FS_sect_try_extend(f, frsp, (haddr_t)TEST_SECT_ADDR80, (hsize_t)TEST_SECT_SIZE15,
                                       (hsize_t)TEST_SECT_SIZE40, 0, NULL)) < 0)
        TEST_ERROR;
    if (true == status)
        TEST_ERROR;

    /* Not able to extend the block: free space manager info remains the same */
    if (check_stats(f, frsp, &state))
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

    PASSED();

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

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (frsp)
            H5FS_close(f, frsp);
        H5Fclose(file);
    }
    H5E_END_TRY
    return 1;
} /* test_sect_extend() */

/*
 * To verify the iteration of free-space sections
 *
 * Create free-space manager with H5FS_CLS_SEPAR_OBJ
 * Create a whole bunch of sections
 * Iterate through all sections and collect size and count for all sections
 * Check info with H5FS_sect_stat()
 */
static unsigned
test_fs_sect_iterate(hid_t fapl)
{
    hid_t         file = H5I_INVALID_HID; /* File ID */
    char          filename[FILENAME_LEN]; /* Filename to use */
    H5F_t        *f       = NULL;         /* Internal file object pointer */
    H5FS_t       *frsp    = NULL;         /* pointer to free space structure */
    haddr_t       fs_addr = HADDR_UNDEF;  /* address of free space */
    uint16_t      nclasses;
    H5FS_create_t cparam; /* creation parameters */

    TEST_free_section_t *sect_node  = NULL;
    unsigned             init_flags = 0, sect_size;
    TEST_iter_ud_t       udata;
    int                  i;
    hsize_t              tot_space, nsects;

    TESTING("iteration of sections in the free-space manager");

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

    /* Create the file to work on */
    if ((file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
        FAIL_STACK_ERROR;

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

    init_cparam(&cparam);
    nclasses             = NELMTS(test_classes);
    udata.tot_size       = 0;
    udata.tot_sect_count = 0;

    /* Tag with the global free space tag */
    H5AC_tag(H5AC__FREESPACE_TAG, NULL);

    init_flags = H5FS_CLS_SEPAR_OBJ;
    if (NULL == (frsp = H5FS_create(f, &fs_addr, &cparam, nclasses, test_classes, &init_flags,
                                    (hsize_t)FSPACE_THRHD_DEF, (hsize_t)FSPACE_ALIGN_DEF)))
        FAIL_STACK_ERROR;

    if (!H5_addr_defined(fs_addr))
        TEST_ERROR;

    for (i = 1; i <= NUM_SECTIONS; i++) {
        if (NULL == (sect_node = (TEST_free_section_t *)malloc(sizeof(TEST_free_section_t))))
            FAIL_STACK_ERROR;

        sect_size = (unsigned)((i - 1) % 9) + 1;
        init_sect_node(sect_node, (haddr_t)i * 10, (hsize_t)sect_size, TEST_FSPACE_SECT_TYPE, H5FS_SECT_LIVE);

        if (H5FS_sect_add(f, frsp, (H5FS_section_info_t *)sect_node, H5FS_ADD_RETURNED_SPACE, NULL, NULL) < 0)
            FAIL_STACK_ERROR;
    } /* end for */

    if (H5FS_sect_iterate(f, frsp, TEST_sects_cb, &udata) < 0)
        TEST_ERROR;

    H5FS_sect_stats(frsp, &tot_space, &nsects);

    if (udata.tot_size != tot_space)
        TEST_ERROR;
    if (udata.tot_sect_count != nsects)
        TEST_ERROR;

    /* Close the free space manager */
    if (H5FS_close(f, frsp) < 0)
        FAIL_STACK_ERROR;
    frsp = NULL;

    /* Delete free space manager */
    if (H5FS_delete(f, fs_addr) < 0)
        FAIL_STACK_ERROR;
    fs_addr = HADDR_UNDEF;

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

    PASSED();

    return 0;

error:
    H5E_BEGIN_TRY
    {
        if (frsp)
            H5FS_close(f, frsp);
        H5Fclose(file);
    }
    H5E_END_TRY
    return 1;
} /* test_fs_sect_iterate() */

int
main(void)
{
    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 */

    h5_test_init();

    if ((fapl = h5_fileaccess()) < 0) {
        nerrors++;
        PUTS_ERROR("Can't get VFD-dependent fapl");
    } /* end if */

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

    /* make sure alignment is not set for tests to succeed */
    if (H5Pset_alignment(fapl, (hsize_t)1, (hsize_t)1) < 0) {
        nerrors++;
        PUTS_ERROR("Can't set alignment");
    } /* end if */

    nerrors += test_fs_create(fapl);
    nerrors += test_fs_sect_add(fapl);
    nerrors += test_fs_sect_merge(fapl);
    nerrors += test_fs_sect_shrink(fapl);
    nerrors += test_fs_sect_find(fapl);
    nerrors += test_fs_sect_change_class(fapl);
    nerrors += test_fs_sect_extend(fapl);
    nerrors += test_fs_sect_iterate(fapl);

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

    if (nerrors)
        goto error;
    puts("All free-space tests passed.");

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

    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);
} /* main() */
