rspec-tracer 0.6.2 → 0.9.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 +26 -0
- data/README.md +333 -260
- data/lib/rspec_tracer/configuration.rb +12 -23
- data/lib/rspec_tracer/html_reporter/Rakefile +18 -0
- data/lib/rspec_tracer/remote_cache/Rakefile +38 -0
- data/lib/rspec_tracer/remote_cache/aws.rb +178 -0
- data/lib/rspec_tracer/remote_cache/cache.rb +36 -144
- data/lib/rspec_tracer/remote_cache/repo.rb +175 -0
- data/lib/rspec_tracer/remote_cache/validator.rb +52 -0
- data/lib/rspec_tracer/reporter.rb +52 -0
- data/lib/rspec_tracer/rspec_runner.rb +14 -4
- data/lib/rspec_tracer/runner.rb +6 -0
- data/lib/rspec_tracer/version.rb +1 -1
- data/lib/rspec_tracer.rb +16 -13
- metadata +28 -21
- data/lib/rspec_tracer/remote_cache/git.rb +0 -113
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
module RemoteCache
|
5
|
+
class Repo
|
6
|
+
class RepoError < StandardError; end
|
7
|
+
|
8
|
+
attr_reader :branch_name, :branch_ref, :branch_refs, :ancestry_refs, :cache_refs
|
9
|
+
|
10
|
+
def initialize(aws)
|
11
|
+
@aws = aws
|
12
|
+
@branch_name = ENV['GIT_BRANCH'].chomp
|
13
|
+
|
14
|
+
raise RepoError, 'GIT_BRANCH environment variable is not set' if @branch_name.nil?
|
15
|
+
|
16
|
+
fetch_head_ref
|
17
|
+
fetch_branch_ref
|
18
|
+
fetch_ancestry_refs
|
19
|
+
fetch_branch_refs
|
20
|
+
generate_cache_refs
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def fetch_head_ref
|
26
|
+
@head_ref = `git rev-parse HEAD`.chomp
|
27
|
+
|
28
|
+
raise RepoError, 'Could not find HEAD commit sha' unless $CHILD_STATUS.success?
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch_branch_ref
|
32
|
+
@merged_parents = []
|
33
|
+
@ignored_refs = []
|
34
|
+
|
35
|
+
unless merged?
|
36
|
+
@branch_ref = @head_ref
|
37
|
+
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
41
|
+
@ignored_refs << @head_ref
|
42
|
+
|
43
|
+
fetch_merged_parents
|
44
|
+
fetch_merged_branch_ref
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch_ancestry_refs
|
48
|
+
ref_list = `git rev-list --max-count=25 #{@branch_ref}`.chomp.split
|
49
|
+
|
50
|
+
raise RepoError, 'Could not find ancestry refs' unless $CHILD_STATUS.success?
|
51
|
+
|
52
|
+
ref_list = ref_list.to_set - @ignored_refs
|
53
|
+
@ancestry_refs = refs_committer_timestamp(ref_list.to_a)
|
54
|
+
|
55
|
+
return if @ancestry_refs.empty?
|
56
|
+
|
57
|
+
print_refs(@ancestry_refs, 'ancestry')
|
58
|
+
end
|
59
|
+
|
60
|
+
def fetch_branch_refs
|
61
|
+
unless @aws.branch_refs?(@branch_name)
|
62
|
+
puts "No branch refs for #{@branch_name} branch found in S3"
|
63
|
+
|
64
|
+
@branch_refs = {}
|
65
|
+
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
download_branch_refs
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate_cache_refs
|
73
|
+
ref_list = @ancestry_refs.merge(@branch_refs)
|
74
|
+
|
75
|
+
if ref_list.empty?
|
76
|
+
@cache_refs = {}
|
77
|
+
|
78
|
+
return
|
79
|
+
end
|
80
|
+
|
81
|
+
@cache_refs = ref_list.sort_by { |_, timestamp| -timestamp }.to_h
|
82
|
+
|
83
|
+
print_refs(@cache_refs, 'cache')
|
84
|
+
end
|
85
|
+
|
86
|
+
def merged?
|
87
|
+
system('git', 'rev-parse', 'HEAD^2', out: File::NULL, err: File::NULL)
|
88
|
+
end
|
89
|
+
|
90
|
+
def fetch_merged_parents
|
91
|
+
first_parent = `git rev-parse HEAD^1`.chomp
|
92
|
+
@merged_parents << first_parent if $CHILD_STATUS.success?
|
93
|
+
|
94
|
+
second_parent = `git rev-parse HEAD^2`.chomp
|
95
|
+
@merged_parents << second_parent if $CHILD_STATUS.success?
|
96
|
+
|
97
|
+
raise RepoError, 'Could not find merged commit parents' if @merged_parents.length != 2
|
98
|
+
end
|
99
|
+
|
100
|
+
def fetch_merged_branch_ref
|
101
|
+
@origin_head_ref = `git rev-parse origin/HEAD`.chomp
|
102
|
+
@branch_ref = nil
|
103
|
+
|
104
|
+
if @merged_parents.first != @origin_head_ref
|
105
|
+
@branch_ref = @head_ref
|
106
|
+
@ignored_refs = []
|
107
|
+
|
108
|
+
return
|
109
|
+
end
|
110
|
+
|
111
|
+
@branch_ref = @merged_parents.last
|
112
|
+
@ignored_refs = @ignored_refs.to_set | `git rev-list #{@branch_ref}..origin/HEAD`.chomp.split
|
113
|
+
|
114
|
+
raise RepoError, 'Could not find ignored refs' unless $CHILD_STATUS.success?
|
115
|
+
end
|
116
|
+
|
117
|
+
def refs_committer_timestamp(ref_list)
|
118
|
+
return {} if ref_list.empty?
|
119
|
+
|
120
|
+
command = <<-COMMAND.strip.gsub(/\s+/, ' ')
|
121
|
+
git show
|
122
|
+
--no-patch
|
123
|
+
--format="%H %ct"
|
124
|
+
#{ref_list.join(' ')}
|
125
|
+
COMMAND
|
126
|
+
|
127
|
+
ref_list = `#{command}`.chomp
|
128
|
+
|
129
|
+
raise RepoError, 'Could not find ancestry refs' unless $CHILD_STATUS.success?
|
130
|
+
|
131
|
+
ref_list.split("\n").map(&:split).to_h.transform_values(&:to_i)
|
132
|
+
end
|
133
|
+
|
134
|
+
def download_branch_refs
|
135
|
+
file_name = File.join(RSpecTracer.cache_path, 'branch_refs.json')
|
136
|
+
|
137
|
+
if @aws.download_branch_refs(branch_name, file_name)
|
138
|
+
@branch_refs = JSON.parse(File.read(file_name)).transform_values(&:to_i)
|
139
|
+
|
140
|
+
return if @branch_refs.empty?
|
141
|
+
|
142
|
+
filter_branch_refs
|
143
|
+
print_refs(@branch_refs, 'branch')
|
144
|
+
else
|
145
|
+
@branch_refs = {}
|
146
|
+
|
147
|
+
File.rm_f(file_name)
|
148
|
+
|
149
|
+
puts "Failed to fetch branch refs for #{@branch_name} branch"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def filter_branch_refs
|
154
|
+
if @ancestry_refs.empty?
|
155
|
+
@branch_refs = @branch_refs.sort_by { |_, timestamp| -timestamp }.first(25).to_h
|
156
|
+
|
157
|
+
return
|
158
|
+
end
|
159
|
+
|
160
|
+
oldest_ancestry_time = @ancestry_refs.values.min
|
161
|
+
|
162
|
+
@branch_refs = @branch_refs
|
163
|
+
.select { |_, timestamp| timestamp >= oldest_ancestry_time }
|
164
|
+
.sort_by { |_, timestamp| -timestamp }
|
165
|
+
.first(25)
|
166
|
+
.to_h
|
167
|
+
end
|
168
|
+
|
169
|
+
def print_refs(refs, type)
|
170
|
+
puts "Fetched the following #{type} refs for #{@branch_name} branch:"
|
171
|
+
puts refs.map { |ref, timestamp| " * #{ref} (commit timestamp: #{timestamp})" }.join("\n")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
module RemoteCache
|
5
|
+
class Validator
|
6
|
+
CACHE_FILES_PER_TEST_SUITE = 8
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@test_suite_id = ENV['TEST_SUITE_ID']
|
10
|
+
@test_suites = ENV['TEST_SUITES']
|
11
|
+
|
12
|
+
if @test_suite_id.nil? ^ @test_suites.nil?
|
13
|
+
raise(
|
14
|
+
ValidationError,
|
15
|
+
'Both the enviornment variables TEST_SUITE_ID and TEST_SUITES are not set'
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
setup
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid?(ref, cache_files)
|
23
|
+
last_run_regex = Regexp.new(format(@last_run_files_regex, ref: ref))
|
24
|
+
|
25
|
+
return false if cache_files.count { |file| file.match?(last_run_regex) } != @last_run_files_count
|
26
|
+
|
27
|
+
cache_regex = Regexp.new(format(@cached_files_regex, ref: ref))
|
28
|
+
|
29
|
+
cache_files.count { |file| file.match?(cache_regex) } == @cached_files_count
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def setup
|
35
|
+
if @test_suites.nil?
|
36
|
+
@last_run_files_count = 1
|
37
|
+
@last_run_files_regex = '/%<ref>s/last_run.json$'
|
38
|
+
@cached_files_count = CACHE_FILES_PER_TEST_SUITE
|
39
|
+
@cached_files_regex = '/%<ref>s/[0-9a-f]{32}/.+.json'
|
40
|
+
else
|
41
|
+
@test_suites = @test_suites.to_i
|
42
|
+
@test_suites_regex = (1..@test_suites).to_a.join('|')
|
43
|
+
|
44
|
+
@last_run_files_count = @test_suites
|
45
|
+
@last_run_files_regex = "/%<ref>s/(#{@test_suites_regex})/last_run.json$"
|
46
|
+
@cached_files_count = CACHE_FILES_PER_TEST_SUITE * @test_suites
|
47
|
+
@cached_files_regex = "/%<ref>s/(#{@test_suites_regex})/[0-9a-f]{32}/.+.json$"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -15,6 +15,7 @@ module RSpecTracer
|
|
15
15
|
|
16
16
|
def register_example(example)
|
17
17
|
@all_examples[example[:example_id]] = example
|
18
|
+
@duplicate_examples[example[:example_id]] << example
|
18
19
|
end
|
19
20
|
|
20
21
|
def on_example_skipped(example_id)
|
@@ -106,6 +107,17 @@ module RSpecTracer
|
|
106
107
|
file_deleted?(file_name) || file_modified?(file_name)
|
107
108
|
end
|
108
109
|
|
110
|
+
def incorrect_analysis?
|
111
|
+
@duplicate_examples.select! { |_, examples| examples.count > 1 }
|
112
|
+
|
113
|
+
return false if @duplicate_examples.empty?
|
114
|
+
|
115
|
+
print_not_use_notice
|
116
|
+
print_duplicate_examples
|
117
|
+
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
109
121
|
def register_dependency(example_id, file_name)
|
110
122
|
@dependency[example_id] << file_name
|
111
123
|
end
|
@@ -169,6 +181,7 @@ module RSpecTracer
|
|
169
181
|
|
170
182
|
def initialize_examples
|
171
183
|
@all_examples = {}
|
184
|
+
@duplicate_examples = Hash.new { |examples, example_id| examples[example_id] = [] }
|
172
185
|
@passed_examples = Set.new
|
173
186
|
@possibly_flaky_examples = Set.new
|
174
187
|
@flaky_examples = Set.new
|
@@ -226,6 +239,45 @@ module RSpecTracer
|
|
226
239
|
@reverse_dependency = report.to_h
|
227
240
|
end
|
228
241
|
|
242
|
+
def print_not_use_notice
|
243
|
+
justify = ' ' * 4
|
244
|
+
four_justify = justify * 4
|
245
|
+
|
246
|
+
puts '=' * 80
|
247
|
+
puts "#{four_justify}IMPORTANT NOTICE -- DO NOT USE RSPEC TRACER"
|
248
|
+
puts '=' * 80
|
249
|
+
puts "#{justify}It would be best to make changes so that the RSpec tracer can uniquely"
|
250
|
+
puts "#{justify}identify all the examples, and then you can enable the RSpec tracer back."
|
251
|
+
puts '=' * 80
|
252
|
+
puts
|
253
|
+
end
|
254
|
+
|
255
|
+
# rubocop:disable Metrics/AbcSize
|
256
|
+
def print_duplicate_examples
|
257
|
+
total = @duplicate_examples.sum { |_, examples| examples.length }
|
258
|
+
|
259
|
+
puts "RSpec tracer could not uniquely identify the following #{total} examples:"
|
260
|
+
|
261
|
+
justify = ' ' * 2
|
262
|
+
nested_justify = justify * 3
|
263
|
+
|
264
|
+
@duplicate_examples.each_pair do |example_id, examples|
|
265
|
+
puts "#{justify}- Example ID: #{example_id} (#{examples.count} examples)"
|
266
|
+
|
267
|
+
examples.each do |example|
|
268
|
+
description = example[:full_description].strip
|
269
|
+
file_name = example[:rerun_file_name].sub(%r{^/}, '')
|
270
|
+
line_number = example[:rerun_line_number]
|
271
|
+
location = "#{file_name}:#{line_number}"
|
272
|
+
|
273
|
+
puts "#{nested_justify}* #{description} (#{location})"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
puts
|
278
|
+
end
|
279
|
+
# rubocop:enable Metrics/AbcSize
|
280
|
+
|
229
281
|
def write_all_examples_report
|
230
282
|
file_name = File.join(@cache_dir, 'all_examples.json')
|
231
283
|
|
@@ -3,13 +3,23 @@
|
|
3
3
|
module RSpecTracer
|
4
4
|
module RSpecRunner
|
5
5
|
# rubocop:disable Metrics/AbcSize
|
6
|
-
def run_specs(
|
6
|
+
def run_specs(example_groups)
|
7
7
|
actual_count = RSpec.world.example_count
|
8
|
+
RSpecTracer.no_examples = actual_count.zero?
|
9
|
+
|
10
|
+
if RSpecTracer.no_examples
|
11
|
+
RSpecTracer.running = true
|
12
|
+
|
13
|
+
super(example_groups)
|
14
|
+
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
8
18
|
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
9
|
-
filtered_examples,
|
19
|
+
filtered_examples, filtered_example_groups = RSpecTracer.filter_examples
|
10
20
|
|
11
21
|
RSpec.world.instance_variable_set(:@filtered_examples, filtered_examples)
|
12
|
-
RSpec.world.instance_variable_set(:@example_groups,
|
22
|
+
RSpec.world.instance_variable_set(:@example_groups, filtered_example_groups)
|
13
23
|
|
14
24
|
current_count = RSpec.world.example_count
|
15
25
|
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
@@ -23,7 +33,7 @@ module RSpecTracer
|
|
23
33
|
|
24
34
|
RSpecTracer.running = true
|
25
35
|
|
26
|
-
super(
|
36
|
+
super(filtered_example_groups)
|
27
37
|
end
|
28
38
|
# rubocop:enable Metrics/AbcSize
|
29
39
|
end
|
data/lib/rspec_tracer/runner.rb
CHANGED
@@ -63,6 +63,10 @@ module RSpecTracer
|
|
63
63
|
@reporter.register_deleted_examples(@cache.all_examples)
|
64
64
|
end
|
65
65
|
|
66
|
+
def incorrect_analysis?
|
67
|
+
@reporter.incorrect_analysis?
|
68
|
+
end
|
69
|
+
|
66
70
|
# rubocop:disable Metrics/AbcSize
|
67
71
|
def generate_missed_coverage
|
68
72
|
missed_coverage = Hash.new do |files_coverage, file_path|
|
@@ -183,6 +187,7 @@ module RSpecTracer
|
|
183
187
|
@cache.flaky_examples.each do |example_id|
|
184
188
|
@filtered_examples[example_id] = EXAMPLE_RUN_REASON[:flaky_example]
|
185
189
|
|
190
|
+
next unless @cache.dependency.key?(example_id)
|
186
191
|
next unless (@changed_files & @cache.dependency[example_id]).empty?
|
187
192
|
|
188
193
|
@reporter.register_possibly_flaky_example(example_id)
|
@@ -195,6 +200,7 @@ module RSpecTracer
|
|
195
200
|
|
196
201
|
@filtered_examples[example_id] = EXAMPLE_RUN_REASON[:failed_example]
|
197
202
|
|
203
|
+
next unless @cache.dependency.key?(example_id)
|
198
204
|
next unless (@changed_files & @cache.dependency[example_id]).empty?
|
199
205
|
|
200
206
|
@reporter.register_possibly_flaky_example(example_id)
|
data/lib/rspec_tracer/version.rb
CHANGED
data/lib/rspec_tracer.rb
CHANGED
@@ -28,7 +28,7 @@ require_relative 'rspec_tracer/version'
|
|
28
28
|
|
29
29
|
module RSpecTracer
|
30
30
|
class << self
|
31
|
-
attr_accessor :running, :pid
|
31
|
+
attr_accessor :running, :pid, :no_examples
|
32
32
|
|
33
33
|
def start(&block)
|
34
34
|
RSpecTracer.running = false
|
@@ -74,7 +74,11 @@ module RSpecTracer
|
|
74
74
|
def at_exit_behavior
|
75
75
|
return unless RSpecTracer.pid == Process.pid && RSpecTracer.running
|
76
76
|
|
77
|
+
::Kernel.exit(1) if runner.incorrect_analysis?
|
78
|
+
|
77
79
|
run_exit_tasks
|
80
|
+
ensure
|
81
|
+
RSpecTracer.running = false
|
78
82
|
end
|
79
83
|
|
80
84
|
def start_example_trace
|
@@ -154,15 +158,11 @@ module RSpecTracer
|
|
154
158
|
def setup_coverage
|
155
159
|
@simplecov = defined?(SimpleCov) && SimpleCov.running
|
156
160
|
|
157
|
-
if simplecov?
|
158
|
-
# rubocop:disable Lint/EmptyBlock
|
159
|
-
SimpleCov.at_exit {}
|
160
|
-
# rubocop:enable Lint/EmptyBlock
|
161
|
-
else
|
162
|
-
require 'coverage'
|
161
|
+
return if simplecov?
|
163
162
|
|
164
|
-
|
165
|
-
|
163
|
+
require 'coverage'
|
164
|
+
|
165
|
+
::Coverage.start
|
166
166
|
end
|
167
167
|
|
168
168
|
def setup_trace_point
|
@@ -175,11 +175,13 @@ module RSpecTracer
|
|
175
175
|
end
|
176
176
|
|
177
177
|
def run_exit_tasks
|
178
|
-
|
178
|
+
if RSpecTracer.no_examples
|
179
|
+
puts 'Skipped reports generation since all examples were filtered out'
|
180
|
+
else
|
181
|
+
generate_reports
|
182
|
+
end
|
179
183
|
|
180
184
|
simplecov? ? run_simplecov_exit_task : run_coverage_exit_task
|
181
|
-
ensure
|
182
|
-
RSpecTracer.running = false
|
183
185
|
end
|
184
186
|
|
185
187
|
def generate_reports
|
@@ -224,12 +226,13 @@ module RSpecTracer
|
|
224
226
|
|
225
227
|
puts 'SimpleCov will now generate coverage report (<3 RSpec tracer)'
|
226
228
|
|
227
|
-
|
229
|
+
coverage_reporter.record_coverage if RSpecTracer.no_examples
|
228
230
|
end
|
229
231
|
|
230
232
|
def run_coverage_exit_task
|
231
233
|
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
232
234
|
|
235
|
+
coverage_reporter.record_coverage if RSpecTracer.no_examples
|
233
236
|
coverage_reporter.generate_final_coverage
|
234
237
|
|
235
238
|
file_name = File.join(RSpecTracer.coverage_path, 'coverage.json')
|
metadata
CHANGED
@@ -1,58 +1,60 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-tracer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abhimanyu Singh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: docile
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.1.0
|
20
17
|
- - "~>"
|
21
18
|
- !ruby/object:Gem::Version
|
22
19
|
version: '1.1'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.1.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 1.1.0
|
30
27
|
- - "~>"
|
31
28
|
- !ruby/object:Gem::Version
|
32
29
|
version: '1.1'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.1.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: rspec-core
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - ">="
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: 3.6.0
|
40
37
|
- - "~>"
|
41
38
|
- !ruby/object:Gem::Version
|
42
39
|
version: '3.6'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 3.6.0
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- - ">="
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: 3.6.0
|
50
47
|
- - "~>"
|
51
48
|
- !ruby/object:Gem::Version
|
52
49
|
version: '3.6'
|
53
|
-
|
54
|
-
|
55
|
-
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 3.6.0
|
53
|
+
description: RSpec Tracer is a specs dependency analyzer, flaky tests detector, tests
|
54
|
+
accelerator, and coverage reporter tool for RSpec. It maintains a list of files
|
55
|
+
for each test, enabling itself to skip tests in the subsequent runs if none of the
|
56
|
+
dependent files are changed. It uses Ruby's built-in coverage library to keep track
|
57
|
+
of the coverage for each test.
|
56
58
|
email:
|
57
59
|
- abhisinghabhimanyu@gmail.com
|
58
60
|
executables: []
|
@@ -69,6 +71,7 @@ files:
|
|
69
71
|
- lib/rspec_tracer/defaults.rb
|
70
72
|
- lib/rspec_tracer/example.rb
|
71
73
|
- lib/rspec_tracer/filter.rb
|
74
|
+
- lib/rspec_tracer/html_reporter/Rakefile
|
72
75
|
- lib/rspec_tracer/html_reporter/assets/javascripts/application.js
|
73
76
|
- lib/rspec_tracer/html_reporter/assets/javascripts/libraries/jquery.js
|
74
77
|
- lib/rspec_tracer/html_reporter/assets/javascripts/plugins/datatables.js
|
@@ -92,8 +95,11 @@ files:
|
|
92
95
|
- lib/rspec_tracer/html_reporter/views/files_dependency.erb
|
93
96
|
- lib/rspec_tracer/html_reporter/views/flaky_examples.erb
|
94
97
|
- lib/rspec_tracer/html_reporter/views/layout.erb
|
98
|
+
- lib/rspec_tracer/remote_cache/Rakefile
|
99
|
+
- lib/rspec_tracer/remote_cache/aws.rb
|
95
100
|
- lib/rspec_tracer/remote_cache/cache.rb
|
96
|
-
- lib/rspec_tracer/remote_cache/
|
101
|
+
- lib/rspec_tracer/remote_cache/repo.rb
|
102
|
+
- lib/rspec_tracer/remote_cache/validator.rb
|
97
103
|
- lib/rspec_tracer/reporter.rb
|
98
104
|
- lib/rspec_tracer/rspec_reporter.rb
|
99
105
|
- lib/rspec_tracer/rspec_runner.rb
|
@@ -107,7 +113,7 @@ licenses:
|
|
107
113
|
- MIT
|
108
114
|
metadata:
|
109
115
|
homepage_uri: https://github.com/avmnu-sng/rspec-tracer
|
110
|
-
source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.
|
116
|
+
source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.9.1
|
111
117
|
changelog_uri: https://github.com/avmnu-sng/rspec-tracer/blob/main/CHANGELOG.md
|
112
118
|
bug_tracker_uri: https://github.com/avmnu-sng/rspec-tracer/issues
|
113
119
|
post_install_message:
|
@@ -125,8 +131,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
131
|
- !ruby/object:Gem::Version
|
126
132
|
version: '0'
|
127
133
|
requirements: []
|
128
|
-
rubygems_version: 3.
|
134
|
+
rubygems_version: 3.2.26
|
129
135
|
signing_key:
|
130
136
|
specification_version: 4
|
131
|
-
summary: RSpec Tracer is a specs dependency
|
137
|
+
summary: RSpec Tracer is a specs dependency analyzer, flaky tests detector, tests
|
138
|
+
accelerator, and coverage reporter tool.
|
132
139
|
test_files: []
|
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RSpecTracer
|
4
|
-
module RemoteCache
|
5
|
-
class Git
|
6
|
-
class GitOperationError < StandardError; end
|
7
|
-
|
8
|
-
attr_reader :branch_ref, :ref_list
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
fetch_head_ref
|
12
|
-
fetch_branch_ref
|
13
|
-
end
|
14
|
-
|
15
|
-
def prepare_for_download
|
16
|
-
fetch_unreachable_refs
|
17
|
-
fetch_ancestry_refs
|
18
|
-
fetch_ordered_refs
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def fetch_head_ref
|
24
|
-
@head_ref = `git rev-parse HEAD`.chomp
|
25
|
-
|
26
|
-
raise GitOperationError, 'Could not find HEAD commit sha' unless $CHILD_STATUS.success?
|
27
|
-
end
|
28
|
-
|
29
|
-
def fetch_branch_ref
|
30
|
-
@merged_parents = []
|
31
|
-
@ignored_refs = []
|
32
|
-
|
33
|
-
unless merged?
|
34
|
-
@branch_ref = @head_ref
|
35
|
-
|
36
|
-
return
|
37
|
-
end
|
38
|
-
|
39
|
-
@ignored_refs << @head_ref
|
40
|
-
|
41
|
-
fetch_merged_parents
|
42
|
-
fetch_merged_branch_ref
|
43
|
-
end
|
44
|
-
|
45
|
-
def merged?
|
46
|
-
system('git', 'rev-parse', 'HEAD^2', out: File::NULL, err: File::NULL)
|
47
|
-
end
|
48
|
-
|
49
|
-
def fetch_merged_parents
|
50
|
-
first_parent = `git rev-parse HEAD^1`.chomp
|
51
|
-
@merged_parents << first_parent if $CHILD_STATUS.success?
|
52
|
-
|
53
|
-
second_parent = `git rev-parse HEAD^2`.chomp
|
54
|
-
@merged_parents << second_parent if $CHILD_STATUS.success?
|
55
|
-
|
56
|
-
raise GitOperationError, 'Could not find merged commit parents' if @merged_parents.length != 2
|
57
|
-
end
|
58
|
-
|
59
|
-
def fetch_merged_branch_ref
|
60
|
-
@origin_head_ref = `git rev-parse origin/HEAD`.chomp
|
61
|
-
@branch_ref = nil
|
62
|
-
|
63
|
-
if @merged_parents.first != @origin_head_ref
|
64
|
-
@branch_ref = @head_ref
|
65
|
-
@ignored_refs = []
|
66
|
-
|
67
|
-
return
|
68
|
-
end
|
69
|
-
|
70
|
-
@branch_ref = @merged_parents.last
|
71
|
-
@ignored_refs = @ignored_refs.to_set | `git rev-list #{@branch_ref}..origin/HEAD`.chomp.split
|
72
|
-
|
73
|
-
raise GitOperationError, 'Could not find ignored refs' unless $CHILD_STATUS.success?
|
74
|
-
end
|
75
|
-
|
76
|
-
def fetch_unreachable_refs
|
77
|
-
command = <<-COMMAND.strip.gsub(/\s+/, ' ')
|
78
|
-
git fsck
|
79
|
-
--no-progress
|
80
|
-
--unreachable
|
81
|
-
--connectivity-only #{@branch_ref}
|
82
|
-
| awk '/commit/ { print $3 }'
|
83
|
-
| head -n 25
|
84
|
-
COMMAND
|
85
|
-
|
86
|
-
@unreachable_refs = `#{command}`.chomp.split
|
87
|
-
|
88
|
-
raise GitOperationError, 'Could not find unreachable refs' unless $CHILD_STATUS.success?
|
89
|
-
end
|
90
|
-
|
91
|
-
def fetch_ancestry_refs
|
92
|
-
@ancestry_refs = `git rev-list --max-count=25 #{@branch_ref}`.chomp.split
|
93
|
-
|
94
|
-
raise GitOperationError, 'Could not find ancestry refs' unless $CHILD_STATUS.success?
|
95
|
-
end
|
96
|
-
|
97
|
-
def fetch_ordered_refs
|
98
|
-
unordered_refs = (@unreachable_refs.to_set | @ancestry_refs) - @ignored_refs
|
99
|
-
|
100
|
-
command = <<-COMMAND.strip.gsub(/\s+/, ' ')
|
101
|
-
git rev-list
|
102
|
-
--topo-order
|
103
|
-
--no-walk=sorted
|
104
|
-
#{unordered_refs.to_a.join(' ')}
|
105
|
-
COMMAND
|
106
|
-
|
107
|
-
@ref_list = `#{command}`.chomp.split
|
108
|
-
|
109
|
-
raise GitOperationError, 'Could not find refs to download cache' unless $CHILD_STATUS.success?
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|