Skip to content

Run the full test suite in WebAssembly CI - #783

Merged
d-chambers merged 5 commits into
devfrom
wasm-full-suite
Jul 26, 2026
Merged

Run the full test suite in WebAssembly CI#783
d-chambers merged 5 commits into
devfrom
wasm-full-suite

Conversation

@d-chambers

@d-chambers d-chambers commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Description

Follow-up to #756: instead of only the smoke script, run the entire pytest suite in a real Pyodide (WebAssembly) runtime on CI.

The new test_wasm_suite job builds the universal wheel, creates a Pyodide 314.0.3 virtual environment with pyodide-build, seeds the example-data cache natively with .github/scripts/cache_test_data.py (via XDG_CACHE_HOME, cached on the registry hash) so the wasm run needs no network, and runs pytest tests -m "not network and not concurrency".

WebAssembly cannot start threads or processes, so tests that inherently spawn them are tagged with a new concurrency marker (28 tests: thread/process pool clients, run_in_threads concurrency tests, subprocess-based lazy-import tests, and rich-progress tests) and deselected only in the wasm job. All other 7,800+ tests run and pass.

Running the suite on wasm32 exposed three genuine portability bugs, fixed here:

  • dascore.utils.time._float_array_to_ns overflowed for integer input arrays on platforms where the default numpy integer is 32-bit (e.g. to_timedelta64(20) returned -1.47 s); integers are now widened to int64 before the seconds→ns multiply. This alone fixed 37 tests on wasm.
  • The ProdML writer stored Count/StartIndex/NumberOfLoci/StartLocusIndex HDF5 attributes as platform-native ints (int32 on wasm32); they are now explicit np.int64.
  • dascore.utils.progress.track crashed any progress-reporting operation (e.g. spool.update()) on WebAssembly because rich progress spawns a refresh thread; it now falls back to plain iteration on emscripten/wasi.

Verified in CI (test_wasm_suite): 7,836 passed / 247 skipped / 128 deselected / 0 failed in 4m39s, and the full native suite passes unchanged.

Changelog

  • fixed: dc.to_datetime64 and dc.to_timedelta64 convert integer arrays exactly. Integer input was routed through np.rint, whose float64 intermediate loses precision past 2^53 ns, so times beyond about 104 days from the epoch were off by up to 512 ns; the multiply also overflowed a 32-bit default integer under WebAssembly.

Checklist

I have (if applicable):

  • referenced the GitHub issue this PR closes.
  • documented the new feature with docstrings and/or appropriate doc page.
  • included tests. See testing guidelines.
  • added the "ready_for_review" tag once the PR is ready to be reviewed.

Summary by CodeRabbit

  • Bug Fixes
    • Improved compatibility with WebAssembly/WASI by gracefully disabling threaded progress updates when background threads aren’t available.
    • Prevented integer overflow when converting integer time values to nanoseconds.
    • Ensured ProdML-written HDF5 attributes consistently use 64-bit integer values.
  • Tests & CI
    • Updated the WebAssembly test workflow to use a consistent Pyodide version, install the built wheel, cache seeded example data, and run the targeted pytest suite.
    • Added a dedicated concurrency test marker and applied it across relevant tests/configuration for clearer selection.

Add a test_wasm_suite job that runs the whole pytest suite in a Pyodide
virtual environment (pyodide-build + node), with example files seeded
natively into the pooch cache via DFS_DATA_DIR so no network access is
needed inside the runtime.

Tests that spawn threads or processes can never pass on WebAssembly, so
they are tagged with a new 'concurrency' marker and deselected along
with 'network' tests in the wasm job only.

Also fix three portability bugs the wasm (32-bit) run exposed:
- _float_array_to_ns overflowed for integer input arrays on platforms
  where the default integer is 32 bits.
- The ProdML writer stored Count/StartIndex/NumberOfLoci/StartLocusIndex
  HDF5 attrs as platform-native ints (int32 on wasm32) instead of int64.
- dascore.utils.progress.track crashed on WebAssembly because rich
  progress needs a refresh thread; it now falls back to plain iteration.
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 93a7610b-9b7a-4a59-b48f-80fade10134b

📥 Commits

Reviewing files that changed from the base of the PR and between 3e19de6 and 625da47.

📒 Files selected for processing (1)
  • .github/workflows/test_wasm.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/test_wasm.yml

📝 Walkthrough

Walkthrough

Changes

WASM test execution

