covered 0.5.2 → 0.6.2

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: 2da5bcb0da1de420cf2833d1f2dfbc4ff26cf1eb85e1552d3b5e79902f09ff5c
4
- data.tar.gz: 6bcbbd747726ac1ec443b97401058b85fce046abd5a777085288e105d0f2010d
3
+ metadata.gz: e0394e15e8b2b00303b22171cf122a90b0ca1172e1a2af3d7a5789cf69c08ad3
4
+ data.tar.gz: 4043a560028ca031e155c2a578995afd6a799b6119d5dbd40f8f342ed0d5a327
5
5
  SHA512:
6
- metadata.gz: e733ded2af93f6c93cd77a09e43a9cdaf297880c518c834596382be8c4283a417f0720b2e942b85e4260470165c75f14b021637e0c9f8421c3e5816f4ab44463
7
- data.tar.gz: 247193bd7021f51519cf39ee6d61e801bfd0bf9677dcf65b6c1f6e925c38dd62b17606a1d5546a19d6a2d09d56be69c942641161a58785adc820342c57e8b1e5
6
+ metadata.gz: fb55691abee0a0fc745591e17c206ccabaa88e7a66f496730c4a377b0777e2e44a30dc84b15f9ea885c336cd88713e320114894753779fe24ee11518bcde9725
7
+ data.tar.gz: 44a25c1e34d4546ce7a90dae4082f7236be2380d385ded731d648088ce99e4c6c3473022fedd3d80953a31ebdd499d522b1c38e28a0f65e4c9f34347058caf46
data/covered.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_development_dependency "trenni", "~> 3.6"
26
26
 
27
- spec.add_development_dependency "bundler", "~> 2.0"
27
+ spec.add_development_dependency "bundler"
28
28
  spec.add_development_dependency "rake", "~> 10.0"
29
29
  spec.add_development_dependency "rspec", "~> 3.0"
30
30
  end
@@ -20,20 +20,20 @@
20
20
 
21
21
  require_relative 'wrapper'
22
22
 
23
+ require 'coverage'
24
+
23
25
  module Covered
24
26
  class Capture < Wrapper
25
27
  def initialize(output)
26
28
  super(output)
27
29
 
28
- @trace = TracePoint.new(:line, :call) do |trace_point|
29
- if path = trace_point.path
30
- @output.mark(path, trace_point.lineno)
30
+ @trace = TracePoint.new(:line, :call) do |event|
31
+ if path = event.path
32
+ @output.mark(path, event.lineno)
31
33
  end
32
34
  end
33
35
  end
34
36
 
35
- attr :paths
36
-
37
37
  def enable
38
38
  super
39
39
 
@@ -46,4 +46,55 @@ module Covered
46
46
  super
47
47
  end
48
48
  end
49
+
50
+ class Cache < Wrapper
51
+ def initialize(output)
52
+ super(output)
53
+ @marks = []
54
+ end
55
+
56
+ def mark(path, lineno, count = 1)
57
+ @marks << path << lineno << count
58
+ end
59
+
60
+ def enable
61
+ super
62
+ end
63
+
64
+ def flush
65
+ @marks.each_slice(3) do |path, lineno, count|
66
+ @output.mark(path, lineno, count)
67
+ end
68
+
69
+ @marks.clear
70
+ end
71
+
72
+ def disable
73
+ super
74
+
75
+ flush
76
+ end
77
+ end
78
+
79
+ # class Capture < Wrapper
80
+ # def enable
81
+ # super
82
+ #
83
+ # ::Coverage.start
84
+ # end
85
+ #
86
+ # def disable
87
+ # result = ::Coverage.result
88
+ #
89
+ # puts result.inspect
90
+ #
91
+ # result.each do |path, lines|
92
+ # lines.each_with_index do |lineno, count|
93
+ # @output.mark(path, lineno, count)
94
+ # end
95
+ # end
96
+ #
97
+ # super
98
+ # end
99
+ # end
49
100
  end
@@ -39,6 +39,9 @@ module Covered
39
39
  def initialize(path, counts = [])
40
40
  @path = path
41
41
  @counts = counts
42
+ @total = 0
43
+
44
+ @annotations = {}
42
45
 
43
46
  @executable_lines = nil
44
47
  @executed_lines = nil
@@ -46,22 +49,38 @@ module Covered
46
49
 
47
50
  attr :path
