rspec-tracer 1.2.2 → 2.0.0.pre.1

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.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +197 -45
  3. data/README.md +439 -429
  4. data/bin/rspec-tracer +15 -0
  5. data/lib/rspec_tracer/cache/Rakefile +43 -0
  6. data/lib/rspec_tracer/cli/cache_clear.rb +98 -0
  7. data/lib/rspec_tracer/cli/cache_info.rb +103 -0
  8. data/lib/rspec_tracer/cli/doctor.rb +275 -0
  9. data/lib/rspec_tracer/cli/explain.rb +148 -0
  10. data/lib/rspec_tracer/cli/report_open.rb +82 -0
  11. data/lib/rspec_tracer/cli.rb +116 -0
  12. data/lib/rspec_tracer/configuration.rb +1100 -3
  13. data/lib/rspec_tracer/engine.rb +1076 -0
  14. data/lib/rspec_tracer/example.rb +21 -6
  15. data/lib/rspec_tracer/filter.rb +35 -0
  16. data/lib/rspec_tracer/line_stub.rb +61 -0
  17. data/lib/rspec_tracer/load_config.rb +2 -2
  18. data/lib/rspec_tracer/logger.rb +15 -0
  19. data/lib/rspec_tracer/rails/README.md +78 -0
  20. data/lib/rspec_tracer/rails/i18n_tracking.rb +137 -0
  21. data/lib/rspec_tracer/rails/notifications.rb +263 -0
  22. data/lib/rspec_tracer/rails/preset.rb +94 -0
  23. data/lib/rspec_tracer/rails/railtie.rb +22 -0
  24. data/lib/rspec_tracer/rails.rb +15 -0
  25. data/lib/rspec_tracer/remote_cache/README.md +140 -0
  26. data/lib/rspec_tracer/remote_cache/Rakefile +35 -11
  27. data/lib/rspec_tracer/remote_cache/archive.rb +137 -0
  28. data/lib/rspec_tracer/remote_cache/backend.rb +73 -0
  29. data/lib/rspec_tracer/remote_cache/git_ancestry.rb +241 -0
  30. data/lib/rspec_tracer/remote_cache/local_fs_backend.rb +439 -0
  31. data/lib/rspec_tracer/remote_cache/redis_backend.rb +554 -0
  32. data/lib/rspec_tracer/remote_cache/s3_backend.rb +712 -0
  33. data/lib/rspec_tracer/remote_cache/user_tasks.rb +397 -0
  34. data/lib/rspec_tracer/remote_cache/validator.rb +40 -62
  35. data/lib/rspec_tracer/remote_cache.rb +22 -0
  36. data/lib/rspec_tracer/reporters/README.md +103 -0
  37. data/lib/rspec_tracer/reporters/base.rb +87 -0
  38. data/lib/rspec_tracer/reporters/coverage_json_reporter.rb +338 -0
  39. data/lib/rspec_tracer/reporters/html/.gitignore +19 -0
  40. data/lib/rspec_tracer/reporters/html/.prettierignore +4 -0
  41. data/lib/rspec_tracer/reporters/html/.prettierrc.json +9 -0
  42. data/lib/rspec_tracer/reporters/html/README.md +80 -0
  43. data/lib/rspec_tracer/reporters/html/dist/assets/index.css +2 -0
  44. data/lib/rspec_tracer/reporters/html/dist/assets/index.js +1 -0
  45. data/lib/rspec_tracer/reporters/html/dist/index.html +24 -0
  46. data/lib/rspec_tracer/reporters/html/eslint.config.js +62 -0
  47. data/lib/rspec_tracer/reporters/html/package-lock.json +4941 -0
  48. data/lib/rspec_tracer/reporters/html/package.json +29 -0
  49. data/lib/rspec_tracer/reporters/html/src/app.jsx +130 -0
  50. data/lib/rspec_tracer/reporters/html/src/components/AllExamples.jsx +86 -0
  51. data/lib/rspec_tracer/reporters/html/src/components/DuplicateExamples.jsx +68 -0
  52. data/lib/rspec_tracer/reporters/html/src/components/ExamplesDependency.jsx +78 -0
  53. data/lib/rspec_tracer/reporters/html/src/components/FilesDependency.jsx +72 -0
  54. data/lib/rspec_tracer/reporters/html/src/components/FlakyExamples.jsx +42 -0
  55. data/lib/rspec_tracer/reporters/html/src/components/ReportTable.jsx +131 -0
  56. data/lib/rspec_tracer/reporters/html/src/components/SearchBar.jsx +19 -0
  57. data/lib/rspec_tracer/reporters/html/src/index.html +23 -0
  58. data/lib/rspec_tracer/reporters/html/src/main.jsx +37 -0
  59. data/lib/rspec_tracer/reporters/html/src/styles.css +434 -0
  60. data/lib/rspec_tracer/reporters/html/vite.config.js +42 -0
  61. data/lib/rspec_tracer/reporters/html_reporter.rb +266 -0
  62. data/lib/rspec_tracer/reporters/json_reporter.rb +88 -0
  63. data/lib/rspec_tracer/reporters/payload_builder.rb +235 -0
  64. data/lib/rspec_tracer/reporters/registry.rb +120 -0
  65. data/lib/rspec_tracer/reporters/terminal_reporter.rb +264 -0
  66. data/lib/rspec_tracer/rspec/README.md +73 -0
  67. data/lib/rspec_tracer/rspec/installation.rb +97 -0
  68. data/lib/rspec_tracer/rspec/metadata.rb +96 -0
  69. data/lib/rspec_tracer/rspec/parallel_tests.rb +459 -0
  70. data/lib/rspec_tracer/rspec/reporter_hook.rb +84 -0
  71. data/lib/rspec_tracer/rspec/runner_hook.rb +178 -0
  72. data/lib/rspec_tracer/source_file.rb +24 -7
  73. data/lib/rspec_tracer/storage/README.md +35 -0
  74. data/lib/rspec_tracer/storage/backend.rb +68 -0
  75. data/lib/rspec_tracer/storage/json_backend.rb +866 -0
  76. data/lib/rspec_tracer/storage/lazy_snapshot.rb +65 -0
  77. data/lib/rspec_tracer/storage/schema.rb +43 -0
  78. data/lib/rspec_tracer/storage/serializer/json.rb +41 -0
  79. data/lib/rspec_tracer/storage/serializer/msgpack.rb +90 -0
  80. data/lib/rspec_tracer/storage/snapshot.rb +127 -0
  81. data/lib/rspec_tracer/storage/sqlite_backend.rb +686 -0
  82. data/lib/rspec_tracer/time_formatter.rb +37 -18
  83. data/lib/rspec_tracer/tracker/README.md +36 -0
  84. data/lib/rspec_tracer/tracker/coverage_adapter.rb +174 -0
  85. data/lib/rspec_tracer/tracker/declared_globs.rb +100 -0
  86. data/lib/rspec_tracer/tracker/dependency_graph.rb +134 -0
  87. data/lib/rspec_tracer/tracker/env_matcher.rb +127 -0
  88. data/lib/rspec_tracer/tracker/env_snapshot.rb +77 -0
  89. data/lib/rspec_tracer/tracker/example_registry.rb +153 -0
  90. data/lib/rspec_tracer/tracker/file_digest.rb +61 -0
  91. data/lib/rspec_tracer/tracker/filter.rb +127 -0
  92. data/lib/rspec_tracer/tracker/input.rb +99 -0
  93. data/lib/rspec_tracer/tracker/io_hooks/file.rb +55 -0
  94. data/lib/rspec_tracer/tracker/io_hooks/io.rb +24 -0
  95. data/lib/rspec_tracer/tracker/io_hooks/json.rb +23 -0
  96. data/lib/rspec_tracer/tracker/io_hooks/kernel.rb +26 -0
  97. data/lib/rspec_tracer/tracker/io_hooks/yaml.rb +38 -0
  98. data/lib/rspec_tracer/tracker/io_hooks.rb +195 -0
  99. data/lib/rspec_tracer/tracker/loaded_files_tracker.rb +295 -0
  100. data/lib/rspec_tracer/tracker/new_file_detector.rb +62 -0
  101. data/lib/rspec_tracer/tracker/whole_suite_invalidators.rb +96 -0
  102. data/lib/rspec_tracer/version.rb +4 -1
  103. data/lib/rspec_tracer.rb +232 -381
  104. metadata +93 -43
  105. data/lib/rspec_tracer/cache.rb +0 -207
  106. data/lib/rspec_tracer/coverage_merger.rb +0 -42
  107. data/lib/rspec_tracer/coverage_reporter.rb +0 -187
  108. data/lib/rspec_tracer/coverage_writer.rb +0 -58
  109. data/lib/rspec_tracer/html_reporter/Rakefile +0 -18
  110. data/lib/rspec_tracer/html_reporter/assets/javascripts/application.js +0 -56
  111. data/lib/rspec_tracer/html_reporter/assets/javascripts/libraries/jquery.js +0 -10881
  112. data/lib/rspec_tracer/html_reporter/assets/javascripts/plugins/datatables.js +0 -15381
  113. data/lib/rspec_tracer/html_reporter/assets/stylesheets/application.css +0 -196
  114. data/lib/rspec_tracer/html_reporter/assets/stylesheets/plugins/datatables.css +0 -459
  115. data/lib/rspec_tracer/html_reporter/assets/stylesheets/plugins/jquery-ui.css +0 -436
  116. data/lib/rspec_tracer/html_reporter/assets/stylesheets/print.css +0 -92
  117. data/lib/rspec_tracer/html_reporter/assets/stylesheets/reset.css +0 -265
  118. data/lib/rspec_tracer/html_reporter/public/application.css +0 -5
  119. data/lib/rspec_tracer/html_reporter/public/application.js +0 -6
  120. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_asc.png +0 -0
  121. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_asc_disabled.png +0 -0
  122. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_both.png +0 -0
  123. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_desc.png +0 -0
  124. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_desc_disabled.png +0 -0
  125. data/lib/rspec_tracer/html_reporter/public/favicon.png +0 -0
  126. data/lib/rspec_tracer/html_reporter/public/loading.gif +0 -0
  127. data/lib/rspec_tracer/html_reporter/reporter.rb +0 -242
  128. data/lib/rspec_tracer/html_reporter/views/duplicate_examples.erb +0 -34
  129. data/lib/rspec_tracer/html_reporter/views/examples.erb +0 -58
  130. data/lib/rspec_tracer/html_reporter/views/examples_dependency.erb +0 -36
  131. data/lib/rspec_tracer/html_reporter/views/files_dependency.erb +0 -36
  132. data/lib/rspec_tracer/html_reporter/views/flaky_examples.erb +0 -38
  133. data/lib/rspec_tracer/html_reporter/views/layout.erb +0 -38
  134. data/lib/rspec_tracer/remote_cache/aws.rb +0 -176
  135. data/lib/rspec_tracer/remote_cache/cache.rb +0 -75
  136. data/lib/rspec_tracer/remote_cache/repo.rb +0 -210
  137. data/lib/rspec_tracer/report_generator.rb +0 -158
  138. data/lib/rspec_tracer/report_merger.rb +0 -68
  139. data/lib/rspec_tracer/report_writer.rb +0 -141
  140. data/lib/rspec_tracer/reporter.rb +0 -204
  141. data/lib/rspec_tracer/rspec_reporter.rb +0 -41
  142. data/lib/rspec_tracer/rspec_runner.rb +0 -56
  143. data/lib/rspec_tracer/ruby_coverage.rb +0 -9
  144. data/lib/rspec_tracer/runner.rb +0 -278
