diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 00000000..86561778 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,99 @@ +name: test + +on: + pull_request: + push: + branches: [main] + +jobs: + python-test: + name: Run python tests and collect coverage + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: cache poetry + uses: actions/cache@v4 + with: + path: ~/.local + key: ${{ runner.os }}-poetry + + - name: install poetry + run: pip install poetry + + - name: install package + run: poetry install --with test + + - name: run tests + run: | + poetry run pytest \ + --mpl --mpl-default-tolerance=10 \ + --cov=pyrenew_hew --cov-report=term --cov-report=xml . + + - name: Upload results to Codecov + uses: codecov/codecov-action@v5 + with: + env_vars: OS,PYTHON + fail_ci_if_error: true + flags: python-tests + files: coverage.xml + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true + + r-test: + name: Run R tests and collect coverage + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::covr, any::xml2 + needs: coverage + working-directory: hewr + + - name: Test coverage + run: | + cov <- covr::package_coverage( + path = "hewr", + quiet = FALSE, + clean = FALSE, + install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") + ) + covr::to_cobertura(cov) + shell: Rscript {0} + + - uses: codecov/codecov-action@v5 + with: + fail_ci_if_error: true + files: ./cobertura.xml + plugins: noop + flags: r-tests + disable_search: true + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Show testthat output + if: always() + run: | + find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true + shell: bash + + - name: Upload test results + if: failure() + uses: actions/upload-artifact@v4 + with: + name: coverage-test-failures + path: ${{ runner.temp }}/package diff --git a/.gitignore b/.gitignore index 761c387b..e8d168e6 100644 --- a/.gitignore +++ b/.gitignore @@ -216,6 +216,8 @@ cython_debug/ # https://github.com/github/gitignore/blob/main/R.gitignore .Rprofile +renv/ +renv.lock # History files .Rhistory diff --git a/pyproject.toml b/pyproject.toml index 094da9c2..e37307d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,14 @@ pygit2 = "^1.16.0" azuretools = {git = "https://github.com/cdcgov/cfa-azuretools"} forecasttools = {git = "https://github.com/CDCgov/forecasttools-py"} +[tool.poetry.group.test] +optional = true + +[tool.poetry.group.test.dependencies] +pytest = "^8.3.2" +pytest-cov = "^5.0.0" +pytest-mpl = "^0.17.0" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 00000000..7763b3cf --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,27 @@ +""" +Tests for the pyrenew-hew utils. +""" + +import jax.numpy as jnp +import pytest + +from pyrenew_hew.utils import convert_to_logmean_log_sd + + +@pytest.mark.parametrize( + ["mean", "sd"], + [ + [ + jnp.array([10]), + jnp.array([0]), + ] + ], +) +def test_convert_to_logmean_log_sd_edge_case_zero_sd(mean, sd): + logmean, logsd = convert_to_logmean_log_sd(mean, sd) + + expected_logmean = jnp.log(10.0) + expected_logsd = 0.0 + + assert jnp.isclose(logmean, expected_logmean) + assert jnp.isclose(logsd, expected_logsd)