rspec-tracer 0.2.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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})"
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})"
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})"
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,52 @@
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
+ remainder = format_duration(remainder)
25
+
26
+ next if remainder.zero?
27
+
28
+ duration << pluralize(remainder, unit)
29
+ end
30
+
31
+ formatted_duration.reverse.join(' ')
32
+ end
33
+
34
+ def format_duration(duration)
35
+ return 0 if duration.negative?
36
+
37
+ precision = duration < 1 ? SECONDS_PRECISION : DEFAULT_PRECISION
38
+
39
+ format("%<duration>0.#{precision}f", duration: duration)
40
+ end
41
+
42
+ def pluralize(duration, unit)
43
+ if duration == 1
44
+ "#{duration} #{unit}"
45
+ else
46
+ "#{duration} #{unit}s"
47
+ end
48
+ end
49
+
50
+ private_class_method :format_duration, :pluralize
51
+ end
52
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSpecTracer
4
- VERSION = '0.2.0'
4
+ VERSION = '0.6.0'
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})"
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})"
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,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-tracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.6.0
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-28 00:00:00.000000000 Z
11
+ date: 2021-09-05 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
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 1.1.0
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '1.1'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: 1.1.0
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.1'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: rspec-core
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -84,20 +90,24 @@ files:
84
90
  - lib/rspec_tracer/html_reporter/views/examples.erb
85
91
  - lib/rspec_tracer/html_reporter/views/examples_dependency.erb
86
92
  - lib/rspec_tracer/html_reporter/views/files_dependency.erb
93
+ - lib/rspec_tracer/html_reporter/views/flaky_examples.erb
87
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
88
97
  - lib/rspec_tracer/reporter.rb
89
98
  - lib/rspec_tracer/rspec_reporter.rb
90
99
  - lib/rspec_tracer/rspec_runner.rb
91
100
  - lib/rspec_tracer/ruby_coverage.rb
92
101
  - lib/rspec_tracer/runner.rb
93
102
  - lib/rspec_tracer/source_file.rb
103
+ - lib/rspec_tracer/time_formatter.rb
94
104
  - lib/rspec_tracer/version.rb
95
105
  homepage: https://github.com/avmnu-sng/rspec-tracer
96
106
  licenses:
97
107
  - MIT
98
108
  metadata:
99
109
  homepage_uri: https://github.com/avmnu-sng/rspec-tracer
100
- source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.2.0
110
+ source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.6.0
101
111
  changelog_uri: https://github.com/avmnu-sng/rspec-tracer/blob/main/CHANGELOG.md
102
112
  bug_tracker_uri: https://github.com/avmnu-sng/rspec-tracer/issues
103
113
  post_install_message: