rspec-tracer 0.1.0
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 +7 -0
- data/CHANGELOG.md +10 -0
- data/LICENSE +21 -0
- data/README.md +248 -0
- data/lib/rspec_tracer/cache.rb +109 -0
- data/lib/rspec_tracer/configuration.rb +134 -0
- data/lib/rspec_tracer/coverage_reporter.rb +179 -0
- data/lib/rspec_tracer/defaults.rb +10 -0
- data/lib/rspec_tracer/example.rb +58 -0
- data/lib/rspec_tracer/filter.rb +68 -0
- data/lib/rspec_tracer/html_reporter/assets/javascripts/application.js +56 -0
- data/lib/rspec_tracer/html_reporter/assets/javascripts/libraries/jquery.js +10881 -0
- data/lib/rspec_tracer/html_reporter/assets/javascripts/plugins/datatables.js +15381 -0
- data/lib/rspec_tracer/html_reporter/assets/stylesheets/application.css +196 -0
- data/lib/rspec_tracer/html_reporter/assets/stylesheets/plugins/datatables.css +459 -0
- data/lib/rspec_tracer/html_reporter/assets/stylesheets/plugins/jquery-ui.css +436 -0
- data/lib/rspec_tracer/html_reporter/assets/stylesheets/print.css +92 -0
- data/lib/rspec_tracer/html_reporter/assets/stylesheets/reset.css +265 -0
- data/lib/rspec_tracer/html_reporter/public/application.css +5 -0
- data/lib/rspec_tracer/html_reporter/public/application.js +6 -0
- data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_asc.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_asc_disabled.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_both.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_desc.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_desc_disabled.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/favicon.png +0 -0
- data/lib/rspec_tracer/html_reporter/public/loading.gif +0 -0
- data/lib/rspec_tracer/html_reporter/reporter.rb +180 -0
- data/lib/rspec_tracer/html_reporter/views/examples.erb +53 -0
- data/lib/rspec_tracer/html_reporter/views/examples_dependency.erb +36 -0
- data/lib/rspec_tracer/html_reporter/views/files_dependency.erb +36 -0
- data/lib/rspec_tracer/html_reporter/views/layout.erb +32 -0
- data/lib/rspec_tracer/reporter.rb +255 -0
- data/lib/rspec_tracer/rspec_reporter.rb +43 -0
- data/lib/rspec_tracer/rspec_runner.rb +25 -0
- data/lib/rspec_tracer/ruby_coverage.rb +9 -0
- data/lib/rspec_tracer/runner.rb +299 -0
- data/lib/rspec_tracer/source_file.rb +31 -0
- data/lib/rspec_tracer/version.rb +5 -0
- data/lib/rspec_tracer.rb +243 -0
- metadata +122 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
module RSpecRunner
|
5
|
+
def run_specs(_example_groups)
|
6
|
+
actual_count = RSpec.world.example_count
|
7
|
+
filtered_examples, example_groups = RSpecTracer.filter_examples
|
8
|
+
|
9
|
+
RSpec.world.instance_variable_set(:@filtered_examples, filtered_examples)
|
10
|
+
RSpec.world.instance_variable_set(:@example_groups, example_groups)
|
11
|
+
|
12
|
+
current_count = RSpec.world.example_count
|
13
|
+
|
14
|
+
puts
|
15
|
+
puts <<-EXAMPLES.strip.gsub(/\s+/, ' ')
|
16
|
+
RSpec tracer is running #{current_count} examples (actual: #{actual_count},
|
17
|
+
skipped: #{actual_count - current_count})
|
18
|
+
EXAMPLES
|
19
|
+
|
20
|
+
RSpecTracer.running = true
|
21
|
+
|
22
|
+
super(example_groups)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,299 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'cache'
|
4
|
+
require_relative 'reporter'
|
5
|
+
|
6
|
+
module RSpecTracer
|
7
|
+
class Runner
|
8
|
+
EXAMPLE_RUN_REASON = {
|
9
|
+
explicit_run: 'Explicit run',
|
10
|
+
no_cache: 'No cache',
|
11
|
+
failed_example: 'Failed previously',
|
12
|
+
pending_example: 'Pending previously',
|
13
|
+
files_changed: 'Files changed'
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
attr_reader :cache, :reporter
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@cache = RSpecTracer::Cache.new
|
20
|
+
@reporter = RSpecTracer::Reporter.new
|
21
|
+
@filtered_examples = {}
|
22
|
+
|
23
|
+
@cache.load_cache_for_run
|
24
|
+
filter_examples_to_run
|
25
|
+
end
|
26
|
+
|
27
|
+
def run_example?(example_id)
|
28
|
+
return true if explicit_run?
|
29
|
+
|
30
|
+
!@cache.all_examples.key?(example_id) || @filtered_examples.key?(example_id)
|
31
|
+
end
|
32
|
+
|
33
|
+
def run_example_reason(example_id)
|
34
|
+
return EXAMPLE_RUN_REASON[:explicit_run] if explicit_run?
|
35
|
+
|
36
|
+
@filtered_examples[example_id] || EXAMPLE_RUN_REASON[:no_cache]
|
37
|
+
end
|
38
|
+
|
39
|
+
def register_example(example)
|
40
|
+
@reporter.register_example(example)
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_example_skipped(example_id)
|
44
|
+
@reporter.on_example_skipped(example_id)
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_example_passed(example_id, execution_result)
|
48
|
+
@reporter.on_example_passed(example_id, execution_result)
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_example_failed(example_id, execution_result)
|
52
|
+
@reporter.on_example_failed(example_id, execution_result)
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_example_pending(example_id, execution_result)
|
56
|
+
@reporter.on_example_pending(example_id, execution_result)
|
57
|
+
end
|
58
|
+
|
59
|
+
def register_deleted_examples
|
60
|
+
@reporter.register_deleted_examples(@cache.all_examples)
|
61
|
+
end
|
62
|
+
|
63
|
+
# rubocop:disable Metrics/AbcSize
|
64
|
+
def generate_missed_coverage
|
65
|
+
missed_coverage = Hash.new do |files_coverage, file_path|
|
66
|
+
files_coverage[file_path] = Hash.new do |strength, line_number|
|
67
|
+
strength[line_number] = 0
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
@cache.cached_examples_coverage.each_pair do |example_id, example_coverage|
|
72
|
+
example_coverage.each_pair do |file_path, line_coverage|
|
73
|
+
next unless @reporter.example_skipped?(example_id)
|
74
|
+
|
75
|
+
file_name = RSpecTracer::SourceFile.file_name(file_path)
|
76
|
+
|
77
|
+
next if @reporter.file_deleted?(file_name)
|
78
|
+
|
79
|
+
line_coverage.each_pair do |line_number, strength|
|
80
|
+
missed_coverage[file_path][line_number] += strength
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
missed_coverage
|
86
|
+
end
|
87
|
+
# rubocop:enable Metrics/AbcSize
|
88
|
+
|
89
|
+
def register_dependency(examples_coverage)
|
90
|
+
examples_coverage.each_pair do |example_id, example_coverage|
|
91
|
+
register_example_files_dependency(example_id)
|
92
|
+
|
93
|
+
example_coverage.each_key do |file_path|
|
94
|
+
source_file = RSpecTracer::SourceFile.from_path(file_path)
|
95
|
+
|
96
|
+
next if RSpecTracer.filters.any? { |filter| filter.match?(source_file) }
|
97
|
+
|
98
|
+
@reporter.register_source_file(source_file)
|
99
|
+
@reporter.register_dependency(example_id, source_file[:file_name])
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
@reporter.pending_examples.each do |example_id|
|
104
|
+
register_example_files_dependency(example_id)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def register_untraced_dependency(trace_point_files)
|
109
|
+
untraced_files = generate_untraced_files(trace_point_files)
|
110
|
+
|
111
|
+
untraced_files.each do |file_path|
|
112
|
+
source_file = RSpecTracer::SourceFile.from_path(file_path)
|
113
|
+
|
114
|
+
next if RSpecTracer.filters.any? { |filter| filter.match?(source_file) }
|
115
|
+
|
116
|
+
@reporter.register_source_file(source_file)
|
117
|
+
|
118
|
+
@reporter.all_examples.each_key do |example_id|
|
119
|
+
@reporter.register_dependency(example_id, source_file[:file_name])
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def register_examples_coverage(examples_coverage)
|
125
|
+
@reporter.register_examples_coverage(examples_coverage)
|
126
|
+
end
|
127
|
+
|
128
|
+
def generate_report
|
129
|
+
@reporter.generate_last_run_report
|
130
|
+
|
131
|
+
generate_failed_examples_report
|
132
|
+
generate_pending_examples_report
|
133
|
+
|
134
|
+
%i[all_files all_examples dependency examples_coverage].each do |report_type|
|
135
|
+
send("generate_#{report_type}_report")
|
136
|
+
end
|
137
|
+
|
138
|
+
@reporter.generate_reverse_dependency_report
|
139
|
+
@reporter.write_reports
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def explicit_run?
|
145
|
+
ENV.fetch('RSPEC_TRACER_NO_SKIP', 'false') == 'true'
|
146
|
+
end
|
147
|
+
|
148
|
+
def filter_examples_to_run
|
149
|
+
add_previously_failed_examples
|
150
|
+
add_previously_pending_examples
|
151
|
+
filter_by_files_changed
|
152
|
+
end
|
153
|
+
|
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
|
158
|
+
end
|
159
|
+
|
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
|
164
|
+
end
|
165
|
+
|
166
|
+
def filter_by_files_changed
|
167
|
+
@cache.dependency.each_pair do |example_id, files|
|
168
|
+
next if @filtered_examples.key?(example_id)
|
169
|
+
|
170
|
+
files.each do |file_name|
|
171
|
+
break if filtered_by_file_changed?(example_id, file_name)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
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]
|
179
|
+
|
180
|
+
return true
|
181
|
+
end
|
182
|
+
|
183
|
+
source_file = registered_source_file(file_name)
|
184
|
+
|
185
|
+
return false if source_file &&
|
186
|
+
@cache.all_files[file_name][:digest] == source_file[:digest]
|
187
|
+
|
188
|
+
@filtered_examples[example_id] = EXAMPLE_RUN_REASON[:files_changed]
|
189
|
+
|
190
|
+
if source_file.nil?
|
191
|
+
@reporter.on_file_deleted(file_name)
|
192
|
+
else
|
193
|
+
@reporter.on_file_modified(file_name)
|
194
|
+
end
|
195
|
+
|
196
|
+
true
|
197
|
+
end
|
198
|
+
|
199
|
+
def generate_untraced_files(trace_point_files)
|
200
|
+
all_files = @reporter.all_files
|
201
|
+
.each_value
|
202
|
+
.with_object([]) { |source_file, files| files << source_file[:file_path] }
|
203
|
+
|
204
|
+
(trace_point_files | fetch_rspec_required_files) - all_files
|
205
|
+
end
|
206
|
+
|
207
|
+
def fetch_rspec_required_files
|
208
|
+
rspec_root = RSpec::Core::RubyProject.root
|
209
|
+
rspec_path = RSpec.configuration.default_path
|
210
|
+
|
211
|
+
RSpec.configuration.requires.map do |file_name|
|
212
|
+
file_name = "#{file_name}.rb" if File.extname(file_name).empty?
|
213
|
+
|
214
|
+
File.join(rspec_root, rspec_path, file_name)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def register_example_files_dependency(example_id)
|
219
|
+
example = @reporter.all_examples[example_id]
|
220
|
+
|
221
|
+
register_example_file_dependency(example_id, example[:file_name])
|
222
|
+
|
223
|
+
return if example[:rerun_file_name] == example[:file_name]
|
224
|
+
|
225
|
+
register_example_file_dependency(example_id, example[:rerun_file_name])
|
226
|
+
end
|
227
|
+
|
228
|
+
def register_example_file_dependency(example_id, file_name)
|
229
|
+
source_file = registered_source_file(file_name)
|
230
|
+
|
231
|
+
@reporter.register_source_file(source_file)
|
232
|
+
@reporter.register_dependency(example_id, file_name)
|
233
|
+
end
|
234
|
+
|
235
|
+
def registered_source_file(file_name)
|
236
|
+
@reporter.all_files[file_name] || RSpecTracer::SourceFile.from_name(file_name)
|
237
|
+
end
|
238
|
+
|
239
|
+
def generate_all_files_report
|
240
|
+
@cache.all_files.each_pair do |file_name, data|
|
241
|
+
next if @reporter.all_files.key?(file_name) ||
|
242
|
+
@reporter.file_deleted?(file_name)
|
243
|
+
|
244
|
+
@reporter.all_files[file_name] = data
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def generate_all_examples_report
|
249
|
+
@cache.all_examples.each_pair do |example_id, data|
|
250
|
+
next if @reporter.all_examples.key?(example_id) ||
|
251
|
+
@reporter.example_deleted?(example_id)
|
252
|
+
|
253
|
+
@reporter.all_examples[example_id] = data
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def generate_failed_examples_report
|
258
|
+
@cache.failed_examples.each do |example_id|
|
259
|
+
next if @reporter.example_deleted?(example_id) ||
|
260
|
+
@reporter.all_examples.key?(example_id)
|
261
|
+
|
262
|
+
@reporter.register_failed_example(example_id)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def generate_pending_examples_report
|
267
|
+
@cache.pending_examples.each do |example_id|
|
268
|
+
next if @reporter.example_deleted?(example_id) ||
|
269
|
+
@reporter.all_examples.key?(example_id)
|
270
|
+
|
271
|
+
@reporter.register_pending_example(example_id)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def generate_dependency_report
|
276
|
+
@cache.dependency.each_pair do |example_id, data|
|
277
|
+
next if @reporter.dependency.key?(example_id) ||
|
278
|
+
@reporter.example_deleted?(example_id)
|
279
|
+
|
280
|
+
@reporter.dependency[example_id] = data.reject do |file_name|
|
281
|
+
@reporter.file_deleted?(file_name)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
@reporter.dependency.transform_values!(&:to_a)
|
286
|
+
end
|
287
|
+
|
288
|
+
def generate_examples_coverage_report
|
289
|
+
@cache.cached_examples_coverage.each_pair do |example_id, data|
|
290
|
+
next if @reporter.examples_coverage.key?(example_id) ||
|
291
|
+
@reporter.example_deleted?(example_id)
|
292
|
+
|
293
|
+
@reporter.examples_coverage[example_id] = data.reject do |file_name|
|
294
|
+
@reporter.file_deleted?(file_name)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
module SourceFile
|
5
|
+
PROJECT_ROOT_REGEX = Regexp.new("^#{Regexp.escape(RSpecTracer.root)}").freeze
|
6
|
+
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def from_path(file_path)
|
10
|
+
return unless File.file?(file_path)
|
11
|
+
|
12
|
+
{
|
13
|
+
file_path: file_path,
|
14
|
+
file_name: file_name(file_path),
|
15
|
+
digest: Digest::MD5.hexdigest(File.read(file_path))
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def from_name(file_name)
|
20
|
+
from_path(file_path(file_name))
|
21
|
+
end
|
22
|
+
|
23
|
+
def file_name(file_path)
|
24
|
+
file_path.sub(PROJECT_ROOT_REGEX, '')
|
25
|
+
end
|
26
|
+
|
27
|
+
def file_path(file_name)
|
28
|
+
File.expand_path(file_name.sub(%r{^/}, ''), RSpecTracer.root)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/rspec_tracer.rb
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
|
5
|
+
require 'digest/md5'
|
6
|
+
require 'docile'
|
7
|
+
require 'fileutils'
|
8
|
+
require 'forwardable'
|
9
|
+
require 'json'
|
10
|
+
require 'pry'
|
11
|
+
require 'set'
|
12
|
+
|
13
|
+
require_relative 'rspec_tracer/configuration'
|
14
|
+
RSpecTracer.extend RSpecTracer::Configuration
|
15
|
+
|
16
|
+
require_relative 'rspec_tracer/coverage_reporter'
|
17
|
+
require_relative 'rspec_tracer/defaults'
|
18
|
+
require_relative 'rspec_tracer/example'
|
19
|
+
require_relative 'rspec_tracer/html_reporter/reporter'
|
20
|
+
require_relative 'rspec_tracer/rspec_reporter'
|
21
|
+
require_relative 'rspec_tracer/rspec_runner'
|
22
|
+
require_relative 'rspec_tracer/ruby_coverage'
|
23
|
+
require_relative 'rspec_tracer/runner'
|
24
|
+
require_relative 'rspec_tracer/source_file'
|
25
|
+
require_relative 'rspec_tracer/version'
|
26
|
+
|
27
|
+
module RSpecTracer
|
28
|
+
class << self
|
29
|
+
attr_accessor :running, :pid
|
30
|
+
|
31
|
+
def start(&block)
|
32
|
+
RSpecTracer.running = false
|
33
|
+
RSpecTracer.pid = Process.pid
|
34
|
+
|
35
|
+
puts 'Started RSpec tracer'
|
36
|
+
|
37
|
+
configure(&block) if block
|
38
|
+
|
39
|
+
initial_setup
|
40
|
+
end
|
41
|
+
|
42
|
+
# rubocop:disable Metrics/AbcSize
|
43
|
+
def filter_examples
|
44
|
+
groups = Set.new
|
45
|
+
to_run = Hash.new { |hash, group| hash[group] = [] }
|
46
|
+
|
47
|
+
RSpec.world.filtered_examples.each_pair do |example_group, examples|
|
48
|
+
examples.each do |example|
|
49
|
+
tracer_example = RSpecTracer::Example.from(example)
|
50
|
+
example_id = tracer_example[:example_id]
|
51
|
+
example.metadata[:rspec_tracer_example_id] = example_id
|
52
|
+
|
53
|
+
if runner.run_example?(example_id)
|
54
|
+
run_reason = runner.run_example_reason(example_id)
|
55
|
+
tracer_example[:run_reason] = run_reason
|
56
|
+
example.metadata[:description] = "#{example.description} (#{run_reason})"
|
57
|
+
|
58
|
+
to_run[example_group] << example
|
59
|
+
groups << example.example_group.parent_groups.last
|
60
|
+
|
61
|
+
runner.register_example(tracer_example)
|
62
|
+
else
|
63
|
+
runner.on_example_skipped(example_id)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
[to_run, groups.to_a]
|
69
|
+
end
|
70
|
+
# rubocop:enable Metrics/AbcSize
|
71
|
+
|
72
|
+
def at_exit_behavior
|
73
|
+
return unless RSpecTracer.pid == Process.pid && RSpecTracer.running
|
74
|
+
|
75
|
+
run_exit_tasks
|
76
|
+
end
|
77
|
+
|
78
|
+
def start_example_trace
|
79
|
+
trace_point.enable if trace_example?
|
80
|
+
end
|
81
|
+
|
82
|
+
def stop_example_trace(success)
|
83
|
+
return unless trace_example?
|
84
|
+
|
85
|
+
trace_point.disable
|
86
|
+
|
87
|
+
unless success
|
88
|
+
@traced_files = Set.new
|
89
|
+
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
93
|
+
@trace_example = false
|
94
|
+
end
|
95
|
+
|
96
|
+
def runner
|
97
|
+
return @runner if defined?(@runner)
|
98
|
+
end
|
99
|
+
|
100
|
+
def coverage_reporter
|
101
|
+
return @coverage_reporter if defined?(@coverage_reporter)
|
102
|
+
end
|
103
|
+
|
104
|
+
def trace_point
|
105
|
+
return @trace_point if defined?(@trace_point)
|
106
|
+
end
|
107
|
+
|
108
|
+
def traced_files
|
109
|
+
return @traced_files if defined?(@traced_files)
|
110
|
+
end
|
111
|
+
|
112
|
+
def trace_example?
|
113
|
+
defined?(@trace_example) ? @trace_example : false
|
114
|
+
end
|
115
|
+
|
116
|
+
def simplecov?
|
117
|
+
return @simplecov if defined?(@simplecov)
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def initial_setup
|
123
|
+
unless setup_rspec
|
124
|
+
puts 'Could not find a running RSpec process'
|
125
|
+
|
126
|
+
return
|
127
|
+
end
|
128
|
+
|
129
|
+
setup_coverage
|
130
|
+
setup_trace_point
|
131
|
+
|
132
|
+
@runner = RSpecTracer::Runner.new
|
133
|
+
@coverage_reporter = RSpecTracer::CoverageReporter.new
|
134
|
+
end
|
135
|
+
|
136
|
+
def setup_rspec
|
137
|
+
runners = ObjectSpace.each_object(::RSpec::Core::Runner) do |runner|
|
138
|
+
runner_clazz = runner.singleton_class
|
139
|
+
clazz = RSpecTracer::RSpecRunner
|
140
|
+
|
141
|
+
runner_clazz.prepend(clazz) unless runner_clazz.ancestors.include?(clazz)
|
142
|
+
|
143
|
+
reporter_clazz = runner.configuration.reporter.singleton_class
|
144
|
+
clazz = RSpecTracer::RSpecReporter
|
145
|
+
|
146
|
+
reporter_clazz.prepend(clazz) unless reporter_clazz.ancestors.include?(clazz)
|
147
|
+
end
|
148
|
+
|
149
|
+
runners.positive?
|
150
|
+
end
|
151
|
+
|
152
|
+
def setup_coverage
|
153
|
+
@simplecov = defined?(SimpleCov) && SimpleCov.running
|
154
|
+
|
155
|
+
if simplecov?
|
156
|
+
# rubocop:disable Lint/EmptyBlock
|
157
|
+
SimpleCov.at_exit {}
|
158
|
+
# rubocop:enable Lint/EmptyBlock
|
159
|
+
else
|
160
|
+
require 'coverage'
|
161
|
+
|
162
|
+
::Coverage.start
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def setup_trace_point
|
167
|
+
@trace_example = true
|
168
|
+
@traced_files = Set.new
|
169
|
+
|
170
|
+
@trace_point = TracePoint.new(:call) do |tp|
|
171
|
+
RSpecTracer.traced_files << tp.path if tp.path.start_with?(RSpecTracer.root)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def run_exit_tasks
|
176
|
+
generate_reports
|
177
|
+
|
178
|
+
simplecov? ? run_simplecov_exit_task : run_coverage_exit_task
|
179
|
+
ensure
|
180
|
+
RSpecTracer.running = false
|
181
|
+
end
|
182
|
+
|
183
|
+
def generate_reports
|
184
|
+
puts 'RSpec tracer is generating reports'
|
185
|
+
|
186
|
+
generate_tracer_reports
|
187
|
+
generate_coverage_reports
|
188
|
+
runner.generate_report
|
189
|
+
RSpecTracer::HTMLReporter::Reporter.new.generate_report
|
190
|
+
end
|
191
|
+
|
192
|
+
def generate_tracer_reports
|
193
|
+
runner.register_deleted_examples
|
194
|
+
runner.register_dependency(coverage_reporter.examples_coverage)
|
195
|
+
runner.register_untraced_dependency(@traced_files)
|
196
|
+
end
|
197
|
+
|
198
|
+
def generate_coverage_reports
|
199
|
+
coverage_reporter.generate_final_examples_coverage
|
200
|
+
coverage_reporter.merge_coverage(runner.generate_missed_coverage)
|
201
|
+
runner.register_examples_coverage(coverage_reporter.examples_coverage)
|
202
|
+
end
|
203
|
+
|
204
|
+
def run_simplecov_exit_task
|
205
|
+
coverage_clazz = ::Coverage.singleton_class
|
206
|
+
clazz = RSpecTracer::RubyCoverage
|
207
|
+
coverage_clazz.prepend(clazz) unless coverage_clazz.ancestors.include?(clazz)
|
208
|
+
|
209
|
+
puts 'SimpleCov will now generate coverage report (<3 RSpec tracer)'
|
210
|
+
|
211
|
+
SimpleCov.result.format!
|
212
|
+
end
|
213
|
+
|
214
|
+
def run_coverage_exit_task
|
215
|
+
coverage_reporter.generate_final_coverage
|
216
|
+
|
217
|
+
file_name = File.join(RSpecTracer.coverage_path, 'coverage.json')
|
218
|
+
|
219
|
+
write_coverage_report(file_name)
|
220
|
+
print_coverage_stats(file_name)
|
221
|
+
end
|
222
|
+
|
223
|
+
def write_coverage_report(file_name)
|
224
|
+
report = {
|
225
|
+
RSpecTracer: {
|
226
|
+
coverage: coverage_reporter.coverage,
|
227
|
+
timestamp: Time.now.utc.to_i
|
228
|
+
}
|
229
|
+
}
|
230
|
+
|
231
|
+
File.write(file_name, JSON.pretty_generate(report))
|
232
|
+
end
|
233
|
+
|
234
|
+
def print_coverage_stats(file_name)
|
235
|
+
stat = coverage_reporter.coverage_stat
|
236
|
+
|
237
|
+
puts <<-REPORT.strip.gsub(/\s+/, ' ')
|
238
|
+
Coverage report generated for RSpecTracer to #{file_name}. #{stat[:covered_lines]}
|
239
|
+
/ #{stat[:total_lines]} LOC (#{stat[:covered_percent]}%) covered
|
240
|
+
REPORT
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|