Skip to main content

Run only the tests affected by your code changes.

Project description

Pintest

Run only the tests affected by your code changes - automatically!

Pintest integrates with your git workflow as a pre-commit hook, running only tests that cover the code you've changed. This dramatically speeds up your development cycle while ensuring quality.

๐ŸŽฏ Key Features

  • ๐Ÿš€ Fast: Run only affected tests, not the entire suite
  • ๐Ÿ”’ Safe: Blocks commits if affected tests fail
  • ๐Ÿ“Š Intelligent: Uses SQLite-based test-to-code mapping for instant lookups
  • ๐Ÿ”„ Incremental Coverage: Combines coverage from test runs
  • โœจ New Test Detection: Always runs newly added tests
  • ๐Ÿง  Auto-Discovery: Detects unmapped tests and builds mapping automatically
  • ๐ŸŽฃ Pre-commit Hook: Automatic integration with git workflow
  • ๐Ÿณ Docker Integration: Ensures containers are running before tests
  • ๐Ÿ”ง Preflight Checks: Validates database connections (postgres, neo4j)
  • ๐Ÿ“ฆ Git LFS Support: Shares pre-built mapping database across team (30-60min savings)
  • ๐Ÿš€ Auto-Push: Automatically pushes commits with updated mapping database

โšก Quick Start (Pre-Commit Hook)

cd ~/workspace/myproject

# Install the pre-commit hook
~/workspace/all/pintest/scripts/install_pre_commit.sh

# That's it! Now every commit will:
# 1. Find tests affected by your changes
# 2. Run only those tests
# 3. Block commit if tests fail
# 4. Combine coverage with existing data

๐Ÿ“‹ How It Works

Pre-Commit Flow

Developer commits changes
         โ†“
Pre-commit hook triggered
         โ†“
Compare staged changes vs development branch
         โ†“
Query SQLite mapping: "Which tests cover these lines?"
         โ†“
Detect unmapped tests (dry-run pytest --collect-only)
         โ†“
Run unmapped tests all-at-once (build mapping)
         โ†“
Run mapped affected tests with coverage
         โ†“
Tests pass? โ†’ Combine coverage โ†’ Allow commit โœ…
Tests fail? โ†’ Block commit โŒ

Auto-Discovery of Unmapped Tests

The pre-commit hook automatically detects tests that exist in your codebase but aren't in the mapping database yet:

  1. Collect all tests: Runs pytest --collect-only to find all available tests
  2. Compare with mapping: Queries .test_mapping.db to see which tests are already mapped
  3. Find delta: Identifies tests that exist but have never been run with coverage
  4. Run all-at-once: Executes all unmapped tests together with --cov-context=test (fastest)
  5. Update mapping: After completion, updates the mapping database with new coverage

Why this matters:

  • New tests you write are automatically added to the mapping
  • Tests added by teammates get mapped when you first commit
  • No need to manually regenerate the entire mapping
  • Mapping database grows organically over time
  • All tests run in a single pytest invocation (fastest possible)

Example:

$ git commit -m "Fix bug in auth.py"

๐Ÿ“ Found 1 changed Python file
๐Ÿ“Š Mapping DB: 1234 tests, 567 files, 45678 mappings
๐Ÿ” Collected 1250 total tests from pytest
โš ๏ธ  Found 16 unmapped test(s)

================================================================================
Building coverage mapping for 16 unmapped test(s)
================================================================================

  Running unmapped test: unit_tests/test_new_feature.py::test_case_1
    โœ“ Mapping updated for: unit_tests/test_new_feature.py::test_case_1
  Running unmapped test: unit_tests/test_new_feature.py::test_case_2
    โœ“ Mapping updated for: unit_tests/test_new_feature.py::test_case_2
  ...
โœ“ Successfully mapped 16 test(s)

================================================================================
Running 3 affected test(s)...
================================================================================
...

Mapping Database

The mapping is stored in .test_mapping.db (SQLite) at your repo root:

CREATE TABLE test_coverage (
    test_name TEXT,
    file_path TEXT,
    line_number INTEGER,
    PRIMARY KEY (test_name, file_path, line_number)
);

Fast lookups: "Which tests cover file X, line Y?"

๐Ÿ”ง Installation

Prerequisites

  • Python 3.8+
  • pytest >= 7.0
  • pytest-cov >= 4.0
  • Git repository

Step 1: Install Package

cd ~/workspace/all/pintest
pip install -e .

Step 2: Build Mapping Database

