Skip to content

test: replace vol-tests submodule with HDF5's in-tree API test suite#73

Draft
brtnfld wants to merge 31 commits into
HDFGroup:masterfrom
brtnfld:feature/api-tests-migration
Draft

test: replace vol-tests submodule with HDF5's in-tree API test suite#73
brtnfld wants to merge 31 commits into
HDFGroup:masterfrom
brtnfld:feature/api-tests-migration

Conversation

@brtnfld

@brtnfld brtnfld commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Replaces the archived HDFGroup/vol-tests submodule with HDF5's in-tree API test suite (test/API/testpar/API).
  • CI builds HDF5 from source as the top-level project and pulls this connector in as an HDF5_VOL_ALLOW_EXTERNAL=LOCAL_DIR subproject - the same mechanism HDF5's own CI uses to test vol-async/vol-cache/vol-rest, rather than depending on a downstream HDF5 install having been built with -DHDF5_TEST_API_INSTALL=ON (a flag almost nobody sets outside Spack). HDF5's own test/API/testpar/API CMakeLists.txt registers this connector's ctest entries automatically via HDF5_EXTERNAL_VOL_TARGETS; there's no private test driver or custom ctest wiring in this repo to maintain.
  • The DAOS server, agent, and pool start once per CI job via plain shell steps, matching how HDF5's own CI starts REST VOL's HSDS server.
  • Unsupported connector features are skipped via the test suite's own H5VL_CAP_FLAG_* checks. Gaps with no capability flag to gate on are excluded at the ctest level; see docs/api-tests-migration.md for the current list and rationale.

This PR is scoped to the test framework only. Connector-source fixes (H5T_COMPLEX support, the attribute decreasing-order iteration fix, external-link/UD-link/H5Oset_comment support needed for full tool-test coverage, etc.) are tracked/landed separately against master.

Test plan

  • daos-test job, hdf5_1.14.6 leg: builds and runs against real DAOS, clean known-gaps-only result (65% tests passed, 6/17 failed, all matching documented gaps)
  • daos-test job, develop leg (non-blocking): tool-test suites (h5dump/h5diff/h5copy/h5ls) also get pulled in via the same HDF5_EXTERNAL_VOL_TARGETS mechanism; triaging those failures is tracked as follow-up work

… test suite)