48
51
  attr :counts
52
+ attr :total
53
+
54
+ attr :annotations
49
55
 
50
56
  def freeze
51
57
  return if frozen?
52
58
 
53
59
  @counts.freeze
60
+ @annotations.freeze
61
+
54
62
  executable_lines
55
63
  executed_lines
56
64
 
57
65
  super
58
66
  end
59
67
 
68
+ def zero?
69
+ @total.zero?
70
+ end
71
+
60
72
  def [] lineno
61
73
  @counts[lineno]
62
74
  end
63
75
 
76
+ def annotate(lineno, annotation)
77
+ @annotations[lineno] ||= []
78
+ @annotations[lineno] << annotation
79
+ end
80
+
64
81
  def mark(lineno, value = 1)
82
+ @total += value
83
+
65
84
  if @counts[lineno]
66
85
  @counts[lineno] += value
67
86
  else
@@ -85,6 +104,10 @@ module Covered
85
104
  executed_lines.count
86
105
  end
87
106
 
107
+ def missing_count
108
+ executable_count - executed_count
109
+ end
110
+
88
111
  include Ratio
89
112
 
90
113
  def print_summary(output)
@@ -37,13 +37,19 @@ module Covered
37
37
  class Policy < Wrapper
38
38
  def initialize
39
39
  super(Files.new)
40
+
41
+ @threshold = 1.0
42
+ @summary_class = PartialSummary
40
43
  end
41
44
 
45
+ attr_accessor :summary_class
46
+ attr_accessor :threshold
47
+
42
48
  def freeze
43
49
  return if frozen?
44
50
 
45
51
  capture
46
- summary
52
+ summary(threshold: @threshold)
47
53
 
48
54
  super
49
55
  end
@@ -69,7 +75,7 @@ module Covered
69
75
  end
70
76
 
71
77
  def capture
72
- @capture ||= Capture.new(@output)
78
+ @capture ||= Capture.new(Cache.new(@output))
73
79
  end
74
80
 
75
81
  def enable
@@ -81,11 +87,11 @@ module Covered
81
87
  end
82
88
 
83
89
  def summary(*args)
84
- @summary ||= Summary.new(@output, *args)
90
+ @summary ||= @summary_class.new(@output, *args)
85
91
  end
86
92
 
87
93
  def print_summary(*args)
88
- summary.print_partial_summary(*args)
94
+ summary.print_summary(*args)
89
95
  end
90
96
  end
91
97
  end
data/lib/covered/rspec.rb CHANGED
@@ -32,6 +32,12 @@ $covered = Covered.policy do
32
32
  include "lib/**/*.rb"
33
33
 
34
34
  source
35
+
36
+ if coverage = ENV['COVERAGE']
37
+ self.summary_class = Covered.const_get(coverage)
38
+ else
39
+ self.summary_class = Covered::BriefSummary
40
+ end
35
41
  end
36
42
 
37
43
  module Covered
@@ -47,18 +53,31 @@ module Covered
47
53
  $covered.print_summary(@output)
48
54
  end
49
55
  end
56
+
57
+ module Policy
58
+ def load_spec_files
59
+ $covered.enable
60
+
61
+ super
62
+ end
63
+
64
+ def covered
65
+ $covered
66
+ end
67
+
68
+ def covered= policy
69
+ $covered = policy
70
+ end
71
+ end
50
72
  end
51
73
  end
52
74
 
75
+ RSpec::Core::Configuration.prepend(Covered::RSpec::Policy)
76
+
53
77
  RSpec.configure do |config|
54
78
  config.add_formatter(Covered::RSpec::Formatter)
55
79
 
56
- config.before(:suite) do
57
- end
58
-
59
80
  config.after(:suite) do
60
81
  $covered.disable
61
82
  end
62
83
  end
63
-
64
- $covered.enable
@@ -29,7 +29,7 @@ module Covered
29
29
  EXECUTABLE = /(.?CALL|.VAR|.ASGN|DEFN)/.freeze
30
30
 
31
31
  # Deviate from the standard policy above, because all the files are already loaded, so we skip NODE_FCALL.
32
- DOGFOOD = /([V]?CALL|.VAR|.ASGN|DEFN)/.freeze
32
+ DOGFOOD = /(^V?CALL|.VAR|.ASGN|DEFN)/.freeze
33
33
 
34
34
  # Ruby trace points don't trigger for argument execution.
