smoke-optimiser 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- smoke_optimiser-0.1.0/.github/workflows/ci.yml +32 -0
- smoke_optimiser-0.1.0/.github/workflows/release.yml +72 -0
- smoke_optimiser-0.1.0/.gitignore +15 -0
- smoke_optimiser-0.1.0/CHANGELOG.md +69 -0
- smoke_optimiser-0.1.0/Justfile +82 -0
- smoke_optimiser-0.1.0/LICENSE +21 -0
- smoke_optimiser-0.1.0/PKG-INFO +143 -0
- smoke_optimiser-0.1.0/README.md +90 -0
- smoke_optimiser-0.1.0/pyproject.toml +140 -0
- smoke_optimiser-0.1.0/smoke_optimiser/__init__.py +1 -0
- smoke_optimiser-0.1.0/smoke_optimiser/__main__.py +9 -0
- smoke_optimiser-0.1.0/smoke_optimiser/cli.py +249 -0
- smoke_optimiser-0.1.0/smoke_optimiser/config.py +152 -0
- smoke_optimiser-0.1.0/smoke_optimiser/environment.py +86 -0
- smoke_optimiser-0.1.0/smoke_optimiser/optimiser/__init__.py +0 -0
- smoke_optimiser-0.1.0/smoke_optimiser/optimiser/filters.py +111 -0
- smoke_optimiser-0.1.0/smoke_optimiser/optimiser/greedy.py +189 -0
- smoke_optimiser-0.1.0/smoke_optimiser/optimiser/models.py +38 -0
- smoke_optimiser-0.1.0/smoke_optimiser/plugin.py +126 -0
- smoke_optimiser-0.1.0/smoke_optimiser/profiler/__init__.py +0 -0
- smoke_optimiser-0.1.0/smoke_optimiser/profiler/models.py +112 -0
- smoke_optimiser-0.1.0/smoke_optimiser/profiler/parser.py +138 -0
- smoke_optimiser-0.1.0/smoke_optimiser/profiler/runner.py +218 -0
- smoke_optimiser-0.1.0/smoke_optimiser/py.typed +0 -0
- smoke_optimiser-0.1.0/smoke_optimiser/reports/__init__.py +0 -0
- smoke_optimiser-0.1.0/smoke_optimiser/reports/repro.py +47 -0
- smoke_optimiser-0.1.0/smoke_optimiser/reports/smoke_suite.py +144 -0
- smoke_optimiser-0.1.0/smoke_optimiser/reports/summary.py +84 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
validate:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
env:
|
|
11
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v6
|
|
14
|
+
|
|
15
|
+
- uses: actions/setup-python@v6
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.13"
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v8.0.0
|
|
21
|
+
|
|
22
|
+
- name: Install just
|
|
23
|
+
uses: taiki-e/install-action@v2
|
|
24
|
+
with:
|
|
25
|
+
tool: just
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: uv sync
|
|
29
|
+
|
|
30
|
+
# Pre-commit logic is defined in the Justfile `precommit` target — do not add steps here.
|
|
31
|
+
- name: Validate project
|
|
32
|
+
run: just precommit
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
pull-requests: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release-please:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
outputs:
|
|
16
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
17
|
+
pr: ${{ steps.release.outputs.pr }}
|
|
18
|
+
steps:
|
|
19
|
+
- uses: googleapis/release-please-action@v4
|
|
20
|
+
id: release
|
|
21
|
+
env:
|
|
22
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
23
|
+
with:
|
|
24
|
+
release-type: python
|
|
25
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
26
|
+
|
|
27
|
+
update-lockfile:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
needs: release-please
|
|
30
|
+
if: ${{ needs.release-please.outputs.pr }}
|
|
31
|
+
permissions:
|
|
32
|
+
contents: write
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v6
|
|
35
|
+
with:
|
|
36
|
+
ref: release-please--branches--main
|
|
37
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
38
|
+
|
|
39
|
+
- name: Install uv
|
|
40
|
+
uses: astral-sh/setup-uv@v8.0.0
|
|
41
|
+
|
|
42
|
+
- name: Update lock file
|
|
43
|
+
run: uv lock
|
|
44
|
+
|
|
45
|
+
- name: Commit updated lock file
|
|
46
|
+
run: |
|
|
47
|
+
git config user.name "github-actions[bot]"
|
|
48
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
49
|
+
git add uv.lock
|
|
50
|
+
git diff --staged --quiet || git commit -m "chore: update uv.lock for version bump"
|
|
51
|
+
git push
|
|
52
|
+
|
|
53
|
+
publish:
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
needs: release-please
|
|
56
|
+
if: ${{ needs.release-please.outputs.release_created }}
|
|
57
|
+
environment: pypi
|
|
58
|
+
permissions:
|
|
59
|
+
id-token: write # Mandatory for Trusted Publishing
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v6
|
|
62
|
+
env:
|
|
63
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
64
|
+
|
|
65
|
+
- name: Install uv
|
|
66
|
+
uses: astral-sh/setup-uv@v8.0.0
|
|
67
|
+
|
|
68
|
+
- name: Build package
|
|
69
|
+
run: uv build
|
|
70
|
+
|
|
71
|
+
- name: Publish to PyPI
|
|
72
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2026-04-28)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add --iterations argument to average test durations across multiple runs ([e85b02c](https://github.com/lawther/smoke-optimiser/commit/e85b02ce94a195bf8510a6d64e889da544fe6826))
|
|
9
|
+
* add --src top-level argument for coverage target with conflict validation ([e6a1373](https://github.com/lawther/smoke-optimiser/commit/e6a13737aed3e1f96c6149036aed4ae3044e19dd))
|
|
10
|
+
* add CLI argument parsing and config merging ([6b77dde](https://github.com/lawther/smoke-optimiser/commit/6b77dde50b42ffd71c5ed8a544f1eb6af439defa))
|
|
11
|
+
* add config model with pyproject.toml loading ([e7f85a7](https://github.com/lawther/smoke-optimiser/commit/e7f85a7fa3eb1e629dba839770cbf1a76044abec))
|
|
12
|
+
* add coverage JSON parser with streaming support ([1cad88f](https://github.com/lawther/smoke-optimiser/commit/1cad88f83c2d107ce2da481f2643548f254b45a3))
|
|
13
|
+
* add full suite coverage metric to summary reports ([db6f218](https://github.com/lawther/smoke-optimiser/commit/db6f218420b748206ad377324f9a0af5d053552d))
|
|
14
|
+
* add greedy set-cover optimiser ([0015142](https://github.com/lawther/smoke-optimiser/commit/00151429c2ef326384286c2dc3777a11465016c0))
|
|
15
|
+
* add heuristic coverage target discovery with warning ([9ada322](https://github.com/lawther/smoke-optimiser/commit/9ada322039e51a8218e6020a507a1bde017cad51))
|
|
16
|
+
* Add Justfile for development task automation ([6aa80bc](https://github.com/lawther/smoke-optimiser/commit/6aa80bc3d41f85f9aa0f74c2e8f7ab333a668666))
|
|
17
|
+
* add machine environment capture ([b8ef0f9](https://github.com/lawther/smoke-optimiser/commit/b8ef0f92e58eecd0d52949a50a8e5343136919b7))
|
|
18
|
+
* add optimiser data models ([4f6bbf4](https://github.com/lawther/smoke-optimiser/commit/4f6bbf4ae1a4a21dfe2fffac1496296eb445cc3b))
|
|
19
|
+
* add optimiser filters (include/exclude/failing) ([5b8c049](https://github.com/lawther/smoke-optimiser/commit/5b8c0492686a08703caa7d73684cc2a7c7369f7d))
|
|
20
|
+
* add plugin summary header showing smoke suite metadata ([7eabb0c](https://github.com/lawther/smoke-optimiser/commit/7eabb0cd806b58db89e40b6bec2619bfce8ea4d4))
|
|
21
|
+
* add profiler data models ([0ece94c](https://github.com/lawther/smoke-optimiser/commit/0ece94cf4d93f7d39217d35d94522f030b902000))
|
|
22
|
+
* add profiler runner (pytest + coverage orchestration) ([dd9b6ae](https://github.com/lawther/smoke-optimiser/commit/dd9b6ae3396afc471809dd52cfa7f097b1edb2d6))
|
|
23
|
+
* add pytest plugin option registration and smoke suite loading ([44eee93](https://github.com/lawther/smoke-optimiser/commit/44eee93464936724cf79e2e2f19b941f82b6b0f6))
|
|
24
|
+
* add report generation (JSON and human-readable) ([4f761b6](https://github.com/lawther/smoke-optimiser/commit/4f761b6ab4a690f20f616ef3d61b4a691a2de2a5))
|
|
25
|
+
* add test collection filtering for --smoke ([f1ab66e](https://github.com/lawther/smoke-optimiser/commit/f1ab66edfdec290db8b83d6d4007bd610ce6de55))
|
|
26
|
+
* allow matching include/exclude filters by test function name ([b85bcd0](https://github.com/lawther/smoke-optimiser/commit/b85bcd066c0d6936c67a7ef28f1d1c36cc9d6ce5))
|
|
27
|
+
* enable direct installation of `smoke-optimiser`, update README instructions, and add MIT license. ([5c9c616](https://github.com/lawther/smoke-optimiser/commit/5c9c616a7133db88c7c43e8d4984ff7a1412f9cf))
|
|
28
|
+
* support comma-separated patterns in include and exclude filters ([985f878](https://github.com/lawther/smoke-optimiser/commit/985f87886beb16fbea560176efcdc2c5c454d16e))
|
|
29
|
+
* support file-level matching in include/exclude filters ([bb8f8db](https://github.com/lawther/smoke-optimiser/commit/bb8f8db337241952d085ccad221262357de24441))
|
|
30
|
+
* warn user about unmatched include/exclude patterns during optimization ([41cabaa](https://github.com/lawther/smoke-optimiser/commit/41cabaa457526812a15f31275512637b895c2700))
|
|
31
|
+
* wire CLI to profiler and optimiser phases ([d2a3534](https://github.com/lawther/smoke-optimiser/commit/d2a35345f87998db077e9a2aa40ab2a409b45433))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Bug Fixes
|
|
35
|
+
|
|
36
|
+
* arbitrary file overwrite (Symlink Attack) ([7e97075](https://github.com/lawther/smoke-optimiser/commit/7e97075139f4ddf64493c73a3e2face57c013c9a))
|
|
37
|
+
* capture heuristically discovered coverage target in repro command ([401f5d3](https://github.com/lawther/smoke-optimiser/commit/401f5d33a276e97d2d05289330b3dd434d749452))
|
|
38
|
+
* Enhance CLI message consistency and add pluralisation ([20a7c56](https://github.com/lawther/smoke-optimiser/commit/20a7c56ed7a711e7cdc4ed1fc7519d4fe010d6be))
|
|
39
|
+
* Fix B110 exception swallowing in config parser ([aa182f9](https://github.com/lawther/smoke-optimiser/commit/aa182f9e3d70e4e396914114423a0b7b6074a512))
|
|
40
|
+
* insecure relative path generation in hook output ([ec54988](https://github.com/lawther/smoke-optimiser/commit/ec54988462fea499c9ccb79f041bdbc24c7269ae))
|
|
41
|
+
* Predictable Temporary File Names in Project Root ([9ffdf7f](https://github.com/lawther/smoke-optimiser/commit/9ffdf7fc9866a8eb02b52bf4cdcd9e94a23be738))
|
|
42
|
+
* prevent information disclosure on invalid profile data ([6d3dfac](https://github.com/lawther/smoke-optimiser/commit/6d3dface5ed0267593c668cf7af8297293a66717))
|
|
43
|
+
* profiler hook loading and streaming parser robustness ([f8e7637](https://github.com/lawther/smoke-optimiser/commit/f8e7637cad0ec6540360b43305bd06361c46fb4c))
|
|
44
|
+
* refactor Justfile for reliable error reporting and 4-space indentation ([4a588ba](https://github.com/lawther/smoke-optimiser/commit/4a588bafd273f784219d16a39ed23566cd9cc951))
|
|
45
|
+
* refactor Justfile recipes for reliable error reporting and add linting ([8b96301](https://github.com/lawther/smoke-optimiser/commit/8b96301ef22b0b0b8420cb257ee500c87c7dda8f))
|
|
46
|
+
* refactor precommit recipe for reliable error reporting ([67f2918](https://github.com/lawther/smoke-optimiser/commit/67f29189b9045ea0dc93a20b7e30e43d654a5833))
|
|
47
|
+
* resolve conflicting coverage dynamic contexts and promote warnings to errors ([17a16dd](https://github.com/lawther/smoke-optimiser/commit/17a16ddd7c7b5d152ce92cdaf19f3e6124f545c7))
|
|
48
|
+
* resolve partial executable paths using `shutil.which` ([d3619b5](https://github.com/lawther/smoke-optimiser/commit/d3619b58f5b8236ad23dbdb993fa0b0183f7ac62))
|
|
49
|
+
* resolve ty check errors and enforce type safety in models ([c3579e9](https://github.com/lawther/smoke-optimiser/commit/c3579e9805726befd28b047cc9b41b778b243ed8))
|
|
50
|
+
* suppress confusing coverage.py JSON report message in runner ([0d567e8](https://github.com/lawther/smoke-optimiser/commit/0d567e88054335afad192f7c8fe3c2d1dcbdfac5))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### Performance Improvements
|
|
54
|
+
|
|
55
|
+
* Lazy greedy evaluation for set cover ([b9a325c](https://github.com/lawther/smoke-optimiser/commit/b9a325c96c94e0063e9d7f18f6620e1e0b81fe42))
|
|
56
|
+
* optimize equivalent group calculation by utilizing frozenset dictionary keys ([57da5a8](https://github.com/lawther/smoke-optimiser/commit/57da5a8bd02ac49fb4f87d40d33964a536697230))
|
|
57
|
+
* optimize greedy set cover selection algorithm in `optimise()` ([#4](https://github.com/lawther/smoke-optimiser/issues/4)) ([4318821](https://github.com/lawther/smoke-optimiser/commit/4318821fdab35abfa7c4bbf28421e1125792f6e3))
|
|
58
|
+
* Optimize slow test ID parsing in coverage JSON ([4cfe127](https://github.com/lawther/smoke-optimiser/commit/4cfe1272940f518ad142fa15b184a988562d9dba))
|
|
59
|
+
* pre-format branch strings in parser ([9cc27c8](https://github.com/lawther/smoke-optimiser/commit/9cc27c89e55836771a9643b91f2ed4e582ff2e3a))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
### Documentation
|
|
63
|
+
|
|
64
|
+
* add comprehensive README with usage and argument documentation ([3666db3](https://github.com/lawther/smoke-optimiser/commit/3666db30c11014692917b63d9b69c26bd3bac8b3))
|
|
65
|
+
* add readme, delete impl plans ([a412bf1](https://github.com/lawther/smoke-optimiser/commit/a412bf1f15fd936157fc4331b1b482d4688d64b8))
|
|
66
|
+
* be super explicit about linting rules ([759fa77](https://github.com/lawther/smoke-optimiser/commit/759fa77a94a2c40873eb0e66220e90d4f8fe6253))
|
|
67
|
+
* plan documents ([172ef92](https://github.com/lawther/smoke-optimiser/commit/172ef92b84a19df8edbc41d9b1755476f616c456))
|
|
68
|
+
|
|
69
|
+
## Changelog
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# List available recipes
|
|
2
|
+
default:
|
|
3
|
+
@just --list
|
|
4
|
+
|
|
5
|
+
# Run all checks (lint, typecheck, test)
|
|
6
|
+
check: lint typecheck test
|
|
7
|
+
|
|
8
|
+
# Run linting and formatting
|
|
9
|
+
lint:
|
|
10
|
+
uv run ruff format
|
|
11
|
+
uv run ruff check --fix
|
|
12
|
+
|
|
13
|
+
# Format the code
|
|
14
|
+
format:
|
|
15
|
+
uv run ruff format
|
|
16
|
+
|
|
17
|
+
# Run type checks
|
|
18
|
+
typecheck:
|
|
19
|
+
uv run ty check
|
|
20
|
+
|
|
21
|
+
# Run tests
|
|
22
|
+
test:
|
|
23
|
+
uv run pytest
|
|
24
|
+
|
|
25
|
+
# Run tests with coverage
|
|
26
|
+
test-cov:
|
|
27
|
+
uv run pytest --cov=smoke_optimiser
|
|
28
|
+
|
|
29
|
+
# Run formatting, linting and tests (quiet on success, shows errors on failure)
|
|
30
|
+
# This Justfile is the Single Source Of Truth (SSOT) for all pre-commit checks.
|
|
31
|
+
precommit:
|
|
32
|
+
#!/usr/bin/env bash
|
|
33
|
+
echo "Running precommit checks..."
|
|
34
|
+
uv lock --check || { echo "❌ uv.lock is out of sync with pyproject.toml"; exit 1; }
|
|
35
|
+
tmpfile=$(mktemp)
|
|
36
|
+
trap 'rm -f "$tmpfile"' EXIT
|
|
37
|
+
(
|
|
38
|
+
set -e
|
|
39
|
+
just _lint-justfile
|
|
40
|
+
uv run ruff format
|
|
41
|
+
uv run ruff check --fix
|
|
42
|
+
git add $(git diff --cached --name-only)
|
|
43
|
+
uv run ty check
|
|
44
|
+
uv run pytest
|
|
45
|
+
) > "$tmpfile" 2>&1
|
|
46
|
+
status=$?
|
|
47
|
+
if [ $status -ne 0 ]; then
|
|
48
|
+
cat "$tmpfile"
|
|
49
|
+
exit $status
|
|
50
|
+
fi
|
|
51
|
+
echo "✅ Precommit checks passed!"
|
|
52
|
+
|
|
53
|
+
# [private] Ensure Justfile recipes don't use && chains (which suppress set -e)
|
|
54
|
+
_lint-justfile:
|
|
55
|
+
#!/usr/bin/env bash
|
|
56
|
+
set -euo pipefail
|
|
57
|
+
violations=$(awk '
|
|
58
|
+
/^[[:space:]]+#!/ { in_shebang = 1 }
|
|
59
|
+
/^[^[:space:]]/ && NF > 0 { in_shebang = 0 }
|
|
60
|
+
!in_shebang && /&&/ && !/^[[:space:]]*#/ { print NR": "$0 }
|
|
61
|
+
' Justfile)
|
|
62
|
+
if [[ -n "$violations" ]]; then
|
|
63
|
+
echo "❌ Justfile recipes must not use && chains. Use separate lines for reliable error reporting."
|
|
64
|
+
echo "$violations"
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Setup the development environment from a fresh clone
|
|
69
|
+
setup-dev:
|
|
70
|
+
@uv sync
|
|
71
|
+
@just setup-git-hooks
|
|
72
|
+
@echo "✅ Development environment setup complete!"
|
|
73
|
+
|
|
74
|
+
# Setup local git hooks
|
|
75
|
+
setup-git-hooks:
|
|
76
|
+
@echo "Setting up local git hooks..."
|
|
77
|
+
@echo "#!/bin/sh" > .git/hooks/pre-commit
|
|
78
|
+
@echo "# This hook invokes the Justfile, which is the Single Source Of Truth for precommit logic." >> .git/hooks/pre-commit
|
|
79
|
+
@echo "# DO NOT add precommit logic here; add it to the 'precommit' recipe in the Justfile." >> .git/hooks/pre-commit
|
|
80
|
+
@echo "just precommit" >> .git/hooks/pre-commit
|
|
81
|
+
@chmod +x .git/hooks/pre-commit
|
|
82
|
+
@echo "✅ Git hooks set up!"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mike Lawther
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: smoke-optimiser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python CLI tool and pytest plugin to identify and run a minimal smoke test suite.
|
|
5
|
+
Project-URL: Homepage, https://github.com/lawther/smoke-optimiser
|
|
6
|
+
Project-URL: Repository, https://github.com/lawther/smoke-optimiser
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/lawther/smoke-optimiser/issues
|
|
8
|
+
Author-email: Mike Lawther <mike.lawther@gmail.com>
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2025 Mike Lawther
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: coverage,optimisation,pytest,smoke-test,testing
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: Intended Audience :: Developers
|
|
34
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
35
|
+
Classifier: Operating System :: OS Independent
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
39
|
+
Classifier: Topic :: Software Development :: Testing
|
|
40
|
+
Requires-Python: >=3.12
|
|
41
|
+
Requires-Dist: coverage~=7.0
|
|
42
|
+
Requires-Dist: ijson~=3.5
|
|
43
|
+
Requires-Dist: pydantic~=2.0
|
|
44
|
+
Requires-Dist: pytest-cov~=7.0
|
|
45
|
+
Requires-Dist: pytest~=9.0
|
|
46
|
+
Requires-Dist: typer~=0.9
|
|
47
|
+
Provides-Extra: dev
|
|
48
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
49
|
+
Requires-Dist: ruff~=0.15; extra == 'dev'
|
|
50
|
+
Provides-Extra: machine
|
|
51
|
+
Requires-Dist: psutil~=7.0; extra == 'machine'
|
|
52
|
+
Description-Content-Type: text/markdown
|
|
53
|
+
|
|
54
|
+
# smoke-optimiser
|
|
55
|
+
|
|
56
|
+
`smoke-optimiser` is a tool and pytest plugin that analyses your test suite to produce a minimal **smoke suite** — a subset of tests that delivers maximum code coverage in minimum wall-clock time.
|
|
57
|
+
|
|
58
|
+
It helps you find the "bang for buck" sweet spot: for example, achieving 80% of your total branch coverage in only 5% of the total runtime.
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
Add `smoke-optimiser` as a development dependency in your project:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uv add --dev smoke-optimiser
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or with pip:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install smoke-optimiser
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This will make the `smoke-optimiser` command available in your environment and register the pytest plugin automatically.
|
|
75
|
+
|
|
76
|
+
## Quickstart
|
|
77
|
+
|
|
78
|
+
1. **Generate the smoke suite**:
|
|
79
|
+
Run the optimiser in your project root. It will automatically detect your source code and profile your tests.
|
|
80
|
+
```bash
|
|
81
|
+
uv run smoke-optimiser
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
2. **Run the smoke suite**:
|
|
85
|
+
Use the `--smoke` flag with pytest to run only the selected high-value tests.
|
|
86
|
+
```bash
|
|
87
|
+
uv run pytest --smoke
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Common Usages
|
|
91
|
+
|
|
92
|
+
### Custom Efficiency Targets
|
|
93
|
+
By default, the tool tries to get maximum coverage within a 15-second time cap. You can tighten these bounds:
|
|
94
|
+
```bash
|
|
95
|
+
# Aim for 80% coverage, but stop if it takes longer than 5 seconds
|
|
96
|
+
uv run smoke-optimiser --target-cov=80 --time-cap=5
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Stabilising Timing Data
|
|
100
|
+
Test execution times can vary. Use `--iterations` to run the suite multiple times and average the results for a more stable smoke suite:
|
|
101
|
+
```bash
|
|
102
|
+
uv run smoke-optimiser --iterations=3
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Mandatory Inclusion/Exclusion
|
|
106
|
+
Force certain tests (or markers) to be included or excluded from the smoke suite:
|
|
107
|
+
```bash
|
|
108
|
+
# Always include authentication tests, but exclude anything marked as 'slow'
|
|
109
|
+
uv run smoke-optimiser --include="tests/test_auth.py" --exclude="@pytest.mark.slow"
|
|
110
|
+
```
|
|
111
|
+
*Multiple items can be separated by commas.*
|
|
112
|
+
|
|
113
|
+
## Command-line Arguments
|
|
114
|
+
|
|
115
|
+
### `smoke-optimiser` (Generator)
|
|
116
|
+
|
|
117
|
+
| Argument | Description | Default |
|
|
118
|
+
| :--- | :--- | :--- |
|
|
119
|
+
| `--src` | The source directory or package to measure coverage for. | *Discovered* |
|
|
120
|
+
| `--iterations` | Number of times to run profiling to average timing data. | `1` |
|
|
121
|
+
| `--time-cap` | Maximum wall-clock runtime (seconds) of the smoke suite. | `15.0` |
|
|
122
|
+
| `--target-cov` | Target % of the full suite's branch coverage to achieve. | `100.0` |
|
|
123
|
+
| `--include` | Comma-separated list of tests, files, or markers to force include. | `[]` |
|
|
124
|
+
| `--exclude` | Comma-separated list of tests, files, or markers to force exclude. | `[]` |
|
|
125
|
+
| `--pytest-args` | Extra arguments forwarded to pytest during profiling. | `""` |
|
|
126
|
+
| `--output-json` | Path for the generated smoke suite definition file. | `.smoke_suite.json` |
|
|
127
|
+
| `--profile-only` | Run only the profiling phase and save intermediate data. | `False` |
|
|
128
|
+
| `--optimise-only` | Run only the optimisation phase using existing profile data. | `False` |
|
|
129
|
+
| `--allow-ordered` | Suppress warning when `pytest-randomly` is not installed. | `False` |
|
|
130
|
+
|
|
131
|
+
### `pytest` (Plugin)
|
|
132
|
+
|
|
133
|
+
| Argument | Description | Default |
|
|
134
|
+
| :--- | :--- | :--- |
|
|
135
|
+
| `--smoke` | Activates the plugin; filters collection to the smoke suite. | `False` |
|
|
136
|
+
| `--smoke-file-path` | Path to the smoke suite JSON file to use. | `.smoke_suite.json` |
|
|
137
|
+
|
|
138
|
+
## How it works
|
|
139
|
+
|
|
140
|
+
1. **Profiling**: It runs your suite with `pytest-cov` and a custom hook to map every single branch execution to specific tests.
|
|
141
|
+
2. **Analysis**: It calculates the "efficiency" of every test (New Branches Covered / Duration).
|
|
142
|
+
3. **Greedy Selection**: It iteratively picks the most efficient test until your coverage target or time cap is reached.
|
|
143
|
+
4. **Redundancy Reporting**: It identifies "Coverage-equivalent groups" — sets of tests that cover the exact same logic.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# smoke-optimiser
|
|
2
|
+
|
|
3
|
+
`smoke-optimiser` is a tool and pytest plugin that analyses your test suite to produce a minimal **smoke suite** — a subset of tests that delivers maximum code coverage in minimum wall-clock time.
|
|
4
|
+
|
|
5
|
+
It helps you find the "bang for buck" sweet spot: for example, achieving 80% of your total branch coverage in only 5% of the total runtime.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add `smoke-optimiser` as a development dependency in your project:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv add --dev smoke-optimiser
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or with pip:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install smoke-optimiser
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This will make the `smoke-optimiser` command available in your environment and register the pytest plugin automatically.
|
|
22
|
+
|
|
23
|
+
## Quickstart
|
|
24
|
+
|
|
25
|
+
1. **Generate the smoke suite**:
|
|
26
|
+
Run the optimiser in your project root. It will automatically detect your source code and profile your tests.
|
|
27
|
+
```bash
|
|
28
|
+
uv run smoke-optimiser
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
2. **Run the smoke suite**:
|
|
32
|
+
Use the `--smoke` flag with pytest to run only the selected high-value tests.
|
|
33
|
+
```bash
|
|
34
|
+
uv run pytest --smoke
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Common Usages
|
|
38
|
+
|
|
39
|
+
### Custom Efficiency Targets
|
|
40
|
+
By default, the tool tries to get maximum coverage within a 15-second time cap. You can tighten these bounds:
|
|
41
|
+
```bash
|
|
42
|
+
# Aim for 80% coverage, but stop if it takes longer than 5 seconds
|
|
43
|
+
uv run smoke-optimiser --target-cov=80 --time-cap=5
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Stabilising Timing Data
|
|
47
|
+
Test execution times can vary. Use `--iterations` to run the suite multiple times and average the results for a more stable smoke suite:
|
|
48
|
+
```bash
|
|
49
|
+
uv run smoke-optimiser --iterations=3
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Mandatory Inclusion/Exclusion
|
|
53
|
+
Force certain tests (or markers) to be included or excluded from the smoke suite:
|
|
54
|
+
```bash
|
|
55
|
+
# Always include authentication tests, but exclude anything marked as 'slow'
|
|
56
|
+
uv run smoke-optimiser --include="tests/test_auth.py" --exclude="@pytest.mark.slow"
|
|
57
|
+
```
|
|
58
|
+
*Multiple items can be separated by commas.*
|
|
59
|
+
|
|
60
|
+
## Command-line Arguments
|
|
61
|
+
|
|
62
|
+
### `smoke-optimiser` (Generator)
|
|
63
|
+
|
|
64
|
+
| Argument | Description | Default |
|
|
65
|
+
| :--- | :--- | :--- |
|
|
66
|
+
| `--src` | The source directory or package to measure coverage for. | *Discovered* |
|
|
67
|
+
| `--iterations` | Number of times to run profiling to average timing data. | `1` |
|
|
68
|
+
| `--time-cap` | Maximum wall-clock runtime (seconds) of the smoke suite. | `15.0` |
|
|
69
|
+
| `--target-cov` | Target % of the full suite's branch coverage to achieve. | `100.0` |
|
|
70
|
+
| `--include` | Comma-separated list of tests, files, or markers to force include. | `[]` |
|
|
71
|
+
| `--exclude` | Comma-separated list of tests, files, or markers to force exclude. | `[]` |
|
|
72
|
+
| `--pytest-args` | Extra arguments forwarded to pytest during profiling. | `""` |
|
|
73
|
+
| `--output-json` | Path for the generated smoke suite definition file. | `.smoke_suite.json` |
|
|
74
|
+
| `--profile-only` | Run only the profiling phase and save intermediate data. | `False` |
|
|
75
|
+
| `--optimise-only` | Run only the optimisation phase using existing profile data. | `False` |
|
|
76
|
+
| `--allow-ordered` | Suppress warning when `pytest-randomly` is not installed. | `False` |
|
|
77
|
+
|
|
78
|
+
### `pytest` (Plugin)
|
|
79
|
+
|
|
80
|
+
| Argument | Description | Default |
|
|
81
|
+
| :--- | :--- | :--- |
|
|
82
|
+
| `--smoke` | Activates the plugin; filters collection to the smoke suite. | `False` |
|
|
83
|
+
| `--smoke-file-path` | Path to the smoke suite JSON file to use. | `.smoke_suite.json` |
|
|
84
|
+
|
|
85
|
+
## How it works
|
|
86
|
+
|
|
87
|
+
1. **Profiling**: It runs your suite with `pytest-cov` and a custom hook to map every single branch execution to specific tests.
|
|
88
|
+
2. **Analysis**: It calculates the "efficiency" of every test (New Branches Covered / Duration).
|
|
89
|
+
3. **Greedy Selection**: It iteratively picks the most efficient test until your coverage target or time cap is reached.
|
|
90
|
+
4. **Redundancy Reporting**: It identifies "Coverage-equivalent groups" — sets of tests that cover the exact same logic.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "smoke-optimiser"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
|
+
description = "A Python CLI tool and pytest plugin to identify and run a minimal smoke test suite."
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Mike Lawther", email = "mike.lawther@gmail.com" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["pytest", "testing", "smoke-test", "coverage", "optimisation"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Software Development :: Testing",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"typer~=0.9",
|
|
28
|
+
"pydantic~=2.0",
|
|
29
|
+
"coverage~=7.0",
|
|
30
|
+
"pytest~=9.0",
|
|
31
|
+
"pytest-cov~=7.0",
|
|
32
|
+
"ijson~=3.5",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
"Homepage" = "https://github.com/lawther/smoke-optimiser"
|
|
37
|
+
"Repository" = "https://github.com/lawther/smoke-optimiser"
|
|
38
|
+
"Bug Tracker" = "https://github.com/lawther/smoke-optimiser/issues"
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
machine = ["psutil~=7.0"]
|
|
42
|
+
dev = ["ruff~=0.15", "pytest"]
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
smoke-optimiser = "smoke_optimiser.__main__:main"
|
|
46
|
+
|
|
47
|
+
[project.entry-points."pytest11"]
|
|
48
|
+
smoke_optimiser = "smoke_optimiser.plugin"
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
line-length = 120
|
|
52
|
+
# these are explanation of the rules that are selected:
|
|
53
|
+
# B: Basic checks
|
|
54
|
+
# C4: Code complexity checks
|
|
55
|
+
# D: Docstring checks
|
|
56
|
+
# E: Error checks
|
|
57
|
+
# F: Formatting checks
|
|
58
|
+
# I: Import checks
|
|
59
|
+
# S: Bandit checks
|
|
60
|
+
# ICN: Import convention checks
|
|
61
|
+
# PGH: Python grammar checks
|
|
62
|
+
# PL: Pylint checks
|
|
63
|
+
# RUF: Ruff checks
|
|
64
|
+
# UP: Unused code checks
|
|
65
|
+
# ANN: Type annotation checks
|
|
66
|
+
# ASYNC: Async code checks
|
|
67
|
+
# SIM: Simple checks
|
|
68
|
+
# LOG: Logging checks
|
|
69
|
+
lint.select = [
|
|
70
|
+
"B",
|
|
71
|
+
"C4",
|
|
72
|
+
"D",
|
|
73
|
+
"E",
|
|
74
|
+
"F",
|
|
75
|
+
"I",
|
|
76
|
+
"S",
|
|
77
|
+
"ICN",
|
|
78
|
+
"PGH",
|
|
79
|
+
"PL",
|
|
80
|
+
"RUF",
|
|
81
|
+
"UP",
|
|
82
|
+
"ANN",
|
|
83
|
+
"ASYNC",
|
|
84
|
+
"SIM",
|
|
85
|
+
"LOG",
|
|
86
|
+
]
|
|
87
|
+
lint.ignore = [
|
|
88
|
+
# pydocstyle
|
|
89
|
+
# D1: Missing docstring in public module
|
|
90
|
+
"D100",
|
|
91
|
+
# D2: Missing docstring in public package
|
|
92
|
+
"D104",
|
|
93
|
+
# D3: Missing docstring in public function
|
|
94
|
+
"D103",
|
|
95
|
+
# D4: Missing docstring in public package
|
|
96
|
+
"D107",
|
|
97
|
+
# D5: Missing docstring in public method
|
|
98
|
+
"D102",
|
|
99
|
+
# D6: Missing docstring in public class
|
|
100
|
+
"D101",
|
|
101
|
+
# D7: Missing docstring in __init__
|
|
102
|
+
"D107",
|
|
103
|
+
# pylint
|
|
104
|
+
# PLR09: Too many <...>
|
|
105
|
+
"PLR0912",
|
|
106
|
+
"PLR0913",
|
|
107
|
+
"PLR0915",
|
|
108
|
+
]
|
|
109
|
+
lint.per-file-ignores = { "tests/*" = ["S101"] }
|
|
110
|
+
lint.pydocstyle.convention = "google"
|
|
111
|
+
|
|
112
|
+
[tool.pytest.ini_options]
|
|
113
|
+
testpaths = ["tests"]
|
|
114
|
+
pythonpath = ["."]
|
|
115
|
+
timeout = 10
|
|
116
|
+
filterwarnings = [
|
|
117
|
+
"error::RuntimeWarning",
|
|
118
|
+
"error::pytest.PytestUnraisableExceptionWarning",
|
|
119
|
+
"error::pytest_cov.engine.CentralCovContextWarning",
|
|
120
|
+
"error::coverage.exceptions.CoverageWarning",
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
[tool.hatch.build.targets.sdist]
|
|
124
|
+
exclude = [
|
|
125
|
+
"/.claude",
|
|
126
|
+
"/CLAUDE.md",
|
|
127
|
+
"/GEMINI.md",
|
|
128
|
+
"/PRD-smoke-optimiser.md",
|
|
129
|
+
"/uv.lock",
|
|
130
|
+
"/tests",
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
[dependency-groups]
|
|
134
|
+
dev = [
|
|
135
|
+
"psutil>=7.2.2",
|
|
136
|
+
"pytest-randomly~=4.0",
|
|
137
|
+
"pytest-timeout~=2.4",
|
|
138
|
+
"ruff>=0.15.4",
|
|
139
|
+
"ty>=0.0.25",
|
|
140
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|