testsift 0.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE +22 -0
- data/README.md +572 -0
- data/bin/testsift +15 -0
- data/lib/testsift/cache/Rakefile +43 -0
- data/lib/testsift/cli/blast_radius.rb +318 -0
- data/lib/testsift/cli/cache_clear.rb +114 -0
- data/lib/testsift/cli/cache_info.rb +106 -0
- data/lib/testsift/cli/doctor.rb +307 -0
- data/lib/testsift/cli/explain.rb +273 -0
- data/lib/testsift/cli/report_open.rb +90 -0
- data/lib/testsift/cli/snapshot_helpers.rb +119 -0
- data/lib/testsift/cli.rb +219 -0
- data/lib/testsift/configuration.rb +1467 -0
- data/lib/testsift/defaults.rb +11 -0
- data/lib/testsift/engine/snapshot_builder.rb +117 -0
- data/lib/testsift/engine.rb +896 -0
- data/lib/testsift/example.rb +188 -0
- data/lib/testsift/filter.rb +103 -0
- data/lib/testsift/line_stub.rb +62 -0
- data/lib/testsift/load_config.rb +15 -0
- data/lib/testsift/load_default_config.rb +46 -0
- data/lib/testsift/load_global_config.rb +33 -0
- data/lib/testsift/load_local_config.rb +17 -0
- data/lib/testsift/logger.rb +59 -0
- data/lib/testsift/rails/README.md +78 -0
- data/lib/testsift/rails/i18n_tracking.rb +137 -0
- data/lib/testsift/rails/notifications.rb +263 -0
- data/lib/testsift/rails/preset.rb +94 -0
- data/lib/testsift/rails/railtie.rb +22 -0
- data/lib/testsift/rails.rb +15 -0
- data/lib/testsift/remote_cache/README.md +140 -0
- data/lib/testsift/remote_cache/Rakefile +61 -0
- data/lib/testsift/remote_cache/archive.rb +137 -0
- data/lib/testsift/remote_cache/backend.rb +73 -0
- data/lib/testsift/remote_cache/git_ancestry.rb +241 -0
- data/lib/testsift/remote_cache/local_fs_backend.rb +439 -0
- data/lib/testsift/remote_cache/redis_backend.rb +554 -0
- data/lib/testsift/remote_cache/s3_backend.rb +712 -0
- data/lib/testsift/remote_cache/user_tasks.rb +436 -0
- data/lib/testsift/remote_cache/validator.rb +50 -0
- data/lib/testsift/remote_cache.rb +22 -0
- data/lib/testsift/reporters/README.md +103 -0
- data/lib/testsift/reporters/base.rb +87 -0
- data/lib/testsift/reporters/coverage_json_reporter.rb +338 -0
- data/lib/testsift/reporters/html/.gitignore +19 -0
- data/lib/testsift/reporters/html/.prettierignore +4 -0
- data/lib/testsift/reporters/html/.prettierrc.json +9 -0
- data/lib/testsift/reporters/html/README.md +80 -0
- data/lib/testsift/reporters/html/dist/assets/index.css +2 -0
- data/lib/testsift/reporters/html/dist/assets/index.js +1 -0
- data/lib/testsift/reporters/html/dist/index.html +24 -0
- data/lib/testsift/reporters/html/eslint.config.js +62 -0
- data/lib/testsift/reporters/html/package-lock.json +4910 -0
- data/lib/testsift/reporters/html/package.json +29 -0
- data/lib/testsift/reporters/html/src/app.jsx +130 -0
- data/lib/testsift/reporters/html/src/components/AllExamples.jsx +86 -0
- data/lib/testsift/reporters/html/src/components/DuplicateExamples.jsx +68 -0
- data/lib/testsift/reporters/html/src/components/ExamplesDependency.jsx +78 -0
- data/lib/testsift/reporters/html/src/components/FilesDependency.jsx +72 -0
- data/lib/testsift/reporters/html/src/components/FlakyExamples.jsx +42 -0
- data/lib/testsift/reporters/html/src/components/ReportTable.jsx +131 -0
- data/lib/testsift/reporters/html/src/components/SearchBar.jsx +19 -0
- data/lib/testsift/reporters/html/src/index.html +23 -0
- data/lib/testsift/reporters/html/src/main.jsx +37 -0
- data/lib/testsift/reporters/html/src/styles.css +434 -0
- data/lib/testsift/reporters/html/vite.config.js +42 -0
- data/lib/testsift/reporters/html_reporter.rb +266 -0
- data/lib/testsift/reporters/json_reporter.rb +88 -0
- data/lib/testsift/reporters/payload_builder.rb +241 -0
- data/lib/testsift/reporters/registry.rb +120 -0
- data/lib/testsift/reporters/terminal_reporter.rb +264 -0
- data/lib/testsift/rspec/README.md +73 -0
- data/lib/testsift/rspec/installation.rb +97 -0
- data/lib/testsift/rspec/metadata.rb +96 -0
- data/lib/testsift/rspec/parallel_tests.rb +459 -0
- data/lib/testsift/rspec/rails_setup.rb +42 -0
- data/lib/testsift/rspec/reporter_hook.rb +90 -0
- data/lib/testsift/rspec/runner_hook.rb +253 -0
- data/lib/testsift/source_file.rb +56 -0
- data/lib/testsift/storage/README.md +35 -0
- data/lib/testsift/storage/backend.rb +130 -0
- data/lib/testsift/storage/json_backend.rb +884 -0
- data/lib/testsift/storage/lazy_snapshot.rb +65 -0
- data/lib/testsift/storage/schema.rb +50 -0
- data/lib/testsift/storage/serializer/json.rb +41 -0
- data/lib/testsift/storage/serializer/msgpack.rb +167 -0
- data/lib/testsift/storage/snapshot.rb +141 -0
- data/lib/testsift/storage/sqlite_backend.rb +709 -0
- data/lib/testsift/time_formatter.rb +74 -0
- data/lib/testsift/tracker/README.md +36 -0
- data/lib/testsift/tracker/coverage_adapter.rb +180 -0
- data/lib/testsift/tracker/declared_globs.rb +100 -0
- data/lib/testsift/tracker/dependency_graph.rb +134 -0
- data/lib/testsift/tracker/env_matcher.rb +127 -0
- data/lib/testsift/tracker/env_snapshot.rb +77 -0
- data/lib/testsift/tracker/example_registry.rb +153 -0
- data/lib/testsift/tracker/file_digest.rb +61 -0
- data/lib/testsift/tracker/filter.rb +127 -0
- data/lib/testsift/tracker/input.rb +99 -0
- data/lib/testsift/tracker/io_hooks/file.rb +55 -0
- data/lib/testsift/tracker/io_hooks/io.rb +24 -0
- data/lib/testsift/tracker/io_hooks/json.rb +23 -0
- data/lib/testsift/tracker/io_hooks/kernel.rb +26 -0
- data/lib/testsift/tracker/io_hooks/yaml.rb +38 -0
- data/lib/testsift/tracker/io_hooks.rb +195 -0
- data/lib/testsift/tracker/loaded_files_tracker.rb +290 -0
- data/lib/testsift/tracker/new_file_detector.rb +62 -0
- data/lib/testsift/tracker/whole_suite_invalidators.rb +96 -0
- data/lib/testsift/version.rb +7 -0
- data/lib/testsift.rb +335 -0
- metadata +193 -0
data/lib/testsift.rb
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'English'
|
|
4
|
+
|
|
5
|
+
require 'digest/md5'
|
|
6
|
+
require 'docile'
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
require 'forwardable'
|
|
9
|
+
require 'json'
|
|
10
|
+
require 'pathname'
|
|
11
|
+
require 'set'
|
|
12
|
+
|
|
13
|
+
require_relative 'testsift/defaults'
|
|
14
|
+
require_relative 'testsift/engine'
|
|
15
|
+
require_relative 'testsift/example'
|
|
16
|
+
# Reporters must load before load_config so the Configuration DSL's
|
|
17
|
+
# `add_reporter` can validate symbol names against
|
|
18
|
+
# `Reporters::Registry::BUILT_INS` when a user `.testsift` calls
|
|
19
|
+
# it at configure time.
|
|
20
|
+
require_relative 'testsift/reporters/base'
|
|
21
|
+
require_relative 'testsift/reporters/payload_builder'
|
|
22
|
+
require_relative 'testsift/reporters/coverage_json_reporter'
|
|
23
|
+
require_relative 'testsift/reporters/json_reporter'
|
|
24
|
+
require_relative 'testsift/reporters/terminal_reporter'
|
|
25
|
+
require_relative 'testsift/reporters/html_reporter'
|
|
26
|
+
require_relative 'testsift/reporters/registry'
|
|
27
|
+
require_relative 'testsift/load_config'
|
|
28
|
+
# RemoteCache is loaded lazily from its Rakefile shim (user-driven),
|
|
29
|
+
# not at gem-load time. The user-facing tasks `testsift:remote_cache:*`
|
|
30
|
+
# pull in `lib/testsift/remote_cache.rb` when the user's Rakefile
|
|
31
|
+
# loads the shim. Test-suite runs that never invoke a cache task pay
|
|
32
|
+
# zero load cost for aws/git subshell code.
|
|
33
|
+
require_relative 'testsift/rspec/installation'
|
|
34
|
+
require_relative 'testsift/rspec/parallel_tests'
|
|
35
|
+
require_relative 'testsift/rspec/rails_setup'
|
|
36
|
+
require_relative 'testsift/source_file'
|
|
37
|
+
require_relative 'testsift/time_formatter'
|
|
38
|
+
require_relative 'testsift/version'
|
|
39
|
+
|
|
40
|
+
# Top-level entry point. Drives the lifecycle:
|
|
41
|
+
#
|
|
42
|
+
# TestSift.start
|
|
43
|
+
# -> RSpec::Installation.install! (prepend RunnerHook + ReporterHook)
|
|
44
|
+
# -> setup_coverage (::Coverage.start unless SimpleCov owns it)
|
|
45
|
+
# -> setup_rails (detect ::Rails::VERSION)
|
|
46
|
+
# -> Engine.new.setup (observers + cache load + filter decisions)
|
|
47
|
+
#
|
|
48
|
+
# at_exit_behavior (installed via `at_exit` elsewhere in the boot
|
|
49
|
+
# flow) runs the finalize stack: Engine#finalize writes the 13-file
|
|
50
|
+
# snapshot via Storage::JsonBackend, Reporters::CoverageJsonReporter
|
|
51
|
+
# writes coverage.json (single owner, replacing the 1.x
|
|
52
|
+
# CoverageReporter + CoverageWriter pair retired in 2.0),
|
|
53
|
+
# ParallelTests#finalize! merges per-worker caches on the last worker.
|
|
54
|
+
module TestSift
|
|
55
|
+
class << self
|
|
56
|
+
# Internal attribute.
|
|
57
|
+
# @api private
|
|
58
|
+
attr_accessor :running, :pid, :no_examples, :duplicate_examples
|
|
59
|
+
|
|
60
|
+
# Boot the tracer. Idempotent — safe to call multiple times in a
|
|
61
|
+
# single process (subsequent calls return without re-installing
|
|
62
|
+
# hooks). Drives the lifecycle:
|
|
63
|
+
#
|
|
64
|
+
# * Installs the RSpec runner / reporter prepend chain.
|
|
65
|
+
# * Starts `::Coverage` unless SimpleCov already owns it.
|
|
66
|
+
# * Detects Rails (memoized in `TestSift.rails?`).
|
|
67
|
+
# * Builds the {TestSift::Engine} and installs observers.
|
|
68
|
+
#
|
|
69
|
+
# Must be called BEFORE any application code loads so the boot
|
|
70
|
+
# set captured by `Coverage.peek_result` is empty. With SimpleCov,
|
|
71
|
+
# call `SimpleCov.start` first; testsift warns at boot when
|
|
72
|
+
# SimpleCov is loaded but not started.
|
|
73
|
+
#
|
|
74
|
+
# @return [void]
|
|
75
|
+
def start
|
|
76
|
+
return if defined?(@started) && @started
|
|
77
|
+
|
|
78
|
+
TestSift.running = false
|
|
79
|
+
TestSift.pid = Process.pid
|
|
80
|
+
@run_started_at = ::Time.now.utc
|
|
81
|
+
@run_monotonic_start = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
82
|
+
@started = true
|
|
83
|
+
|
|
84
|
+
TestSift.logger.debug "Started TestSift (pid: #{TestSift.pid})"
|
|
85
|
+
|
|
86
|
+
warn_on_simplecov_load_order_mistake
|
|
87
|
+
|
|
88
|
+
@parallel_tests = TestSift::RSpec::ParallelTests.active?
|
|
89
|
+
TestSift::RSpec::ParallelTests.setup! if parallel_tests?
|
|
90
|
+
initial_setup
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# SimpleCov load-order is part of the documented contract -
|
|
94
|
+
# SimpleCov.start MUST run before TestSift.start when both are
|
|
95
|
+
# used together (see README §SimpleCov interop). When the user
|
|
96
|
+
# has SimpleCov loaded but not started, we'd silently call
|
|
97
|
+
# ::Coverage.start ourselves and SimpleCov's later setup would
|
|
98
|
+
# bolt onto a Coverage already in flight, with the user's
|
|
99
|
+
# add_filter calls applied after testsift started consuming
|
|
100
|
+
# data. Surface the load-order mistake at start time so the user
|
|
101
|
+
# gets a one-line warning instead of mysteriously-broken
|
|
102
|
+
# coverage output.
|
|
103
|
+
def warn_on_simplecov_load_order_mistake
|
|
104
|
+
return unless defined?(::SimpleCov)
|
|
105
|
+
return if ::SimpleCov.respond_to?(:running) && ::SimpleCov.running
|
|
106
|
+
|
|
107
|
+
TestSift.logger.warn(
|
|
108
|
+
'SimpleCov is loaded but not started. ' \
|
|
109
|
+
'Call SimpleCov.start before TestSift.start so the ' \
|
|
110
|
+
'tracer respects SimpleCov\'s filter chain. See README ' \
|
|
111
|
+
'section "Working with SimpleCov".'
|
|
112
|
+
)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Internal method on the tracer pipeline.
|
|
116
|
+
# @api private
|
|
117
|
+
def at_exit_behavior
|
|
118
|
+
return unless TestSift.pid == Process.pid && TestSift.running
|
|
119
|
+
|
|
120
|
+
::Kernel.exit(1) if duplicate_examples
|
|
121
|
+
|
|
122
|
+
run_exit_tasks
|
|
123
|
+
ensure
|
|
124
|
+
if TestSift::RSpec::ParallelTests.active? &&
|
|
125
|
+
TestSift::RSpec::ParallelTests.last_process?
|
|
126
|
+
TestSift::RSpec::ParallelTests.remove_lock_file!
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
TestSift.running = false
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# The current {TestSift::Engine} instance, or nil if
|
|
133
|
+
# {.start} hasn't been called yet.
|
|
134
|
+
#
|
|
135
|
+
# @return [TestSift::Engine, nil]
|
|
136
|
+
def engine
|
|
137
|
+
@engine if defined?(@engine)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# True if SimpleCov was loaded AND running at the time
|
|
141
|
+
# {.start} was invoked. Determines whether testsift
|
|
142
|
+
# owns `::Coverage.start` itself (false) or defers to
|
|
143
|
+
# SimpleCov's coverage lifecycle (true).
|
|
144
|
+
#
|
|
145
|
+
# @return [Boolean]
|
|
146
|
+
def simplecov?
|
|
147
|
+
defined?(@simplecov) && @simplecov == true
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# True if `parallel_tests` is in use (detected via
|
|
151
|
+
# `ParallelTests.active?` at {.start} time). Affects
|
|
152
|
+
# cache + report directory scoping (per-worker dirs)
|
|
153
|
+
# and finalize-time merging.
|
|
154
|
+
#
|
|
155
|
+
# @return [Boolean]
|
|
156
|
+
def parallel_tests?
|
|
157
|
+
defined?(@parallel_tests) && @parallel_tests == true
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# True if Rails is loaded in this process (detected via
|
|
161
|
+
# `defined?(::Rails::VERSION)` at {.start} time). Memoized;
|
|
162
|
+
# subsequent Rails activations within the same run are not
|
|
163
|
+
# re-detected. Drives the auto-installation of Rails-side
|
|
164
|
+
# observers (template + AR notification subscribers).
|
|
165
|
+
#
|
|
166
|
+
# @return [Boolean]
|
|
167
|
+
def rails?
|
|
168
|
+
defined?(@rails) && @rails == true
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
private
|
|
172
|
+
|
|
173
|
+
# Internal method on the tracer pipeline.
|
|
174
|
+
# @api private
|
|
175
|
+
def initial_setup
|
|
176
|
+
TestSift::RSpec::Installation.install!
|
|
177
|
+
|
|
178
|
+
setup_coverage
|
|
179
|
+
setup_rails
|
|
180
|
+
|
|
181
|
+
@engine = TestSift::Engine.new(
|
|
182
|
+
configuration: TestSift,
|
|
183
|
+
read_cache_path: parallel_tests? ? File.dirname(TestSift.cache_path) : nil
|
|
184
|
+
)
|
|
185
|
+
@engine.setup
|
|
186
|
+
TestSift::RSpec::RailsSetup.install(engine: @engine, configuration: TestSift)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Internal method on the tracer pipeline.
|
|
190
|
+
# @api private
|
|
191
|
+
def setup_coverage
|
|
192
|
+
@simplecov = defined?(SimpleCov) && SimpleCov.running
|
|
193
|
+
|
|
194
|
+
return if simplecov?
|
|
195
|
+
|
|
196
|
+
require 'coverage'
|
|
197
|
+
|
|
198
|
+
return if ::Coverage.respond_to?(:running?) && ::Coverage.running?
|
|
199
|
+
|
|
200
|
+
::Coverage.start(**coverage_modes_for_start)
|
|
201
|
+
rescue RuntimeError
|
|
202
|
+
# ::Coverage.start raises if already started on some Rubies
|
|
203
|
+
# without a running? predicate; safe to ignore (matches
|
|
204
|
+
# Engine#ensure_coverage_started behavior).
|
|
205
|
+
nil
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Detects Rails by the presence of `::Rails::VERSION`. Users who
|
|
209
|
+
# require `testsift/rails` transitively load the Railtie (when
|
|
210
|
+
# Rails is also present); this method only sets the flag consumed
|
|
211
|
+
# by `TestSift.rails?`. Safe when Rails is absent - the
|
|
212
|
+
# `defined?` guard returns nil, flag stays false.
|
|
213
|
+
def setup_rails
|
|
214
|
+
@rails = defined?(::Rails::VERSION) && !::Rails::VERSION.nil?
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Internal method on the tracer pipeline.
|
|
218
|
+
# @api private
|
|
219
|
+
def run_exit_tasks
|
|
220
|
+
if TestSift.no_examples
|
|
221
|
+
TestSift.logger.info 'Skipped reports generation since all examples were filtered out'
|
|
222
|
+
else
|
|
223
|
+
snapshot = run_finalize
|
|
224
|
+
# Under parallel_tests, defer reporter emission until
|
|
225
|
+
# last-process finalize merges per-worker snapshots into the
|
|
226
|
+
# top-level cache. Each worker still persists its per-worker
|
|
227
|
+
# snapshot for the merge to consume. Earlier behavior had every
|
|
228
|
+
# worker emit reporters into testsift_report/parallel_tests_N/
|
|
229
|
+
# and purge_worker_dirs! removed those dirs - leaving the user
|
|
230
|
+
# with no usable terminal/JSON/HTML output. Now reporters fire
|
|
231
|
+
# ONCE at the merged top-level location.
|
|
232
|
+
emit_reporters(snapshot) if snapshot && !parallel_tests?
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
emit_coverage_json
|
|
236
|
+
|
|
237
|
+
TestSift::RSpec::ParallelTests.finalize! if parallel_tests?
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Engine-owned finalize path. Writes the 15-file JSON cache via
|
|
241
|
+
# Storage::JsonBackend. Per-example coverage deltas live on the
|
|
242
|
+
# Engine; 2.0 retired the CoverageReporter mid-flow piece (the
|
|
243
|
+
# legacy `coverage_reporter.generate_final_examples_coverage +
|
|
244
|
+
# merge_coverage(engine.merge_skipped_coverage(...))` is now folded
|
|
245
|
+
# into Reporters::CoverageJsonReporter#generate, which fires from
|
|
246
|
+
# `emit_coverage_json` after this method returns).
|
|
247
|
+
def run_finalize
|
|
248
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
249
|
+
|
|
250
|
+
snapshot = engine.finalize
|
|
251
|
+
|
|
252
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
253
|
+
elapsed = TestSift::TimeFormatter.format_time(ending - starting)
|
|
254
|
+
|
|
255
|
+
TestSift.logger.debug "TestSift persisted cache (took #{elapsed})"
|
|
256
|
+
snapshot
|
|
257
|
+
rescue StandardError => e
|
|
258
|
+
# Graceful-degradation contract per ARCHITECTURE.md
|
|
259
|
+
# section Cache corruption recovery: never propagate storage errors
|
|
260
|
+
# into the user's test suite. Read-only cache_path, disk-full
|
|
261
|
+
# mid-write, permission flips between runs - log and skip
|
|
262
|
+
# report emission. The caller (run_exit_tasks) checks for nil
|
|
263
|
+
# before calling emit_reporters; coverage / parallel_tests
|
|
264
|
+
# finalize paths run independently downstream.
|
|
265
|
+
TestSift.logger.warn(
|
|
266
|
+
"testsift: cache persistence failed (#{e.class}: #{e.message}); " \
|
|
267
|
+
'skipping report generation. Verify cache_path is writable.'
|
|
268
|
+
)
|
|
269
|
+
nil
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Fire the configured reporters against the persisted
|
|
273
|
+
# Snapshot. Fires per-worker under parallel_tests (same cadence as
|
|
274
|
+
# coverage.json emission); each worker produces its own report.json
|
|
275
|
+
# under its per-worker report_dir. The Registry rescues every
|
|
276
|
+
# reporter individually, so a buggy reporter warns and continues -
|
|
277
|
+
# never propagates a non-zero exit into the user's test suite
|
|
278
|
+
# (graceful degradation contract, same as Storage backends).
|
|
279
|
+
def emit_reporters(snapshot)
|
|
280
|
+
TestSift::Reporters::Registry.emit_all(
|
|
281
|
+
configuration: TestSift,
|
|
282
|
+
snapshot: snapshot,
|
|
283
|
+
report_dir: TestSift.report_path,
|
|
284
|
+
run_metadata: build_run_metadata
|
|
285
|
+
)
|
|
286
|
+
rescue StandardError => e
|
|
287
|
+
TestSift.logger.warn(
|
|
288
|
+
"testsift: reporter pipeline failed (#{e.class}: #{e.message})"
|
|
289
|
+
)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Internal method on the tracer pipeline.
|
|
293
|
+
# @api private
|
|
294
|
+
def build_run_metadata
|
|
295
|
+
{
|
|
296
|
+
pid: TestSift.pid,
|
|
297
|
+
run_time: run_elapsed_seconds,
|
|
298
|
+
started_at: defined?(@run_started_at) ? @run_started_at : nil,
|
|
299
|
+
cache_path: TestSift.cache_path,
|
|
300
|
+
parallel_tests: TestSift.parallel_tests?,
|
|
301
|
+
rails: TestSift.rails?
|
|
302
|
+
}
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Internal method on the tracer pipeline.
|
|
306
|
+
# @api private
|
|
307
|
+
def run_elapsed_seconds
|
|
308
|
+
return nil unless defined?(@run_monotonic_start) && @run_monotonic_start
|
|
309
|
+
|
|
310
|
+
(::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @run_monotonic_start).round(4)
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# Dedicated coverage.json firing path, parallel to the
|
|
314
|
+
# report-reporters Registry pipeline. Fires unconditionally - even
|
|
315
|
+
# when no examples ran (matches 1.x where coverage.json gets
|
|
316
|
+
# written with whatever boot-time peek_result returned + filter +
|
|
317
|
+
# stub). The emitter handles SimpleCov interop internally
|
|
318
|
+
# (installs `::Coverage.singleton_class.prepend` shim instead of
|
|
319
|
+
# writing coverage.json when SimpleCov is loaded).
|
|
320
|
+
def emit_coverage_json
|
|
321
|
+
return unless engine
|
|
322
|
+
|
|
323
|
+
TestSift::Reporters::CoverageJsonReporter.new(
|
|
324
|
+
snapshot: nil,
|
|
325
|
+
report_dir: TestSift.report_path,
|
|
326
|
+
run_metadata: build_run_metadata,
|
|
327
|
+
logger: TestSift.logger
|
|
328
|
+
).generate
|
|
329
|
+
rescue StandardError => e
|
|
330
|
+
TestSift.logger.warn(
|
|
331
|
+
"testsift: coverage.json emit failed (#{e.class}: #{e.message})"
|
|
332
|
+
)
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: testsift
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Sebastian Sala
|
|
8
|
+
- Abhimanyu Singh
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2026-11-09 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: docile
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - "~>"
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: '1.4'
|
|
21
|
+
type: :runtime
|
|
22
|
+
prerelease: false
|
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - "~>"
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
version: '1.4'
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
29
|
+
name: rspec-core
|
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - "~>"
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '3.12'
|
|
35
|
+
type: :runtime
|
|
36
|
+
prerelease: false
|
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - "~>"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '3.12'
|
|
42
|
+
description: TestSift records the inputs each RSpec example consumes -- using Ruby's
|
|
43
|
+
built-in coverage library plus explicit declarations -- and turns that record into
|
|
44
|
+
a flaky-test detector, a per-example dependency map, and optional CI acceleration
|
|
45
|
+
that skips the examples whose recorded inputs are unchanged. It never skips failed,
|
|
46
|
+
flaky, or pending examples.
|
|
47
|
+
email:
|
|
48
|
+
executables:
|
|
49
|
+
- testsift
|
|
50
|
+
extensions: []
|
|
51
|
+
extra_rdoc_files: []
|
|
52
|
+
files:
|
|
53
|
+
- CHANGELOG.md
|
|
54
|
+
- LICENSE
|
|
55
|
+
- README.md
|
|
56
|
+
- bin/testsift
|
|
57
|
+
- lib/testsift.rb
|
|
58
|
+
- lib/testsift/cache/Rakefile
|
|
59
|
+
- lib/testsift/cli.rb
|
|
60
|
+
- lib/testsift/cli/blast_radius.rb
|
|
61
|
+
- lib/testsift/cli/cache_clear.rb
|
|
62
|
+
- lib/testsift/cli/cache_info.rb
|
|
63
|
+
- lib/testsift/cli/doctor.rb
|
|
64
|
+
- lib/testsift/cli/explain.rb
|
|
65
|
+
- lib/testsift/cli/report_open.rb
|
|
66
|
+
- lib/testsift/cli/snapshot_helpers.rb
|
|
67
|
+
- lib/testsift/configuration.rb
|
|
68
|
+
- lib/testsift/defaults.rb
|
|
69
|
+
- lib/testsift/engine.rb
|
|
70
|
+
- lib/testsift/engine/snapshot_builder.rb
|
|
71
|
+
- lib/testsift/example.rb
|
|
72
|
+
- lib/testsift/filter.rb
|
|
73
|
+
- lib/testsift/line_stub.rb
|
|
74
|
+
- lib/testsift/load_config.rb
|
|
75
|
+
- lib/testsift/load_default_config.rb
|
|
76
|
+
- lib/testsift/load_global_config.rb
|
|
77
|
+
- lib/testsift/load_local_config.rb
|
|
78
|
+
- lib/testsift/logger.rb
|
|
79
|
+
- lib/testsift/rails.rb
|
|
80
|
+
- lib/testsift/rails/README.md
|
|
81
|
+
- lib/testsift/rails/i18n_tracking.rb
|
|
82
|
+
- lib/testsift/rails/notifications.rb
|
|
83
|
+
- lib/testsift/rails/preset.rb
|
|
84
|
+
- lib/testsift/rails/railtie.rb
|
|
85
|
+
- lib/testsift/remote_cache.rb
|
|
86
|
+
- lib/testsift/remote_cache/README.md
|
|
87
|
+
- lib/testsift/remote_cache/Rakefile
|
|
88
|
+
- lib/testsift/remote_cache/archive.rb
|
|
89
|
+
- lib/testsift/remote_cache/backend.rb
|
|
90
|
+
- lib/testsift/remote_cache/git_ancestry.rb
|
|
91
|
+
- lib/testsift/remote_cache/local_fs_backend.rb
|
|
92
|
+
- lib/testsift/remote_cache/redis_backend.rb
|
|
93
|
+
- lib/testsift/remote_cache/s3_backend.rb
|
|
94
|
+
- lib/testsift/remote_cache/user_tasks.rb
|
|
95
|
+
- lib/testsift/remote_cache/validator.rb
|
|
96
|
+
- lib/testsift/reporters/README.md
|
|
97
|
+
- lib/testsift/reporters/base.rb
|
|
98
|
+
- lib/testsift/reporters/coverage_json_reporter.rb
|
|
99
|
+
- lib/testsift/reporters/html/.gitignore
|
|
100
|
+
- lib/testsift/reporters/html/.prettierignore
|
|
101
|
+
- lib/testsift/reporters/html/.prettierrc.json
|
|
102
|
+
- lib/testsift/reporters/html/README.md
|
|
103
|
+
- lib/testsift/reporters/html/dist/assets/index.css
|
|
104
|
+
- lib/testsift/reporters/html/dist/assets/index.js
|
|
105
|
+
- lib/testsift/reporters/html/dist/index.html
|
|
106
|
+
- lib/testsift/reporters/html/eslint.config.js
|
|
107
|
+
- lib/testsift/reporters/html/package-lock.json
|
|
108
|
+
- lib/testsift/reporters/html/package.json
|
|
109
|
+
- lib/testsift/reporters/html/src/app.jsx
|
|
110
|
+
- lib/testsift/reporters/html/src/components/AllExamples.jsx
|
|
111
|
+
- lib/testsift/reporters/html/src/components/DuplicateExamples.jsx
|
|
112
|
+
- lib/testsift/reporters/html/src/components/ExamplesDependency.jsx
|
|
113
|
+
- lib/testsift/reporters/html/src/components/FilesDependency.jsx
|
|
114
|
+
- lib/testsift/reporters/html/src/components/FlakyExamples.jsx
|
|
115
|
+
- lib/testsift/reporters/html/src/components/ReportTable.jsx
|
|
116
|
+
- lib/testsift/reporters/html/src/components/SearchBar.jsx
|
|
117
|
+
- lib/testsift/reporters/html/src/index.html
|
|
118
|
+
- lib/testsift/reporters/html/src/main.jsx
|
|
119
|
+
- lib/testsift/reporters/html/src/styles.css
|
|
120
|
+
- lib/testsift/reporters/html/vite.config.js
|
|
121
|
+
- lib/testsift/reporters/html_reporter.rb
|
|
122
|
+
- lib/testsift/reporters/json_reporter.rb
|
|
123
|
+
- lib/testsift/reporters/payload_builder.rb
|
|
124
|
+
- lib/testsift/reporters/registry.rb
|
|
125
|
+
- lib/testsift/reporters/terminal_reporter.rb
|
|
126
|
+
- lib/testsift/rspec/README.md
|
|
127
|
+
- lib/testsift/rspec/installation.rb
|
|
128
|
+
- lib/testsift/rspec/metadata.rb
|
|
129
|
+
- lib/testsift/rspec/parallel_tests.rb
|
|
130
|
+
- lib/testsift/rspec/rails_setup.rb
|
|
131
|
+
- lib/testsift/rspec/reporter_hook.rb
|
|
132
|
+
- lib/testsift/rspec/runner_hook.rb
|
|
133
|
+
- lib/testsift/source_file.rb
|
|
134
|
+
- lib/testsift/storage/README.md
|
|
135
|
+
- lib/testsift/storage/backend.rb
|
|
136
|
+
- lib/testsift/storage/json_backend.rb
|
|
137
|
+
- lib/testsift/storage/lazy_snapshot.rb
|
|
138
|
+
- lib/testsift/storage/schema.rb
|
|
139
|
+
- lib/testsift/storage/serializer/json.rb
|
|
140
|
+
- lib/testsift/storage/serializer/msgpack.rb
|
|
141
|
+
- lib/testsift/storage/snapshot.rb
|
|
142
|
+
- lib/testsift/storage/sqlite_backend.rb
|
|
143
|
+
- lib/testsift/time_formatter.rb
|
|
144
|
+
- lib/testsift/tracker/README.md
|
|
145
|
+
- lib/testsift/tracker/coverage_adapter.rb
|
|
146
|
+
- lib/testsift/tracker/declared_globs.rb
|
|
147
|
+
- lib/testsift/tracker/dependency_graph.rb
|
|
148
|
+
- lib/testsift/tracker/env_matcher.rb
|
|
149
|
+
- lib/testsift/tracker/env_snapshot.rb
|
|
150
|
+
- lib/testsift/tracker/example_registry.rb
|
|
151
|
+
- lib/testsift/tracker/file_digest.rb
|
|
152
|
+
- lib/testsift/tracker/filter.rb
|
|
153
|
+
- lib/testsift/tracker/input.rb
|
|
154
|
+
- lib/testsift/tracker/io_hooks.rb
|
|
155
|
+
- lib/testsift/tracker/io_hooks/file.rb
|
|
156
|
+
- lib/testsift/tracker/io_hooks/io.rb
|
|
157
|
+
- lib/testsift/tracker/io_hooks/json.rb
|
|
158
|
+
- lib/testsift/tracker/io_hooks/kernel.rb
|
|
159
|
+
- lib/testsift/tracker/io_hooks/yaml.rb
|
|
160
|
+
- lib/testsift/tracker/loaded_files_tracker.rb
|
|
161
|
+
- lib/testsift/tracker/new_file_detector.rb
|
|
162
|
+
- lib/testsift/tracker/whole_suite_invalidators.rb
|
|
163
|
+
- lib/testsift/version.rb
|
|
164
|
+
homepage: https://github.com/salasebas/testsift
|
|
165
|
+
licenses:
|
|
166
|
+
- MIT
|
|
167
|
+
metadata:
|
|
168
|
+
homepage_uri: https://github.com/salasebas/testsift
|
|
169
|
+
source_code_uri: https://github.com/salasebas/testsift/tree/v0.0.1
|
|
170
|
+
changelog_uri: https://github.com/salasebas/testsift/blob/v0.0.1/CHANGELOG.md
|
|
171
|
+
bug_tracker_uri: https://github.com/salasebas/testsift/issues
|
|
172
|
+
rubygems_mfa_required: 'true'
|
|
173
|
+
post_install_message:
|
|
174
|
+
rdoc_options: []
|
|
175
|
+
require_paths:
|
|
176
|
+
- lib
|
|
177
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
178
|
+
requirements:
|
|
179
|
+
- - ">="
|
|
180
|
+
- !ruby/object:Gem::Version
|
|
181
|
+
version: 3.1.0
|
|
182
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
183
|
+
requirements:
|
|
184
|
+
- - ">="
|
|
185
|
+
- !ruby/object:Gem::Version
|
|
186
|
+
version: '0'
|
|
187
|
+
requirements: []
|
|
188
|
+
rubygems_version: 3.5.22
|
|
189
|
+
signing_key:
|
|
190
|
+
specification_version: 4
|
|
191
|
+
summary: 'Affected-test selection for RSpec: re-run only the examples whose recorded
|
|
192
|
+
inputs changed, with flaky-test detection and a per-example dependency map.'
|
|
193
|
+
test_files: []
|