35
35
  # Constants are loaded when the file loads, so they are less interesting.
@@ -43,6 +43,8 @@ module Covered
43
43
 
44
44
  @executable = executable
45
45
  @ignore = ignore
46
+
47
+ @annotations = {}
46
48
  end
47
49
 
48
50
  def enable
@@ -76,24 +78,30 @@ module Covered
76
78
  node.nil? or node.type.to_s =~ @ignore
77
79
  end
78
80
 
79
- def expand(node, counts, level = 0)
80
- # puts "#{node.first_lineno}: #{node.inspect}"
81
-
82
- counts[node.first_lineno] ||= 0 if executable?(node)
83
-
84
- # puts "#{"\t"*level}#{node.type} (#{node.first_lineno})"
85
- node.children.each do |child|
86
- next unless child.is_a? RubyVM::AbstractSyntaxTree::Node
87
-
88
- next if ignore?(child)
89
-
90
- expand(child, counts, level + 1)
81
+ def expand(node, coverage, level = 0)
82
+ if node.is_a? RubyVM::AbstractSyntaxTree::Node
83
+ if ignore?(node)
84
+ coverage.annotate(node.first_lineno, "ignoring #{node.type}")
85
+ else
86
+ if executable?(node)
87
+ # coverage.annotate(node.first_lineno, "executable #{node.type}")
88
+ coverage.counts[node.first_lineno] ||= 0
89
+ else
90
+ # coverage.annotate(node.first_lineno, "not executable #{node.type}")
91
+ end
92
+
93
+ expand(node.children, coverage, level + 1)
94
+ end
95
+ elsif node.is_a? Array
96
+ node.each do |child|
97
+ expand(child, coverage, level)
98
+ end
99
+ else
100
+ return false
91
101
  end
92
102
  end
93
103
 
94
104
  def parse(path)
95
- # puts "Parse #{path}"
96
-
97
105
  if source = @paths[path]
98
106
  RubyVM::AbstractSyntaxTree.parse(source)
99
107
  elsif File.exist?(path)
@@ -107,7 +115,7 @@ module Covered
107
115
  @output.each do |coverage|
108
116
  # This is a little bit inefficient, perhaps add a cache layer?
109
117
  if top = parse(coverage.path)
110
- expand(top, coverage.counts)
118
+ self.expand(top, coverage)
111
119
  end
112
120
 
113
121
  yield coverage.freeze
@@ -28,26 +28,38 @@ module Covered
28
28
  def initialize(output, threshold: 1.0)
29
29
  super(output)
30
30
 
31
- @statistics = nil
32
-
33
31
  @threshold = threshold
34
32
  end
35
33
 
36
34
  def each
37
- @statistics = Statistics.new
35
+ statistics = Statistics.new
38
36
 
39
37
  super do |coverage|
40
- @statistics << coverage
38
+ statistics << coverage
41
39
 
42
- if coverage.ratio < @threshold
40
+ if @threshold.nil? or coverage.ratio < @threshold
43
41
  yield coverage
44
42
  end
45
43
  end
44
+
45
+ return statistics
46
+ end
47
+
48
+ def print_annotations(output, coverage, line, line_offset)
49
+ if annotations = coverage.annotations[line_offset]
50
+ output.write("#{line_offset}|".rjust(8))
51
+ output.write("*|".rjust(8))
52
+
53
+ output.write line.match(/^\s+/)
54
+ output.write '# '
55
+
56
+ output.puts Rainbow(annotations.join(", ")).bright
57
+ end
46
58
  end
47
59
 
48
60
  # A coverage array gives, for each line, the number of line execution by the interpreter. A nil value means coverage is disabled for this line (lines like else and end).
49
61
  def print_summary(output = $stdout)
50
- self.each do |coverage|
62
+ statistics = self.each do |coverage|
51
63
  line_offset = 1
52
64
  output.puts "", Rainbow(coverage.path).bold.underline
53
65
 
@@ -57,8 +69,10 @@ module Covered
57
69
  file.each_line do |line|
58
70
  count = counts[line_offset]
59
71
 
72
+ print_annotations(output, coverage, line, line_offset)
73
+
60
74
  output.write("#{line_offset}|".rjust(8))
61
- output.write("#{count}:".rjust(8))
75
+ output.write("#{count}|".rjust(8))
62
76
 
63
77
  if count == nil
64
78
  output.write Rainbow(line).faint
@@ -80,45 +94,79 @@ module Covered
80
94
  coverage.print_summary(output)
81
95
  end
82
96
 
83
- @statistics.print_summary(output)
97
+ statistics.print_summary(output)
84
98
  end
85
-
86
- def print_partial_summary(output = $stdout, before: 4, after: 4)
87
- statistics = Statistics.new
99
+ end
100
+
101
+ class BriefSummary < Summary
102
+ def print_summary(output = $stdout, before: 4, after: 4)
103
+ ordered = []
104
+
105
+ statistics = self.each do |coverage|
106
+ ordered << coverage unless coverage.complete?
107
+ end
88
108
 
89
- self.each do |coverage|
109
+ output.puts
110
+ statistics.print_summary(output)
111
+
112
+ if ordered.any?
113
+ output.puts "", "Least Coverage:"
114
+ ordered.sort_by!(&:missing_count).reverse!
115
+
116
+ ordered.first(5).each do |coverage|
117
+ output.write Rainbow(coverage.path).orange
118
+ output.puts ": #{coverage.missing_count} lines not executed!"
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ class PartialSummary < Summary
125
+ def print_summary(output = $stdout, before: 4, after: 4)
126
+ statistics = self.each do |coverage|
90
127
  line_offset = 1
91
128
  output.puts "", Rainbow(coverage.path).bold.underline
92
129
 
93
130
  counts = coverage.counts
131
+ last_line = nil
94
132
 
95
- File.open(coverage.path, "r") do |file|
96
- file.each_line do |line|
97
- range = Range.new([line_offset - before, 0].max, line_offset+after)
98
-
99
- if counts[range]&.include?(0)
100
- count = counts[line_offset]
101
-
102
- prefix = "#{line_offset} ".rjust(8) + "#{count} ".rjust(8)
133
+ unless coverage.zero?
134
+ File.open(coverage.path, "r") do |file|
135
+ file.each_line do |line|
136
+ range = Range.new([line_offset - before, 0].max, line_offset+after)
103
137
 
104
- if count == nil
105
- output.write prefix
106
- output.write Rainbow(line).faint
107
- elsif count == 0
108
- output.write Rainbow(prefix).background(:darkred)
109
- output.write Rainbow(line).red
110
- else
111
- output.write Rainbow(prefix).background(:darkgreen)
112
- output.write Rainbow(line).green
138
+ if counts[range]&.include?(0)
139
+ count = counts[line_offset]
140
+
141
+ if last_line and last_line != line_offset-1
142
+ output.puts ":".rjust(16)
143
+ end
144
+
145
+ print_annotations(output, coverage, line, line_offset)
146
+
147
+ prefix = "#{line_offset}|".rjust(8) + "#{count}|".rjust(8)
148
+
149
+ if count == nil
150
+ output.write prefix
151
+ output.write Rainbow(line).faint
152
+ elsif count == 0
153
+ output.write Rainbow(prefix).background(:darkred)
154
+ output.write Rainbow(line).red
155
+ else
156
+ output.write Rainbow(prefix).background(:darkgreen)
157
+ output.write Rainbow(line).green
158
+ end
159
+
160
+ # If there was no newline at end of file, we add one:
161
+ unless line.end_with? $/
162
+ output.puts
163
+ end
164
+
165
+ last_line = line_offset
113
166
  end
114
167
 
115
- # If there was no newline at end of file, we add one:
116
- unless line.end_with? $/
117
- output.puts
118
- end
168
+ line_offset += 1
119
169
  end
120
-
121
- line_offset += 1
122
170
  end
123
171
  end
124
172
 
@@ -126,7 +174,7 @@ module Covered
126
174
  end
127
175
 
128
176
  output.puts
129
- @statistics.print_summary(output)
177
+ statistics.print_summary(output)
130
178
  end
131
179
  end
132
180
  end
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Covered
22
- VERSION = "0.5.2"
22
+ VERSION = "0.6.2"
23
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: covered
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-07 00:00:00.000000000 Z
11
+ date: 2018-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rainbow
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '2.0'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '2.0'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -125,8 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
127
  requirements: []
128
- rubyforge_project:
129
- rubygems_version: 2.7.8
128
+ rubygems_version: 3.0.0.beta3
130
129
  signing_key:
131
130
  specification_version: 4
132
131
  summary: A modern approach to code coverage.