rspec-tracer 0.3.0 → 0.6.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.
@@ -2,24 +2,29 @@
2
2
 
3
3
  module RSpecTracer
4
4
  module RSpecRunner
5
+ # rubocop:disable Metrics/AbcSize
5
6
  def run_specs(_example_groups)
6
7
  actual_count = RSpec.world.example_count
8
+ starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
7
9
  filtered_examples, example_groups = RSpecTracer.filter_examples
8
10
 
9
11
  RSpec.world.instance_variable_set(:@filtered_examples, filtered_examples)
10
12
  RSpec.world.instance_variable_set(:@example_groups, example_groups)
11
13
 
12
14
  current_count = RSpec.world.example_count
15
+ ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
16
+ elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
13
17
 
14
18
  puts
15
19
  puts <<-EXAMPLES.strip.gsub(/\s+/, ' ')
16
20
  RSpec tracer is running #{current_count} examples (actual: #{actual_count},
17
- skipped: #{actual_count - current_count})
21
+ skipped: #{actual_count - current_count}) (took #{elpased})
18
22
  EXAMPLES
19
23
 
20
24
  RSpecTracer.running = true
21
25
 
22
26
  super(example_groups)
23
27
  end
28
+ # rubocop:enable Metrics/AbcSize
24
29
  end
25
30
  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
+ flaky_example: 'Flaky example',
11
12
  failed_example: 'Failed previously',
12
13
  pending_example: 'Pending previously',
13
14
  files_changed: 'Files changed'
@@ -20,6 +21,8 @@ module RSpecTracer
20
21
  @reporter = RSpecTracer::Reporter.new
21
22
  @filtered_examples = {}
22
23
 
24
+ return if @cache.run_id.nil?
25
+
23
26
  @cache.load_cache_for_run
24
27
  filter_examples_to_run
25
28
  end
@@ -127,15 +130,19 @@ module RSpecTracer
127
130
 
128
131
  def generate_report
129
132
  @reporter.generate_last_run_report
133
+ generate_examples_status_report
130
134
 
131
- generate_failed_examples_report
132
- generate_pending_examples_report
135
+ %i[all_files all_examples dependency examples_coverage reverse_dependency].each do |report_type|
136
+ starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
133
137
 
134
- %i[all_files all_examples dependency examples_coverage].each do |report_type|
135
138
  send("generate_#{report_type}_report")
139
+
140
+ ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
141
+ elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
142
+
143
+ puts "RSpec tracer generated #{report_type.to_s.tr('_', ' ')} report (took #{elpased})" if RSpecTracer.verbose?
136
144
  end
137
145
 
138
- @reporter.generate_reverse_dependency_report
139
146
  @reporter.write_reports
140
147
  end
141
148
 
@@ -146,54 +153,74 @@ module RSpecTracer
146
153
  end
147
154
 
148
155
  def filter_examples_to_run
149
- add_previously_failed_examples
150
- add_previously_pending_examples
156
+ starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
157
+ @changed_files = fetch_changed_files
158
+
159
+ filter_by_example_status
151
160
  filter_by_files_changed
152
- end
153
161
 
154
- def add_previously_failed_examples
155
- @cache.failed_examples.each do |example_id|
156
- @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:failed_example]
157
- end
162
+ ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
163
+ elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
164
+
165
+ puts "RSpec tracer processed cache (took #{elpased})" if RSpecTracer.verbose?
158
166
  end
159
167
 
160
- def add_previously_pending_examples
161
- @cache.pending_examples.each do |example_id|
162
- @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:pending_example]
163
- end
168
+ def filter_by_example_status
169
+ add_previously_flaky_examples
170
+ add_previously_failed_examples
171
+ add_previously_pending_examples
164
172
  end
165
173
 
166
174
  def filter_by_files_changed
167
175
  @cache.dependency.each_pair do |example_id, files|
168
176
  next if @filtered_examples.key?(example_id)
177
+ next if (@changed_files & files).empty?
169
178
 