Layer / File(s) Summary
WASM runtime compatibility
dascore/utils/progress.py, dascore/utils/time.py
WASM platforms bypass threaded Rich progress, and integer timestamp inputs are widened before nanosecond conversion.
Concurrency test classification
pyproject.toml, tests/**
The concurrency marker is registered and applied to concurrency-sensitive tests.
Pyodide CI suite
.github/workflows/test_wasm.yml
The workflow triggers on test changes, synchronizes the Pyodide version, builds the wheel, seeds cached data, and runs tests in a Pyodide environment with network and concurrency tests excluded.

ProdML attribute typing

Layer / File(s) Summary
Normalize ProdML numeric attributes
dascore/io/prodml/utils.py
Selected acquisition and array HDF5 attributes are written as np.int64 values.

Possibly related PRs

  • DASDAE/dascore#756: Modifies the Pyodide workflow for universal wheel testing and pinned Pyodide usage.

Suggested labels: CI, IO

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: running the full test suite in WebAssembly CI.
Description check ✅ Passed The description follows the required template and includes a clear summary plus a checklist with relevant items addressed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wasm-full-suite

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e996b9ac4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/test_wasm.yml Outdated
Comment on lines +102 to +103
for name in get_fetcher().registry:
fetch(name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude large unused files when seeding the cache

On every cold cache, this iterates the complete Pooch registry, including whale_1.hdf5, which the repository explicitly classifies in LARGE_REGISTRY_FILES and excludes from both the test parametrization and the normal .github/scripts/cache_test_data.py priming path. Consequently, the new 30-minute job downloads a large external dataset that no selected test uses, risking a timeout and unnecessarily bloating the Actions cache; seed via the existing script or get_registry_df(exclude_large=True) instead.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3e19de6 — the step now calls .github/scripts/cache_test_data.py (exclude_large=True) instead of walking the raw registry, same as the mamba install action. Confirmed real: the first cold-cache run did download whale_1.hdf5, and nothing selected reads it (SKIP_DATA_FILES in test_common_io.py plus the exclude_large=True parametrization). Seeded set drops from 50 files to 49.

- name: Run test suite in WebAssembly
run: >
.venv-pyodide/bin/python -m pytest tests
-m "not network and not concurrency"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Deselect the remaining process-pool test

This marker expression still selects tests/test_core/test_spool.py::TestMap::test_process_client, even though that test constructs a ProcessPoolExecutor. On WebAssembly, ProcessPoolExecutor raises NotImplementedError from its system-limit check because multiprocessing.synchronize is unavailable, while the proc_client fixture catches only PermissionError, OSError, and RuntimeError; the advertised WASM suite therefore errors instead of skipping this test. Mark the test as concurrency or explicitly handle NotImplementedError in the fixture.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not changing this one. The full wasm suite went green on CI before any fix: 7,836 passed / 247 skipped / 128 deselected / 0 failed, 0 errors (run 30188499383). test_process_client neither errored nor failed there, so the predicted NotImplementedError from _check_system_limits does not surface on this Pyodide build.

Marking it concurrency would deselect a test that currently passes on wasm, which costs coverage for no benefit. Leaving it selected also means CI tells us if a future Pyodide release changes this, rather than hiding it behind a marker.

@codecov

codecov Bot commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.98%. Comparing base (d4a21fc) to head (625da47).
⚠️ Report is 4 commits behind head on dev.

Additional details and impacted files
@@             Coverage Diff             @@
##               dev     #783      +/-   ##
===========================================
- Coverage   100.00%   99.98%   -0.02%     
===========================================
  Files          164      164              
  Lines        17589    17663      +74     
===========================================
+ Hits         17589    17661      +72     
- Misses           0        2       +2     
Flag Coverage Δ
network 48.11% <100.00%> (-0.21%) ⬇️
unittests 99.98% <100.00%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

DFS_DATA_DIR is a pooch-level override that beats the configured cache
directory, which broke the two downloader tests that set it via
config_context. XDG_CACHE_HOME only shifts the default location, so the
seeded cache is still shared between the native and wasm runs while
config overrides keep working.
@coderabbitai coderabbitai Bot added the CI continuous integration label Jul 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/test_wasm.yml (1)

47-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pyodide version differs between the two WASM jobs.

test_wasm pins pyodide@314.0.2 (npm) while the new test_wasm_suite job uses PYODIDE_VERSION: '314.0.3'. Both jobs validate the same wheel against WebAssembly; running them against different Pyodide versions risks inconsistent results and makes failures harder to reason about. Consider aligning both jobs on the same Pyodide version unless the difference is deliberate.

Also applies to: 60-60

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test_wasm.yml around lines 47 - 48, Align the Pyodide
version used by the existing test_wasm job with the PYODIDE_VERSION configured
for test_wasm_suite, updating the npm install step around “Install Pyodide” so
both WASM jobs validate against the same release.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/test_wasm.yml:
- Around line 68-71: Update the actions/checkout@v4 step in test_wasm_suite to
explicitly disable persisted credentials by setting persist-credentials to
false, while preserving the existing fetch-tags and fetch-depth settings.

---

Nitpick comments:
In @.github/workflows/test_wasm.yml:
- Around line 47-48: Align the Pyodide version used by the existing test_wasm
job with the PYODIDE_VERSION configured for test_wasm_suite, updating the npm
install step around “Install Pyodide” so both WASM jobs validate against the
same release.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 23098063-8f46-42ee-a312-3834623d56df

📥 Commits

Reviewing files that changed from the base of the PR and between d4a21fc and 315296d.

📒 Files selected for processing (18)
  • .github/workflows/test_wasm.yml
  • dascore/io/prodml/utils.py
  • dascore/utils/progress.py
  • dascore/utils/time.py
  • pyproject.toml
  • tests/test_core/test_directory_spool.py
  • tests/test_core/test_spool.py
  • tests/test_imports.py
  • tests/test_io/test_index/test_catalog.py
  • tests/test_io/test_index/test_index_edge_cases.py
  • tests/test_io/test_index/test_schema.py
  • tests/test_io/test_index/test_union.py
  • tests/test_io/test_io_core.py
  • tests/test_units.py
  • tests/test_utils/test_config.py
  • tests/test_utils/test_misc.py
  • tests/test_utils/test_namespace.py
  • tests/test_utils/test_progress.py

Comment thread .github/workflows/test_wasm.yml
The smoke test pinned pyodide@314.0.2 via npm while the new full-suite
job installed the 314.0.3 xbuildenv, so the two jobs validated different
releases. Both now read PYODIDE_VERSION from one workflow-level env.
@d-chambers

Copy link
Copy Markdown
Contributor Author

CodeRabbit review addressed:

  • Pyodide version mismatch — fixed here. PYODIDE_VERSION is now a single workflow-level env (314.0.3) that both test_wasm and test_wasm_suite read, so the npm smoke test and the xbuildenv suite exercise the same release. Verified locally: the smoke script passes against pyodide@314.0.3 on node 24.
  • persist-credentials on the new checkout — split out to Stop persisting GITHUB_TOKEN in CI checkouts #788. Every other actions/checkout in the repo had the same issue, so Stop persisting GITHUB_TOKEN in CI checkouts #788 fixes all 12 steps at once instead of leaving the pattern half-applied here.

The inline seeding loop walked the whole Pooch registry, including
whale_1.hdf5, which LARGE_REGISTRY_FILES excludes from the test
parametrization and no selected test reads. Reuse
.github/scripts/cache_test_data.py, which the mamba action already uses,
so the job skips the large download and the Actions cache stays small.
@coderabbitai coderabbitai Bot added the IO Work for reading/writing different formats label Jul 26, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
.github/workflows/test_wasm.yml (1)

91-96: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Pin the pyodide-build toolchain version.

The Pyodide runtime is pinned to 314.0.3, but this install resolves the latest pyodide-build on every run. Pin an exact, tested compatible version or install it from a lockfile to prevent future CLI/xbuildenv drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test_wasm.yml around lines 91 - 96, Update the “Create
Pyodide virtual environment” step to install an exact, tested pyodide-build
version compatible with the pinned PYODIDE_VERSION 0.26.3, or source that
version from the project’s lockfile, before invoking pyodide xbuildenv and venv
commands.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/test_wasm.yml:
- Around line 91-96: Update the “Create Pyodide virtual environment” step to
install an exact, tested pyodide-build version compatible with the pinned
PYODIDE_VERSION 0.26.3, or source that version from the project’s lockfile,
before invoking pyodide xbuildenv and venv commands.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9fefec5e-fa33-48d3-accb-4f0d9a9d0ed8

📥 Commits

Reviewing files that changed from the base of the PR and between 315296d and 3e19de6.

📒 Files selected for processing (1)
  • .github/workflows/test_wasm.yml

The runtime was pinned to 314.0.3 but pyodide-build resolved to whatever
was newest at run time, so a toolchain release could change the
xbuildenv underneath a pinned runtime. Pin 0.37.0, the version the last
green run installed.
@d-chambers

Copy link
Copy Markdown
Contributor Author

Latest CodeRabbit nitpick addressed in 625da47: pyodide-build is now pinned via PYODIDE_BUILD_VERSION: '0.37.0' instead of resolving whatever is newest at run time. 0.37.0 is the version the last green test_wasm_suite run installed, so the pin matches a proven-good toolchain rather than an untested guess. Pinning the runtime while floating the builder left the xbuildenv able to change underneath a pinned PYODIDE_VERSION, which defeats the point of the earlier version alignment.

@d-chambers
d-chambers merged commit 062ab5b into dev Jul 26, 2026
27 of 28 checks passed
@d-chambers
d-chambers deleted the wasm-full-suite branch July 26, 2026 17:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI continuous integration IO Work for reading/writing different formats

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant