# Coverage and CI/CD How the test suite is measured for coverage, and how the automated DTAP streets move a change from a developer's machine to production. See also [Testing overview](/docs/testing.html) and [Running tests via make](/docs/testing-make.html). > Audience: administrators and maintainers. These pages are hidden from members and guests, in the sidebar, the search index, and the documentation export. ## Coverage Coverage is measured with `coverage.py` over the `devplacepy` package, configured in `.coveragerc`: - **Source** is `devplacepy`; `tests/*` and `sitecustomize.py` are omitted. - **`parallel = true`** so coverage data from subprocesses is collected and later combined. The end-to-end tests run the application as a separate Uvicorn subprocess: `COVERAGE_PROCESS_START` plus a `sitecustomize.py` hook starts coverage inside that server process, so browser-driven requests count toward coverage, not just the in-process tests. - **`sigterm = true`** so the subprocess flushes its coverage data when it is terminated at the end of the session. - **`show_missing = true`** lists the uncovered lines in the report. The `make` targets that produce coverage: | Target | What it does | |--------|--------------| | `make coverage` | Runs the full serial suite under coverage, then `coverage combine` and `coverage report`. | | `make coverage-headed` | The same, with a visible browser. | | `make coverage-html` | Runs `make coverage`, then `coverage html` and writes the browsable report to `htmlcov/index.html`. | The whole suite runs in a single process (serial execution is enforced globally in `pyproject.toml`), so `coverage combine` merges only two profiles: the test process and the application's Uvicorn subprocess. There are no per-worker profiles to reconcile, which keeps the combined report stable. ## Continuous integration: Gitea Actions The pipeline is defined in `.gitea/workflows/test.yaml` and runs on every **push and pull request to `master`**. Steps: 1. Check out the repository. 2. Set up Python 3.13. 3. Install dependencies: `pip install -e ".[dev]"` and `python -m playwright install chromium --with-deps`. 4. Run the full serial suite under coverage: `python -m coverage run -m pytest tests/`, with `PLAYWRIGHT_HEADLESS=1` and `COVERAGE_PROCESS_START` pointing at `.coveragerc`. The serial flags (`--tb=line -p no:xdist`) come from `addopts` in `pyproject.toml`. 5. Build the coverage report (`coverage combine`, `coverage report`, `coverage html`). This step runs with `if: always()`, so a report is produced even when tests fail. 6. Publish the `coverage-html` artifact (the `htmlcov/` directory), always. 7. On failure only, upload the `failure-screenshots` artifact from `/tmp/devplace_test_screenshots/` (saved by the `pytest_runtest_makereport` hook in `conftest.py`). `make coverage` locally reproduces exactly what CI runs, so you can confirm a green pipeline before pushing. ## The automated DTAP streets DevPlace promotes a change through automated Development, Test, Acceptance, and Production stages, wired to two branches (`master` and `production`) and the CI pipeline above: | Street | Where | What happens | |--------|-------|--------------| | **Development** | a developer's machine, `make dev` | Code is written and run against the shared local SQLite database with live reload. Locally, `make test` and `make coverage` are available before pushing. | | **Test** | Gitea Actions on `master` | Every push and pull request to `master` triggers the full suite (all three tiers - `tests/unit`, `tests/api`, `tests/e2e`) under coverage. This is the automated quality gate: the coverage report is published as an artifact and failure screenshots are captured. | | **Acceptance** | the `master` to `production` promotion | `make deploy` fast-forwards the release branch: `git checkout production && git merge master && git push origin production`. Only commits that are already green on `master` reach `production`, so the promotion is the controlled release gate. | | **Production** | the host running Docker Compose | The production host pulls the `production` branch and runs the nginx + app stack. Most updates are a `git pull` plus a restart because code is bind-mounted; the image is rebuilt only when dependencies change. See the [Production overview](/docs/production.html) section. | The streets are sequential and gated: nothing reaches `production` that has not passed the Test street on `master`, and the `production` branch is only ever fast-forwarded from tested `master` commits. Coverage is archived at the Test street on every run, so the coverage trend is always available as a CI artifact.