kettle-soup-cover 1.0.9 → 1.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +123 -4
- data/CITATION.cff +20 -0
- data/CONTRIBUTING.md +15 -11
- data/LICENSE.txt +1 -1
- data/README.md +587 -187
- data/REEK +0 -0
- data/SECURITY.md +0 -0
- data/exe/kettle-soup-cover +362 -0
- data/lib/kettle/soup/cover/config.rb +4 -2
- data/lib/kettle/soup/cover/constants.rb +24 -8
- data/lib/kettle/soup/cover/version.rb +1 -1
- data/lib/kettle-soup-cover.rb +2 -1
- data/sig/kettle/soup/cover.rbs +5 -5
- data.tar.gz.sig +0 -0
- metadata +72 -120
- metadata.gz.sig +0 -0
data/REEK
ADDED
|
File without changes
|
data/SECURITY.md
CHANGED
|
File without changes
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Combined coverage report script
|
|
5
|
+
# Usage: kettle-soup-cover [options] [coverage.json path]
|
|
6
|
+
# -d, --detail Show detailed uncovered line/branch locations
|
|
7
|
+
# -f, --file FILE Show details for specific file (partial path/glob match)
|
|
8
|
+
# -p, --path PATH Path to coverage.json to read (default: $K_SOUP_COV_DIR/coverage.json)
|
|
9
|
+
# -n, --num NUM Max uncovered line/branch details to show per file (default: 20)
|
|
10
|
+
# -l, --lines Show only line coverage
|
|
11
|
+
# -b, --branches Show only branch coverage
|
|
12
|
+
# --no-lines Don't include line coverage
|
|
13
|
+
# --no-branches Don't include branch coverage
|
|
14
|
+
# -h, --help Show help message
|
|
15
|
+
|
|
16
|
+
require "json"
|
|
17
|
+
require "optparse"
|
|
18
|
+
require "kettle/soup/cover"
|
|
19
|
+
|
|
20
|
+
file_filter = nil
|
|
21
|
+
max_details = 20
|
|
22
|
+
show_detail = false
|
|
23
|
+
coverage_path = nil
|
|
24
|
+
explicit_coverage_path = false
|
|
25
|
+
|
|
26
|
+
opts = OptionParser.new do |opt|
|
|
27
|
+
opt.banner = "Usage: kettle-soup-cover [options] [coverage.json path]"
|
|
28
|
+
opt.on("-d", "--detail", "Show detailed uncovered line/branch locations") { show_detail = true }
|
|
29
|
+
opt.on("-fFILE", "--file=FILE", String, "Show details for specific file (partial path/glob match)") { |v| file_filter = v }
|
|
30
|
+
opt.on("-pPATH", "--path=PATH", String, "Path to coverage.json to read (default: $K_SOUP_COV_DIR/coverage.json)") do |v|
|
|
31
|
+
coverage_path = v
|
|
32
|
+
explicit_coverage_path = true
|
|
33
|
+
end
|
|
34
|
+
opt.on("-nNUM", "--num=NUM", Integer, "Max uncovered line/branch details to show per file (default: 20)") do |v|
|
|
35
|
+
max_details = v
|
|
36
|
+
end
|
|
37
|
+
opt.on_tail("-h", "--help", "Show this message") do
|
|
38
|
+
puts opt
|
|
39
|
+
exit
|
|
40
|
+
end
|
|
41
|
+
opt.on("-l", "--lines", "Show only line coverage") { @show_lines = true }
|
|
42
|
+
opt.on("-b", "--branches", "Show only branch coverage") { @show_branches = true }
|
|
43
|
+
opt.on("--no-lines", "Don't include line coverage") { @show_lines = false }
|
|
44
|
+
opt.on("--no-branches", "Don't include branch coverage") { @show_branches = false }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
opts.parse!(ARGV)
|
|
48
|
+
# If a positional arg is present (leftover after parsing) treat it as coverage json path
|
|
49
|
+
coverage_dir = Kettle::Soup::Cover::COVERAGE_DIR
|
|
50
|
+
if ARGV.any?
|
|
51
|
+
coverage_path ||= ARGV.shift
|
|
52
|
+
explicit_coverage_path = true
|
|
53
|
+
end
|
|
54
|
+
coverage_path ||= "#{coverage_dir}/coverage.json"
|
|
55
|
+
|
|
56
|
+
# Helper to match a file path against the provided file filter
|
|
57
|
+
def path_matches_filter?(path, short_path, filter)
|
|
58
|
+
return true if filter.nil? || filter == ""
|
|
59
|
+
|
|
60
|
+
# If filter looks like a glob (contains wildcard chars), use fnmatch.
|
|
61
|
+
if /[*?\[\]]/.match?(filter)
|
|
62
|
+
File.fnmatch?(filter, short_path, File::FNM_EXTGLOB) || File.fnmatch?(filter, path, File::FNM_EXTGLOB)
|
|
63
|
+
else
|
|
64
|
+
short_path.include?(filter) || path.include?(filter)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Fail early if JSON formatter is not active; coverage.json may be stale or missing
|
|
69
|
+
unless explicit_coverage_path || Kettle::Soup::Cover::FORMATTERS.any? { |f| f[:type] == :json }
|
|
70
|
+
abort <<~MSG
|
|
71
|
+
Kettle::Soup::Cover is not configured to generate a JSON coverage report.
|
|
72
|
+
The 'kettle-soup-cover' script requires the json formatter (K_SOUP_COV_FORMATTERS includes "json")
|
|
73
|
+
so that coverage/coverage.json is available and consistent with the HTML/LCOV/XML output.
|
|
74
|
+
|
|
75
|
+
Configure K_SOUP_COV_FORMATTERS to include json:
|
|
76
|
+
export K_SOUP_COV_FORMATTERS="json,html"
|
|
77
|
+
|
|
78
|
+
Or explicitly pass a path to a valid coverage.json generated with json formatter using the -p/--path option or as a positional arg:
|
|
79
|
+
kettle-soup-cover -p ./coverage/coverage.json
|
|
80
|
+
kettle-soup-cover ./coverage/coverage.json
|
|
81
|
+
|
|
82
|
+
Note: `-f/--file` is a file-filter (partial path/glob match) and is not a path-to-json option.
|
|
83
|
+
MSG
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
if Kettle::Soup::Cover::VERBOSE || Kettle::Soup::Cover::DEBUG
|
|
87
|
+
puts "Using Kettle::Soup::Cover::COVERAGE_DIR=#{coverage_dir}"
|
|
88
|
+
puts "Using coverage_path=#{coverage_path}"
|
|
89
|
+
puts "K_SOUP_COV_FORMATTERS=#{Kettle::Soup::Cover::FORMATTERS.map { |f| f[:type].to_s }.join(",")}"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
unless File.exist?(coverage_path)
|
|
93
|
+
puts "Coverage file not found: #{coverage_path}"
|
|
94
|
+
puts "Run bin/rake coverage first to generate coverage data."
|
|
95
|
+
exit 1
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# If neither -l/--no-lines nor -b/--no-branches provided, show both
|
|
99
|
+
# Otherwise, compute defaults so explicit flags control output
|
|
100
|
+
if @show_lines.nil? && @show_branches.nil?
|
|
101
|
+
@show_lines = true
|
|
102
|
+
@show_branches = true
|
|
103
|
+
elsif @show_lines.nil?
|
|
104
|
+
# show_lines unspecified: derive from show_branches
|
|
105
|
+
@show_lines = !@show_branches
|
|
106
|
+
elsif @show_branches.nil?
|
|
107
|
+
# show_branches unspecified: derive from show_lines
|
|
108
|
+
@show_branches = !@show_lines
|
|
109
|
+
end
|
|
110
|
+
data = JSON.parse(File.read(coverage_path))
|
|
111
|
+
files = data["coverage"]
|
|
112
|
+
|
|
113
|
+
def collect_line_info(files, file_filter: nil)
|
|
114
|
+
results = []
|
|
115
|
+
files.each do |path, info|
|
|
116
|
+
next unless info.is_a?(Hash)
|
|
117
|
+
next unless info["lines"]
|
|
118
|
+
|
|
119
|
+
lines = info["lines"]
|
|
120
|
+
total = 0
|
|
121
|
+
covered = 0
|
|
122
|
+
uncovered_lines = []
|
|
123
|
+
|
|
124
|
+
lines.each_with_index do |hit_count, index|
|
|
125
|
+
next if hit_count.nil? || hit_count == "ignored"
|
|
126
|
+
|
|
127
|
+
line_num = index + 1
|
|
128
|
+
total += 1
|
|
129
|
+
if hit_count.to_i > 0
|
|
130
|
+
covered += 1
|
|
131
|
+
else
|
|
132
|
+
uncovered_lines << line_num
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
uncovered = total - covered
|
|
137
|
+
next if uncovered == 0
|
|
138
|
+
|
|
139
|
+
pct = (total > 0) ? (covered.to_f / total * 100).round(1) : 100.0
|
|
140
|
+
short_path = path.split("/lib/").last || path.split("/").last(2).join("/")
|
|
141
|
+
|
|
142
|
+
results << {
|
|
143
|
+
uncovered: uncovered,
|
|
144
|
+
total: total,
|
|
145
|
+
covered: covered,
|
|
146
|
+
pct: pct,
|
|
147
|
+
path: short_path,
|
|
148
|
+
full_path: path,
|
|
149
|
+
uncovered_lines: uncovered_lines,
|
|
150
|
+
}
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
results.select! { |r| path_matches_filter?(r[:full_path], r[:path], file_filter) } if file_filter
|
|
154
|
+
|
|
155
|
+
results.sort_by! { |r| -r[:uncovered] }
|
|
156
|
+
results
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def collect_branch_info(files, file_filter: nil)
|
|
160
|
+
results = []
|
|
161
|
+
files.each do |path, info|
|
|
162
|
+
next unless info.is_a?(Hash)
|
|
163
|
+
next unless info["branches"]
|
|
164
|
+
|
|
165
|
+
branches = info["branches"]
|
|
166
|
+
total = 0
|
|
167
|
+
covered = 0
|
|
168
|
+
uncovered_details = []
|
|
169
|
+
|
|
170
|
+
if branches.is_a?(Array)
|
|
171
|
+
branches.each do |branch|
|
|
172
|
+
next unless branch.is_a?(Hash)
|
|
173
|
+
next if branch["coverage"] == "ignored"
|
|
174
|
+
|
|
175
|
+
total += 1
|
|
176
|
+
if branch["coverage"].to_i > 0
|
|
177
|
+
covered += 1
|
|
178
|
+
else
|
|
179
|
+
uncovered_details << {
|
|
180
|
+
condition: branch["type"],
|
|
181
|
+
cond_line: branch["start_line"],
|
|
182
|
+
branch: branch["type"],
|
|
183
|
+
branch_line: branch["start_line"],
|
|
184
|
+
}
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
uncovered = total - covered
|
|
190
|
+
next if uncovered == 0
|
|
191
|
+
|
|
192
|
+
pct = (total > 0) ? (covered.to_f / total * 100).round(1) : 100.0
|
|
193
|
+
short_path = path.split("/lib/").last || path.split("/").last(2).join("/")
|
|
194
|
+
|
|
195
|
+
results << {
|
|
196
|
+
uncovered: uncovered,
|
|
197
|
+
total: total,
|
|
198
|
+
covered: covered,
|
|
199
|
+
pct: pct,
|
|
200
|
+
path: short_path,
|
|
201
|
+
full_path: path,
|
|
202
|
+
details: uncovered_details.sort_by do |d|
|
|
203
|
+
d[:branch_line] || 0
|
|
204
|
+
end,
|
|
205
|
+
}
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
results.select! { |r| path_matches_filter?(r[:full_path], r[:path], file_filter) } if file_filter
|
|
209
|
+
|
|
210
|
+
results.sort_by! { |r| -r[:uncovered] }
|
|
211
|
+
results
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def calculate_line_totals(files, file_filter: nil)
|
|
215
|
+
total_lines = 0
|
|
216
|
+
covered_lines = 0
|
|
217
|
+
file_count = 0
|
|
218
|
+
|
|
219
|
+
files.each do |path, info|
|
|
220
|
+
next unless info.is_a?(Hash)
|
|
221
|
+
next unless info["lines"]
|
|
222
|
+
short_path = path.split("/lib/").last || path.split("/").last(2).join("/")
|
|
223
|
+
next if file_filter && !path_matches_filter?(path, short_path, file_filter)
|
|
224
|
+
|
|
225
|
+
file_count += 1
|
|
226
|
+
info["lines"].each do |hit|
|
|
227
|
+
next if hit.nil? || hit == "ignored"
|
|
228
|
+
total_lines += 1
|
|
229
|
+
covered_lines += 1 if hit.to_i > 0
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
{total: total_lines, covered: covered_lines, files: file_count}
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def calculate_branch_totals(files, file_filter: nil)
|
|
237
|
+
total_branches = 0
|
|
238
|
+
covered_branches = 0
|
|
239
|
+
file_count = 0
|
|
240
|
+
|
|
241
|
+
files.each do |path, info|
|
|
242
|
+
next unless info.is_a?(Hash)
|
|
243
|
+
next unless info["branches"]
|
|
244
|
+
short_path = path.split("/lib/").last || path.split("/").last(2).join("/")
|
|
245
|
+
next if file_filter && !path_matches_filter?(path, short_path, file_filter)
|
|
246
|
+
|
|
247
|
+
file_count += 1
|
|
248
|
+
branches = info["branches"]
|
|
249
|
+
if branches.is_a?(Hash)
|
|
250
|
+
branches.each do |_cond, branch_data|
|
|
251
|
+
next unless branch_data.is_a?(Hash)
|
|
252
|
+
branch_data.each do |_id, hit_count|
|
|
253
|
+
next if hit_count == "ignored"
|
|
254
|
+
total_branches += 1
|
|
255
|
+
covered_branches += 1 if hit_count.to_i > 0
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
elsif branches.is_a?(Array)
|
|
259
|
+
branches.each do |b|
|
|
260
|
+
next unless b.is_a?(Hash)
|
|
261
|
+
next if b["coverage"] == "ignored"
|
|
262
|
+
total_branches += 1
|
|
263
|
+
covered_branches += 1 if b["coverage"].to_i > 0
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
{total: total_branches, covered: covered_branches, files: file_count}
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
line_totals = calculate_line_totals(files, file_filter: file_filter)
|
|
272
|
+
branch_totals = calculate_branch_totals(files, file_filter: file_filter)
|
|
273
|
+
|
|
274
|
+
if @show_lines
|
|
275
|
+
puts "==== Line coverage ===="
|
|
276
|
+
line_results = collect_line_info(files, file_filter: file_filter)
|
|
277
|
+
if line_results.empty?
|
|
278
|
+
puts "All files fully covered (lines)!"
|
|
279
|
+
else
|
|
280
|
+
puts "Files with uncovered (-d to expand) lines (-n=#{max_details}; use n=0 for unlimited):"
|
|
281
|
+
puts "-" * 70
|
|
282
|
+
line_results.each do |r|
|
|
283
|
+
puts format("%3d uncovered | %5.1f%% | %s", r[:uncovered], r[:pct], r[:path])
|
|
284
|
+
next unless show_detail && r[:uncovered_lines].any?
|
|
285
|
+
|
|
286
|
+
lines_to_show = r[:uncovered_lines].first(max_details)
|
|
287
|
+
ranges = []
|
|
288
|
+
current_range = nil
|
|
289
|
+
lines_to_show.each do |line|
|
|
290
|
+
if current_range.nil?
|
|
291
|
+
current_range = [line, line]
|
|
292
|
+
elsif line == current_range[1] + 1
|
|
293
|
+
current_range[1] = line
|
|
294
|
+
else
|
|
295
|
+
ranges << current_range
|
|
296
|
+
current_range = [line, line]
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
ranges << current_range if current_range
|
|
300
|
+
range_strs = ranges.map { |r| (r[0] == r[1]) ? r[0].to_s : "#{r[0]}-#{r[1]}" }
|
|
301
|
+
puts " Lines: [#{range_strs.join(", ")}]"
|
|
302
|
+
puts " ... and #{r[:uncovered_lines].length - max_details} more" if r[:uncovered_lines].length > max_details
|
|
303
|
+
end
|
|
304
|
+
puts "-" * 70
|
|
305
|
+
total_uncovered = line_results.sum { |r| r[:uncovered] }
|
|
306
|
+
total_lines = line_totals[:total]
|
|
307
|
+
total_pct = (total_lines > 0) ? (line_totals[:covered].to_f / total_lines * 100).round(1) : 100.0
|
|
308
|
+
puts format("Total: %d uncovered lines out of %d (%.1f%% coverage)", total_uncovered, total_lines, total_pct)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
## Summary will be printed at the end of the report
|
|
313
|
+
|
|
314
|
+
if @show_branches
|
|
315
|
+
puts "\n==== Branch coverage ===="
|
|
316
|
+
branch_results = collect_branch_info(files, file_filter: file_filter)
|
|
317
|
+
if branch_results.empty?
|
|
318
|
+
puts "All files fully covered (branches)!"
|
|
319
|
+
else
|
|
320
|
+
puts "Files with uncovered (-d to expand) branches (-n=#{max_details}; use n=0 for unlimited):"
|
|
321
|
+
puts "-" * 70
|
|
322
|
+
branch_results.each do |r|
|
|
323
|
+
puts format("%3d uncovered | %5.1f%% | %s", r[:uncovered], r[:pct], r[:path])
|
|
324
|
+
next unless show_detail && r[:details].any?
|
|
325
|
+
|
|
326
|
+
details_to_show = r[:details].first(max_details)
|
|
327
|
+
details_to_show.each do |d|
|
|
328
|
+
puts format(
|
|
329
|
+
" Line %3d: %s -> %s branch not taken",
|
|
330
|
+
d[:branch_line] || d[:cond_line] || 0,
|
|
331
|
+
d[:condition],
|
|
332
|
+
d[:branch],
|
|
333
|
+
)
|
|
334
|
+
end
|
|
335
|
+
puts " ... and #{r[:details].length - max_details} more" if r[:details].length > max_details
|
|
336
|
+
end
|
|
337
|
+
puts "-" * 70
|
|
338
|
+
total_uncovered = branch_results.sum { |r| r[:uncovered] }
|
|
339
|
+
total_branches = branch_totals[:total]
|
|
340
|
+
total_pct = (total_branches > 0) ? (branch_totals[:covered].to_f / total_branches * 100).round(1) : 100.0
|
|
341
|
+
puts format("Total: %d uncovered branches out of %d (%.1f%% coverage)", total_uncovered, total_branches, total_pct)
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Final summary
|
|
346
|
+
line_totals = calculate_line_totals(files, file_filter: file_filter)
|
|
347
|
+
branch_totals = calculate_branch_totals(files, file_filter: file_filter)
|
|
348
|
+
|
|
349
|
+
line_pct = if line_totals[:total] > 0
|
|
350
|
+
(line_totals[:covered].to_f / line_totals[:total] * 100)
|
|
351
|
+
else
|
|
352
|
+
100.0
|
|
353
|
+
end
|
|
354
|
+
branch_pct = if branch_totals[:total] > 0
|
|
355
|
+
(branch_totals[:covered].to_f / branch_totals[:total] * 100)
|
|
356
|
+
else
|
|
357
|
+
100.0
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
puts "\n==== Summary Report ===="
|
|
361
|
+
puts format("LINE COVERAGE: %6.2f%% -- %d/%d lines in %d files", line_pct, line_totals[:covered], line_totals[:total], line_totals[:files])
|
|
362
|
+
puts format("BRANCH COVERAGE: %6.2f%% -- %d/%d branches in %d files", branch_pct, branch_totals[:covered], branch_totals[:total], branch_totals[:files])
|
|
@@ -34,9 +34,11 @@ SimpleCov.configure do
|
|
|
34
34
|
formatter SimpleCov::Formatter::HTMLFormatter
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
# Use Merging (merges RSpec + Cucumber Test Results)
|
|
37
|
+
# Use Merging (merges coverage from multiple test runs, e.g., RSpec + Cucumber Test Results)
|
|
38
|
+
# This is essential for projects that split tests into multiple rake tasks
|
|
39
|
+
# (e.g., FFI specs, integration specs, unit specs run separately)
|
|
38
40
|
use_merging(Kettle::Soup::Cover::Constants::USE_MERGING) unless Kettle::Soup::Cover::Constants::USE_MERGING.nil?
|
|
39
|
-
merge_timeout(Kettle::Soup::Cover::Constants::MERGE_TIMEOUT) if Kettle::Soup::Cover::Constants::MERGE_TIMEOUT
|
|
41
|
+
merge_timeout(Kettle::Soup::Cover::Constants::MERGE_TIMEOUT) if Kettle::Soup::Cover::Constants::MERGE_TIMEOUT.nonzero?
|
|
40
42
|
|
|
41
43
|
# Fail build when missed coverage targets
|
|
42
44
|
# NOTE: Checking SpecTracker.instance.full_suite? here would be awesome, but won't work
|
|
@@ -57,12 +57,23 @@ module Kettle
|
|
|
57
57
|
)
|
|
58
58
|
.split(",")
|
|
59
59
|
.map { |dir_name| %r{^/#{Regexp.escape(dir_name)}/} }
|
|
60
|
-
FORMATTERS =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
FORMATTERS = begin
|
|
61
|
+
list = ENV_GET.call(
|
|
62
|
+
"FORMATTERS",
|
|
63
|
+
IS_CI ? "html,xml,rcov,lcov,json,tty" : "html,tty",
|
|
64
|
+
)
|
|
65
|
+
.split(",")
|
|
66
|
+
.map { |fmt_name| FORMATTER_PLUGINS[fmt_name.strip.to_sym] }
|
|
67
|
+
.compact
|
|
68
|
+
|
|
69
|
+
# If MAX_ROWS is explicitly set to "0", skip tty output from simplecov-console
|
|
70
|
+
max_rows = ENV.fetch("MAX_ROWS", nil)
|
|
71
|
+
if max_rows && max_rows.strip == "0"
|
|
72
|
+
list = list.reject { |f| f && f[:type] == :tty }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
list
|
|
76
|
+
end
|
|
66
77
|
MIN_COVERAGE_HARD = ENV_GET.call("MIN_HARD", CI).casecmp?(TRUE)
|
|
67
78
|
MIN_COVERAGE_BRANCH = ENV_GET.call("MIN_BRANCH", "80").to_i
|
|
68
79
|
MIN_COVERAGE_LINE = ENV_GET.call("MIN_LINE", "80").to_i
|
|
@@ -76,8 +87,13 @@ module Kettle
|
|
|
76
87
|
is_mac = RbConfig::CONFIG["host_os"].include?("darwin")
|
|
77
88
|
# Set to "" to prevent opening a browser with the coverage rake task
|
|
78
89
|
OPEN_BIN = ENV_GET.call("OPEN_BIN", is_mac ? "open" : "xdg-open")
|
|
79
|
-
|
|
80
|
-
|
|
90
|
+
# Enable merging by default to aggregate coverage across multiple test runs
|
|
91
|
+
# (e.g., separate RSpec tasks for FFI tests, integration tests, unit tests)
|
|
92
|
+
# Set K_SOUP_COV_USE_MERGING=false to disable
|
|
93
|
+
USE_MERGING = ENV_GET.call("USE_MERGING", TRUE).casecmp?(TRUE)
|
|
94
|
+
# Default merge timeout of 1 hour (3600 seconds) - enough for most test suites
|
|
95
|
+
# Set K_SOUP_COV_MERGE_TIMEOUT to override
|
|
96
|
+
MERGE_TIMEOUT = ENV_GET.call("MERGE_TIMEOUT", "3600").to_i
|
|
81
97
|
VERBOSE = ENV_GET.call("VERBOSE", FALSE).casecmp?(TRUE)
|
|
82
98
|
|
|
83
99
|
include Kettle::Change.new(
|
data/lib/kettle-soup-cover.rb
CHANGED
data/sig/kettle/soup/cover.rbs
CHANGED
|
@@ -26,15 +26,15 @@ module Kettle
|
|
|
26
26
|
VERBOSE: bool
|
|
27
27
|
VERSION: String
|
|
28
28
|
|
|
29
|
-
def delete_const: -> void
|
|
29
|
+
def delete_const: () -> void
|
|
30
30
|
|
|
31
|
-
def install_tasks: -> void
|
|
31
|
+
def install_tasks: () -> void
|
|
32
32
|
|
|
33
|
-
def load_filters: -> void
|
|
33
|
+
def load_filters: () -> void
|
|
34
34
|
|
|
35
|
-
def load_formatters: -> void
|
|
35
|
+
def load_formatters: () -> void
|
|
36
36
|
|
|
37
|
-
def reset_const: -> void
|
|
37
|
+
def reset_const: () -> void
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
end
|
data.tar.gz.sig
CHANGED
|
Binary file
|