rspec-tracer 0.7.0 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []