rspec-tracer 0.7.0 → 0.9.2

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.
@@ -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
@@ -2,9 +2,10 @@
2
2
 
3
3
  module RSpecTracer
4
4
  class Reporter
5
- attr_reader :all_examples, :possibly_flaky_examples, :flaky_examples, :pending_examples,
6
- :all_files, :modified_files, :deleted_files, :dependency, :reverse_dependency,
7
- :examples_coverage, :last_run
5
+ attr_reader :all_examples, :interrupted_examples, :possibly_flaky_examples,
6
+ :flaky_examples, :pending_examples, :all_files, :modified_files,
7
+ :deleted_files, :dependency, :reverse_dependency, :examples_coverage,
8
+ :last_run
8
9
 
9
10
  def initialize
10
11
  initialize_examples
@@ -15,6 +16,7 @@ module RSpecTracer
15
16
 
16
17
  def register_example(example)
17
18
  @all_examples[example[:example_id]] = example
19
+ @duplicate_examples[example[:example_id]] << example
18
20
  end
19
21
 
20
22
  def on_example_skipped(example_id)
@@ -36,8 +38,21 @@ module RSpecTracer
36
38
  @all_examples[example_id][:execution_result] = formatted_execution_result(result)
37
39
  end
38
40
 
41
+ def register_interrupted_examples
42
+ @all_examples.each_pair do |example_id, example|
43
+ next if example.key?(:execution_result)
44
+
45
+ @interrupted_examples << example_id
46
+ end
47
+
48
+ return if @interrupted_examples.empty?
49
+
50
+ puts "RSpec tracer is not processing #{@interrupted_examples.count} interrupted examples"
51
+ end
52
+
39
53
  def register_deleted_examples(seen_examples)
40
54
  @deleted_examples = seen_examples.keys.to_set - (@skipped_examples | @all_examples.keys)
55
+ @deleted_examples -= @interrupted_examples
41
56
 
42
57
  @deleted_examples.select! do |example_id|
43
58
  example = seen_examples[example_id]
@@ -62,6 +77,10 @@ module RSpecTracer
62
77
  @pending_examples << example_id
63
78
  end
64
79
 
80
+ def example_interrupted?(example_id)
81
+ @interrupted_examples.include?(example_id)
82
+ end
83
+
65
84
  def example_passed?(example_id)
66
85
  @passed_examples.include?(example_id)
67
86
  end
@@ -106,6 +125,17 @@ module RSpecTracer
106
125
  file_deleted?(file_name) || file_modified?(file_name)
107
126
  end
108
127
 
128
+ def incorrect_analysis?
129
+ @duplicate_examples.select! { |_, examples| examples.count > 1 }
130
+
131
+ return false if @duplicate_examples.empty?
132
+
133
+ print_not_use_notice
134
+ print_duplicate_examples
135
+
136
+ true
137
+ end
138
+
109
139
  def register_dependency(example_id, file_name)
110
140
  @dependency[example_id] << file_name
111
141
  end
@@ -116,6 +146,8 @@ module RSpecTracer
116
146
 
117
147
  def generate_reverse_dependency_report
118
148
  @dependency.each_pair do |example_id, files|
149
+ next if @interrupted_examples.include?(example_id)
150
+
119
151
  example_file = @all_examples[example_id][:rerun_file_name]
120
152
 
121
153
  files.each do |file_name|
@@ -133,9 +165,11 @@ module RSpecTracer
133
165
  pid: RSpecTracer.pid,
134
166
  actual_count: RSpec.world.example_count + @skipped_examples.count,
135
167
  example_count: RSpec.world.example_count,
168
+ interrupted_examples: @interrupted_examples.count,
136
169
  failed_examples: @failed_examples.count,
137
170
  skipped_examples: @skipped_examples.count,
138
- pending_examples: @pending_examples.count
171
+ pending_examples: @pending_examples.count,
172
+ flaky_examples: @flaky_examples.count
139
173
  }
140
174
  end
141
175
 
@@ -169,6 +203,8 @@ module RSpecTracer
169
203
 
170
204
  def initialize_examples
171
205
  @all_examples = {}
206
+ @duplicate_examples = Hash.new { |examples, example_id| examples[example_id] = [] }
207
+ @interrupted_examples = Set.new
172
208
  @passed_examples = Set.new
173
209
  @possibly_flaky_examples = Set.new
174
210
  @flaky_examples = Set.new
@@ -226,6 +262,45 @@ module RSpecTracer
226
262
  @reverse_dependency = report.to_h
227
263
  end
228
264
 
265
+ def print_not_use_notice
266
+ justify = ' ' * 4
267
+ four_justify = justify * 4
268
+
269
+ puts '=' * 80
270
+ puts "#{four_justify}IMPORTANT NOTICE -- DO NOT USE RSPEC TRACER"
271
+ puts '=' * 80
272
+ puts "#{justify}It would be best to make changes so that the RSpec tracer can uniquely"
273
+ puts "#{justify}identify all the examples, and then you can enable the RSpec tracer back."
274
+ puts '=' * 80
275
+ puts
276
+ end
277
+
278
+ # rubocop:disable Metrics/AbcSize
279
+ def print_duplicate_examples
280
+ total = @duplicate_examples.sum { |_, examples| examples.length }
281
+
282
+ puts "RSpec tracer could not uniquely identify the following #{total} examples:"
283
+
284
+ justify = ' ' * 2
285
+ nested_justify = justify * 3
286
+
287
+ @duplicate_examples.each_pair do |example_id, examples|
288
+ puts "#{justify}- Example ID: #{example_id} (#{examples.count} examples)"
289
+
290
+ examples.each do |example|
291
+ description = example[:full_description].strip
292
+ file_name = example[:rerun_file_name].sub(%r{^/}, '')
293
+ line_number = example[:rerun_line_number]
294
+ location = "#{file_name}:#{line_number}"
295
+
296
+ puts "#{nested_justify}* #{description} (#{location})"
297
+ end
298
+ end
299
+
300
+ puts
301
+ end
302
+ # rubocop:enable Metrics/AbcSize
303
+
229
304
  def write_all_examples_report
230
305
  file_name = File.join(@cache_dir, 'all_examples.json')
231
306
 
@@ -3,13 +3,23 @@
3
3
  module RSpecTracer
4
4
  module RSpecRunner
5
5
  # rubocop:disable Metrics/AbcSize
6
- def run_specs(_example_groups)
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, example_groups = RSpecTracer.filter_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, 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(example_groups)
36
+ super(filtered_example_groups)
27
37
  end
28
38
  # rubocop:enable Metrics/AbcSize
29
39
  end
@@ -8,6 +8,7 @@ module RSpecTracer
8
8
  EXAMPLE_RUN_REASON = {
9
9
  explicit_run: 'Explicit run',
10
10
  no_cache: 'No cache',
11
+ interrupted: 'Interrupted previously',
11
12
  flaky_example: 'Flaky example',
12
13
  failed_example: 'Failed previously',
13
14
  pending_example: 'Pending previously',
@@ -59,10 +60,18 @@ module RSpecTracer
59
60
  @reporter.on_example_pending(example_id, execution_result)
60
61
  end
61
62
 
63
+ def register_interrupted_examples
64
+ @reporter.register_interrupted_examples
65
+ end
66
+
62
67
  def register_deleted_examples
63
68
  @reporter.register_deleted_examples(@cache.all_examples)
64
69
  end
65
70
 
71
+ def incorrect_analysis?
72
+ @reporter.incorrect_analysis?
73
+ end
74
+
66
75
  # rubocop:disable Metrics/AbcSize
67
76
  def generate_missed_coverage
68
77
  missed_coverage = Hash.new do |files_coverage, file_path|
@@ -73,6 +82,7 @@ module RSpecTracer
73
82
 
74
83
  @cache.cached_examples_coverage.each_pair do |example_id, example_coverage|
75
84
  example_coverage.each_pair do |file_path, line_coverage|
85
+ next if @reporter.example_interrupted?(example_id)
76
86
  next unless @reporter.example_skipped?(example_id)
77
87
 
78
88
  file_name = RSpecTracer::SourceFile.file_name(file_path)
@@ -93,6 +103,8 @@ module RSpecTracer
93
103
  filtered_files = Set.new
94
104
 
95
105
  examples_coverage.each_pair do |example_id, example_coverage|
106
+ next if @reporter.example_interrupted?(example_id)
107
+
96
108
  register_example_files_dependency(example_id)
97
109
 
98
110
  example_coverage.each_key do |file_path|
@@ -118,6 +130,8 @@ module RSpecTracer
118
130
  @reporter.register_source_file(source_file)
119
131
 
120
132
  @reporter.all_examples.each_key do |example_id|
133
+ next if @reporter.example_interrupted?(example_id)
134
+
121
135
  @reporter.register_dependency(example_id, source_file[:file_name])
122
136
  end
123
137
  end
@@ -165,6 +179,7 @@ module RSpecTracer
165
179
  end
166
180
 
167
181
  def filter_by_example_status
182
+ add_previously_interrupted_examples
168
183
  add_previously_flaky_examples
169
184
  add_previously_failed_examples
170
185
  add_previously_pending_examples
@@ -179,10 +194,17 @@ module RSpecTracer
179
194
  end
180
195
  end
181
196
 
197
+ def add_previously_interrupted_examples
198
+ @cache.interrupted_examples.each do |example_id|
199
+ @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:interrupted]
200
+ end
201
+ end
202
+
182
203
  def add_previously_flaky_examples
183
204
  @cache.flaky_examples.each do |example_id|
184
205
  @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:flaky_example]
185
206
 
207
+ next unless @cache.dependency.key?(example_id)
186
208
  next unless (@changed_files & @cache.dependency[example_id]).empty?
187
209
 
