fix: flaky test #619
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Rust | |
| on: | |
| push: | |
| branches: ["main"] | |
| pull_request: | |
| branches: ["main"] | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| # Quick check: every sub-crate compiles independently with default features. | |
| # This catches broken inter-crate boundaries before the heavier coverage job | |
| # exercises the full umbrella. | |
| check_subcrates: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| crate: | |
| - stochastic-rs-core | |
| - stochastic-rs-distributions | |
| - stochastic-rs-stochastic | |
| - stochastic-rs-copulas | |
| - stochastic-rs-stats | |
| - stochastic-rs-quant | |
| - stochastic-rs-ai | |
| - stochastic-rs-viz | |
| steps: | |
| - uses: actions/checkout@v3 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Cargo check (${{ matrix.crate }}) | |
| run: cargo check -p ${{ matrix.crate }} --no-default-features | |
| # Umbrella + features matrix. Covers feature combinations that exercise | |
| # cross-crate cfg-gated code (notably the python+gpu Array2 overlap caught | |
| # by the 2026-05-07 audit §4.1). | |
| check_umbrella: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| features: | |
| - "" # default | |
| - "ai" | |
| - "openblas" | |
| - "yahoo" | |
| - "python" | |
| - "gpu" | |
| - "python,gpu" | |
| - "openblas,ai,yahoo" | |
| steps: | |
| - uses: actions/checkout@v3 | |
| - name: Install OpenBLAS (when needed) | |
| if: contains(matrix.features, 'openblas') | |
| run: sudo apt-get update && sudo apt-get install -y libopenblas-dev | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Cargo check (features=${{ matrix.features }}) | |
| run: | | |
| if [ -z "${{ matrix.features }}" ]; then | |
| cargo check -p stochastic-rs --all-targets | |
| else | |
| cargo check -p stochastic-rs --all-targets --features "${{ matrix.features }}" | |
| fi | |
| # Lint + format gates. Catches drift independently of the feature matrix. | |
| lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v3 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy, rustfmt | |
| - name: cargo fmt --check | |
| run: cargo fmt --all -- --check | |
| - name: cargo clippy (workspace) | |
| run: cargo clippy --workspace --no-default-features --all-targets -- -D warnings | |
| # In-process Python smoke test (audit §7.9): builds the cdylib via | |
| # `maturin develop` and exercises the 5 main API surfaces (distributions, | |
| # stochastic, copulas, stats, quant) including seed-determinism for the | |
| # samplers that accept a `seed=` keyword. | |
| python_smoke: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v3 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.x" | |
| - name: Install OpenBLAS | |
| run: sudo apt-get update && sudo apt-get install -y libopenblas-dev | |
| # `maturin develop` requires an active virtualenv on Linux; the | |
| # ubuntu-latest runner does not have one by default. Create one and | |
| # invoke maturin / python via the venv's binaries (no `source` so | |
| # each `run:` block — which is its own shell — still picks it up). | |
| - name: Set up venv + install build deps | |
| run: | | |
| python -m venv .venv | |
| .venv/bin/pip install --upgrade pip | |
| .venv/bin/pip install maturin numpy pytest scipy | |
| - name: maturin develop (openblas) | |
| run: .venv/bin/maturin develop --release --features openblas | |
| - name: Run python_smoke.py | |
| # `-u` flushes prints so the last `[OK]`/`[FAIL]` line before any | |
| # crash shows up in the log; `-X faulthandler` prints a C+Python | |
| # traceback on SIGSEGV, pinning the crashing native call. | |
| run: .venv/bin/python -X faulthandler -u stochastic-rs-py/python_smoke.py | |
| - name: Run pytest suite | |
| # The pytest suite (stochastic-rs-py/tests/) covers all five API | |
| # surfaces beyond the smoke script; conftest.py skips with a clear | |
| # message if the extension is somehow not importable. | |
| run: .venv/bin/python -m pytest stochastic-rs-py/tests/ -v | |
| # Fast correctness gate: the whole workspace test-suite via cargo-nextest | |
| # (no coverage instrumentation), so a failing/flaky test surfaces in minutes | |
| # instead of only inside the multi-hour coverage job. The `ci` nextest | |
| # profile prints one line per finished test with a running `(N/total)` | |
| # counter, so the log shows which test is executing and how far along the | |
| # run is. | |
| test: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 90 | |
| steps: | |
| - uses: actions/checkout@v3 | |
| - name: Install OpenBLAS | |
| run: sudo apt-get update && sudo apt-get install -y libopenblas-dev | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install nextest | |
| uses: taiki-e/install-action@v2 | |
| with: | |
| tool: nextest | |
| # `--release` because the heavy Monte-Carlo tests (Malliavin, rBergomi, | |
| # Fourier-Malliavin) are unusably slow unoptimised; `--exclude | |
| # stochastic-rs-py` because the cdylib is exercised by pytest in the | |
| # python_smoke job. Mirrors the coverage job's feature set for parity. | |
| - name: cargo nextest run (workspace) | |
| run: cargo nextest run --workspace --exclude stochastic-rs-py --features openblas --profile ci | |
| # nextest does not run doctests; this gates the crates' doc examples | |
| # (e.g. the README quickstart rendered on docs.rs) that plain `cargo | |
| # test` used to cover. `--release` reuses the artifacts built above. | |
| - name: cargo test --doc (workspace) | |
| run: cargo test --release --workspace --exclude stochastic-rs-py --features openblas --doc | |
| # Full test run with openblas + coverage. Heavyweight + instrumented, so it | |
| # only runs on pushes to main; the fast `test` job gates pull requests. | |
| test_with_coverage: | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'push' | |
| timeout-minutes: 240 | |
| steps: | |
| - uses: actions/checkout@v3 | |
| - name: Install system dependencies | |
| run: sudo apt-get update && sudo apt-get install -y libopenblas-dev | |
| - uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| components: llvm-tools-preview | |
| - name: Add llvm-tools to PATH | |
| run: echo "${HOME}/.rustup/toolchains/$(rustup show active-toolchain | cut -d' ' -f1)/bin" >> $GITHUB_PATH | |
| - name: Install grcov | |
| run: cargo install grcov | |
| - name: Install nextest | |
| uses: taiki-e/install-action@v2 | |
| with: | |
| tool: nextest | |
| - name: Clean Build Artifacts | |
| run: cargo clean | |
| - name: Run tests with coverage | |
| env: | |
| CARGO_INCREMENTAL: "0" | |
| CARGO_PROFILE_RELEASE_LTO: "false" | |
| CARGO_PROFILE_RELEASE_CODEGEN_UNITS: "1" | |
| CARGO_PROFILE_RELEASE_DEBUG: "1" | |
| RUSTFLAGS: "-C instrument-coverage" | |
| LLVM_PROFILE_FILE: "coverage-%p-%m.profraw" | |
| # nextest runs each test in its own process (one profraw per test, kept | |
| # unique by the %p pid above) and the `ci-coverage` profile prints the | |
| # running (N/total) counter; grcov then merges every profraw as before. | |
| run: | | |
| cargo nextest run --release --workspace --exclude stochastic-rs-py --features openblas --profile ci-coverage | |
| - name: Generate coverage report | |
| run: | | |
| grcov . \ | |
| --binary-path ./target/release/ \ | |
| -s . \ | |
| -t lcov \ | |
| --branch \ | |
| --ignore-not-existing \ | |
| --llvm \ | |
| -o lcov.info | |
| # nextest writes one profraw per test (~1300 files, >1 GB total). | |
| # grcov has merged them into lcov.info above, so delete them — else | |
| # the Codecov upload sweeps up a gigabyte of raw profiling data | |
| # instead of the single lcov report, which Codecov cannot parse. | |
| find . -name '*.profraw' -delete | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: lcov.info | |
| # Upload only lcov.info; do not let the CLI discover/attach anything | |
| # else in the tree (belt-and-braces with the profraw cleanup above). | |
| disable_search: true | |
| fail_ci_if_error: true | |
| verbose: true | |
| env: | |
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} |