cd ~/workspace/myproject

# Build mapping database (resumable)
delta build-mapping --repo-root ~/workspace/myproject --verbose

If interrupted, resume with:

delta build-mapping --repo-root ~/workspace/myproject --resume --verbose

Step 3: Option for Batch Build from Existing Coverage (Advanced)

If you already have a .coverage file generated with --cov-context=test, you can build/update the database directly from it:

# Update mapping database using existing .coverage
delta update-mapping --repo-root ~/workspace/myproject --verbose

๐Ÿ“– See ITERATIVE_SETUP_GUIDE.md for detailed guide

Option C: Use Pre-built Database (Git LFS)

If your repo uses Git LFS to store .test_mapping.db, you can skip building:

# Install Git LFS (one-time)
brew install git-lfs  # macOS
# or: sudo apt-get install git-lfs  # Linux

# Initialize in repo
cd ~/workspace/myproject
git lfs install

# Pull the pre-built database
git lfs pull

Benefits:

  • โœ… Instant - no rebuild needed
  • โœ… Shared across team
  • โœ… Always available after clone/pull

See GIT_LFS_SETUP.md in your repo.

Step 4: Install Pre-Commit Hook

cd ~/workspace/myproject
~/workspace/all/pintest/scripts/install_pre_commit.sh

Done! Now every commit will run only affected tests.

๐Ÿ“– Usage

Pre-Commit Hook (Automatic)

Just commit normally:

git add src/my_module.py
git commit -m "Fix bug in authentication"

# Hook runs automatically:
# - Finds tests covering src/my_module.py
# - Runs only those tests
# - Blocks commit if tests fail
# - Combines coverage on success

Manual Test Running

# Show what tests would run
pintest run --repo-root ~/workspace/myproject --dry-run --verbose

# Run affected tests manually
pintest run --repo-root ~/workspace/myproject

# Compare against different branch
pintest run --repo-root ~/workspace/myproject --base-branch develop

# Pass pytest arguments
pintest run --repo-root ~/workspace/myproject -- -x --pdb

Update Mapping

Update the mapping database when you add new tests or change coverage significantly:

# Build/update mapping database
delta build-mapping --repo-root ~/workspace/myproject --verbose

After adding many tests (iterative resume):

# Resume where you left off (only runs unmapped tests)
delta build-mapping --repo-root ~/workspace/myproject --resume --verbose

When to update:

  • After adding new test files
  • After significant refactoring
  • Weekly (recommended for active projects)
  • When mapping feels "stale" (tests not being selected correctly)

๐ŸŽ›๏ธ Commands

pintest run

Run affected tests based on changes.

pintest run [OPTIONS] [-- PYTEST_ARGS]

Options:
  --repo-root PATH        Repository root (default: current directory)
  --base-branch BRANCH    Branch to compare against (default: master)
  --coverage-file PATH    Path to .coverage file
  --dry-run              Show tests without running
  --min-tests N          Minimum tests required
  -v, --verbose          Detailed output

pintest update-mapping

Update test mapping database from coverage data.

pintest update-mapping [OPTIONS]

Options:
  --repo-root PATH        Repository root (default: current directory)
  --coverage-file PATH    Source .coverage file
  --mapping-db PATH       Target mapping database path
  -v, --verbose          Detailed output

๐Ÿ” How Test Selection Works

1. Changed Files Detection

git diff --cached development...HEAD

Identifies:

  • Which Python files changed
  • Which lines were added/modified
  • Which files are test files

2. Test Mapping Lookup

SELECT DISTINCT test_name
FROM test_coverage
WHERE file_path = 'src/auth.py'
AND line_number IN (42, 43, 44)

Fast O(1) lookup using SQLite indexes.

3. Test Categories

Category Always Run? Example
New tests โœ… Yes test_new_feature.py (added in commit)
Modified tests โœ… Yes test_auth.py (changed in commit)
Coverage-based โœ… Yes Tests that cover changed lines
Unaffected โŒ No Tests not covering any changes

4. Coverage Combining

# Existing coverage from previous runs
.coverage (before)

# New coverage from affected tests
.coverage.new

# Combined result
.coverage (after) = .coverage (before) + .coverage.new

Uses coverage combine to merge data safely.

๐Ÿ’ก Examples

Example 1: Bug Fix

# You fix a bug in src/auth.py line 42
vim src/auth.py

git add src/auth.py
git commit -m "Fix login timeout bug"

