rspec-tracer 1.0.0 → 1.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 +4 -4
- data/CHANGELOG.md +81 -0
- data/lib/rspec_tracer/cache.rb +18 -15
- data/lib/rspec_tracer/configuration.rb +4 -4
- data/lib/rspec_tracer/coverage_merger.rb +2 -1
- data/lib/rspec_tracer/coverage_reporter.rb +3 -2
- data/lib/rspec_tracer/coverage_writer.rb +1 -1
- data/lib/rspec_tracer/filter.rb +8 -8
- data/lib/rspec_tracer/html_reporter/reporter.rb +1 -1
- data/lib/rspec_tracer/remote_cache/aws.rb +3 -3
- data/lib/rspec_tracer/remote_cache/cache.rb +2 -2
- data/lib/rspec_tracer/remote_cache/repo.rb +4 -4
- data/lib/rspec_tracer/remote_cache/validator.rb +6 -4
- data/lib/rspec_tracer/report_merger.rb +1 -1
- data/lib/rspec_tracer/report_writer.rb +12 -12
- data/lib/rspec_tracer/rspec_reporter.rb +5 -5
- data/lib/rspec_tracer/rspec_runner.rb +1 -1
- data/lib/rspec_tracer/runner.rb +12 -2
- data/lib/rspec_tracer/source_file.rb +9 -1
- data/lib/rspec_tracer/version.rb +1 -1
- data/lib/rspec_tracer.rb +28 -21
- metadata +16 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 85f9e51c157fec46651a9276dd4b098d4ff749ef07efe66284b8c317b2b4d288
|
|
4
|
+
data.tar.gz: 676738fc078e588ac51233939274ed7245c9cdf41145dd86a2cdf7ce7068060d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c30eb3e54a8c19d7ad560cd03e860a12a9c5ead82a55af2b570e6a29737fb9c213a9ece0d0335b7882e8c65e851093e9f78710efe8df10e40fa54f01d2c84e51
|
|
7
|
+
data.tar.gz: fa0db0dd8257fc3841758fafbae04df2bd0abd64950b524ba3df10aace8a7965c11533d28dd78a51a4bfaf45dbdc34b634d48ccdce5b0c565a59ed3ee02cc7a1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,84 @@
|
|
|
1
|
+
## [1.0.1] - 2026-04-24
|
|
2
|
+
|
|
3
|
+
Long-tail maintenance release. Backports high-impact crash and correctness
|
|
4
|
+
fixes from 1.1.x / 1.2.x onto the v1.0.0 foundation so users on Ruby 2.5 -
|
|
5
|
+
3.0 who can't upgrade can still pick them up via `gem 'rspec-tracer', '~> 1.0'`.
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `Cache#cached_examples_coverage` returns `{}` (not `nil`) when `last_run.json`
|
|
10
|
+
is present but `examples_coverage.json` is missing, preventing a
|
|
11
|
+
NoMethodError on the next consumer. (B1, from v1.1.0)
|
|
12
|
+
- `Runner#generate_missed_coverage` tolerates a nil cached coverage map and
|
|
13
|
+
nil per-line strength entries. (B2, from v1.1.0)
|
|
14
|
+
- `Runner#register_file_dependency` / `#register_example_file_dependency`
|
|
15
|
+
skip gracefully when `SourceFile.from_path` / `.from_name` resolves to
|
|
16
|
+
nil (e.g. gem-generated examples whose path is absent at runtime),
|
|
17
|
+
instead of crashing. (B3, from v1.1.0)
|
|
18
|
+
- `CoverageReporter#merge_coverage` treats a nil existing line-coverage
|
|
19
|
+
entry as 0 when summing skipped-test contributions. (B4, from v1.1.0)
|
|
20
|
+
- `parallel_tests`-mode merge-worker election no longer deadlocks under
|
|
21
|
+
slow CI: the elected worker is now picked via
|
|
22
|
+
`::ParallelTests.first_process?` (immutable at spawn) instead of the
|
|
23
|
+
lock-file max TEST_ENV_NUMBER (racy on slow runners). (from v1.1.1)
|
|
24
|
+
- Pin `encoding: 'UTF-8'` on every legacy `File.read` / `File.write` JSON
|
|
25
|
+
I/O site so shells with `LANG=` unset no longer crash the tracer on
|
|
26
|
+
multibyte spec descriptions. Cache (11), report_writer (12), and nine
|
|
27
|
+
other lib/ call sites covered. (from v1.1.2)
|
|
28
|
+
- `SourceFile.from_path` computes the file digest via `File.binread`, hashing
|
|
29
|
+
raw bytes regardless of Encoding.default_external. (from v1.1.2)
|
|
30
|
+
- `SourceFile.file_path` returns an absolute-external path unchanged when
|
|
31
|
+
the referenced file exists on disk (e.g. shared examples from vendored
|
|
32
|
+
gems at `/opt/bundle/gems/...`), preventing silent drop of dependency
|
|
33
|
+
registration. The guard is narrow — `start_with?('/') &&
|
|
34
|
+
!start_with?(root) && File.file?(path)` — so stripped-root cache forms
|
|
35
|
+
like `/spec/foo.rb` continue through the existing expand_path branch and
|
|
36
|
+
cache `file_name` keys stay byte-identical to v1.0.0. (C1, from v1.2.0)
|
|
37
|
+
- `RemoteCache::Aws#upload_dir` error message corrected from
|
|
38
|
+
"Failed to download files from" to "Failed to upload files from". (C3,
|
|
39
|
+
from v1.2.0)
|
|
40
|
+
- `RemoteCache::Validator::ValidationError` declared as a proper
|
|
41
|
+
`StandardError` subclass inside `Validator`; previously the
|
|
42
|
+
`TEST_SUITE_ID ^ TEST_SUITES` XOR-guard raised a `NameError:
|
|
43
|
+
uninitialized constant` instead of the intended validation error.
|
|
44
|
+
(from v1.2.0)
|
|
45
|
+
- `enviornment` → `environment` typo in the same XOR-guard raise message.
|
|
46
|
+
(from v1.2.0)
|
|
47
|
+
- `RemoteCache::Validator`'s single-suite `@cached_files_regex` anchored
|
|
48
|
+
with a trailing `$` so files with extensions beyond `.json` (e.g.
|
|
49
|
+
`.json.backup`) no longer match as cache files. (from v1.2.0)
|
|
50
|
+
- `RemoteCache::Repo#initialize` guards `ENV['GIT_BRANCH']` for nil
|
|
51
|
+
before calling `.chomp`; previously a `NoMethodError: undefined
|
|
52
|
+
method 'chomp' for nil:NilClass` crashed the init path and masked
|
|
53
|
+
the intended `RepoError` message when `GIT_BRANCH` was not set in
|
|
54
|
+
the environment. (from v1.1.0 PR #51)
|
|
55
|
+
- `RemoteCache::Repo#download_branch_refs` uses `FileUtils.rm_f`
|
|
56
|
+
(not `File.rm_f`) to clean up a partial `branch_refs.json` on a
|
|
57
|
+
failed AWS download. `File.rm_f` is undefined — `rm_f` is a
|
|
58
|
+
FileUtils method — so the failing-download branch would crash with
|
|
59
|
+
`NoMethodError` instead of cleaning up and logging. (from v1.1.0
|
|
60
|
+
PR #65)
|
|
61
|
+
|
|
62
|
+
### Note on exclusions
|
|
63
|
+
|
|
64
|
+
The following items from 1.1.x / 1.2.0 are intentionally NOT in this
|
|
65
|
+
release to preserve the 1.0.0 cache-format contract and Ruby 2.5+ floor:
|
|
66
|
+
|
|
67
|
+
- Default-filter expansion (1.1.0 — adds `/lib/rspec_tracer/`,
|
|
68
|
+
`/usr/local/lib/ruby/`, etc. to the default filters). Changing the
|
|
69
|
+
default filter set shifts the files present in `all_files.json` for
|
|
70
|
+
most users, which would invalidate existing caches on upgrade.
|
|
71
|
+
- `USE_TEST_SUITE_ID_CACHE` opt-in ENV flag (1.2.0). This is a new
|
|
72
|
+
feature, not a bug fix; users who want it can upgrade to 1.2.x.
|
|
73
|
+
- The 1.1+ configuration DSL refactor (anonymous block forwarding,
|
|
74
|
+
alias_method wrapping, ENV.fetch normalizations). These are
|
|
75
|
+
Ruby-3.1-exclusive in places and orthogonal to the crash-fix scope.
|
|
76
|
+
|
|
77
|
+
### Ruby support
|
|
78
|
+
|
|
79
|
+
Gemspec `required_ruby_version` unchanged at `>= 2.5.0`. CI gates
|
|
80
|
+
Ruby 2.5 - 4.0 inclusive on `ubuntu-latest`.
|
|
81
|
+
|
|
1
82
|
## [1.0.0] - 2021-10-21
|
|
2
83
|
|
|
3
84
|
### Added
|
data/lib/rspec_tracer/cache.rb
CHANGED
|
@@ -17,6 +17,7 @@ module RSpecTracer
|
|
|
17
17
|
@pending_examples = Set.new
|
|
18
18
|
@all_files = {}
|
|
19
19
|
@dependency = Hash.new { |hash, key| hash[key] = Set.new }
|
|
20
|
+
@examples_coverage = {}
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
def load_cache_for_run
|
|
@@ -50,23 +51,25 @@ module RSpecTracer
|
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def cached_examples_coverage
|
|
53
|
-
return @examples_coverage if
|
|
54
|
+
return @examples_coverage if @examples_coverage_loaded
|
|
55
|
+
|
|
56
|
+
@examples_coverage_loaded = true
|
|
54
57
|
|
|
55
58
|
cache_path = RSpecTracer.cache_path
|
|
56
59
|
cache_path = File.dirname(cache_path) if RSpecTracer.parallel_tests?
|
|
57
60
|
run_id = last_run_id(cache_path)
|
|
58
61
|
|
|
59
|
-
return @examples_coverage
|
|
62
|
+
return @examples_coverage if run_id.nil?
|
|
60
63
|
|
|
61
64
|
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
62
65
|
cache_dir = File.join(cache_path, run_id)
|
|
63
|
-
|
|
66
|
+
load_examples_coverage_cache(cache_dir)
|
|
64
67
|
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
65
68
|
elapsed = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
|
66
69
|
|
|
67
70
|
puts "RSpec tracer loaded cached examples coverage (took #{elapsed})" if RSpecTracer.verbose?
|
|
68
71
|
|
|
69
|
-
|
|
72
|
+
@examples_coverage
|
|
70
73
|
end
|
|
71
74
|
|
|
72
75
|
private
|
|
@@ -76,7 +79,7 @@ module RSpecTracer
|
|
|
76
79
|
|
|
77
80
|
return unless File.file?(file_name)
|
|
78
81
|
|
|
79
|
-
JSON.parse(File.read(file_name))['run_id']
|
|
82
|
+
JSON.parse(File.read(file_name, encoding: 'UTF-8'))['run_id']
|
|
80
83
|
end
|
|
81
84
|
|
|
82
85
|
def load_all_examples_cache(cache_dir, discard_run_reason: true)
|
|
@@ -84,7 +87,7 @@ module RSpecTracer
|
|
|
84
87
|
|
|
85
88
|
return unless File.file?(file_name)
|
|
86
89
|
|
|
87
|
-
@all_examples = JSON.parse(File.read(file_name)).transform_values do |examples|
|
|
90
|
+
@all_examples = JSON.parse(File.read(file_name, encoding: 'UTF-8')).transform_values do |examples|
|
|
88
91
|
examples.transform_keys(&:to_sym)
|
|
89
92
|
end
|
|
90
93
|
|
|
@@ -99,7 +102,7 @@ module RSpecTracer
|
|
|
99
102
|
|
|
100
103
|
return unless File.file?(file_name)
|
|
101
104
|
|
|
102
|
-
@duplicate_examples = JSON.parse(File.read(file_name)).transform_values do |examples|
|
|
105
|
+
@duplicate_examples = JSON.parse(File.read(file_name, encoding: 'UTF-8')).transform_values do |examples|
|
|
103
106
|
examples.map { |example| example.transform_keys(&:to_sym) }
|
|
104
107
|
end
|
|
105
108
|
end
|
|
@@ -109,7 +112,7 @@ module RSpecTracer
|
|
|
109
112
|
|
|
110
113
|
return unless File.file?(file_name)
|
|
111
114
|
|
|
112
|
-
@interrupted_examples = JSON.parse(File.read(file_name)).to_set
|
|
115
|
+
@interrupted_examples = JSON.parse(File.read(file_name, encoding: 'UTF-8')).to_set
|
|
113
116
|
end
|
|
114
117
|
|
|
115
118
|
def load_flaky_examples_cache(cache_dir)
|
|
@@ -117,7 +120,7 @@ module RSpecTracer
|
|
|
117
120
|
|
|
118
121
|
return unless File.file?(file_name)
|
|
119
122
|
|
|
120
|
-
@flaky_examples = JSON.parse(File.read(file_name)).to_set
|
|
123
|
+
@flaky_examples = JSON.parse(File.read(file_name, encoding: 'UTF-8')).to_set
|
|
121
124
|
end
|
|
122
125
|
|
|
123
126
|
def load_failed_examples_cache(cache_dir)
|
|
@@ -125,7 +128,7 @@ module RSpecTracer
|
|
|
125
128
|
|
|
126
129
|
return unless File.file?(file_name)
|
|
127
130
|
|
|
128
|
-
@failed_examples = JSON.parse(File.read(file_name)).to_set
|
|
131
|
+
@failed_examples = JSON.parse(File.read(file_name, encoding: 'UTF-8')).to_set
|
|
129
132
|
end
|
|
130
133
|
|
|
131
134
|
def load_pending_examples_cache(cache_dir)
|
|
@@ -133,7 +136,7 @@ module RSpecTracer
|
|
|
133
136
|
|
|
134
137
|
return unless File.file?(file_name)
|
|
135
138
|
|
|
136
|
-
@pending_examples = JSON.parse(File.read(file_name)).to_set
|
|
139
|
+
@pending_examples = JSON.parse(File.read(file_name, encoding: 'UTF-8')).to_set
|
|
137
140
|
end
|
|
138
141
|
|
|
139
142
|
def load_skipped_examples_cache(cache_dir)
|
|
@@ -141,7 +144,7 @@ module RSpecTracer
|
|
|
141
144
|
|
|
142
145
|
return unless File.file?(file_name)
|
|
143
146
|
|
|
144
|
-
@skipped_examples = JSON.parse(File.read(file_name)).to_set
|
|
147
|
+
@skipped_examples = JSON.parse(File.read(file_name, encoding: 'UTF-8')).to_set
|
|
145
148
|
end
|
|
146
149
|
|
|
147
150
|
def load_all_files_cache(cache_dir)
|
|
@@ -149,7 +152,7 @@ module RSpecTracer
|
|
|
149
152
|
|
|
150
153
|
return unless File.file?(file_name)
|
|
151
154
|
|
|
152
|
-
@all_files = JSON.parse(File.read(file_name)).transform_values do |files|
|
|
155
|
+
@all_files = JSON.parse(File.read(file_name, encoding: 'UTF-8')).transform_values do |files|
|
|
153
156
|
files.transform_keys(&:to_sym)
|
|
154
157
|
end
|
|
155
158
|
end
|
|
@@ -159,7 +162,7 @@ module RSpecTracer
|
|
|
159
162
|
|
|
160
163
|
return unless File.file?(file_name)
|
|
161
164
|
|
|
162
|
-
@dependency = JSON.parse(File.read(file_name)).transform_values(&:to_set)
|
|
165
|
+
@dependency = JSON.parse(File.read(file_name, encoding: 'UTF-8')).transform_values(&:to_set)
|
|
163
166
|
end
|
|
164
167
|
|
|
165
168
|
def load_examples_coverage_cache(cache_dir)
|
|
@@ -167,7 +170,7 @@ module RSpecTracer
|
|
|
167
170
|
|
|
168
171
|
return unless File.file?(file_name)
|
|
169
172
|
|
|
170
|
-
@examples_coverage = JSON.parse(File.read(file_name))
|
|
173
|
+
@examples_coverage = JSON.parse(File.read(file_name, encoding: 'UTF-8'))
|
|
171
174
|
end
|
|
172
175
|
end
|
|
173
176
|
end
|
|
@@ -25,7 +25,7 @@ module RSpecTracer
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def cache_dir
|
|
28
|
-
@cache_dir ||=
|
|
28
|
+
@cache_dir ||= ENV['RSPEC_TRACER_CACHE_DIR'] || DEFAULT_CACHE_DIR
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def cache_path
|
|
@@ -41,7 +41,7 @@ module RSpecTracer
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def report_dir
|
|
44
|
-
@report_dir ||=
|
|
44
|
+
@report_dir ||= ENV['RSPEC_TRACER_REPORT_DIR'] || DEFAULT_REPORT_DIR
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def report_path
|
|
@@ -57,7 +57,7 @@ module RSpecTracer
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def coverage_dir
|
|
60
|
-
@coverage_dir ||=
|
|
60
|
+
@coverage_dir ||= ENV['RSPEC_TRACER_COVERAGE_DIR'] || DEFAULT_COVERAGE_DIR
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def coverage_path
|
|
@@ -114,7 +114,7 @@ module RSpecTracer
|
|
|
114
114
|
if ParallelTests.first_process?
|
|
115
115
|
'parallel_tests_1'
|
|
116
116
|
else
|
|
117
|
-
"parallel_tests_#{ENV
|
|
117
|
+
"parallel_tests_#{ENV.fetch('TEST_ENV_NUMBER', nil)}"
|
|
118
118
|
end
|
|
119
119
|
end
|
|
120
120
|
|
|
@@ -14,7 +14,8 @@ module RSpecTracer
|
|
|
14
14
|
reports_dir.each do |report_dir|
|
|
15
15
|
next unless File.directory?(report_dir)
|
|
16
16
|
|
|
17
|
-
cache_coverage = JSON.parse(File.read("#{report_dir}/coverage.json"
|
|
17
|
+
cache_coverage = JSON.parse(File.read("#{report_dir}/coverage.json",
|
|
18
|
+
encoding: 'UTF-8'))['RSpecTracer']['coverage']
|
|
18
19
|
|
|
19
20
|
cache_coverage.each_pair do |file_name, line_coverage|
|
|
20
21
|
unless @coverage.key?(file_name)
|
|
@@ -72,7 +72,8 @@ module RSpecTracer
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
line_coverage.each_pair do |line_number, strength|
|
|
75
|
-
|
|
75
|
+
index = line_number.to_i
|
|
76
|
+
line_coverage_dup[index] = (line_coverage_dup[index] || 0) + strength
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
@coverage[file_path] = line_coverage_dup.freeze
|
|
@@ -164,7 +165,7 @@ module RSpecTracer
|
|
|
164
165
|
|
|
165
166
|
def jruby_line_stub(file_path)
|
|
166
167
|
lines = File.foreach(file_path).map { nil }
|
|
167
|
-
root_node = ::JRuby.parse(File.read(file_path))
|
|
168
|
+
root_node = ::JRuby.parse(File.read(file_path, encoding: 'UTF-8'))
|
|
168
169
|
|
|
169
170
|
visitor = org.jruby.ast.visitor.NodeVisitor.impl do |_name, node|
|
|
170
171
|
if node.newline?
|
data/lib/rspec_tracer/filter.rb
CHANGED
|
@@ -4,14 +4,6 @@ module RSpecTracer
|
|
|
4
4
|
class Filter
|
|
5
5
|
attr_reader :filter
|
|
6
6
|
|
|
7
|
-
def initialize(filter)
|
|
8
|
-
@filter = filter
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def match?(_source_file)
|
|
12
|
-
raise "#{self.class.name}#match? is not intended for direct use"
|
|
13
|
-
end
|
|
14
|
-
|
|
15
7
|
def self.register(filter)
|
|
16
8
|
return filter if filter.is_a?(Filter)
|
|
17
9
|
|
|
@@ -32,6 +24,14 @@ module RSpecTracer
|
|
|
32
24
|
raise ArgumentError, 'Unknow filter'
|
|
33
25
|
end
|
|
34
26
|
end
|
|
27
|
+
|
|
28
|
+
def initialize(filter)
|
|
29
|
+
@filter = filter
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def match?(_source_file)
|
|
33
|
+
raise "#{self.class.name}#match? is not intended for direct use"
|
|
34
|
+
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
class ArrayFilter < RSpecTracer::Filter
|
|
@@ -211,7 +211,7 @@ module RSpecTracer
|
|
|
211
211
|
end
|
|
212
212
|
|
|
213
213
|
def template(name)
|
|
214
|
-
ERB.new(File.read(File.join(File.dirname(__FILE__), 'views/', "#{name}.erb")))
|
|
214
|
+
ERB.new(File.read(File.join(File.dirname(__FILE__), 'views/', "#{name}.erb"), encoding: 'UTF-8'))
|
|
215
215
|
end
|
|
216
216
|
|
|
217
217
|
def example_status_css_class(example_status)
|
|
@@ -125,7 +125,7 @@ module RSpecTracer
|
|
|
125
125
|
remote_dir = s3_dir(ref, run_id)
|
|
126
126
|
local_dir = File.join(RSpecTracer.cache_path, run_id)
|
|
127
127
|
|
|
128
|
-
raise AwsError, "Failed to
|
|
128
|
+
raise AwsError, "Failed to upload files from #{local_dir}" unless system(
|
|
129
129
|
@aws_cli,
|
|
130
130
|
's3',
|
|
131
131
|
'cp',
|
|
@@ -142,7 +142,7 @@ module RSpecTracer
|
|
|
142
142
|
private
|
|
143
143
|
|
|
144
144
|
def setup_s3
|
|
145
|
-
s3_uri = ENV
|
|
145
|
+
s3_uri = ENV.fetch('RSPEC_TRACER_S3_URI', nil)
|
|
146
146
|
|
|
147
147
|
raise AwsError, 'RSPEC_TRACER_S3_URI environment variable is not set' if s3_uri.nil?
|
|
148
148
|
|
|
@@ -165,7 +165,7 @@ module RSpecTracer
|
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
def s3_dir(ref, run_id = nil)
|
|
168
|
-
test_suite_id = ENV
|
|
168
|
+
test_suite_id = ENV.fetch('TEST_SUITE_ID', nil)
|
|
169
169
|
|
|
170
170
|
if test_suite_id.nil?
|
|
171
171
|
"s3://#{@s3_bucket}/#{@s3_path}/#{ref}/#{run_id}/".sub(%r{/+$}, '/')
|
|
@@ -64,7 +64,7 @@ module RSpecTracer
|
|
|
64
64
|
|
|
65
65
|
ref_list = @repo.branch_refs.merge(@repo.branch_ref => branch_ref_time.to_i)
|
|
66
66
|
|
|
67
|
-
File.write(file_name, JSON.pretty_generate(ref_list))
|
|
67
|
+
File.write(file_name, JSON.pretty_generate(ref_list), encoding: 'UTF-8')
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def last_run_id
|
|
@@ -72,7 +72,7 @@ module RSpecTracer
|
|
|
72
72
|
|
|
73
73
|
raise CacheError, 'Could not find any local cache to upload' unless File.file?(file_name)
|
|
74
74
|
|
|
75
|
-
JSON.parse(File.read(file_name))['run_id']
|
|
75
|
+
JSON.parse(File.read(file_name, encoding: 'UTF-8'))['run_id']
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
end
|
|
@@ -8,11 +8,11 @@ module RSpecTracer
|
|
|
8
8
|
attr_reader :branch_name, :branch_ref, :branch_refs, :ancestry_refs, :cache_refs
|
|
9
9
|
|
|
10
10
|
def initialize(aws)
|
|
11
|
+
raise RepoError, 'GIT_BRANCH environment variable is not set' if ENV['GIT_BRANCH'].nil?
|
|
12
|
+
|
|
11
13
|
@aws = aws
|
|
12
14
|
@branch_name = ENV['GIT_BRANCH'].chomp
|
|
13
15
|
|
|
14
|
-
raise RepoError, 'GIT_BRANCH environment variable is not set' if @branch_name.nil?
|
|
15
|
-
|
|
16
16
|
fetch_head_ref
|
|
17
17
|
fetch_branch_ref
|
|
18
18
|
fetch_ancestry_refs
|
|
@@ -135,7 +135,7 @@ module RSpecTracer
|
|
|
135
135
|
file_name = File.join(RSpecTracer.cache_path, 'branch_refs.json')
|
|
136
136
|
|
|
137
137
|
if @aws.download_branch_refs(branch_name, file_name)
|
|
138
|
-
@branch_refs = JSON.parse(File.read(file_name)).transform_values(&:to_i)
|
|
138
|
+
@branch_refs = JSON.parse(File.read(file_name, encoding: 'UTF-8')).transform_values(&:to_i)
|
|
139
139
|
|
|
140
140
|
return if @branch_refs.empty?
|
|
141
141
|
|
|
@@ -144,7 +144,7 @@ module RSpecTracer
|
|
|
144
144
|
else
|
|
145
145
|
@branch_refs = {}
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
FileUtils.rm_f(file_name)
|
|
148
148
|
|
|
149
149
|
puts "Failed to fetch branch refs for #{@branch_name} branch"
|
|
150
150
|
end
|
|
@@ -3,16 +3,18 @@
|
|
|
3
3
|
module RSpecTracer
|
|
4
4
|
module RemoteCache
|
|
5
5
|
class Validator
|
|
6
|
+
class ValidationError < StandardError; end
|
|
7
|
+
|
|
6
8
|
CACHE_FILES_PER_TEST_SUITE = 11
|
|
7
9
|
|
|
8
10
|
def initialize
|
|
9
|
-
@test_suite_id = ENV
|
|
10
|
-
@test_suites = ENV
|
|
11
|
+
@test_suite_id = ENV.fetch('TEST_SUITE_ID', nil)
|
|
12
|
+
@test_suites = ENV.fetch('TEST_SUITES', nil)
|
|
11
13
|
|
|
12
14
|
if @test_suite_id.nil? ^ @test_suites.nil?
|
|
13
15
|
raise(
|
|
14
16
|
ValidationError,
|
|
15
|
-
'Both the
|
|
17
|
+
'Both the environment variables TEST_SUITE_ID and TEST_SUITES are not set'
|
|
16
18
|
)
|
|
17
19
|
end
|
|
18
20
|
|
|
@@ -36,7 +38,7 @@ module RSpecTracer
|
|
|
36
38
|
@last_run_files_count = 1
|
|
37
39
|
@last_run_files_regex = '/%<ref>s/last_run.json$'
|
|
38
40
|
@cached_files_count = CACHE_FILES_PER_TEST_SUITE
|
|
39
|
-
@cached_files_regex = '/%<ref>s/[0-9a-f]{32}/.+.json'
|
|
41
|
+
@cached_files_regex = '/%<ref>s/[0-9a-f]{32}/.+.json$'
|
|
40
42
|
else
|
|
41
43
|
@test_suites = @test_suites.to_i
|
|
42
44
|
@test_suites_regex = (1..@test_suites).to_a.join('|')
|
|
@@ -70,7 +70,7 @@ module RSpecTracer
|
|
|
70
70
|
|
|
71
71
|
def merge_last_run_report(cache_dir)
|
|
72
72
|
file_name = File.join(cache_dir, 'last_run.json')
|
|
73
|
-
cached_last_run = JSON.parse(File.read(file_name), symbolize_names: true)
|
|
73
|
+
cached_last_run = JSON.parse(File.read(file_name, encoding: 'UTF-8'), symbolize_names: true)
|
|
74
74
|
cached_last_run[:pid] = [cached_last_run[:pid]]
|
|
75
75
|
|
|
76
76
|
cached_last_run.delete_if { |key, _| %i[run_id timestamp].include?(key) }
|
|
@@ -68,74 +68,74 @@ module RSpecTracer
|
|
|
68
68
|
def write_all_examples_report
|
|
69
69
|
file_name = File.join(@cache_dir, 'all_examples.json')
|
|
70
70
|
|
|
71
|
-
File.write(file_name, JSON.pretty_generate(@reporter.all_examples))
|
|
71
|
+
File.write(file_name, JSON.pretty_generate(@reporter.all_examples), encoding: 'UTF-8')
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def write_duplicate_examples_report
|
|
75
75
|
file_name = File.join(@cache_dir, 'duplicate_examples.json')
|
|
76
76
|
|
|
77
|
-
File.write(file_name, JSON.pretty_generate(@reporter.duplicate_examples))
|
|
77
|
+
File.write(file_name, JSON.pretty_generate(@reporter.duplicate_examples), encoding: 'UTF-8')
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def write_interrupted_examples_report
|
|
81
81
|
file_name = File.join(@cache_dir, 'interrupted_examples.json')
|
|
82
82
|
|
|
83
|
-
File.write(file_name, JSON.pretty_generate(@reporter.interrupted_examples.sort.to_a))
|
|
83
|
+
File.write(file_name, JSON.pretty_generate(@reporter.interrupted_examples.sort.to_a), encoding: 'UTF-8')
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def write_flaky_examples_report
|
|
87
87
|
file_name = File.join(@cache_dir, 'flaky_examples.json')
|
|
88
88
|
|
|
89
|
-
File.write(file_name, JSON.pretty_generate(@reporter.flaky_examples.sort.to_a))
|
|
89
|
+
File.write(file_name, JSON.pretty_generate(@reporter.flaky_examples.sort.to_a), encoding: 'UTF-8')
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
def write_failed_examples_report
|
|
93
93
|
file_name = File.join(@cache_dir, 'failed_examples.json')
|
|
94
94
|
|
|
95
|
-
File.write(file_name, JSON.pretty_generate(@reporter.failed_examples.sort.to_a))
|
|
95
|
+
File.write(file_name, JSON.pretty_generate(@reporter.failed_examples.sort.to_a), encoding: 'UTF-8')
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def write_pending_examples_report
|
|
99
99
|
file_name = File.join(@cache_dir, 'pending_examples.json')
|
|
100
100
|
|
|
101
|
-
File.write(file_name, JSON.pretty_generate(@reporter.pending_examples.sort.to_a))
|
|
101
|
+
File.write(file_name, JSON.pretty_generate(@reporter.pending_examples.sort.to_a), encoding: 'UTF-8')
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def write_skipped_examples_report
|
|
105
105
|
file_name = File.join(@cache_dir, 'skipped_examples.json')
|
|
106
106
|
|
|
107
|
-
File.write(file_name, JSON.pretty_generate(@reporter.skipped_examples.sort.to_a))
|
|
107
|
+
File.write(file_name, JSON.pretty_generate(@reporter.skipped_examples.sort.to_a), encoding: 'UTF-8')
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
def write_all_files_report
|
|
111
111
|
file_name = File.join(@cache_dir, 'all_files.json')
|
|
112
112
|
|
|
113
|
-
File.write(file_name, JSON.pretty_generate(@reporter.all_files))
|
|
113
|
+
File.write(file_name, JSON.pretty_generate(@reporter.all_files), encoding: 'UTF-8')
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
def write_dependency_report
|
|
117
117
|
file_name = File.join(@cache_dir, 'dependency.json')
|
|
118
118
|
|
|
119
|
-
File.write(file_name, JSON.pretty_generate(@reporter.dependency))
|
|
119
|
+
File.write(file_name, JSON.pretty_generate(@reporter.dependency), encoding: 'UTF-8')
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
def write_reverse_dependency_report
|
|
123
123
|
file_name = File.join(@cache_dir, 'reverse_dependency.json')
|
|
124
124
|
|
|
125
|
-
File.write(file_name, JSON.pretty_generate(@reporter.reverse_dependency))
|
|
125
|
+
File.write(file_name, JSON.pretty_generate(@reporter.reverse_dependency), encoding: 'UTF-8')
|
|
126
126
|
end
|
|
127
127
|
|
|
128
128
|
def write_examples_coverage_report
|
|
129
129
|
file_name = File.join(@cache_dir, 'examples_coverage.json')
|
|
130
130
|
|
|
131
|
-
File.write(file_name, JSON.pretty_generate(@reporter.examples_coverage))
|
|
131
|
+
File.write(file_name, JSON.pretty_generate(@reporter.examples_coverage), encoding: 'UTF-8')
|
|
132
132
|
end
|
|
133
133
|
|
|
134
134
|
def write_last_run_report
|
|
135
135
|
file_name = File.join(@report_dir, 'last_run.json')
|
|
136
136
|
last_run_data = @reporter.last_run.merge(run_id: @run_id, timestamp: Time.now.utc)
|
|
137
137
|
|
|
138
|
-
File.write(file_name, JSON.pretty_generate(last_run_data))
|
|
138
|
+
File.write(file_name, JSON.pretty_generate(last_run_data), encoding: 'UTF-8')
|
|
139
139
|
end
|
|
140
140
|
end
|
|
141
141
|
end
|
|
@@ -6,7 +6,7 @@ module RSpecTracer
|
|
|
6
6
|
RSpecTracer.coverage_reporter.record_coverage
|
|
7
7
|
RSpecTracer.start_example_trace if RSpecTracer.trace_example?
|
|
8
8
|
|
|
9
|
-
super
|
|
9
|
+
super
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def example_finished(example)
|
|
@@ -16,28 +16,28 @@ module RSpecTracer
|
|
|
16
16
|
example_id = example.metadata[:rspec_tracer_example_id]
|
|
17
17
|
RSpecTracer.coverage_reporter.compute_diff(example_id)
|
|
18
18
|
|
|
19
|
-
super
|
|
19
|
+
super
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def example_passed(example)
|
|
23
23
|
example_id = example.metadata[:rspec_tracer_example_id]
|
|
24
24
|
RSpecTracer.runner.on_example_passed(example_id, example.execution_result)
|
|
25
25
|
|
|
26
|
-
super
|
|
26
|
+
super
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def example_failed(example)
|
|
30
30
|
example_id = example.metadata[:rspec_tracer_example_id]
|
|
31
31
|
RSpecTracer.runner.on_example_failed(example_id, example.execution_result)
|
|
32
32
|
|
|
33
|
-
super
|
|
33
|
+
super
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def example_pending(example)
|
|
37
37
|
example_id = example.metadata[:rspec_tracer_example_id]
|
|
38
38
|
RSpecTracer.runner.on_example_pending(example_id, example.execution_result)
|
|
39
39
|
|
|
40
|
-
super
|
|
40
|
+
super
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
end
|
data/lib/rspec_tracer/runner.rb
CHANGED
|
@@ -77,7 +77,7 @@ module RSpecTracer
|
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
-
@cache.cached_examples_coverage.each_pair do |example_id, example_coverage|
|
|
80
|
+
(@cache.cached_examples_coverage || {}).each_pair do |example_id, example_coverage|
|
|
81
81
|
example_coverage.each_pair do |file_path, line_coverage|
|
|
82
82
|
next if @reporter.example_interrupted?(example_id) ||
|
|
83
83
|
@reporter.duplicate_example?(example_id)
|
|
@@ -89,7 +89,7 @@ module RSpecTracer
|
|
|
89
89
|
next if @reporter.file_deleted?(file_name)
|
|
90
90
|
|
|
91
91
|
line_coverage.each_pair do |line_number, strength|
|
|
92
|
-
missed_coverage[file_path][line_number] += strength
|
|
92
|
+
missed_coverage[file_path][line_number] += strength || 0
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
end
|
|
@@ -271,6 +271,11 @@ module RSpecTracer
|
|
|
271
271
|
|
|
272
272
|
source_file = RSpecTracer::SourceFile.from_name(file_name)
|
|
273
273
|
|
|
274
|
+
if source_file.nil?
|
|
275
|
+
puts "Skipping missing source file #{file_name} for example #{example_id}" if RSpecTracer.verbose?
|
|
276
|
+
return
|
|
277
|
+
end
|
|
278
|
+
|
|
274
279
|
@reporter.register_source_file(source_file)
|
|
275
280
|
@reporter.register_dependency(example_id, file_name)
|
|
276
281
|
end
|
|
@@ -281,6 +286,11 @@ module RSpecTracer
|
|
|
281
286
|
|
|
282
287
|
source_file = RSpecTracer::SourceFile.from_path(file_path)
|
|
283
288
|
|
|
289
|
+
if source_file.nil?
|
|
290
|
+
puts "Skipping missing source file #{file_path} for example #{example_id}" if RSpecTracer.verbose?
|
|
291
|
+
return false
|
|
292
|
+
end
|
|
293
|
+
|
|
284
294
|
return false if RSpecTracer.filters.any? { |filter| filter.match?(source_file) }
|
|
285
295
|
|
|
286
296
|
@reporter.register_source_file(source_file)
|
|
@@ -12,7 +12,7 @@ module RSpecTracer
|
|
|
12
12
|
{
|
|
13
13
|
file_path: file_path,
|
|
14
14
|
file_name: file_name(file_path),
|
|
15
|
-
digest: Digest::MD5.hexdigest(File.
|
|
15
|
+
digest: Digest::MD5.hexdigest(File.binread(file_path))
|
|
16
16
|
}
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -25,7 +25,15 @@ module RSpecTracer
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def file_path(file_name)
|
|
28
|
+
return file_name if absolute_external_file?(file_name)
|
|
29
|
+
|
|
28
30
|
File.expand_path(file_name.sub(%r{^/}, ''), RSpecTracer.root)
|
|
29
31
|
end
|
|
32
|
+
|
|
33
|
+
def absolute_external_file?(file_name)
|
|
34
|
+
file_name.start_with?('/') &&
|
|
35
|
+
!file_name.start_with?(RSpecTracer.root) &&
|
|
36
|
+
File.file?(file_name)
|
|
37
|
+
end
|
|
30
38
|
end
|
|
31
39
|
end
|
data/lib/rspec_tracer/version.rb
CHANGED
data/lib/rspec_tracer.rb
CHANGED
|
@@ -111,27 +111,27 @@ module RSpecTracer
|
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
def runner
|
|
114
|
-
|
|
114
|
+
@runner if defined?(@runner)
|
|
115
115
|
end
|
|
116
116
|
|
|
117
117
|
def coverage_reporter
|
|
118
|
-
|
|
118
|
+
@coverage_reporter if defined?(@coverage_reporter)
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def coverage_merger
|
|
122
|
-
|
|
122
|
+
@coverage_merger if defined?(@coverage_merger)
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
def report_merger
|
|
126
|
-
|
|
126
|
+
@report_merger if defined?(@report_merger)
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
def trace_point
|
|
130
|
-
|
|
130
|
+
@trace_point if defined?(@trace_point)
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
def traced_files
|
|
134
|
-
|
|
134
|
+
@traced_files if defined?(@traced_files)
|
|
135
135
|
end
|
|
136
136
|
|
|
137
137
|
def trace_example?
|
|
@@ -139,11 +139,11 @@ module RSpecTracer
|
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
def simplecov?
|
|
142
|
-
|
|
142
|
+
defined?(@simplecov) && @simplecov == true
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
def parallel_tests?
|
|
146
|
-
|
|
146
|
+
defined?(@parallel_tests) && @parallel_tests == true
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
private
|
|
@@ -165,7 +165,7 @@ module RSpecTracer
|
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
def initial_setup
|
|
168
|
-
unless setup_rspec
|
|
168
|
+
unless setup_rspec?
|
|
169
169
|
puts 'Could not find a running RSpec process'
|
|
170
170
|
|
|
171
171
|
return
|
|
@@ -179,7 +179,7 @@ module RSpecTracer
|
|
|
179
179
|
end
|
|
180
180
|
|
|
181
181
|
def parallel_tests_setup
|
|
182
|
-
@parallel_tests = !(ENV
|
|
182
|
+
@parallel_tests = !(ENV.fetch('TEST_ENV_NUMBER', nil) && ENV.fetch('PARALLEL_TEST_GROUPS', nil)).nil?
|
|
183
183
|
|
|
184
184
|
return unless parallel_tests?
|
|
185
185
|
|
|
@@ -208,7 +208,7 @@ module RSpecTracer
|
|
|
208
208
|
end
|
|
209
209
|
end
|
|
210
210
|
|
|
211
|
-
def setup_rspec
|
|
211
|
+
def setup_rspec?
|
|
212
212
|
runners = ObjectSpace.each_object(::RSpec::Core::Runner) do |runner|
|
|
213
213
|
runner_clazz = runner.singleton_class
|
|
214
214
|
clazz = RSpecTracer::RSpecRunner
|
|
@@ -345,7 +345,7 @@ module RSpecTracer
|
|
|
345
345
|
|
|
346
346
|
next unless File.directory?(cache_dir)
|
|
347
347
|
|
|
348
|
-
run_id = JSON.parse(File.read(File.join(cache_dir, 'last_run.json')))['run_id']
|
|
348
|
+
run_id = JSON.parse(File.read(File.join(cache_dir, 'last_run.json'), encoding: 'UTF-8'))['run_id']
|
|
349
349
|
|
|
350
350
|
reports_dir << File.join(cache_dir, run_id)
|
|
351
351
|
end
|
|
@@ -425,18 +425,25 @@ module RSpecTracer
|
|
|
425
425
|
true
|
|
426
426
|
end
|
|
427
427
|
|
|
428
|
+
# Elects the worker that performs the per-run merge. Delegates to
|
|
429
|
+
# `::ParallelTests.first_process?`, which returns true iff
|
|
430
|
+
# `TEST_ENV_NUMBER.to_i <= 1` — i.e. for exactly one worker per run,
|
|
431
|
+
# regardless of how many workers were spawned vs. CPU count.
|
|
432
|
+
#
|
|
433
|
+
# The prior lock-file-max election deadlocked under slow CI: a fast
|
|
434
|
+
# worker could finish before a slow worker loaded spec_helper and
|
|
435
|
+
# registered its TEST_ENV_NUMBER; both then self-elected and spun on
|
|
436
|
+
# each other's pid inside wait_for_other_processes_to_finish.
|
|
437
|
+
#
|
|
438
|
+
# `track_parallel_tests_test_env_number` and the lock-file cleanup
|
|
439
|
+
# in at_exit_behavior are retained so that users observing
|
|
440
|
+
# `parallel_tests.lock` see no behavior change — the file is still
|
|
441
|
+
# written and removed, just no longer consulted for election.
|
|
428
442
|
def parallel_tests_last_process?
|
|
429
443
|
return false unless parallel_tests?
|
|
444
|
+
return false unless defined?(::ParallelTests)
|
|
430
445
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
File.open(RSpecTracer.parallel_tests_lock_file, 'r') do |f|
|
|
434
|
-
f.flock(File::LOCK_SH)
|
|
435
|
-
|
|
436
|
-
max_test_num = f.read.to_i
|
|
437
|
-
end
|
|
438
|
-
|
|
439
|
-
ENV['TEST_ENV_NUMBER'].to_i == max_test_num
|
|
446
|
+
::ParallelTests.first_process?
|
|
440
447
|
end
|
|
441
448
|
end
|
|
442
449
|
end
|
metadata
CHANGED
|
@@ -1,55 +1,54 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rspec-tracer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abhimanyu Singh
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: docile
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
|
-
- - ">="
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: 1.1.0
|
|
20
16
|
- - "~>"
|
|
21
17
|
- !ruby/object:Gem::Version
|
|
22
18
|
version: '1.1'
|
|
19
|
+
- - ">="
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 1.1.0
|
|
23
22
|
type: :runtime
|
|
24
23
|
prerelease: false
|
|
25
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
26
25
|
requirements:
|
|
27
|
-
- - ">="
|
|
28
|
-
- !ruby/object:Gem::Version
|
|
29
|
-
version: 1.1.0
|
|
30
26
|
- - "~>"
|
|
31
27
|
- !ruby/object:Gem::Version
|
|
32
28
|
version: '1.1'
|
|
29
|
+
- - ">="
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: 1.1.0
|
|
33
32
|
- !ruby/object:Gem::Dependency
|
|
34
33
|
name: rspec-core
|
|
35
34
|
requirement: !ruby/object:Gem::Requirement
|
|
36
35
|
requirements:
|
|
37
|
-
- - ">="
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: 3.6.0
|
|
40
36
|
- - "~>"
|
|
41
37
|
- !ruby/object:Gem::Version
|
|
42
38
|
version: '3.6'
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: 3.6.0
|
|
43
42
|
type: :runtime
|
|
44
43
|
prerelease: false
|
|
45
44
|
version_requirements: !ruby/object:Gem::Requirement
|
|
46
45
|
requirements:
|
|
47
|
-
- - ">="
|
|
48
|
-
- !ruby/object:Gem::Version
|
|
49
|
-
version: 3.6.0
|
|
50
46
|
- - "~>"
|
|
51
47
|
- !ruby/object:Gem::Version
|
|
52
48
|
version: '3.6'
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: 3.6.0
|
|
53
52
|
description: RSpec Tracer is a specs dependency analyzer, flaky tests detector, tests
|
|
54
53
|
accelerator, and coverage reporter tool for RSpec. It maintains a list of files
|
|
55
54
|
for each test, enabling itself to skip tests in the subsequent runs if none of the
|
|
@@ -119,10 +118,9 @@ licenses:
|
|
|
119
118
|
- MIT
|
|
120
119
|
metadata:
|
|
121
120
|
homepage_uri: https://github.com/avmnu-sng/rspec-tracer
|
|
122
|
-
source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v1.0.
|
|
121
|
+
source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v1.0.1
|
|
123
122
|
changelog_uri: https://github.com/avmnu-sng/rspec-tracer/blob/main/CHANGELOG.md
|
|
124
123
|
bug_tracker_uri: https://github.com/avmnu-sng/rspec-tracer/issues
|
|
125
|
-
post_install_message:
|
|
126
124
|
rdoc_options: []
|
|
127
125
|
require_paths:
|
|
128
126
|
- lib
|
|
@@ -137,8 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
137
135
|
- !ruby/object:Gem::Version
|
|
138
136
|
version: '0'
|
|
139
137
|
requirements: []
|
|
140
|
-
rubygems_version: 3.
|
|
141
|
-
signing_key:
|
|
138
|
+
rubygems_version: 3.6.9
|
|
142
139
|
specification_version: 4
|
|
143
140
|
summary: RSpec Tracer is a specs dependency analyzer, flaky tests detector, tests
|
|
144
141
|
accelerator, and coverage reporter tool.
|