single_cov 1.1.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc46724cb995c3bf97cfb5e59f0ffb96ab508522e56269386769f9a40bbf35b3
4
- data.tar.gz: e7a93ad442bc9145900505b8d8c76e510aa2e9e4b7cf3ce9e4a03368fcbdeb8e
3
+ metadata.gz: 6cd1fb991b879dcaf753d2aa30c4f94ac3f97659cf602df4f9adab874046c16f
4
+ data.tar.gz: c2351364b4482ceff98314b881fd5b774e9040c117479674b8fb41c0b550dac4
5
5
  SHA512:
6
- metadata.gz: 88acb179ed998ce309df2e5bcf80814591c0187c98093827e767f5ada0335f2a5db726b13d1ae56aa0f6a4134e17b69b9ca86d37033fe530c859f8637798ce4c
7
- data.tar.gz: 1a2e84b437f57143087cfcba3249c871d9039331b45be8b1c7b5f3ecea046c1dec09d5c3cddcfa0687de9d32a872b0c8c77a26e42e6898d27fa3b8f1a5e6b63b
6
+ metadata.gz: 813924bc4aaad528db8afe9640178f3cfad2c11ffa4042573e4810a83cf941014e3d77352436b1c999330c70c066619054b88a3117e5d87885f95ce619dfaf2d
7
+ data.tar.gz: 31915f85ff52971fb4691f1fe52680c026b8f8e70c6b390b0c538e84ced5e59b035803130f09d5df03f4eba7731608c46f02ba28df366506322559495baf588d
@@ -3,41 +3,62 @@ module SingleCov
3
3
  MAX_OUTPUT = 40
4
4
  APP_FOLDERS = ["models", "serializers", "helpers", "controllers", "mailers", "views", "jobs", "channels"]
5
5
  BRANCH_COVERAGE_SUPPORTED = (RUBY_VERSION >= "2.5.0")
6
+ UNCOVERED_COMMENT_MARKER = "uncovered"
6
7
 
7
8
  class << self
9
+ # enable coverage reporting, changed by forking-test-runner to combine multiple reports
10
+ attr_accessor :coverage_report
11
+
8
12
  # optionally rewrite the file we guessed with a lambda
9
13
  def rewrite(&block)
10
14
  @rewrite = block
11
15
  end
12
16
 
13
17
  def not_covered!
18
+ store_pid
14
19
  end
15
20
 
16
21
  def covered!(file: nil, uncovered: 0)
17
22
  file = guess_and_check_covered_file(file)
18
23
  COVERAGES << [file, uncovered]
24
+ store_pid
19
25
  end
20
26
 
21
27
  def all_covered?(result)
22
28
  errors = COVERAGES.map do |file, expected_uncovered|
23
29
  if coverage = result["#{root}/#{file}"]
24
30
  line_coverage = (coverage.is_a?(Hash) ? coverage.fetch(:lines) : coverage)
25
- uncovered = line_coverage.each_with_index.map { |c, i| i + 1 if c == 0 }.compact
31
+ uncovered_lines = line_coverage.each_with_index.map { |c, i| i + 1 if c == 0 }.compact
32
+
26
33
  branch_coverage = (coverage.is_a?(Hash) && coverage[:branches])
34
+ uncovered_branches = (branch_coverage ? uncovered_branches(branch_coverage, uncovered_lines) : [])
27
35
 
28
- if branch_coverage
29
- uncovered.concat uncovered_branches(file, branch_coverage, uncovered)
30
- end
36
+ uncovered = uncovered_lines.concat uncovered_branches
37
+ next if uncovered.size == expected_uncovered
31
38
 
39
+ # ignore lines that are marked as uncovered via comments
40
+ # NOTE: ideally we should also warn when using uncovered but the section is indeed covered
41
+ content = File.readlines(file)
42
+ uncovered.reject! do |line_start, _, _, _|
43
+ content[line_start - 1].include?(UNCOVERED_COMMENT_MARKER)
44
+ end
32
45
  next if uncovered.size == expected_uncovered
33
46
 
34
- # branches are unsorted and added to the end, only sort when necessary
47
+ # branches are unsorted and added to the end, only sort when displayed
35
48
  if branch_coverage
36
- uncovered.sort_by! { |line_start, char_start, line_end, char_end| [line_start, char_start || 0] }
49
+ uncovered.sort_by! { |line_start, char_start, _, _| [line_start, char_start || 0] }
37
50
  end
38
51
 
39
52
  uncovered.map! do |line_start, char_start, line_end, char_end|