Add a daos-test job to CI that installs DAOS 2.6.5, builds HDF5 from
source (matrixed over hdf5_1.14.6 and develop, the latter marked
continue-on-error since it tracks a moving upstream target), builds the
connector against it, and runs the full ctest suite (h5vl_test plus
HDF5's own native/parallel test binaries ported to run against a VOL
connector, plus the connector's own h5daos_test_* targets) against a
live, ephemeral, single-node DAOS instance.

Key pieces:
- Rocky Linux 8 container running --privileged (required to disable
  transparent hugepages, which DAOS's SPDK housekeeping needs even with
  a pure-RAM storage tier).
- test/scripts/daos_server.yml.in: RAM-emulated SCM storage tier sized
  and configured for small/CI hosts (system_ram_reserved lowered,
  scm_hugepages_disabled, nr_hugepages fixed).
- test/scripts/daos_pool.sh.in: rewritten for the modern dmg CLI syntax,
  polling for engine join and pool queryability before signaling
  readiness to the test driver, to fix races where the harness would
  start the client before the pool was actually usable.
- Excludes 3 known, tracked failures (h5_test_testhdf5,
  h5_partest_t_bigio, h5vl_ext_h5daos_test_metadata_parallel) so CI stays
  green on everything that's actually been verified so far - see the PR
  description for root-cause details on each.
- .gitmodules: test/vol temporarily points at the brtnfld/vol-tests fork
  branch pending HDFGroup/vol-tests#67 merging; revert to
  HDFGroup/vol-tests once that lands.
@brtnfld brtnfld changed the title test: replace HDFGroup/vol-tests with HDF5's in-tree API test suite Replace vol-tests with HDF5's in-tree API test suite + HDF5 2.0 support Jul 6, 2026
brtnfld added 13 commits July 7, 2026 10:05
…ia CI testing

Real, confirmed bugs found while exercising the connector against a live
DAOS instance for the first time:

- daos_vol_type.c / daos_vol_private.h / daos_vol_obj.c: H5_daos_dtype_t
  cached committed datatypes as a persistently-open hid_t, which looks
  identical to a user-visible open object to HDF5 core's native
  H5Fget_obj_count(H5F_OBJ_ALL, ...) accounting. Cache the datatype in
  its serialized (H5Tencode'd) form instead.
- daos_vol.c: stop falsely advertising H5VL_CAP_FLAG_EXTERNAL_LINKS and
  H5VL_CAP_FLAG_UD_LINKS - H5_daos_link_create() hard-errors on
  H5VL_LINK_CREATE_UD (how H5Lcreate_external() is implemented
  internally by HDF5), so external/UD links were never actually
  supported despite the advertised flags.
- daos_vol_link.c: reject H5Lcreate_hard() across two different files,
  matching the existing check already present in the H5Lmove/H5Lcopy
  path.
- daos_vol_obj.c: handle H5I_ATTR in the H5VL_OBJECT_BY_SELF
  target-object resolution used by H5Oget_info and H5Ovisit, instead of
  type-punning the attribute's H5_daos_item_t header as a full
  H5_daos_obj_t.

HDF5 2.0 (tracked by the develop branch) API compatibility:

- daos_vol_private.h: add H5_VERSION_GE-gated macros for H5Tdecode
  (H5Tdecode1(buf) vs H5Tdecode2(buf, buf_size)) and for looking up this
  connector's own registered ID by class value
  (H5VLpeek_connector_id_by_value(), removed in 2.0, vs
  H5VLget_connector_id_by_value()). Update all 5 H5Tdecode call sites
  (daos_vol_attr.c, daos_vol_dset.c, daos_vol_map.c, daos_vol_obj.c) to
  use the new macro.

Two fixes previously bundled here have been split out to their own
branches/PRs: the H5Fget_obj_count/H5Fget_obj_ids container-label-vs-
pointer comparison fix (obj-count-fix branch) and the
H5_daos_link_ibco_task() creation-order-untracked fallback fix for
H5Ovisit (best-effort-index-fix branch).
Points at the merged HDFGroup/vol-tests#67, which fixes test-driver
robustness against benign DAOS log noise, gates known DAOS-specific test
gaps on connector name rather than skipping unconditionally, and fixes
HDF5 2.0 compatibility issues in the vendored test suite; and HDFGroup#68, which
fixes an unrelated pre-existing CI workflow typo (ctest --build isn't a
valid flag) discovered while investigating that suite's own CI status.
.gitmodules is reverted to the upstream vol-tests URL now that both
fixes have landed there.
17 is available in the same Ubuntu 24.04 (ubuntu-latest) universe repo
that already provides 14, and produces byte-identical output on every
file in src/*.c and src/*.h (verified against clang-format 14.0.6 and
17.0.6 directly) - only one trivial, cosmetic rule changed anywhere in
the repo (a space after "//" in a single commented-out line in
test/vol/hdf5_testpar/testpar.h). Drops the comment explaining the
13->14 bump, since that reasoning ("14 is the oldest still available")
no longer applies once jumping straight to the current version.
h5vl_test now hits two known bugs: over-counting on repeated file
opens, and H5Ovisit hard-erroring on a group without creation-order
tracking. The first failure kills the shared DAOS server mid-run,
cascading into most of the rest of the suite. Skip it here until
those are resolved and reviewed.
…area

Expose DAOS_SERVER_TARGETS as a CMake cache variable instead of hard-coding
the engine to 4 targets and papering over CI's smaller core count with
DAOS_TARGET_OVERSUBSCRIBE; CI now passes -DDAOS_SERVER_TARGETS="$(nproc)"
to match the runner it's actually on.

Enable HDF5_VOL_TEST_ENABLE_PART so h5vl_test is registered as one ctest
entry per functional area (attribute, dataset, file, group, ...) instead of
a single binary, each against its own DAOS server instance. CI now excludes
only the two known-broken areas (h5vl_test_file, h5vl_test_object) instead
of skipping the whole suite, since a crash in one area no longer takes the
rest down with it.

Also drops two CMake comments that only described now-removed CLI flags
and provider names with no remaining bearing on the current config.
The libdaos prerequisite only listed a years-old minimum version with no
indication of what's actually exercised now that CI builds and runs the
test suite against a live DAOS instance.
H5_DAOS_G_INIT() previously used H5VLpeek_connector_id_by_value() on
HDF5 <2.0 (no owned reference) and H5VLget_connector_id_by_value() on
>=2.0 (which does take one) via a version-gated macro, but H5_daos_term()
unconditionally just discarded H5_DAOS_g without releasing that reference.
H5VLget_connector_id_by_value() has been available since the VOL's
introduction, so use it unconditionally and drop the peek/get macro, and
release its reference in H5_daos_term() before forgetting the ID.
0b50093 switched H5_daos_dtype_t to cache committed datatypes in their
H5Tencode'd form, to keep a live hid_t from inflating
H5Fget_obj_count(H5F_OBJ_ALL, ...)'s count. Per review, a VOL connector
holding an ID for its own object bookkeeping is legitimate and expected
to be counted, so revert to keeping the copied hid_t directly (as before
0b50093), while keeping the H5_DAOS_TDECODE macro for HDF5 2.0's
H5Tdecode1/H5Tdecode2 split.
…of skipping it

h5vl_test doesn't crash or cascade - the prior run (before it was excluded)
completed with 807/896 (90%) internal checks passing; the ctest entry was
only marked FAILED because the driver treats any "FAILED" substring in
output as fatal, with no way to mark individual checks as known-broken from
outside the test binary.

Gate the two specific failing checks (overlapping-file-opens object count,
and H5Ovisit(_by_name) by creation order on an untracked root group) on the
connector name in vol-tests itself (HDFGroup/vol-tests#70), matching the
existing pattern already used in that file. .gitmodules points at the fork
branch until the PR merges upstream.

Revert the h5vl_test PART-mode split and CI exclusion from the last two
commits - no longer needed since h5vl_test runs clean as a single test now.
… in CI

Every single test in the last run failed identically: server starts,
storage formats, but the engine never reports "Joined" within the pool
script's 30s poll window - reproducible on the very first test, ruling out
resource exhaustion from repeated cycles. The only infra-relevant change
was replacing the hardcoded targets=4 + DAOS_TARGET_OVERSUBSCRIBE=1 with
-DDAOS_SERVER_TARGETS="$(nproc)" and no oversubscribe: unlike a plain
too-many-targets mismatch (which makes the engine refuse to start
immediately with a clear error), nproc apparently doesn't reflect the
actually-usable core count in this container, and without the
oversubscribe fallback the engine hangs during startup instead.

Restore DAOS_TARGET_OVERSUBSCRIBE=1 as an unconditional safety net and
drop CI's nproc override, falling back to the proven-working default of 4
targets. DAOS_SERVER_TARGETS remains CMake-configurable for anyone who
wants to tune it explicitly.
Every test in the last CI run crashed identically on process shutdown
after otherwise completing cleanly:
"h5vl_test: src/common/tse.c:775: tse_sched_progress: Assertion '0' failed"
(SIGABRT). Root cause: H5_daos_term() is invoked by the library while
closing the last reference to H5_DAOS_g itself, so calling H5Idec_ref()
on that same ID from within the callback recurses into still-being-torn-
down library state, corrupting the DAOS task scheduler on the way out.

Keep H5_DAOS_G_INIT()'s switch to H5VLget_connector_id_by_value() (still
correct - it works the same on both HDF5 <2.0 and >=2.0, so the version
macro was unnecessary), but revert closing that reference in
H5_daos_term(); the original code's "no need to close it here" was right.
jhendersonHDF flagged (PR HDFGroup#71) that H5_daos_term() not releasing the
H5VLget_connector_id_by_value() reference is a real leak in the case
where term() is reached outside of the ID's own refcount hitting
zero - just not one worth chasing right now. Document why closing it
unconditionally already caused a confirmed crash, and what a correct
fix would actually require (H5is_library_terminating() plus a way to
know H5_DAOS_g's provenance), rather than leaving the tradeoff
implicit. Tracked in HDFGroup#74.
Runs test/API and testpar/API (built via -DHDF5_TEST_API_INSTALL=ON)
through a dependency-free Python test driver instead of the defunct
vol-tests submodule and its vendored C++/KWSys driver. Unsupported
features are handled via H5VL_CAP_FLAG_* checks in the test suite
itself plus a per-interface/-binary -x subtest exclusion mechanism for
gaps with no capability flag to gate on.

See docs/api-tests-migration.md for how it's wired.
@brtnfld
brtnfld force-pushed the feature/api-tests-migration branch from dd2c74d to 1907823 Compare July 8, 2026 15:04
@brtnfld brtnfld changed the title Replace vol-tests with HDF5's in-tree API test suite + HDF5 2.0 support test: replace vol-tests submodule with HDF5's in-tree API test suite Jul 8, 2026
brtnfld added 7 commits July 8, 2026 10:15
The server and client shared one --mpiexec-max-numprocs value in
h5vl_test_driver.py, so configuring the client's numprocs to 2 (for
parallel API tests) also launched two daos_server ranks. The second
rank found the control-server port already bound by the first and
exited immediately with "another daos_server process is already
running", which the driver correctly reported as a failed startup -
causing every test in the CI run to fail. The server now always gets
its own --mpiexec-server-max-numprocs, hardcoded to 1 in
test/CMakeLists.txt since a DAOS server engine here is always a
single process regardless of how many client ranks a given test uses.
daos_pool.sh formats storage and creates a pool against whatever the
currently running server has initialized. Running --init-command (rm
-rf on the DAOS SCM mount) after the server already started wiped that
storage out from under it instead of giving the next test a clean
mount before startup, so pool create failed to produce a UUID and the
driver correctly reported the token as missing.
client-init's output was only collected silently for token-regex
matching, so a "token not found" failure gave no visibility into what
daos_pool.sh / dmg actually printed. Print it live like the
server/client-helper streams already are, and flush those prints
immediately so ctest's captured output reflects real chronological
order instead of buffering stdout until process exit.
subprocess.run(shlex.split(cmd), ...) never expands shell globs -
shlex.split() only tokenizes, it doesn't invoke a shell. --init-command
is "rm -rf \${DAOS_SERVER_SCM_MNT}/* \${...}/*.h5", so rm received the
literal argument "/mnt/daos/*" (a nonexistent filename), -f silently
swallowed the error, and cleanup was a no-op on every test run. Later
tests inherited the first test's already-formatted DAOS storage and
already-created pool, causing dmg storage format/pool create to fail
with "system is running" / "pool label already exists". Running these
developer-authored command strings through a real shell (shell=True)
lets the glob expand as intended.
…river

Depending on an HDF5 install configured with -DHDF5_TEST_API_INSTALL=ON
(a flag almost nobody sets outside Spack) meant this connector's test
suite silently did nothing for most HDF5 installations, and duplicated
orchestration logic (a private Python driver, custom ctest wiring) that
HDF5 already provides for exactly this purpose.

CI now builds HDF5 from source as the top-level project and pulls this
connector in as an HDF5_VOL_ALLOW_EXTERNAL=LOCAL_DIR subproject, the same
mechanism HDF5's own CI uses to test vol-async/vol-cache/vol-rest. HDF5's
own test/API and testpar/API suites register this connector's ctest
entries automatically via HDF5_EXTERNAL_VOL_TARGETS. The DAOS server,
agent, and pool now start once per CI job via plain shell steps instead of
once per test, removing the private test driver entirely.

test/daos_vol's native tests are exposed to HDF5's suite the same way, via
HDF5_API_EXT_SERIAL_TESTS/HDF5_API_EXT_PARALLEL_TESTS (previously named
HDF5_VOL_EXT_*_TESTS, which didn't match the variable names HDF5's
test/API and testpar/API CMakeLists.txt actually read, so they were never
being registered at all).
…river

Depending on an HDF5 install configured with -DHDF5_TEST_API_INSTALL=ON
(a flag almost nobody sets outside Spack) meant this connector's test
suite silently did nothing for most HDF5 installations, and duplicated
orchestration logic (a private Python driver, custom ctest wiring) that
HDF5 already provides for exactly this purpose.

CI now builds HDF5 from source as the top-level project and pulls this
connector in as an HDF5_VOL_ALLOW_EXTERNAL=LOCAL_DIR subproject, the same
mechanism HDF5's own CI uses to test vol-async/vol-cache/vol-rest. HDF5's
own test/API and testpar/API suites register this connector's ctest
entries automatically via HDF5_EXTERNAL_VOL_TARGETS. The DAOS server,
agent, and pool now start once per CI job via plain shell steps instead of
once per test.

test/daos_vol's native tests are exposed to HDF5's suite the same way, via
HDF5_API_EXT_SERIAL_TESTS/HDF5_API_EXT_PARALLEL_TESTS (previously named
HDF5_VOL_EXT_*_TESTS, which didn't match the variable names HDF5's
test/API and testpar/API CMakeLists.txt actually read, so they were never
being registered at all).
daos-test runs inside a rockylinux:8 container. \${{ github.workspace }}
resolves to the host runner's path (/home/runner/work/vol-daos/vol-daos),
not the container's actual mount point (/__w/vol-daos/vol-daos) - so
HDF5_VOL_PATH01 pointed at a path that doesn't exist inside the
container, and CMakeVOL.cmake's existence check failed immediately.
Computing these paths via $(pwd) at shell runtime instead gives the
container's own view of the workspace.
brtnfld added 7 commits July 8, 2026 18:03
CMakeVOL.cmake pre-sets HDF5_FOUND before HDF5's own src/ subdirectory
(which creates the hdf5-shared target) has been added, so this eager
if(TARGET ...) check fails even though the target is a valid forward
reference that will exist by generate time. HDF5_VOL_ALLOW_EXTERNAL is
only ever set by an outer HDF5 build doing exactly this, so use it to
distinguish this case from a genuinely misconfigured standalone HDF5
install missing shared libraries.
Same underlying issue as the previous hdf5-shared target-existence fix,
but at CMake's generate step instead of configure: export(TARGETS
hdf5_vol_daos ...) requires every target hdf5_vol_daos links against to
be part of a resolvable export set, but hdf5-shared isn't part of any
export set until HDF5 itself is installed. This export is only meant for
exposing vol-daos's targets to a parent project in the standalone build,
so skip it under the same HDF5_VOL_ALLOW_EXTERNAL condition.
daos_agent_sh lives in test/scripts/, but the daos_agent.sock it starts
lives one level up in test/ (DAOS_AGENT_DIR in test/CMakeLists.txt).
DAOS_AGENT_DRPC_DIR was derived from dirname(daos_agent_sh), landing one
directory too deep, so every test failed at H5_daos_init() with
DER_AGENT_COMM before ever reaching real API test code.
dmg reporting a created pool doesn't mean the pool is immediately
attachable by an application (as opposed to dmg's own admin RPCs,
already polled by daos_pool.sh via "dmg pool query"). daos_control.log
shows a transient "pool ... is locked" message right as the pool comes
online, and every test run immediately afterward failed at
H5_daos_init() with DER_AGENT_COMM, while tests running later in the
same job (after other tests had already spent 5-25s) succeeded
consistently.
…_PATH

These were computed via `find build -name ...`, which returns paths
relative to the current directory. HDF5's own test/API/CMakeLists.txt
runs each HDF5_VOL_vol-daos-h5_api_test_<iface> test with its own
WORKING_DIRECTORY (a per-connector subdirectory), not the directory
these paths were computed relative to, so DAOS_AGENT_DRPC_DIR and
HDF5_PLUGIN_PATH resolved to the wrong location for every test in that
group - explaining why they all failed at H5_daos_init() with
DER_AGENT_COMM while HDF5_API_TESTS_EXTRA entries (registered the same
way but apparently not sensitive to this) passed. The previous 15s
"pool settling" delay didn't change this failure at all once tried
against a real run, so it's removed - it wasn't the actual cause.
The old -E pattern excluded h5_api_test_attribute (still correct) and
h5_api_ext_test_testhdf5 (wrong name - it's actually
h5_api_test_testhdf5, and it passes anyway, so no exclusion was needed).
Real CI runs against the inline-build pivot now show two more known,
non-regression gaps: h5_api_test_parallel_t_bigio (DER_NOSPACE, pool too
small) and h5_api_test_parallel_testphdf5 (h5oflusherror, a native-VOL-
only expectation DAOS correctly doesn't share). Also documents the
h5dumpgentest crash cascade found on the develop leg's broader tool-test
matches as a separate, tracked connector-feature gap (missing external
link / UD link / H5Oset_comment support), not something this PR fixes.
Removed in 46aa501 because it passed quickly in two prior runs, but a
later run showed it hitting the same known by-idx/paginated attribute
lookup gap as h5_api_test_attribute (tattr.c iterates hundreds of
attributes; each failing H5Aget_info_by_idx/H5Aget_name_by_idx call
cascades diagnostic output). It manifests non-deterministically as
either a fast per-call failure or, when it cascades across enough
attributes, a full ctest timeout - not a new regression, the same
pre-existing gap the original pre-migration CI already excluded this
binary for.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant