single_cov 1.1.0 → 1.4.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 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.