# Hook runs:
# ๐Ÿ“ Found 1 changed Python file
# ๐Ÿ“Š Mapping DB: 1234 tests, 567 files
# ๐Ÿ“ src/auth.py: 3 test(s) cover changed lines
#     - unit_tests/test_auth.py::test_login_timeout
#     - unit_tests/test_auth.py::test_login_retry
#     - integration_tests/test_full_auth_flow.py::test_complete_flow
#
# Running 3 affected test(s)...
# โœ… All tests passed. Commit allowed.

Example 2: New Test Added

# You add a new test file
vim unit_tests/test_new_feature.py

git add unit_tests/test_new_feature.py
git commit -m "Add tests for new feature"

# Hook runs:
# โœจ New test files (always run): 1
#   + unit_tests/test_new_feature.py
#
# Running 1 test file...
# โœ… All tests passed. Commit allowed.

Example 3: Refactoring

# You refactor a utility function used everywhere
vim src/utils/helpers.py

git add src/utils/helpers.py
git commit -m "Refactor helper function"

# Hook runs:
# ๐Ÿ“ src/utils/helpers.py: 47 test(s) cover changed lines
#
# Running 47 affected test(s)...
# (Only runs 47, not all 1234 tests!)

๐Ÿšซ Bypassing the Hook

For urgent commits or when hook is broken:

git commit --no-verify -m "Urgent hotfix"

๐Ÿ”ง Configuration

Change Base Branch

Edit .git/hooks/pre-commit:

python3 -m delta.pre_commit_hook \
    --repo-root "$REPO_ROOT" \
    --base-branch main \  # Changed from development
    --verbose

Customize Coverage Options

The hook runs pytest with:

pytest --cov=. --cov-context=test --cov-append --cov-report=

To customize, edit delta/pre_commit_hook.py.

๐Ÿ“Š Statistics

Typical time savings:

Project Size Total Tests Avg Affected Time Before Time After Savings
Small (500 tests) 500 ~15 2 min 10 sec 92%
Medium (2000 tests) 2000 ~30 8 min 30 sec 94%
Large (5000 tests) 5000 ~50 20 min 1 min 95%

Actual savings depend on test distribution and change scope.

๐Ÿ› Troubleshooting

"Mapping database not found"

delta build-mapping --verbose

"No tests selected"

  • Check if .delta/test_mapping.db exists and is recent
  • Update mapping: delta build-mapping
  • Run with --verbose to see what's happening

"Tests that should run aren't selected"

Regenerate mapping:

delta build-mapping --verbose

"Hook runs all tests"

Check that mapping database exists:

ls -lh .delta/test_mapping.db

If missing, generate it with delta build-mapping.

๐Ÿ“ Files Created

  • .delta/test_mapping.db - SQLite database with test-to-code mappings (gitignored)
  • .coverage - pytest coverage data (gitignored)
  • .git/hooks/pre-commit - Pre-commit hook script

๐Ÿค Contributing

Delta is an open-source developer productivity tool.

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

deltatest_cli-0.4.16.tar.gz (56.6 kB view details)

Uploaded Source

Built Distribution

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

deltatest_cli-0.4.16-py3-none-any.whl (53.0 kB view details)

Uploaded Python 3

File details

Details for the file deltatest_cli-0.4.16.tar.gz.

File metadata

  • Download URL: deltatest_cli-0.4.16.tar.gz
  • Upload date:
  • Size: 56.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for deltatest_cli-0.4.16.tar.gz
Algorithm Hash digest
SHA256 df55bed47b8bfb59a19e20ce35b998d99ae1786b2e2c9482014ebe080884f411
MD5 dc5d97832a19400a08fe3ec7c47796d1
BLAKE2b-256 d552d3bc38b59b8728b36034b6998b21636c0730500940fe9dc3bdc2930375f3

See more details on using hashes here.

File details

Details for the file deltatest_cli-0.4.16-py3-none-any.whl.

File metadata

  • Download URL: deltatest_cli-0.4.16-py3-none-any.whl
  • Upload date:
  • Size: 53.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for deltatest_cli-0.4.16-py3-none-any.whl
Algorithm Hash digest
SHA256 7c9438f7d0fb93ea95cfc4e69922bba70022bd022b7d4cf4014f0b652a1293c7
MD5 8c09884653bfd7eea34047eab6607f21
BLAKE2b-256 ee174139981b0dbcfb1e39f2e3707f0bc9dc20a732687baf497349fff7e6438f

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