170
- files.each do |file_name|
171
- break if filtered_by_file_changed?(example_id, file_name)
172
- end
179
+ @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:files_changed]
173
180
  end
174
181
  end
175
182
 
176
- def filtered_by_file_changed?(example_id, file_name)
177
- if @reporter.file_changed?(file_name)
178
- @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:files_changed]
183
+ def add_previously_flaky_examples
184
+ @cache.flaky_examples.each do |example_id|
185
+ @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:flaky_example]
179
186
 
180
- return true
187
+ next unless (@changed_files & @cache.dependency[example_id]).empty?
188
+
189
+ @reporter.register_possibly_flaky_example(example_id)
181
190
  end
191
+ end
182
192
 
183
- source_file = registered_source_file(file_name)
193
+ def add_previously_failed_examples
194
+ @cache.failed_examples.each do |example_id|
195
+ next if @filtered_examples.key?(example_id)
196
+
197
+ @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:failed_example]
184
198
 
185
- return false if source_file &&
186
- @cache.all_files[file_name][:digest] == source_file[:digest]
199
+ next unless (@changed_files & @cache.dependency[example_id]).empty?
200
+
201
+ @reporter.register_possibly_flaky_example(example_id)
202
+ end
203
+ end
204
+
205
+ def add_previously_pending_examples
206
+ @cache.pending_examples.each do |example_id|
207
+ @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:pending_example]
208
+ end
209
+ end
187
210
 
188
- @filtered_examples[example_id] = EXAMPLE_RUN_REASON[:files_changed]
211
+ def fetch_changed_files
212
+ @cache.all_files.each_value do |cached_file|
213
+ file_name = cached_file[:file_name]
214
+ source_file = RSpecTracer::SourceFile.from_name(file_name)
189
215
 
190
- if source_file.nil?
191
- @reporter.on_file_deleted(file_name)
192
- else
193
- @reporter.on_file_modified(file_name)
216
+ if source_file.nil?
217
+ @reporter.on_file_deleted(file_name)
218
+ elsif cached_file[:digest] != source_file[:digest]
219
+ @reporter.on_file_modified(file_name)
220
+ end
194
221
  end
195
222
 
196
- true
223
+ @reporter.modified_files | @reporter.deleted_files
197
224
  end
198
225
 
199
226
  def generate_untraced_files(trace_point_files)
@@ -227,14 +254,23 @@ module RSpecTracer
227
254
  end
228
255
 
229
256
  def register_example_file_dependency(example_id, file_name)
230
- source_file = registered_source_file(file_name)
257
+ source_file = RSpecTracer::SourceFile.from_name(file_name)
231
258
 
232
259
  @reporter.register_source_file(source_file)
233
260
  @reporter.register_dependency(example_id, file_name)
234
261
  end
235
262
 
236
- def registered_source_file(file_name)
237
- @reporter.all_files[file_name] || RSpecTracer::SourceFile.from_name(file_name)
263
+ def generate_examples_status_report
264
+ starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
265
+
266
+ generate_flaky_examples_report
267
+ generate_failed_examples_report
268
+ generate_pending_examples_report
269
+
270
+ ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
271
+ elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
272
+
273
+ puts "RSpec tracer generated flaky, failed, and pending examples report (took #{elpased})" if RSpecTracer.verbose?
238
274
  end
239
275
 
240
276
  def generate_all_files_report
@@ -255,6 +291,16 @@ module RSpecTracer
255
291
  end
256
292
  end
257
293
 
294
+ def generate_flaky_examples_report
295
+ @reporter.possibly_flaky_examples.each do |example_id|
296
+ next if @reporter.example_deleted?(example_id)
297
+ next unless @cache.flaky_examples.include?(example_id) ||
298
+ @reporter.example_passed?(example_id)
299
+
300
+ @reporter.register_flaky_example(example_id)
301
+ end
302
+ end
303
+
258
304
  def generate_failed_examples_report
259
305
  @cache.failed_examples.each do |example_id|
260
306
  next if @reporter.example_deleted?(example_id) ||
@@ -296,5 +342,9 @@ module RSpecTracer
296
342
  end
297
343
  end
298
344
  end
345
+
346
+ def generate_reverse_dependency_report
347
+ @reporter.generate_reverse_dependency_report
348
+ end
299
349
  end
300
350
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpecTracer
4
+ module TimeFormatter
5
+ DEFAULT_PRECISION = 2
6
+ SECONDS_PRECISION = 5
7
+
8
+ UNITS = {
9
+ second: 60,
10
+ minute: 60,
11
+ hour: 24,
12
+ day: Float::INFINITY
13
+ }.freeze
14
+
15
+ module_function
16
+
17
+ def format_time(seconds)
18
+ return pluralize(format_duration(seconds), 'second') if seconds < 60
19
+
20
+ formatted_duration = UNITS.each_pair.with_object([]) do |(unit, count), duration|
21
+ next unless seconds.positive?
22
+
23
+ seconds, remainder = seconds.divmod(count)
24
+
25
+ next if remainder.zero?
26
+
27
+ duration << pluralize(format_duration(remainder), unit)
28
+ end
29
+
30
+ formatted_duration.reverse.join(' ')
31
+ end
32
+
33
+ def format_duration(duration)
34
+ return 0 if duration.negative?
35
+
36
+ precision = duration < 1 ? SECONDS_PRECISION : DEFAULT_PRECISION
37
+
38
+ strip_trailing_zeroes(format("%<duration>0.#{precision}f", duration: duration))
39
+ end
40
+
41
+ def strip_trailing_zeroes(formatted_duration)
42
+ formatted_duration.sub(/(?:(\..*[^0])0+|\.0+)$/, '\1')
43
+ end
44
+
45
+ def pluralize(duration, unit)
46
+ if (duration.to_f - 1).abs < Float::EPSILON
47
+ "#{duration} #{unit}"
48
+ else
49
+ "#{duration} #{unit}s"
50
+ end
51
+ end
52
+
53
+ private_class_method :format_duration, :strip_trailing_zeroes, :pluralize
54
+ end
55
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSpecTracer
4
- VERSION = '0.3.0'
4
+ VERSION = '0.6.1'
5
5
  end
data/lib/rspec_tracer.rb CHANGED
@@ -17,11 +17,13 @@ require_relative 'rspec_tracer/coverage_reporter'
17
17
  require_relative 'rspec_tracer/defaults'
18
18
  require_relative 'rspec_tracer/example'
19
19
  require_relative 'rspec_tracer/html_reporter/reporter'
20
+ require_relative 'rspec_tracer/remote_cache/cache'
20
21
  require_relative 'rspec_tracer/rspec_reporter'
21
22
  require_relative 'rspec_tracer/rspec_runner'
22
23
  require_relative 'rspec_tracer/ruby_coverage'
23
24
  require_relative 'rspec_tracer/runner'
24
25
  require_relative 'rspec_tracer/source_file'
26
+ require_relative 'rspec_tracer/time_formatter'
25
27
  require_relative 'rspec_tracer/version'
26
28
 
27
29
  module RSpecTracer
@@ -183,22 +185,36 @@ module RSpecTracer
183
185
  def generate_reports
184
186
  puts 'RSpec tracer is generating reports'
185
187
 
186
- generate_tracer_reports
187
- generate_coverage_reports
188
+ process_dependency
189
+ process_coverage
188
190
  runner.generate_report
189
191
  RSpecTracer::HTMLReporter::Reporter.new.generate_report
190
192
  end
191
193
 
192
- def generate_tracer_reports
194
+ def process_dependency
195
+ starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
196
+
193
197
  runner.register_deleted_examples
194
198
  runner.register_dependency(coverage_reporter.examples_coverage)
195
199
  runner.register_untraced_dependency(@traced_files)