40
- char_start ? "#{file}:#{line_start}:#{char_start}-#{line_end}:#{char_end}" : "#{file}:#{line_start}"
53
+ if char_start # branch coverage
54
+ if line_start == line_end
55
+ "#{file}:#{line_start}:#{char_start}-#{char_end}"
56
+ else # possibly unreachable since branches always seem to be on the same line
57
+ "#{file}:#{line_start}:#{char_start}-#{line_end}:#{char_end}"
58
+ end
59
+ else
60
+ "#{file}:#{line_start}"
61
+ end
41
62
  end
42
63
 
43
64
  warn_about_bad_coverage(file, expected_uncovered, uncovered)
@@ -100,7 +121,11 @@ module SingleCov
100
121
  start_coverage_recording
101
122
 
102
123
  override_at_exit do |status, _exception|
103
- exit 1 if (!defined?(@disabled) || !@disabled) && status == 0 && !SingleCov.all_covered?(coverage_results)
124
+ if enabled? && main_process? && status == 0
125
+ results = coverage_results
126
+ generate_report results
127
+ exit 1 unless SingleCov.all_covered?(results)
128
+ end
104
129
  end
105
130
  end
106
131
 
@@ -111,7 +136,19 @@ module SingleCov
111
136
 
112
137
  private
113
138
 
114
- def uncovered_branches(file, coverage, uncovered_lines)
139
+ def enabled?
140
+ (!defined?(@disabled) || !@disabled)
141
+ end
142
+
143
+ def store_pid
144
+ @pid = Process.pid
145
+ end
146
+
147
+ def main_process?
148
+ (!defined?(@pid) || @pid == Process.pid)
149
+ end
150
+
151
+ def uncovered_branches(coverage, uncovered_lines)
115
152
  # {[branch_id] => {[branch_part] => coverage}} --> {branch_part -> sum-of-coverage}
116
153
  sum = Hash.new(0)
117
154
  coverage.each_value do |branch|
@@ -121,8 +158,8 @@ module SingleCov
121
158
  end
122
159
 
123
160
  # show missing coverage
124
- found = sum.select { |k, v| v.zero? && !uncovered_lines.include?(k[0]) }.
125
- map { |k, _| [k[0], k[1]+1, k[2], k[3]+1] }
161
+ sum.select! { |k, v| v.zero? && !uncovered_lines.include?(k[0]) }
162
+ found = sum.map { |k, _| [k[0], k[1]+1, k[2], k[3]+1] }
126
163
  found.uniq!
127
164
  found
128
165
  end
@@ -313,8 +350,8 @@ module SingleCov
313
350
  end
314
351
 
315
352
  # remove test extension
316
- unless file_part.sub!(/_(?:test|spec)\.rb\b.*/, '.rb')
317
- raise "Unable to remove test extension from #{file} ... _test.rb and _spec.rb are supported"
353
+ if !file_part.sub!(/_(?:test|spec)\.rb\b.*/, '.rb') && !file_part.sub!(/\/test_/, "/")
354
+ raise "Unable to remove test extension from #{file} ... /test_, _test.rb and _spec.rb are supported"
318
355
  end
319
356
 
320
357
  # put back the subfolder
@@ -328,5 +365,21 @@ module SingleCov
328
365
  def root
329
366
  @root ||= (defined?(Bundler) && Bundler.root.to_s.sub(/\/gemfiles$/, '')) || Dir.pwd
330
367
  end
368
+
369
+ def generate_report(results)
370
+ return unless report = coverage_report
371
+
372
+ # not a hard dependency for the whole library
373
+ require "json"
374
+ require "fileutils"
375
+
376
+ used = COVERAGES.map { |f, _| "#{root}/#{f}" }
377
+ covered = results.select { |k, _| used.include?(k) }
378
+ data = JSON.pretty_generate(
379
+ "Unit Tests" => {"coverage" => covered, "timestamp" => Time.now.to_i }
380
+ )
381
+ FileUtils.mkdir_p(File.dirname(report))
382
+ File.write report, data
383
+ end
331
384
  end
332
385
  end
@@ -1,3 +1,3 @@
1
1
  module SingleCov
2
- VERSION = "1.1.0"
2
+ VERSION = "1.4.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: single_cov
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-05 00:00:00.000000000 Z
11
+ date: 2020-10-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: michael@grosser.it
@@ -38,8 +38,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
40
  requirements: []
41
- rubyforge_project:
42
- rubygems_version: 2.7.6
41
+ rubygems_version: 3.1.3
43
42
  signing_key:
44
43
  specification_version: 4
45
44
  summary: Actionable code coverage.