Budgeted local test selection
Project description
covopt
covopt is a small command-line tool for budgeted local test selection.
It reads a diff from stdin, loads a precomputed model describing which tests are informative for which parts of the repository, and emits a subset of tests that maximizes expected signal under a runtime budget.
The intended workflow is:
git diff | covopt select -t 10 | xargs pytest
This is not a full test runner and does not integrate with pytest directly. Its only job is to:
- parse changed code from a diff,
- map those changes into an internal repository-space representation,
- score candidate tests by expected value,
- penalize redundant tests,
- choose a subset that fits a wall-clock budget,
- print selected test IDs to
stdout, one per line.
Motivation
Large test suites often contain many tests that are highly correlated. For local iteration, running all tests is too expensive, while running only “affected” tests can still waste time on near-duplicates.
covopt treats test selection as a budgeted optimization problem:
- Return: expected defect-detection signal in changed areas
- Cost: estimated runtime of a test
- Risk: residual untested change exposure
- Correlation: redundancy between tests
The goal is to select a small, diverse, high-value subset of tests for fast feedback, while leaving full validation to CI.
Core idea
The repository is modeled as a feature space over code regions such as files, modules, classes, or functions.
Each test has:
- a sparse signal vector over that space,
- an estimated runtime cost,
- optional metadata such as historical failures or flakiness.
A diff is converted into a weighted change vector over the same space.
Selection then solves:
- maximize coverage of changed regions,
- prefer tests with high marginal gain per second,
- avoid picking multiple tests with nearly identical signal,
- stop when the budget is exhausted.
In practice this is implemented as a budgeted greedy optimizer over a diminishing-returns objective.
Non-goals
For the first version, this project does not aim to provide:
- direct
pytestplugin support, - dynamic collection of coverage data,
- mutation testing,
- distributed execution,
- CI orchestration,
- perfect safety guarantees.
This is a local feedback accelerator, not a replacement for the full test suite.
CLI
Basic usage
git diff | covopt select -t 10
Prints selected test node IDs to stdout.
Example
git diff HEAD~1 | covopt select -t 10 | xargs pytest
Arguments
usage: covopt select [-h] -t SECONDS [-n N] [--verbose]
Optional
-
-n NRestrict optimization to the topNcandidate tests after initial scoring. -
--verboseEmit diagnostics tostderr.
Input / output contract
Input
stdin must contain a unified diff, for example from:
git diff
git diff HEAD~1
git show <commit>
Output
By default, the tool prints one test per line:
tests/unit/foo/test_parser.py::test_basic_parse
tests/unit/bar/test_config.py::test_defaults
tests/integration/api/test_health.py::test_healthcheck
This makes it easy to pipe into xargs pytest.
With --verbose, the tool emits structured output including estimated total runtime and selection scores to stderr. Errors are always written to stderr.
Model format
The selector consumes a model containing:
- repository feature definitions,
- per-test sparse signal vectors,
- pairwise or cluster-level redundancy information,
- test runtime estimates,
- optional weights and calibration parameters.
A minimal conceptual schema looks like this:
{
"features": [
"pkg.module_a",
"pkg.module_a.fn_x",
"pkg.module_b",
"pkg.module_b.ClassY.method_z"
],
"tests": {
"tests/unit/test_a.py::test_one": {
"cost": 0.8,
"signal": {
"pkg.module_a": 0.9,
"pkg.module_a.fn_x": 1.0
}
}
}
}
The model is stored in a .covopt file that is generated when invoking the tool.
The exact on-disk format is an implementation detail and may evolve.
Selection algorithm
At a high level:
- Parse the diff from
stdin - Map changed files / hunks / symbols to repository features
- Build a weighted change vector
- Score candidate tests by overlap with the change vector
- Iteratively pick the test with the best marginal gain / cost
- Apply diminishing returns to already-covered regions
- Penalize redundant tests using similarity
- Stop when the next test would exceed the budget
A typical utility function is:
utility(S) = sum over features j of w_j * f(sum over tests i in S of A_ij)
Where:
w_jis the change weight for featurejA_ijis the signal of testion featurejf(...)is a saturating function so duplicate tests add less value
This makes the selector naturally prefer diverse tests over repeated variants of the same test shape.
Project structure
covopt/
__main__.py # CLI entrypoint
cli.py # argv parsing and I/O orchestration
diff_parser.py # unified diff parsing
feature_space.py # repo feature mapping
scoring.py # initial candidate scoring
optimize.py # budgeted greedy selection
output.py # stdout / json formatting
Exit behavior
0: successful selection- non-zero: invalid arguments, malformed diff, or internal failure
Diagnostics should go to stderr. Selected tests should go to stdout only.
Design principles
- Fast startup: suitable for local shell pipelines
- Deterministic: same diff => same selection
- Composable: works well with Unix pipes
- Model-driven: selection logic is decoupled from model construction
- Conservative stdout: only emit test IDs unless JSON mode is requested
Example workflow
git diff | covopt select -t 10 | xargs pytest
Full CI still runs the complete suite:
pytest
Future work
- richer repository feature extraction
- learned test-value calibration from historical failures
- improved redundancy modeling
- coverage/model builders
- optional
pytestintegration - support for multiple optimization strategies
Status
Early-stage experimental project focused on the core algorithm and CLI. The first milestone is a reliable selector that can consume a diff, apply a time budget, and emit a useful non-redundant subset of tests for local development.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file covopt-0.1.0a0.tar.gz.
File metadata
- Download URL: covopt-0.1.0a0.tar.gz
- Upload date:
- Size: 30.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f64f153a801f9308308059c9e077e371dcce118af46293fe3ea7ec6b1f581549
|
|
| MD5 |
978bd590febcfd2d5729ecae1d789216
|
|
| BLAKE2b-256 |
24d313270e529ca506628fb68ddd3bb3ba26eea40dccc21dd53b123bd14edbd9
|
File details
Details for the file covopt-0.1.0a0-py3-none-any.whl.
File metadata
- Download URL: covopt-0.1.0a0-py3-none-any.whl
- Upload date:
- Size: 11.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bdbc97e445044e45be6ed95aa1e858d5d0e1924da0027506732932101fceb796
|
|
| MD5 |
10bc42e333a678578f95128e328cfdb0
|
|
| BLAKE2b-256 |
6f97d152a2137f7e503fdd125808e98550d65689309b277c97d61aa614348b1a
|