200
+
201
+ ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
202
+ elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
203
+
204
+ puts "RSpec tracer processed dependency (took #{elpased})" if RSpecTracer.verbose?
196
205
  end
197
206
 
198
- def generate_coverage_reports
207
+ def process_coverage
208
+ starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
209
+
199
210
  coverage_reporter.generate_final_examples_coverage
200
211
  coverage_reporter.merge_coverage(runner.generate_missed_coverage)
201
212
  runner.register_examples_coverage(coverage_reporter.examples_coverage)
213
+
214
+ ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
215
+ elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
216
+
217
+ puts "RSpec tracer processed coverage (took #{elpased})" if RSpecTracer.verbose?
202
218
  end
203
219
 
204
220
  def run_simplecov_exit_task
@@ -212,12 +228,18 @@ module RSpecTracer
212
228
  end
213
229
 
214
230
  def run_coverage_exit_task
231
+ starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
232
+
215
233
  coverage_reporter.generate_final_coverage
216
234
 
217
235
  file_name = File.join(RSpecTracer.coverage_path, 'coverage.json')
218
236
 
219
237
  write_coverage_report(file_name)
220
- print_coverage_stats(file_name)
238
+
239
+ ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
240
+ elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
241
+
242
+ print_coverage_stats(file_name, elpased)
221
243
  end
222
244
 
223
245
  def write_coverage_report(file_name)
@@ -228,15 +250,16 @@ module RSpecTracer
228
250
  }
229
251
  }
230
252
 
231
- File.write(file_name, JSON.pretty_generate(report))
253
+ File.write(file_name, JSON.generate(report))
232
254
  end
233
255
 
234
- def print_coverage_stats(file_name)
256
+ def print_coverage_stats(file_name, elpased)
235
257
  stat = coverage_reporter.coverage_stat
236
258
 
237
259
  puts <<-REPORT.strip.gsub(/\s+/, ' ')
238
260
  Coverage report generated for RSpecTracer to #{file_name}. #{stat[:covered_lines]}
239
261
  / #{stat[:total_lines]} LOC (#{stat[:covered_percent]}%) covered
262
+ (took #{elpased})
240
263
  REPORT
241
264
  end
242
265
  end
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.3.0
4
+ version: 0.6.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-08-30 00:00:00.000000000 Z
11
+ date: 2021-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docile
@@ -90,20 +90,24 @@ files:
90
90
  - lib/rspec_tracer/html_reporter/views/examples.erb
91
91
  - lib/rspec_tracer/html_reporter/views/examples_dependency.erb
92
92
  - lib/rspec_tracer/html_reporter/views/files_dependency.erb
93
+ - lib/rspec_tracer/html_reporter/views/flaky_examples.erb
93
94
  - lib/rspec_tracer/html_reporter/views/layout.erb
95
+ - lib/rspec_tracer/remote_cache/cache.rb
96
+ - lib/rspec_tracer/remote_cache/git.rb
94
97
  - lib/rspec_tracer/reporter.rb
95
98
  - lib/rspec_tracer/rspec_reporter.rb
96
99
  - lib/rspec_tracer/rspec_runner.rb
97
100
  - lib/rspec_tracer/ruby_coverage.rb
98
101
  - lib/rspec_tracer/runner.rb
99
102
  - lib/rspec_tracer/source_file.rb
103
+ - lib/rspec_tracer/time_formatter.rb
100
104
  - lib/rspec_tracer/version.rb
101
105
  homepage: https://github.com/avmnu-sng/rspec-tracer
102
106
  licenses:
103
107
  - MIT
104
108
  metadata:
105
109
  homepage_uri: https://github.com/avmnu-sng/rspec-tracer
106
- source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.3.0
110
+ source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.6.1
107
111
  changelog_uri: https://github.com/avmnu-sng/rspec-tracer/blob/main/CHANGELOG.md
108
112
  bug_tracker_uri: https://github.com/avmnu-sng/rspec-tracer/issues
109
113
  post_install_message: