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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -2
- data/README.md +146 -12
- data/lib/rspec_tracer/cache.rb +27 -3
- data/lib/rspec_tracer/configuration.rb +6 -0
- data/lib/rspec_tracer/defaults.rb +7 -1
- data/lib/rspec_tracer/html_reporter/reporter.rb +28 -6
- data/lib/rspec_tracer/html_reporter/views/flaky_examples.erb +38 -0
- data/lib/rspec_tracer/html_reporter/views/layout.erb +3 -0
- data/lib/rspec_tracer/remote_cache/cache.rb +187 -0
- data/lib/rspec_tracer/remote_cache/git.rb +113 -0
- data/lib/rspec_tracer/reporter.rb +40 -11
- data/lib/rspec_tracer/rspec_runner.rb +6 -1
- data/lib/rspec_tracer/runner.rb +84 -34
- data/lib/rspec_tracer/time_formatter.rb +55 -0
- data/lib/rspec_tracer/version.rb +1 -1
- data/lib/rspec_tracer.rb +30 -7
- metadata +7 -3
@@ -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
|
data/lib/rspec_tracer/runner.rb
CHANGED
@@ -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
|
-
|
132
|
-
|
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
|
-
|
150
|
-
|
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
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
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
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
-
|
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
|
177
|
-
|
178
|
-
@filtered_examples[example_id] = EXAMPLE_RUN_REASON[:
|
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
|
-
|
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
|
-
|
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
|
-
|
186
|
-
|
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
|
-
|
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
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
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
|
-
|
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 =
|
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
|
237
|
-
|
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
|
data/lib/rspec_tracer/version.rb
CHANGED
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
|
-
|
187
|
-
|
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
|
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
|
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
|
-
|
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.
|
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.
|
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-
|
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.
|
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:
|