188
210
  @reporter.register_possibly_flaky_example(example_id)
@@ -195,6 +217,7 @@ module RSpecTracer
195
217
 
196
218
  @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:failed_example]
197
219
 
220
+ next unless @cache.dependency.key?(example_id)
198
221
  next unless (@changed_files & @cache.dependency[example_id]).empty?
199
222
 
200
223
  @reporter.register_possibly_flaky_example(example_id)
@@ -243,6 +266,8 @@ module RSpecTracer
243
266
  end
244
267
 
245
268
  def register_example_files_dependency(example_id)
269
+ return if @reporter.example_interrupted?(example_id)
270
+
246
271
  example = @reporter.all_examples[example_id]
247
272
 
248
273
  register_example_file_dependency(example_id, example[:file_name])
@@ -253,6 +278,8 @@ module RSpecTracer
253
278
  end
254
279
 
255
280
  def register_example_file_dependency(example_id, file_name)
281
+ return if @reporter.example_interrupted?(example_id)
282
+
256
283
  source_file = RSpecTracer::SourceFile.from_name(file_name)
257
284
 
258
285
  @reporter.register_source_file(source_file)
@@ -260,6 +287,8 @@ module RSpecTracer
260
287
  end
261
288
 
262
289
  def register_file_dependency(example_id, file_path)
290
+ return if @reporter.example_interrupted?(example_id)
291
+
263
292
  source_file = RSpecTracer::SourceFile.from_path(file_path)
264
293
 
265
294
  return false if RSpecTracer.filters.any? { |filter| filter.match?(source_file) }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSpecTracer
4
- VERSION = '0.7.0'
4
+ VERSION = '0.9.2'
5
5
  end
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
- ::Coverage.start
165
- end
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
- generate_reports
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
@@ -194,6 +196,7 @@ module RSpecTracer
194
196
  def process_dependency
195
197
  starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
196
198
 
199
+ runner.register_interrupted_examples
197
200
  runner.register_deleted_examples
198
201
  runner.register_dependency(coverage_reporter.examples_coverage)
199
202
  runner.register_untraced_dependency(@traced_files)
@@ -224,12 +227,13 @@ module RSpecTracer
224
227
 
225
228
  puts 'SimpleCov will now generate coverage report (<3 RSpec tracer)'
226
229
 
227
- SimpleCov.result.format!
230
+ coverage_reporter.record_coverage if RSpecTracer.no_examples
228
231
  end
229
232
 
230
233
  def run_coverage_exit_task
231
234
  starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
232
235
 
236
+ coverage_reporter.record_coverage if RSpecTracer.no_examples
233
237
  coverage_reporter.generate_final_coverage
234
238
 
235
239
  file_name = File.join(RSpecTracer.coverage_path, 'coverage.json')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-tracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.9.2
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-10 00:00:00.000000000 Z
11
+ date: 2021-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docile
@@ -50,9 +50,11 @@ dependencies:
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
52
  version: 3.6.0
53
- description: RSpec Tracer is a specs dependency analysis tool and a test skipper for
54
- RSpec. It maintains a list of files for each test, enabling itself to skip tests
55
- in the subsequent runs if none of the dependent files are changed.
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: []
@@ -94,8 +96,10 @@ files:
94
96
  - lib/rspec_tracer/html_reporter/views/flaky_examples.erb
95
97
  - lib/rspec_tracer/html_reporter/views/layout.erb
96
98
  - lib/rspec_tracer/remote_cache/Rakefile
99
+ - lib/rspec_tracer/remote_cache/aws.rb
97
100
  - lib/rspec_tracer/remote_cache/cache.rb
98
- - lib/rspec_tracer/remote_cache/git.rb
101
+ - lib/rspec_tracer/remote_cache/repo.rb
102
+ - lib/rspec_tracer/remote_cache/validator.rb
99
103
  - lib/rspec_tracer/reporter.rb
100
104
  - lib/rspec_tracer/rspec_reporter.rb
101
105
  - lib/rspec_tracer/rspec_runner.rb
@@ -109,7 +113,7 @@ licenses:
109
113
  - MIT
110
114
  metadata:
111
115
  homepage_uri: https://github.com/avmnu-sng/rspec-tracer
112
- source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.7.0
116
+ source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.9.2
113
117
  changelog_uri: https://github.com/avmnu-sng/rspec-tracer/blob/main/CHANGELOG.md
114
118
  bug_tracker_uri: https://github.com/avmnu-sng/rspec-tracer/issues
115
119
  post_install_message:
@@ -130,5 +134,6 @@ requirements: []
130
134
  rubygems_version: 3.2.26
131
135
  signing_key:
132
136
  specification_version: 4
133
- summary: RSpec Tracer is a specs dependency analysis tool and a test skipper for RSpec
137
+ summary: RSpec Tracer is a specs dependency analyzer, flaky tests detector, tests
138
+ accelerator, and coverage reporter tool.
134
139
  test_files: []