Skip to main content

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:

  1. parse changed code from a diff,
  2. map those changes into an internal repository-space representation,
  3. score candidate tests by expected value,
  4. penalize redundant tests,
  5. choose a subset that fits a wall-clock budget,
  6. 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 pytest plugin 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 N Restrict optimization to the top N candidate tests after initial scoring.

  • --verbose Emit diagnostics to stderr.

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:

  1. Parse the diff from stdin
  2. Map changed files / hunks / symbols to repository features
  3. Build a weighted change vector
  4. Score candidate tests by overlap with the change vector
  5. Iteratively pick the test with the best marginal gain / cost
  6. Apply diminishing returns to already-covered regions
  7. Penalize redundant tests using similarity
  8. 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_j is the change weight for feature j
  • A_ij is the signal of test i on feature j
  • f(...) 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 pytest integration
  • 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

covopt-0.1.0a2.tar.gz (30.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

covopt-0.1.0a2-py3-none-any.whl (11.7 kB view details)

Uploaded Python 3

File details

Details for the file covopt-0.1.0a2.tar.gz.

File metadata

  • Download URL: covopt-0.1.0a2.tar.gz
  • Upload date:
  • Size: 30.3 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

Hashes for covopt-0.1.0a2.tar.gz
Algorithm Hash digest
SHA256 8e94ab41dddf1b0d1a09b3a9fc9c77d385cf1dbc4754ef411d222c8d66ba6481
MD5 7b372c9fb33040921a7d336d9a8b1134
BLAKE2b-256 ab4aac002923b3fd0f95462f83a930360b5c5481b32eeee958e181166d97b3ff

See more details on using hashes here.

File details

Details for the file covopt-0.1.0a2-py3-none-any.whl.

File metadata

  • Download URL: covopt-0.1.0a2-py3-none-any.whl
  • Upload date:
  • Size: 11.7 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

Hashes for covopt-0.1.0a2-py3-none-any.whl
Algorithm Hash digest
SHA256 1862f31d779080fcd5ea25dff5cebbaab6d86772bd43f0a36645edcf654c1c1c
MD5 6ecacc7e0bbdb71bf897faf267b30783
BLAKE2b-256 2935c1ff17d0798ee2f6ed9401a1949f9fea897fdc9485e816d0be1d17886b81

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page