data/README.md CHANGED
@@ -1,498 +1,508 @@
1
- ![](./readme_files/rspec_tracer.png)
1
+ # rspec-tracer
2
2
 
3
- [![Open Source Helpers](https://www.codetriage.com/avmnu-sng/rspec-tracer/badges/users.svg)](https://www.codetriage.com/avmnu-sng/rspec-tracer)
3
+ [![CI](https://github.com/avmnu-sng/rspec-tracer/actions/workflows/ci.yml/badge.svg)](https://github.com/avmnu-sng/rspec-tracer/actions/workflows/ci.yml)
4
4
  [![Gem Version](https://badge.fury.io/rb/rspec-tracer.svg)](https://badge.fury.io/rb/rspec-tracer)
5
-
6
- ![](./readme_files/next_run.gif)
7
-
8
- RSpec Tracer is a **specs dependency analyzer**, **flaky tests detector**, **tests accelerator**,
9
- and **coverage reporter** tool. It maintains a list of files for each test, enabling
10
- itself to skip tests in the subsequent runs if none of the dependent files are changed.
11
-
12
- It uses [Ruby's built-in coverage library](https://ruby-doc.org/stdlib/libdoc/coverage/rdoc/Coverage.html)
13
- to keep track of the coverage for each test. For each test executed, the coverage
14
- diff provides the desired file list. RSpec Tracer takes care of reporting the
15
- **correct code coverage when skipping tests** by using the cached reports. Also,
16
- note that it will **never skip**:
17
-
18
- - **Flaky examples**
19
- - **Failed examples**
20
- - **Pending examples**
21
-
22
- Knowing the examples and files dependency gives us a better insight into the codebase,
23
- and we have **a clear idea of what to test for when making any changes**. With this data,
24
- we can also analyze the coupling between different components and much more.
25
-
26
- RSpec Tracer requires **Ruby 2.5+** and **rspec-core >= 3.6.0**. To use with **Rails 5+**,
27
- make sure to use **rspec-rails >= 4.0.0**. If you are using SimpleCov, it is
28
- recommended to use **simplecov >= 0.17.0**. To use RSpec Tracer **cache on CI**, you
29
- need to have an **S3 bucket** and **[AWS CLI](https://aws.amazon.com/cli/)**
30
- installed.
31
-
32
- > You should take some time and go through the **[document](./RSPEC_TRACER.md)**
33
- describing the **intention** and implementation details of **managing dependency**,
34
- **managing flaky tests**, **skipping tests**, and **caching on CI**.
35
-
36
- ## Table of Contents
37
-
38
- * [Getting Started](#getting-started)
39
- * [Working with JRuby](#working-with-jruby)
40
- * [Working with Parallel Tests](#working-with-parallel-tests)
41
- * [Configuring CI](#configuring-ci)
42
- * [Advanced Configuration](#advanced-configuration)
43
- * [Available Settings](#available-settings)
44
- * [Filters](#filters)
45
- * [Duplicate Examples](#duplicate-examples)
46
- * [Demo](#demo)
47
-
48
- ## Getting Started
49
-
50
- 1. Add this line to your `Gemfile` and `bundle install`:
51
- ```ruby
52
- gem 'rspec-tracer', '~> 1.0', group: :test, require: false
53
- ```
54
-
55
- And, add the followings to your `.gitignore`:
56
- ```
57
- rspec_tracer.lock
58
- rspec_tracer_cache/
59
- rspec_tracer_coverage/
60
- rspec_tracer_report/
61
- ```
62
- 2. Load and launch RSpec Tracer at the very top of `spec_helper.rb` (or `rails_helper.rb`,
63
- `test/test_helper.rb`). Note that `RSpecTracer.start` must be issued **before loading
64
- any of the application code.**
65
-
66
- ```ruby
67
- # Load RSpec Tracer
68
- require 'rspec_tracer'
69
- RSpecTracer.start
70
- ```
71
-
72
- **If you are using SimpleCov**, load RSpec Tracer right after the SimpleCov load
73
- and launch:
74
-
75
- ```ruby
76
- require 'simplecov'
77
- SimpleCov.start
78
-
79
- # Load RSpec Tracer
80
- require 'rspec_tracer'
81
- RSpecTracer.start
82
- ```
83
-
84
- If you use RSpec Tracer with SimpleCov, then **SimpleCov would not report branch
85
- coverage results even when enabled**.
86
-
87
- 3. After running your tests, open `rspec_tracer_report/index.html` in the browser
88
- of your choice.
89
-
90
- ### Working with JRuby
91
-
92
- It is recommend to use **JRuby 9.2.10.0+**. Also, configure it with **`JRUBY_OPTS="--debug -X+O"`**
93
- or have the `.jrubyrc` file:
5
+ [![codecov](https://codecov.io/gh/avmnu-sng/rspec-tracer/branch/main/graph/badge.svg)](https://codecov.io/gh/avmnu-sng/rspec-tracer)
6
+ [![Docs](https://img.shields.io/badge/docs-online-blue)](https://avmnu-sng.github.io/rspec-tracer/)
7
+
8
+ **RSpec Tracer** is a specs dependency analyzer, flaky-test detector,
9
+ test accelerator, and coverage reporter for RSpec. It records the
10
+ inputs every example consumes Ruby files (via `Coverage`), file
11
+ I/O (via prepended `File` / `IO` / `YAML` / `JSON` hooks), Rails
12
+ template + AR notifications, and user-declared globs — then re-runs
13
+ only the examples whose inputs changed since the last run.
14
+
15
+ It never skips:
16
+ - **Failed**, **flaky**, or **pending** examples.
17
+ - Examples whose dependent inputs changed (the whole point).
18
+
19
+ 📚 **Full documentation site**: [avmnu-sng.github.io/rspec-tracer](https://avmnu-sng.github.io/rspec-tracer/) — YARD API reference, sample HTML report, cookbook, and per-version coverage report.
20
+
21
+ For a complete account of the input taxonomy and how the engine fits
22
+ together, read [`ARCHITECTURE.md`](ARCHITECTURE.md).
23
+
24
+ ## Table of contents
25
+
26
+ - [Quick start](#quick-start)
27
+ - [How it works](#how-it-works)
28
+ - [Per-example `tracks:` DSL](#per-example-tracks-dsl)
29
+ - [Rails quick start](#rails-quick-start)
30
+ - [Configuring CI](#configuring-ci)
31
+ - [Remote cache backends](#remote-cache-backends)
32
+ - [Pluggable storage](#pluggable-storage)
33
+ - [Command-line tools](#command-line-tools)
34
+ - [SimpleCov interop](#simplecov-interop)
35
+ - [FAQ + comparison](#faq--comparison)
36
+ - [Reports](#reports)
37
+ - [Documentation + coverage](#documentation--coverage)
38
+ - [Help and community](#help-and-community)
39
+ - [Section anchor map (1.x → 2.0)](#section-anchor-map-1x--20)
40
+
41
+ ## Quick start
42
+
43
+ Requires Ruby 3.1+ and rspec-core 3.12+. Rails 7.0+ when using Rails;
44
+ Rails 8.0 needs Ruby 3.2+. JRuby 9.4 is supported.
45
+
46
+ 1. Add the gem:
47
+
48
+ ```ruby
49
+ # 2.0 is in pre-release. Pin to the pre-release version explicitly;
50
+ # switch to '~> 2.0' once 2.0.0 final ships.
51
+ gem 'rspec-tracer', '= 2.0.0.pre.1', group: :test, require: false
52
+ ```
53
+
54
+ `bundle install` will resolve the pre-release version. You can
55
+ also install ad-hoc with `gem install rspec-tracer --pre`.
56
+
57
+ 2. Add the canonical directories to your `.gitignore`:
58
+
59
+ ```
60
+ rspec_tracer.lock
61
+ rspec_tracer_cache/
62
+ rspec_tracer_coverage/
63
+ rspec_tracer_report/
64
+ ```
65
+
66
+ 3. Load and start at the very top of `spec_helper.rb` /
67
+ `rails_helper.rb`, **before any application code:**
68
+
69
+ ```ruby
70
+ require 'rspec_tracer'
71
+ RSpecTracer.start
72
+ ```
73
+
74
+ With SimpleCov, start SimpleCov first (load order is part of the
75
+ contract):
76
+
77
+ ```ruby
78
+ require 'simplecov'
79
+ SimpleCov.start
80
+ require 'rspec_tracer'
81
+ RSpecTracer.start
82
+ ```
83
+
84
+ 4. Run your suite. After the run, open
85
+ `rspec_tracer_report/index.html` for the HTML report, and
86
+ `rspec_tracer_report/report.json` for machine-readable output.
87
+
88
+ The terminal prints a one-line summary with cache size and the
89
+ reasons examples re-ran:
90
+
91
+ ```
92
+ rspec-tracer: 1,820 examples · 42 re-run · 1,778 skipped (97% cached)
93
+ by reason: 38 Files changed · 4 Failed previously
94
+ cache: rspec_tracer_cache (14.4 MiB; +6.7 KiB vs prev run)
95
+ ```
96
+
97
+ ## How it works
98
+
99
+ Every test is a pure function of its inputs; the tracker's job is to
100
+ identify every input and hash it. Inputs come from six buckets:
101
+
102
+ 1. **Ruby-executed source** — observed via Ruby's `Coverage` module.
103
+ 2. **File I/O** (`File.read`, `YAML.load_file`, etc.) — observed via
104
+ `Module#prepend` hooks.
105
+ 3. **Framework events** — Rails template renders + (opt-in) AR
106
+ schema-touching queries via `ActiveSupport::Notifications`.
107
+ 4. **Declared globs** — what you tell the tracker to track via
108
+ `track_files` / `tracks:` / `track_rails_defaults`.
109
+ 5. **Whole-suite invalidators** — `Gemfile.lock`, `.ruby-version`,
110
+ `.rspec-tracer`, the rspec-tracer gem version itself.
111
+ 6. **Truly unobservable inputs** — env-var branches, refinements
112
+ in unexecuted files. Use `track_env` / `tracks: { env: ... }`
113
+ to declare them; the tracker can't auto-detect them.
114
+
115
+ A digest of every observed input is stored per-example. The next run
116
+ loads the cached digest set, recomputes per-input digests, and skips
117
+ examples whose inputs are unchanged.
118
+
119
+ Read [`ARCHITECTURE.md`](ARCHITECTURE.md) for the full layer
120
+ structure (Tracker / Storage / RemoteCache / Reporters), data flows,
121
+ and extension protocols.
122
+
123
+ ### Per-example precision under Rails `config.eager_load`
124
+
125
+ A precision tradeoff worth knowing before you run a suite that uses
126
+ Rails:
127
+
128
+ - **`config.eager_load = false`** in test env — per-example precision
129
+ works as designed. Files autoloaded during an example land in
130
+ per-example deps; editing one re-runs only the examples that
131
+ touched it.
132
+ - **`config.eager_load = true`** (Rails default for CI tests to
133
+ mirror prod) — all `app/` files load at boot and land in the
134
+ whole-suite invalidator set. Editing any `app/` file re-runs
135
+ every example. **Safe** (never misses a dep) but **coarser** than
136
+ per-example attribution.
137
+
138
+ If your CI suite runs with `eager_load = true` and your `app/`-edit
139
+ cycle is bottlenecked by whole-suite re-runs, set `config.eager_load
140
+ = false` in test env to recover per-example precision. A future 2.1
141
+ enhancement (working name: `track_class_attribution`) will install
142
+ class-dispatch tracking to trim the invalidator scope under
143
+ eager-loaded test environments — see
144
+ [`CHANGELOG.md`](CHANGELOG.md) "Deferred to 2.1" for the planned
145
+ contract.
146
+
147
+ ## Per-example `tracks:` DSL
148
+
149
+ Annotate any describe / context / example with extra inputs the
150
+ tracker can't auto-observe — config files baked at boot, env-var
151
+ branches, non-Ruby deps:
94
152
 
95
153
  ```ruby
96
- debug.fullTrace=true
97
- objectspace.enabled=true
154
+ RSpec.describe AdminController,
155
+ tracks: { files: 'app/policies/**/*.rb',
156
+ env: 'ROLE_CONFIG' } do
157
+ it 'gates on the feature flag' do
158
+ # ...
159
+ end
160
+ end
98
161
  ```
99
162
 
100
- ### Working with Parallel Tests
163
+ | Key | Value shape | Semantics |
164
+ |----------|-----------------------------------|------------------------------------------------------------------------|
165
+ | `:files` | String glob OR Array of strings | Each matched file is attached as a `:declared`-kind dep on the example. |
166
+ | `:env` | String name OR Array OR wildcard | Each named env var is digested at finalize. Missing env reads as empty. |
101
167
 
102
- The Rspec tracer, by default, supports working with [parallel_tests](https://github.com/grosser/parallel_tests/)
103
- gem. It maintains a lock file `rspec_tracer.lock` to identify the last
104
- running process. Usually, you are not required to do anything special unless you
105
- interrupt the execution in between and the process did not complete correctly.
106
- In such a case, you must delete the lock file before the next run.
168
+ **Wildcards** (`'RAILS_*'`, `'*_TOKEN'`, `'*'`) expand against the
169
+ live ENV at register time; the persisted snapshot only carries
170
+ concrete keys.
107
171
 
108
- ```sh
109
- rm -f rspec_tracer.lock && bundle exec parallel_rspec
110
- ```
172
+ **Cascade.** Nested groups contribute additively. A parent's
173
+ `tracks: { files: 'a/*' }` and a child's `tracks: { env: 'B' }` both
174
+ attach to the child's examples; the child does not clobber the parent
175
+ on any shared key.
111
176
 
112
- ## Configuring CI
177
+ **Use `track_files` / `track_env` for the global case.** Files /
178
+ env vars **every** test depends on (`Gemfile.lock`, `AUTH_TOKEN`,
179
+ `RAILS_ENV`) belong in `.rspec-tracer`:
113
180
 
114
- To enable RSpec Tracer to share cache between different builds on CI, update the
115
- Rakefile in your project to have the following:
116
- ```ruby
117
- spec = Gem::Specification.find_by_name('rspec-tracer')
118
-
119
- load "#{spec.gem_dir}/lib/rspec_tracer/remote_cache/Rakefile"
120
- ```
121
-
122
- Before running tests, download the remote cache using the following rake task:
123
- ```sh
124
- bundle exec rake rspec_tracer:remote_cache:download
125
- ```
126
-
127
- After running tests, upload the local cache using the following rake task:
128
- ```sh
129
- bundle exec rake rspec_tracer:remote_cache:upload
130
- ```
131
-
132
- You must set the following environment variables:
133
-
134
- - **`GIT_DEFAULT_BRANCH`** is the default branch name for the repo, e.g., `main` or `master`.
135
-
136
- - **`GIT_BRANCH`** is the git branch name you are running the CI build on.
137
-
138
- - **`TEST_SUITES`** is the total number of different test suites you are running.
139
- ```sh
140
- export TEST_SUITES=8
141
- ```
142
-
143
- - **`TEST_SUITE_ID`** is the CI build ID. If you have a large set of tests to run,
144
- it is recommended to run them in separate groups. This way, RSpec Tracer is not
145
- overwhelmed with loading massive cached data in the memory. Also, it generates and
146
- uses cache for specific test suites and not merge them.
147
- ```sh
148
- TEST_SUITE_ID=1 bundle exec rspec spec/models
149
- TEST_SUITE_ID=2 bundle exec rspec spec/helpers
150
- ```
151
-
152
- - **`USE_TEST_SUITE_ID_CACHE`** (optional, default unset) changes how the remote
153
- cache validator treats per-suite caches. When set to the exact string `"true"`,
154
- the validator accepts the current `TEST_SUITE_ID`'s cache independently of the
155
- other suites' state — so if one suite aborts mid-CI, the remaining suites can
156
- still reuse their own cached results on the next run. When unset (the default),
157
- 1.1.x behaviour is preserved byte-for-byte: the validator requires every
158
- `TEST_SUITES` slot to be present before accepting any cache.
159
- ```sh
160
- export TEST_SUITES=3
161
- export TEST_SUITE_ID=1
162
- export USE_TEST_SUITE_ID_CACHE=true
163
- bundle exec rspec spec/models
164
- ```
165
- Only the literal string `"true"` activates the flag; `"1"`, `"yes"`, or any
166
- other truthy value keeps the default behaviour.
167
-
168
- ## Advanced Configuration
169
-
170
- Configuration settings must be defined in **`.rspec-tracer`** file:
171
181
  ```ruby
172
182
  RSpecTracer.configure do
173
- config_option 'foo'
183
+ track_files 'config/locales/**/*.yml', 'db/schema.rb', 'Gemfile.lock'
184
+ track_env 'AUTH_TOKEN', 'DATABASE_URL', 'RAILS_*'
174
185
  end
175
186
  ```
176
187
 
177
- You can additionally define a global config file `~/.rspec-tracer` to share the
178
- common settings across projects.
179
-
180
- ### Available Settings
181
-
182
- - **`root dir`** to set the project root. The default value is the current working
183
- directory.
188
+ ## Rails quick start
184
189
 
185
- - **`project_name name`** to set the project name. The default value is the last
186
- part of the project root directory.
187
-
188
- - **`cache_dir dir`** to set the cache directory. The default value is `./rspec_tracer_cache`.
189
- You can also set the **`RSPEC_TRACER_CACHE_DIR`** environment variable.
190
-
191
- - **`coverage_dir dir`** to set the coverage reports directory. The default is the
192
- `./rspec_tracer_coverage`. You can also set the **`RSPEC_TRACER_COVERAGE_DIR`**
193
- environment variable.
194
-
195
- - **`report_dir dir`** to set the HTML reports directory. The default is the
196
- `./rspec_tracer_report`. You can also set the **`RSPEC_TRACER_REPORT_DIR`**
197
- environment variable.
198
-
199
- - **`reports_s3_path uri`** to set the AWS S3 URI for all the reports from the current
200
- run. You can also set the **`RSPEC_TRACER_REPORTS_S3_PATH`** environment variable.
201
-
202
- - **`use_local_aws bool_flag`** to use the `awslocal` AWS CLI with `LocalStack`. You can
203
- also set the **`RSPEC_TRACER_USE_LOCAL_AWS`** environment variable.
204
-
205
- - **`upload_non_ci_reports bool_flag`** to upload execution reports in a non-CI
206
- environment. You can also set the **`RSPEC_TRACER_UPLOAD_NON_CI_REPORTS`** environment
207
- variable.
208
-
209
- - **`run_all_examples bool_flag`** to always run all the examples irrespective of cache.
210
- You can also set the **`RSPEC_TRACER_RUN_ALL_EXAMPLES`** environment variable.
211
-
212
- - **`fail_on_duplicates bool_flag`** to fail with a non-zero exit code in case of
213
- duplicate examples. The default value is `true`. You can also set the **`RSPEC_TRACER_FAIL_ON_DUPLICATES`**
214
- environment variable.
215
-
216
- - **`lock_file file`** to set the lock file when executing with `parallel_tests`. The default
217
- value is `./rspec_tracer.lock`. You can also set the **`RSPEC_TRACER_LOCK_FILE`** environment
218
- variable.
219
-
220
- - **`log_level level`** to set the log level. The default value is `info`. The possible
221
- values are `off`, `debug`, `info`, `warn`, and `error`. You can also set the
222
- **`RSPEC_TRACER_LOG_LEVEL`** environment variable.
223
-
224
- - **`add_filter filter`** to apply filters on the source files to exclude them
225
- from the dependent files list.
226
-
227
- - **`filters.clear`** to remove the configured dependent files filters so far.
228
-
229
- - **`add_coverage_filter filter`** to apply filters on the source files to exclude
230
- them from the coverage report.
190
+ ```ruby
191
+ # spec/rails_helper.rb
192
+ require 'simplecov' # if used; load BEFORE rspec_tracer
193
+ SimpleCov.start
231
194
 
232
- - **`coverage_filters.clear`** to remove the configured coverage files filters so far.
195
+ require 'rspec_tracer'
196
+ RSpecTracer.start
233
197
 
234
- - **`coverage_track_files glob`** to include files in the given glob pattern in
235
- the coverage report if these files are not already present.
198
+ require File.expand_path('../config/environment', __dir__)
199
+ require 'rspec/rails'
200
+ ```
236
201
 
237
202
  ```ruby
203
+ # .rspec-tracer
238
204
  RSpecTracer.configure do
239
- # Configure project root
240
- root '/tmp/my_project'
241
-
242
- # Clear existing filters
243
- filters.clear
244
- # Add dependent files filter
245
- add_filter %r{^/tasks/}
246
-
247
- # Clear existing coverage filters
248
- coverage_filters.clear
249
- # Add coverage files filter
250
- add_coverage_filter %w[/features/ /spec/ /tests/]
251
-
252
- # Define glob to track files in the coverage report
253
- coverage_track_files '{app,lib}/**/*.rb'
205
+ track_rails_defaults
254
206
  end
255
207
  ```
256
208
 
257
- ## Filters
209
+ `track_rails_defaults` attaches the common Rails-side declared globs:
210
+ views, locales, fixtures, factories, helpers, config. Drop a
211
+ specific glob to hand attribution off to the per-example subscribers
212
+ instead:
258
213
 
259
- By default, RSpec Tracer ignores all the files outside of the project root directory -
260
- otherwise you would end up with the source files in the gems you are using in the
261
- project. It also applies the following filters:
262
214
  ```ruby
263
215
  RSpecTracer.configure do
264
- log_level 'warn'
265
-
266
- add_filter '/vendor/bundle/'
267
-
268
- add_coverage_filter %w[
269
- /autotest/
270
- /features/
271
- /spec/
272
- /test/
273
- /vendor/bundle/
274
- ].freeze
216
+ # Templates → render_template.action_view subscriber attributes
217
+ # them per-example. Schema → opt-in sql.active_record observer
218
+ # attributes db/schema.rb + db/structure.sql per AR-touching
219
+ # example (read the Narrow AR-schema attribution section before
220
+ # enabling).
221
+ track_rails_defaults except: [:views, :schema]
222
+ track_ar_schema_notifications
275
223
  end
276
224
  ```
277
225
 
278
- ### Defining Custom Filteres
226
+ Supported Rails matrix: 7.0 / 7.1 / 7.2 / 8.0 (8.0 requires Ruby
227
+ 3.2+). On JRuby use `JRUBY_OPTS="--debug -X+O"` and the JDBC
228
+ adapter (`gem 'activerecord-jdbcsqlite3-adapter', '~> 71.0',
229
+ platforms: :jruby` for Rails 7.1; track the major to your Rails
230
+ line).
279
231
 
280
- You can currently define a filter using either a String or Regexp (that will then
281
- be Regexp-matched against each source file's name relative to the project root),
282
- a block or by passing in your own Filter class.
232
+ ### Narrow AR-schema attribution
283
233
 
284
- - **String Filter**: The string filter matches files that have the given string
285
- in their name. For example, the following string filter will remove all files that
286
- have `"/helpers/"` in their name.
287
- ```ruby
288
- RSpecTracer.configure do
289
- add_filter '/helpers/'
290
- end
291
- ```
292
-
293
- - **Regex Filter**: The regex filter removes all files that have a successful name
294
- match against the given regex expression. This simple regex filter will remove
295
- all files that start with `%r{^/helper/}` in their name:
296
- ```ruby
297
- RSpecTracer.configure do
298
- add_filter %r{^/helpers/}
299
- end
300
- ```
301
-
302
- - **Block Filter**: Block filters receive a `Hash` object and expect your block
303
- to return either **true** (if the file is to be removed from the result) or **false**
304
- (if the result should be kept). In the below example, the filter will remove all
305
- files that match `"/helpers/"` in their path.
306
- ```ruby
307
- RSpecTracer.configure do
308
- add_filter do |source_file|
309
- source_file[:file_path].include?('/helpers/')
310
- end
311
- end
312
- ```
313
-
314
- You can also use `source_file[:name]` to define the return value of the block
315
- filter for the given source file.
234
+ `track_ar_schema_notifications` promises per-example attribution of
235
+ `db/schema.rb` via the `sql.active_record` subscriber. **The narrow
236
+ promise only holds when no per-example AR cleanup mechanism fires
237
+ queries inside the per-example bucket.** Common Rails setups trip
238
+ this:
316
239
 
317
- - **Array Filter**: You can pass in an array containing any of the other filter types:
318
- ```ruby
319
- RSpecTracer.configure do
320
- add_filter ['/helpers/', %r{^/utils/}]
321
- end
322
- ```
240
+ - `use_transactional_fixtures = true` (Rails default) per-example
241
+ BEGIN/COMMIT fires `sql.active_record`.
242
+ - DatabaseCleaner `:truncation` / `:deletion` / `:transaction` in
243
+ `around` hooks — cleanup queries fire inside the bucket.
323
244
 
324
- ## Duplicate Examples
245
+ Either case attributes `db/schema.rb` to every AR-touching example
246
+ (safe, but widens invalidation). A boot-time warn fires when the
247
+ precondition is unmet so you don't discover this from a confused
248
+ cache-hit-rate chart later.
325
249
 
326
- To uniquely identify the examples is one of the requirements for the correctness
327
- of the RSpec Tracer. Sometimes, it would not be possible to do so depending upon
328
- how we have written the specs. The following attributes determine the uniqueness:
250
+ For genuinely narrow attribution: set `use_transactional_fixtures =
251
+ false` and use sequence-based factories (or another non-AR cleanup
252
+ mechanism that doesn't fire `sql.active_record` inside the example
253
+ window).
329
254
 
330
- - The example group
331
- - The example full description
332
- - The spec file location, i.e., file name and line number
333
- - All the shared examples and contexts
255
+ ### Working with parallel_tests
334
256
 
335
- Consider the following `Calculator` module:
336
- ```ruby
337
- module Calculator
338
- module_function
257
+ Supported out of the box. The tracker writes to per-worker
258
+ directories and merges at finalize on the elected last worker. If you
259
+ interrupt a run mid-flight, delete the lock file:
339
260
 
340
- def add(a, b) a + b; end
341
- def sub(a, b) a - b; end
342
- def mul(a, b) a * b; end
343
- end
261
+ ```sh
262
+ rm -f rspec_tracer.lock && bundle exec parallel_rspec
344
263
  ```
345
264
 
346
- And the corresponding spec file `spec/calculator_spec.rb`:
347
- ```ruby
348
- RSpec.describe Calculator do
349
- describe '#add' do
350
- [
351
- [1, 2, 3],
352
- [0, 0, 0],
353
- [5, 32, 37],
354
- [-1, -8, -9],
355
- [10, -10, 0]
356
- ].each { |a, b, r| it { expect(described_class.add(a, b)).to eq(r) } }
357
- end
358
-
359
- describe '#sub' do
360
- [
361
- [1, 2, -1],
362
- [10, 0, 10],
363
- [37, 5, 32],
364
- [-1, -8, 7],
365
- [10, 10, 0]
366
- ].each do |a, b, r|
367
- it 'performs subtraction' do
368
- expect(described_class.sub(a, b)).to eq(r)
369
- end
370
- end
371
- end
265
+ ## Configuring CI
372
266
 
373
- describe '#mul' do
374
- [
375
- [1, 2, -2],
376
- [10, 0, 0],
377
- [5, 7, 35],
378
- [-1, -8, 8],
379
- [10, 10, 100]
380
- ].each do |a, b, r|
381
- it "multiplies #{a} and #{b} to #{r}" do
382
- expect(described_class.mul(a, b)).to eq(r)
383
- end
384
- end
385
- end
386
- end
387
- ```
267
+ The 1.x flow is preserved bit-for-bit. In your project's `Rakefile`:
388
268
 
389
- Running the spec with `bundle exec rspec spec/calculator_spec.rb` generates the
390
- following output:
391
- ```
392
- Calculator
393
- #mul
394
- multiplies 5 and 7 to 35
395
- multiplies 10 and 10 to 100
396
- multiplies 10 and 0 to 0
397
- multiplies 1 and 2 to -2 (FAILED - 1)
398
- multiplies -1 and -8 to 8
399
- #add
400
- example at ./spec/calculator_spec.rb:13
401
- example at ./spec/calculator_spec.rb:13
402
- example at ./spec/calculator_spec.rb:13
403
- example at ./spec/calculator_spec.rb:13
404
- example at ./spec/calculator_spec.rb:13
405
- #sub
406
- performs subtraction
407
- performs subtraction
408
- performs subtraction
409
- performs subtraction
410
- performs subtraction
269
+ ```ruby
270
+ spec = Gem::Specification.find_by_name('rspec-tracer')
271
+ load "#{spec.gem_dir}/lib/rspec_tracer/remote_cache/Rakefile"
411
272
  ```
412
273
 
413
- In this scenario, RSpec Tracer cannot determine the `Calculator#add` and
414
- `Calculator#sub` group examples.
274
+ Then in your CI pipeline:
415
275
 
276
+ ```sh
277
+ bundle exec rake rspec_tracer:remote_cache:download
278
+ bundle exec rspec
279
+ bundle exec rake rspec_tracer:remote_cache:upload
416
280
  ```
417
- ================================================================================
418
- IMPORTANT NOTICE -- RSPEC TRACER COULD NOT IDENTIFY SOME EXAMPLES UNIQUELY
419
- ================================================================================
420
- RSpec tracer could not uniquely identify the following 10 examples:
421
- - Example ID: eabd51a899db4f64d5839afe96004f03 (5 examples)
422
- * Calculator#add (spec/calculator_spec.rb:13)
423
- * Calculator#add (spec/calculator_spec.rb:13)
424
- * Calculator#add (spec/calculator_spec.rb:13)
425
- * Calculator#add (spec/calculator_spec.rb:13)
426
- * Calculator#add (spec/calculator_spec.rb:13)
427
- - Example ID: 72171b502c5a42b9aa133f165cf09ec2 (5 examples)
428
- * Calculator#sub performs subtraction (spec/calculator_spec.rb:24)
429
- * Calculator#sub performs subtraction (spec/calculator_spec.rb:24)
430
- * Calculator#sub performs subtraction (spec/calculator_spec.rb:24)
431
- * Calculator#sub performs subtraction (spec/calculator_spec.rb:24)
432
- * Calculator#sub performs subtraction (spec/calculator_spec.rb:24)
433
- ```
434
-
435
- ## Demo
436
-
437
- **First Run**
438
- ![](./readme_files/first_run.gif)
439
-
440
- **Next Run**
441
- ![](./readme_files/next_run.gif)
442
-
443
- You get the following three reports:
444
-
445
- ### All Examples Report
446
281
 
447
- These reports provide basic test information:
448
-
449
- **First Run**
450
-
451
- ![](./readme_files/examples_report_first_run.png)
452
-
453
- **Next Run**
282
+ With these env vars:
283
+
284
+ - **`GIT_DEFAULT_BRANCH`** — your repo's default branch (`main` /
285
+ `master`).
286
+ - **`GIT_BRANCH`** — the branch the build is on.
287
+ - **`TEST_SUITES`** — total number of suite shards (when sharding).
288
+ - **`TEST_SUITE_ID`** — current shard identifier.
289
+ - **`RSPEC_TRACER_REMOTE_CACHE_URI`** — the remote-cache URI
290
+ (`s3://bucket/prefix`, `file:///tmp/cache`, `redis://host`).
291
+
292
+ ### Caching rspec-tracer in CI
293
+
294
+ If you don't have S3 — or only need cache between runs of a single
295
+ workflow — GitHub Actions' built-in cache is a drop-in storage
296
+ substrate. Restore `rspec_tracer_cache/` before specs, run the
297
+ suite, let the post-step save it back. A reference workflow lives at
298
+ [`.github/workflows/example-tracer-cache.yml`](.github/workflows/example-tracer-cache.yml).
299
+ The cache step:
300
+
301
+ ```yaml
302
+ - name: Restore rspec-tracer cache
303
+ uses: actions/cache@v5
304
+ with:
305
+ path: rspec_tracer_cache
306
+ key: >-
307
+ ${{ runner.os
308
+ }}-${{ hashFiles('.ruby-version')
309
+ }}-${{ hashFiles('Gemfile.lock')
310
+ }}-rspec-tracer-cache
311
+ restore-keys: |
312
+ ${{ runner.os }}-${{ hashFiles('.ruby-version') }}-
313
+ ${{ runner.os }}-
314
+ ```
454
315
 
455
- ![](./readme_files/examples_report_next_run.png)
316
+ The 4-component cache key (`runner.os` + Ruby version + the
317
+ rspec-tracer gem's own version + your project's Gemfile lock)
318
+ invalidates when something would make the previous run's decisions
319
+ incorrect: native gem binaries differ across runner OSes, Ruby ABI
320
+ changes invalidate native extensions, a tracer upgrade can change the
321
+ cache schema, and gem-set drift is the most common cache-staleness
322
+ trigger.
456
323
 
457
- ### Duplicate Examples Report
324
+ **The pattern translates 1:1 to CircleCI, GitLab CI, Buildkite, and
325
+ Heroku CI**; only the YAML envelope is GHA-specific. See
326
+ [`docs/CI_RECIPES.md`](docs/CI_RECIPES.md) for per-provider recipes.
458
327
 
459
- These reports provide duplicate tests information.
328
+ ## Remote cache backends
460
329
 
461
- ![](./readme_files/duplicate_examples_report.png)
330
+ Three backends ship in 2.0. Pick one in `.rspec-tracer`:
462
331
 
463
- ### Flaky Examples Report
332
+ ```ruby
333
+ RSpecTracer.configure do
334
+ # S3 (preserves 1.x layout; supports awslocal / LocalStack).
335
+ remote_cache_backend :s3, local: false
464
336
 
465
- These reports provide flaky tests information. Assuming **the following two tests
466
- failed in the first run.**
337
+ # Filesystem-backed (no S3 needed).
338
+ # remote_cache_backend :local_fs, root: '/tmp/rspec-tracer-cache'
467
339
 
468
- **Next Run**
340
+ # Redis (with optional per-key TTL + PR-branch tracking sidecar).
341
+ # remote_cache_backend :redis, url: ENV['REDIS_URL'], ttl: 7 * 86_400
342
+ end
343
+ ```
469
344
 
470
- ![](./readme_files/flaky_examples_report_first_run.png)
345
+ The `rake rspec_tracer:remote_cache:*` task surface is unchanged —
346
+ backend selection happens in config; the rake tasks dispatch
347
+ identically.
471
348
 
472
- **Another Run**
349
+ ## Pluggable storage
473
350
 
474
- ![](./readme_files/flaky_examples_report_next_run.png)
351
+ Two on-disk storage formats:
475
352
 
476
- ### Examples Dependency Report
353
+ ```ruby
354
+ RSpecTracer.configure do
355
+ # JSON (default) — preserves the 1.x 10-file layout per run.
356
+ storage_backend :json
477
357
 
478
- These reports show a list of dependent files for each test.
358
+ # SQLite single-file database. Faster cold reads above ~5,000
359
+ # examples. JRuby auto-falls-back to :json with a one-time warn.
360
+ # storage_backend :sqlite
361
+ end
362
+ ```
479
363
 
480
- ![](./readme_files/examples_dependency_report.png)
364
+ Or override per-run via env: `RSPEC_TRACER_STORAGE=sqlite`.
481
365
 
482
- ### Files Dependency Report
366
+ ## Command-line tools
483
367
 
484
- These reports provide information on the total number of tests that will run after changing this particular file.
368
+ `bin/rspec-tracer` exposes five sub-commands:
485
369
 
486
- ![](./readme_files/files_dependency_report.png)
370
+ ```sh
371
+ bin/rspec-tracer doctor # diagnose config + environment
372
+ bin/rspec-tracer cache:info # size, last run, invalidation stats
373
+ bin/rspec-tracer cache:clear # rm cache dirs
374
+ bin/rspec-tracer report:open # open the HTML report
375
+ bin/rspec-tracer explain <id> # why is <example_id> scheduled to (re-)run?
376
+ ```
487
377
 
488
- ## Contributing
378
+ The CLI is opt-in for local-dev convenience. The
379
+ `rake rspec_tracer:remote_cache:*` tasks remain first-class for CI
380
+ integration — nothing in the CLI replaces them.
489
381
 
490
- Read the [contribution guide](https://github.com/avmnu-sng/rspec-tracer/blob/main/.github/CONTRIBUTING.md).
382
+ ## SimpleCov interop
491
383
 
492
- ## License
384
+ **Load order is part of the contract.** SimpleCov first, then
385
+ rspec-tracer:
493
386
 
494
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
387
+ ```ruby
388
+ require 'simplecov'
389
+ SimpleCov.start
495
390
 
496
- ## Code of Conduct
391
+ require 'rspec_tracer'
392
+ RSpecTracer.start
393
+ ```
497
394
 
498
- Everyone interacting in the Rspec Tracer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [Code of Conduct](https://github.com/avmnu-sng/rspec-tracer/blob/main/.github/CODE_OF_CONDUCT.md).
395
+ If you call `require 'simplecov'` but skip `SimpleCov.start` before
396
+ `RSpecTracer.start`, a boot-time warn fires pointing this out
397
+ (silent-degradation breaks coverage output).
398
+
399
+ **Branch coverage works alongside rspec-tracer in 2.0.** The 1.x
400
+ caveat ("SimpleCov would not report branch coverage results even
401
+ when enabled") is no longer applicable — the coverage emission
402
+ decoupled from SimpleCov's branch-tracking. Re-enable
403
+ `enable_coverage :branch` in your `SimpleCov.start` block and you get
404
+ both.
405
+
406
+ Filter chains compose: SimpleCov's `add_filter` / `coverage_filters`
407
+ control SimpleCov's HTML output; rspec-tracer's `add_filter` /
408
+ `add_coverage_filter` control which files contribute to the
409
+ dependency graph.
410
+
411
+ ## FAQ + comparison
412
+
413
+ **Why not just SimpleCov filtering?** SimpleCov tells you which
414
+ *files* are covered. rspec-tracer tells you which *examples* depend
415
+ on which inputs, so it can skip the ones whose inputs didn't change.
416
+ The two solve adjacent problems; you typically want both.
417
+
418
+ **Knapsack / Knapsack Pro / Test Boosters?** Those are test-splitting
419
+ tools — they shard your suite across CI workers. rspec-tracer is
420
+ orthogonal: it skips already-passing examples on a single worker.
421
+ Compose them: shard with Knapsack, then skip with rspec-tracer.
422
+ A composition smoke spec (`spec/regressions/knapsack_coexistence_spec.rb`)
423
+ proves they coexist.
424
+
425
+ **`RSpec::Retry` / `RSpec::Rerun`?** Retries flaky failures.
426
+ rspec-tracer detects flaky examples (same inputs, different outcomes)
427
+ and refuses to skip them on the next run. Compose: retry to make a
428
+ flaky suite green, let rspec-tracer flag the flakes for you to
429
+ actually fix.
430
+
431
+ **Monorepo with N apps?** Set per-app `cache_dir` in each app's
432
+ `.rspec-tracer` (the default `rspec_tracer_cache/` would otherwise
433
+ collide across apps).
434
+
435
+ **`tracks:` vs `track_rails_defaults` overlap?** When both attribute
436
+ the same file to an example, the declared-glob attribution wins.
437
+ Deterministic; see [`ARCHITECTURE.md`](ARCHITECTURE.md) "Input
438
+ taxonomy" for the rule.
439
+
440
+ ## Reports
441
+
442
+ After the run, `rspec_tracer_report/index.html` opens five HTML
443
+ reports:
444
+
445
+ - **All Examples** — basic test info (id, status, duration, the inputs
446
+ the example consumed).
447
+ - **Duplicate Examples** — pairs RSpec couldn't uniquely identify
448
+ (file:line collisions; only appears when duplicates are present).
449
+ - **Flaky Examples** — examples that passed after previously failing
450
+ without any dependency change. The tracker refuses to skip these
451
+ on subsequent runs.
452
+ - **Examples Dependency** — per-example file dependency lists; the
453
+ "what does this test depend on?" view.
454
+ - **Files Dependency** — the inverse: "what tests run if I change
455
+ this file?" The unique-to-rspec-tracer report that makes refactors
456
+ safer.
457
+
458
+ Plus a machine-readable `rspec_tracer_report/report.json` for CI
459
+ dashboards and the terminal one-liner shown in [Quick start](#quick-start).
460
+
461
+ ## Documentation + coverage
462
+
463
+ - **Docs site**: [avmnu-sng.github.io/rspec-tracer](https://avmnu-sng.github.io/rspec-tracer/)
464
+ publishes a YARD API reference, the cookbook, internals deep-dives,
465
+ and the sample HTML report for every rolling main + tagged release.
466
+ The landing page lets you switch versions; per-version subpaths look
467
+ like `/main/yard/`, `/main/demo/`, `/v2.0.0.pre.1/yard/`, etc.
468
+
469
+ - **Coverage**: tracked by [Codecov](https://codecov.io/gh/avmnu-sng/rspec-tracer)
470
+ (per-PR delta + diff-coverage gate ≥ 90%; project history). Multi-
471
+ suite resultsets (`unit`, `edge-cases`, `regressions-plain`, `fuzz`)
472
+ merge via SimpleCov.collate in the CI `coverage` job; the same
473
+ merger runs locally via `task coverage:merge` after `task test:*`
474
+ runs (open `coverage/index.html` for the local HTML report).
475
+
476
+ ## Help and community
477
+
478
+ - **Bug reports + feature requests** — [GitHub Issues](https://github.com/avmnu-sng/rspec-tracer/issues).
479
+ Include Ruby / Rails / RSpec / SimpleCov versions and your
480
+ `.rspec-tracer` config.
481
+ - **Usage questions, design discussion, "how do I X?"** — [GitHub Discussions](https://github.com/avmnu-sng/rspec-tracer/discussions).
482
+ - **Roadmap** — [`ROADMAP.md`](ROADMAP.md) at repo root + the live
483
+ [project board](https://github.com/users/avmnu-sng/projects).
484
+ - **Architecture deep-dive** — [`ARCHITECTURE.md`](ARCHITECTURE.md).
485
+ - **Upgrading from 1.x** — [`UPGRADING.md`](UPGRADING.md).
486
+ - **Contributing** — [`.github/CONTRIBUTING.md`](.github/CONTRIBUTING.md).
487
+
488
+ Released under the [MIT License](LICENSE). Everyone interacting in
489
+ the project is expected to follow the
490
+ [Code of Conduct](.github/CODE_OF_CONDUCT.md).
491
+
492
+ ## Section anchor map (1.x → 2.0)
493
+
494
+ The README was restructured in 2.0. If you bookmarked a 1.x section,
495
+ here's where its content lives now:
496
+
497
+ | 1.x anchor | 2.0 anchor |
498
+ |---------------------------------------|---------------------------------------------------------|
499
+ | `#getting-started` | [Quick start](#quick-start) |
500
+ | `#working-with-jruby` | [Quick start](#quick-start) (JRuby noted under floors in [`UPGRADING.md`](UPGRADING.md)) |
501
+ | `#working-with-parallel-tests` | [Working with parallel_tests](#working-with-parallel_tests) under Rails quick start |
502
+ | `#configuring-ci` | [Configuring CI](#configuring-ci) |
503
+ | `#caching-rspec-tracer-in-ci` | [Caching rspec-tracer in CI](#caching-rspec-tracer-in-ci) |
504
+ | `#advanced-configuration` | Split between [Per-example `tracks:` DSL](#per-example-tracks-dsl), [Pluggable storage](#pluggable-storage), and [`UPGRADING.md`](UPGRADING.md) |
505
+ | `#available-settings` | Inline across the new sections; see also [`ARCHITECTURE.md`](ARCHITECTURE.md) |
506
+ | `#filters` | Inline under [SimpleCov interop](#simplecov-interop) and the configuration DSL examples |
507
+ | `#duplicate-examples` | [Reports](#reports) under "Duplicate Examples" |
508
+ | `#demo` | [Reports](#reports) |