testprune 0.1.0 → 0.3.0
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.
- checksums.yaml +4 -4
- data/README.md +65 -26
- data/lib/testprune/analysis.rb +1 -1
- data/lib/testprune/cli.rb +45 -6
- data/lib/testprune/runner.rb +12 -0
- data/lib/testprune/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e0524c23f51b9afdbb9731dee285f81be5571abcff4730b09194865edcb01d0d
|
|
4
|
+
data.tar.gz: 648d2e72e49dc16a083b7ca116889e4694fdaa72e6e1337207bdc7da7376260f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 762a0cf8e298644d06094814b81e9579312c16247a649013da282207623e88349a0918a95657f9a33125fb9b0a029369630071806b484b8c5869f4979dafc858
|
|
7
|
+
data.tar.gz: a245409289d680cfe06bc08ad2afdc004ae1d542f9e2895e70d0840834852f8ebf75d95215129f1afd7c58d84b34925864d5af02ae34ae933b70c34aa35a0a75
|
data/README.md
CHANGED
|
@@ -11,9 +11,12 @@
|
|
|
11
11
|
```sh
|
|
12
12
|
gem install testprune # no Gemfile change required
|
|
13
13
|
|
|
14
|
-
testprune
|
|
14
|
+
testprune scan # runs your suite once, records per-test coverage
|
|
15
15
|
testprune report # see what's redundant — read-only
|
|
16
16
|
testprune apply # approve removals → .patch file → git apply
|
|
17
|
+
|
|
18
|
+
# or all at once:
|
|
19
|
+
testprune prune # scan + apply in a single step
|
|
17
20
|
```
|
|
18
21
|
|
|
19
22
|
Works with **Minitest** and **RSpec**. No config files. No changes to your project required.
|
|
@@ -54,7 +57,7 @@ end
|
|
|
54
57
|
|
|
55
58
|
```sh
|
|
56
59
|
bundle install
|
|
57
|
-
bundle exec testprune
|
|
60
|
+
bundle exec testprune scan
|
|
58
61
|
```
|
|
59
62
|
|
|
60
63
|
---
|
|
@@ -64,20 +67,46 @@ bundle exec testprune run
|
|
|
64
67
|
### Step 1 — Capture
|
|
65
68
|
|
|
66
69
|
```sh
|
|
67
|
-
#
|
|
68
|
-
|
|
70
|
+
# No args: autodetects framework; prompts to exclude slow/integration folders
|
|
71
|
+
# (any test/ subdirectory matching: selenium, request, piper, integration)
|
|
72
|
+
testprune scan
|
|
73
|
+
|
|
74
|
+
# Positional paths — no `--` needed
|
|
75
|
+
testprune scan test/api test/services test/models
|
|
76
|
+
testprune scan test/models test/jobs -s app -s lib
|
|
69
77
|
|
|
70
78
|
# Explicit command (pass after --)
|
|
71
|
-
testprune
|
|
72
|
-
testprune
|
|
73
|
-
testprune
|
|
79
|
+
testprune scan -- bundle exec rspec spec/models
|
|
80
|
+
testprune scan -- bundle exec ruby -Itest test/my_test.rb
|
|
81
|
+
testprune scan -- bundle exec rails test test/controllers/
|
|
74
82
|
|
|
75
83
|
# Restrict which source files are analyzed (-s is repeatable)
|
|
76
|
-
testprune
|
|
84
|
+
testprune scan -s app -s lib -s packs
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
When run with no arguments, `testprune scan` checks your `test/` directory and flags any subdirectories whose names contain `selenium`, `request`, `piper`, or `integration` — folders that tend to be slow, browser-driven, or external and are generally poor candidates for coverage analysis. You'll be prompted to exclude them before the run starts:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
testprune: found folders that may be slow or integration-heavy:
|
|
91
|
+
test/integration
|
|
92
|
+
test/selenium
|
|
93
|
+
Include them in this run? [y/N]:
|
|
77
94
|
```
|
|
78
95
|
|
|
96
|
+
Answering `N` (the default) scopes the run to the remaining folders using `rails test`. Answering `y` proceeds with the normal autodetected command.
|
|
97
|
+
|
|
79
98
|
Writes `tmp/.testprune/run.json` — per-test coverage deltas and wall times. This is the only step that boots your suite.
|
|
80
99
|
|
|
100
|
+
### One-step workflow
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
testprune prune # scan + apply in sequence — boots suite, then prompts for approval
|
|
104
|
+
testprune prune test/models test/jobs -s app -s lib
|
|
105
|
+
testprune prune -- bundle exec rails test test/models/
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`prune` runs `scan` to capture coverage data, then immediately runs `apply` — which prints the report and prompts before writing any patch. Use this when you want the full workflow without running three separate commands.
|
|
109
|
+
|
|
81
110
|
### Step 2 — Report
|
|
82
111
|
|
|
83
112
|
```sh
|
|
@@ -154,7 +183,7 @@ Removed tests are **commented out** (not deleted) with a reason annotation — y
|
|
|
154
183
|
│
|
|
155
184
|
▼
|
|
156
185
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
157
|
-
│ CAPTURE (testprune
|
|
186
|
+
│ CAPTURE (testprune scan) │
|
|
158
187
|
│ │
|
|
159
188
|
│ Coverage.setup(lines:, branches:, methods:) │
|
|
160
189
|
│ │ │
|
|
@@ -318,37 +347,47 @@ removal** — testprune can't tell what it uniquely exercises.
|
|
|
318
347
|
### Minitest project (standard layout)
|
|
319
348
|
|
|
320
349
|
```sh
|
|
321
|
-
testprune
|
|
350
|
+
testprune scan -s app -s lib
|
|
322
351
|
testprune report -s app -s lib
|
|
323
352
|
testprune apply -s app -s lib
|
|
353
|
+
|
|
354
|
+
# or all at once:
|
|
355
|
+
testprune prune -s app -s lib
|
|
324
356
|
```
|
|
325
357
|
|
|
326
358
|
### RSpec project
|
|
327
359
|
|
|
328
360
|
```sh
|
|
329
|
-
testprune
|
|
361
|
+
testprune scan -s app -s lib -- bundle exec rspec
|
|
330
362
|
testprune report -s app -s lib
|
|
331
363
|
```
|
|
332
364
|
|
|
333
365
|
### Rails app
|
|
334
366
|
|
|
335
367
|
```sh
|
|
336
|
-
#
|
|
337
|
-
testprune
|
|
368
|
+
# Scan with no args — testprune will prompt to exclude selenium/integration folders
|
|
369
|
+
testprune scan -s app -s lib
|
|
370
|
+
|
|
371
|
+
# Scope to specific directories (positional args, no -- needed)
|
|
372
|
+
testprune scan -s app -s lib test/models
|
|
373
|
+
testprune scan -s app -s lib test/models test/jobs test/services
|
|
338
374
|
|
|
339
|
-
#
|
|
340
|
-
testprune
|
|
341
|
-
|
|
342
|
-
#
|
|
375
|
+
# Explicit command if you need full control
|
|
376
|
+
testprune scan -s app -s lib -s packs -- bundle exec rails test test/models/
|
|
377
|
+
|
|
378
|
+
# Full workflow in one step
|
|
379
|
+
testprune prune -s app -s lib test/models test/jobs
|
|
343
380
|
```
|
|
344
381
|
|
|
382
|
+
> **Note:** `run.json` is overwritten on each `testprune scan`. Run report/apply after each capture pass.
|
|
383
|
+
|
|
345
384
|
### Rails app with Spring
|
|
346
385
|
|
|
347
386
|
Spring preloads your app but forks the test process — the forked child doesn't
|
|
348
387
|
inherit `RUBYOPT` where testprune's autostart lives. Disable it for the run:
|
|
349
388
|
|
|
350
389
|
```sh
|
|
351
|
-
DISABLE_SPRING=1 testprune
|
|
390
|
+
DISABLE_SPRING=1 testprune scan -s app -s lib -- bundle exec rails test test/models/
|
|
352
391
|
```
|
|
353
392
|
|
|
354
393
|
### Projects using SimpleCov (or other coverage gems)
|
|
@@ -379,11 +418,11 @@ rv run --ruby 3.2 env RUBYOPT="-I$(gem contents testprune | grep autostart | xar
|
|
|
379
418
|
|
|
380
419
|
# Simpler: install testprune under the managed ruby so RUBYOPT is not needed
|
|
381
420
|
rv run --ruby 3.2 gem install testprune
|
|
382
|
-
rv run --ruby 3.2 bundle exec testprune
|
|
421
|
+
rv run --ruby 3.2 bundle exec testprune scan
|
|
383
422
|
```
|
|
384
423
|
|
|
385
424
|
The easiest path is always to install testprune under the same Ruby that runs
|
|
386
|
-
your suite. If `TESTPRUNE_DEBUG=1 testprune
|
|
425
|
+
your suite. If `TESTPRUNE_DEBUG=1 testprune scan` prints nothing from `[testprune-debug]`,
|
|
387
426
|
the autostart never loaded — this is the cause.
|
|
388
427
|
|
|
389
428
|
### Monorepo / packs
|
|
@@ -392,7 +431,7 @@ Run each pack separately and analyze with its source path:
|
|
|
392
431
|
|
|
393
432
|
```sh
|
|
394
433
|
# Capture one pack
|
|
395
|
-
testprune
|
|
434
|
+
testprune scan -s packs/tenancy/app -- \
|
|
396
435
|
bundle exec rails test packs/tenancy/test/
|
|
397
436
|
|
|
398
437
|
# Report for that pack
|
|
@@ -427,7 +466,7 @@ jobs:
|
|
|
427
466
|
ruby-version: '3.2'
|
|
428
467
|
bundler-cache: true
|
|
429
468
|
- run: gem install testprune
|
|
430
|
-
- run: testprune
|
|
469
|
+
- run: testprune scan -s app -s lib -- bundle exec rails test test/models/
|
|
431
470
|
- run: testprune report -s app -s lib --json > testprune-report.json
|
|
432
471
|
- uses: actions/upload-artifact@v4
|
|
433
472
|
with:
|
|
@@ -462,9 +501,9 @@ testprune report -s app -s lib || true
|
|
|
462
501
|
|
|
463
502
|
| Variable | Effect |
|
|
464
503
|
|----------|--------|
|
|
465
|
-
| `TESTPRUNE_ROOT` | Set the project root (default: `Dir.pwd`). Set automatically by `testprune
|
|
466
|
-
| `TESTPRUNE_SOURCE_PATHS` | Colon-separated source paths. Set automatically by `testprune
|
|
467
|
-
| `TESTPRUNE_OUTPUT_DIR` | Output directory. Set automatically by `testprune
|
|
504
|
+
| `TESTPRUNE_ROOT` | Set the project root (default: `Dir.pwd`). Set automatically by `testprune scan`. |
|
|
505
|
+
| `TESTPRUNE_SOURCE_PATHS` | Colon-separated source paths. Set automatically by `testprune scan`. |
|
|
506
|
+
| `TESTPRUNE_OUTPUT_DIR` | Output directory. Set automatically by `testprune scan`. |
|
|
468
507
|
| `TESTPRUNE_DEBUG` | Print adapter-load diagnostics (`[testprune-debug] autostart loaded in pid …`). Useful when capture produces no `run.json`. |
|
|
469
508
|
| `DISABLE_SPRING` | Disable Spring preloader so the test process inherits testprune's instrumentation. |
|
|
470
509
|
|
|
@@ -494,7 +533,7 @@ overhead. On very large suites (10k+ tests) this is noticeable but acceptable
|
|
|
494
533
|
it's mitigated by restricting `--source` to the paths you care about.
|
|
495
534
|
|
|
496
535
|
**run.json is machine-local.** Coverage paths are absolute. Don't run
|
|
497
|
-
`testprune
|
|
536
|
+
`testprune scan` on CI and `testprune report` on a laptop with a different home
|
|
498
537
|
directory — the paths won't match. Always run all three commands on the same
|
|
499
538
|
machine.
|
|
500
539
|
|
data/lib/testprune/analysis.rb
CHANGED
|
@@ -29,7 +29,7 @@ module Testprune
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
unless File.exist?(@config.run_file)
|
|
32
|
-
raise Error, "no
|
|
32
|
+
raise Error, "no coverage data found. Run `testprune run` first to capture per-test coverage."
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
run = begin
|
data/lib/testprune/cli.rb
CHANGED
|
@@ -9,18 +9,22 @@ module Testprune
|
|
|
9
9
|
# report analyzes run.json and prints grouped candidates (read-only)
|
|
10
10
|
# apply prompts for approval, then writes a removal patch (never edits in place)
|
|
11
11
|
class CLI
|
|
12
|
+
NOISY_PATTERNS = %w[selenium request piper integration].freeze
|
|
13
|
+
|
|
12
14
|
BANNER = <<~TXT
|
|
13
15
|
testprune — audit a Ruby test suite for redundant coverage
|
|
14
16
|
|
|
15
17
|
Usage:
|
|
16
|
-
testprune
|
|
18
|
+
testprune scan [options] [-- <test command>]
|
|
17
19
|
testprune report [options]
|
|
18
20
|
testprune apply [options]
|
|
21
|
+
testprune prune [options] [-- <test command>]
|
|
19
22
|
|
|
20
23
|
Commands:
|
|
21
|
-
|
|
24
|
+
scan Run the target suite instrumented; capture per-test coverage + timing
|
|
22
25
|
report Analyze captured data and print removal candidates (read-only)
|
|
23
26
|
apply Review candidates, ask for approval, emit a git-applyable patch
|
|
27
|
+
prune Run scan + apply in one step (the full workflow)
|
|
24
28
|
|
|
25
29
|
Options:
|
|
26
30
|
-s, --source PATH Source dir to analyze (repeatable; default: app, lib)
|
|
@@ -40,9 +44,10 @@ module Testprune
|
|
|
40
44
|
argv = argv.dup
|
|
41
45
|
command = argv.shift
|
|
42
46
|
case command
|
|
43
|
-
when '
|
|
47
|
+
when 'scan' then cmd_scan(argv)
|
|
44
48
|
when 'report' then cmd_report(argv)
|
|
45
49
|
when 'apply' then cmd_apply(argv)
|
|
50
|
+
when 'prune' then cmd_prune(argv)
|
|
46
51
|
when '-v', '--version' then puts(Testprune::VERSION)
|
|
47
52
|
when nil, '-h', '--help' then puts(BANNER)
|
|
48
53
|
else
|
|
@@ -88,12 +93,46 @@ module Testprune
|
|
|
88
93
|
end
|
|
89
94
|
end
|
|
90
95
|
|
|
91
|
-
def
|
|
96
|
+
def cmd_scan(argv)
|
|
92
97
|
cmd_argv, test_command = split_test_command(argv)
|
|
93
|
-
opts, = parse_options(cmd_argv)
|
|
98
|
+
opts, paths = parse_options(cmd_argv)
|
|
94
99
|
apply_config(opts)
|
|
95
100
|
require_relative 'runner'
|
|
96
|
-
Runner.new(Testprune.config)
|
|
101
|
+
runner = Runner.new(Testprune.config)
|
|
102
|
+
if test_command.nil? && paths.empty?
|
|
103
|
+
test_command = prompt_noisy_exclusions(runner)
|
|
104
|
+
elsif test_command.nil?
|
|
105
|
+
test_command = runner.command_for_paths(paths)
|
|
106
|
+
end
|
|
107
|
+
runner.call(test_command)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def prompt_noisy_exclusions(runner)
|
|
111
|
+
test_dir = File.join(Testprune.config.root, 'test')
|
|
112
|
+
return nil unless File.directory?(test_dir)
|
|
113
|
+
|
|
114
|
+
all_subdirs = Dir.children(test_dir)
|
|
115
|
+
.select { |d| File.directory?(File.join(test_dir, d)) }
|
|
116
|
+
.sort
|
|
117
|
+
noisy = all_subdirs.select { |d| NOISY_PATTERNS.any? { |p| d.downcase.include?(p) } }
|
|
118
|
+
return nil if noisy.empty?
|
|
119
|
+
|
|
120
|
+
warn("testprune: found folders that may be slow or integration-heavy:")
|
|
121
|
+
noisy.each { |d| warn(" test/#{d}") }
|
|
122
|
+
$stderr.print("Include them in this run? [y/N]: ")
|
|
123
|
+
answer = $stdin.gets&.strip&.downcase
|
|
124
|
+
|
|
125
|
+
return nil if %w[y yes].include?(answer)
|
|
126
|
+
|
|
127
|
+
kept = (all_subdirs - noisy).map { |d| "test/#{d}" }
|
|
128
|
+
return nil if kept.empty?
|
|
129
|
+
|
|
130
|
+
runner.command_for_paths(kept)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def cmd_prune(argv)
|
|
134
|
+
cmd_scan(argv)
|
|
135
|
+
cmd_apply([])
|
|
97
136
|
end
|
|
98
137
|
|
|
99
138
|
def cmd_report(argv)
|
data/lib/testprune/runner.rb
CHANGED
|
@@ -39,6 +39,18 @@ module Testprune
|
|
|
39
39
|
ok
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
def command_for_paths(paths)
|
|
43
|
+
bundler = File.exist?(File.join(@config.root, 'Gemfile'))
|
|
44
|
+
prefix = bundler ? %w[bundle exec] : []
|
|
45
|
+
if File.directory?(File.join(@config.root, 'spec'))
|
|
46
|
+
prefix + %w[rspec] + paths
|
|
47
|
+
elsif File.exist?(File.join(@config.root, 'bin', 'rails'))
|
|
48
|
+
prefix + %w[rails test] + paths
|
|
49
|
+
else
|
|
50
|
+
prefix + %w[rake test] + paths
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
42
54
|
private
|
|
43
55
|
|
|
44
56
|
def env
|
data/lib/testprune/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: testprune
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Seth MacPherson
|
|
@@ -83,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
83
83
|
- !ruby/object:Gem::Version
|
|
84
84
|
version: '0'
|
|
85
85
|
requirements: []
|
|
86
|
-
rubygems_version:
|
|
86
|
+
rubygems_version: 4.0.3
|
|
87
87
|
specification_version: 4
|
|
88
88
|
summary: Audits a Ruby test suite for duplicate/redundant coverage using Prism AST
|
|
89
89
|
+ Coverage data
|