covered 0.19.1 → 0.20.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: 22da5b4e371b8fb06056b96b3f6a9a0d7755a1684e9e4371d45beae886ba9616
4
- data.tar.gz: c841065dece4f121afd7e4b3fe070327c0de15c5fedd972e089950eccf11375b
3
+ metadata.gz: e865810f0e3c42f28a7bf1569449240d46f6304a0803b2d34006c3d8ecb4b17a
4
+ data.tar.gz: b0da723e52175e6766fae5c9d4e9c61b4619fd19867ee6ce0970008c61bc7330
5
5
  SHA512:
6
- metadata.gz: c63bdb36d6fb7573cf9bd82fe2bc6f441729470d40b2b5043c164b4ff6d8d6e43513a88692443942613bbb307964814d5b7510325d1da53bdac93b2301d50328
7
- data.tar.gz: eb1812bc6debd69bd01004ab0042aa45167a56788fecee49f2f1710689a1cd6fa6d99026352d5a45eaecb0f904720763d7cc3de600a870ac1fdb656a70b80406
6
+ metadata.gz: 4858522284e9e50cff92558194a7caf5a08604b45602bd83b6137d71e0a6537a3569bae82270efde1f3ee6fefa58015fbcac036c8eba36a72f52f3f41cfc1078
7
+ data.tar.gz: ee3779802478b0d9097e8ec00c93a2cc25c6a01e72963c762f0e463759b5284b43820d3213b0927e9402db77c31a639f04ac4fc3beceab5b9f199a3b84028c30
checksums.yaml.gz.sig CHANGED
@@ -1,4 +1,5 @@
1
- <�HkH����;������҇*O}1л�Ak�����
2
- �qe���]�y: �����P��r�
3
- sGQhG��)�<��c
4
- ���nXs�ըG]�y�(���M�w�k;�Z�������!^!�{����!��s�1 Q6y� q�
1
+ ��j�}�Xo7򊡔�)��iD���:J��
2
+ �!kG^]��*��;�8e~����>��|�~�\(mZ��Ed ��
3
+ B
4
+ a��ƛ���N�؇�kS1
5
+ “����ʃ2�
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022, by Samuel Williams.
4
+ # Copyright, 2022-2023, by Samuel Williams.
5
5
 
6
6
  def initialize(context)
7
7
  super
@@ -23,11 +23,11 @@ def parse(paths: [], execute: false)
23
23
 
24
24
  if execute
25
25
  capture = Covered::Capture.new(output)
26
- capture.enable
26
+ capture.start
27
27
  paths.each do |path|
28
28
  load path
29
29
  end
30
- capture.disable
30
+ capture.finish
31
31
 
32
32
  files.paths = files.paths.slice(*paths)
33
33
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022, by Samuel Williams.
4
+ # Copyright, 2022-2023, by Samuel Williams.
5
5
 
6
6
  def initialize(context)
7
7
  super
@@ -20,8 +20,6 @@ def validate(paths: nil, minimum: 1.0)
20
20
  Covered::Persist.new(config.output, path).load!(ignore_mtime: true)
21
21
  end
22
22
 
23
- config.flush
24
-
25
23
  statistics = Covered::Statistics.new
26
24
 
27
25
  config.each do |coverage|
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2023, by Samuel Williams.
5
+
6
+ require_relative 'config'
7
+
8
+ module Coverage
9
+ module Autostart
10
+ # Start recording coverage information.
11
+ # Usage: RUBYOPT=-rcovered/autostart ruby my_script.rb
12
+ def self.autostart!
13
+ config = Covered::Config.load
14
+ config.start
15
+
16
+ pid = Process.pid
17
+
18
+ at_exit do
19
+ # Don't break forked children:
20
+ if Process.pid == pid
21
+ config.finish
22
+
23
+ if config.report?
24
+ config.call($stderr)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ Coverage::Autostart.autostart!
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2018-2022, by Samuel Williams.
4
+ # Copyright, 2018-2023, by Samuel Williams.
5
5
 
6
6
  require_relative 'wrapper'
7
7
 
@@ -9,32 +9,39 @@ require 'coverage'
9
9
 
10
10
  module Covered
11
11
  class Capture < Wrapper
12
- def enable
12
+ def start
13
13
  super
14
14
 
15
15
  ::Coverage.start(lines: true, eval: true)
16
16
  end
17
17
 
18
- def disable
18
+ def clear
19
+ super
20
+
21
+ ::Coverage.result(stop: false, clear: true)
22
+ end
23
+
24
+ def finish
19
25
  results = ::Coverage.result
20
-
26
+
21
27
  results.each do |path, result|
22
28
  lines = result[:lines]
23
-
29
+ path = self.expand_path(path)
30
+
24
31
  lines.each_with_index do |count, lineno|
25
32
  @output.mark(path, lineno+1, count) if count
26
33
  end
27
34
  end
28
-
35
+
29
36
  super
30
37
  end
31
38
 
32
39
  def execute(source, binding: TOPLEVEL_BINDING)
33
- enable
40
+ start
34
41
 
35
42
  eval(source.code!, binding, source.path, source.line_offset)
36
43
  ensure
37
- disable
44
+ finish
38
45
  end
39
46
  end
40
47
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022, by Samuel Williams.
4
+ # Copyright, 2022-2023, by Samuel Williams.
5
5
 
6
6
  require_relative 'policy'
7
7
 
@@ -9,8 +9,12 @@ module Covered
9
9
  class Config
10
10
  PATH = "config/covered.rb"
11
11
 
12
+ def self.root
13
+ ENV['COVERED_ROOT'] || Dir.pwd
14
+ end
15
+
12
16
  def self.path(root)
13
- path = ::File.join(root, PATH)
17
+ path = ::File.expand_path(PATH, root)
14
18
 
15
19
  if ::File.exist?(path)
16
20
  return path
@@ -21,7 +25,7 @@ module Covered
21
25
  ENV['COVERAGE']
22
26
  end
23
27
 
24
- def self.load(root: Dir.pwd, coverage: self.coverage)
28
+ def self.load(root: self.root, coverage: self.coverage)
25
29
  derived = Class.new(self)
26
30
 
27
31
  if path = self.path(root)
@@ -37,12 +41,16 @@ module Covered
37
41
  @root = root
38
42
  @coverage = coverage
39
43
  @policy = nil
44
+
45
+ @environment = nil
40
46
  end
41
47
 
42
- def record?
48
+ def report?
43
49
  !!@coverage
44
50
  end
45
51
 
52
+ alias :record? :report?
53
+
46
54
  attr :coverage
47
55
 
48
56
  def policy
@@ -53,16 +61,22 @@ module Covered
53
61
  policy.output
54
62
  end
55
63
 
56
- def enable
57
- policy.enable
58
- end
59
-
60
- def disable
61
- policy.disable
64
+ def start
65
+ # Save and setup the environment:
66
+ @environment = ENV.to_h
67
+ autostart!
68
+
69
+ # Start coverage tracking:
70
+ policy.start
62
71
  end
63
72
 
64
- def flush
65
- policy.flush
73
+ def finish
74
+ # Finish coverage tracking:
75
+ policy.finish
76
+
77
+ # Restore the environment:
78
+ ENV.replace(@environment)
79
+ @environment = nil
66
80
  end
67
81
 
68
82
  def call(output)
@@ -73,13 +87,22 @@ module Covered
73
87
  policy.each(&block)
74
88
  end
75
89
 
90
+ def ignore_paths
91
+ ['test/', 'fixtures/', 'spec/', 'vendor/', 'config/']
92
+ end
93
+
94
+
76
95
  # Override this method to implement your own policy.
77
96
  def make_policy(policy)
78
97
  # Only files in the root would be tracked:
79
98
  policy.root(@root)
80
99
 
100
+ patterns = ignore_paths.map do |path|
101
+ File.join(@root, path)
102
+ end
103
+
81
104
  # We will ignore any files in the test or spec directory:
82
- policy.skip(/^.*\/(test|fixtures|spec|vendor|config)\//)
105
+ policy.skip(Regexp.union(patterns))
83
106
 
84
107
  # We will include all files under lib, even if they aren't loaded:
85
108
  policy.include("lib/**/*.rb")
@@ -88,5 +111,26 @@ module Covered
88
111
 
89
112
  policy.reports!(@coverage)
90
113
  end
114
+
115
+ protected
116
+
117
+ REQUIRE_COVERED_AUTOSTART = '-rcovered/autostart'
118
+
119
+ def autostart!
120
+ if rubyopt = ENV['RUBYOPT'] and !rubyopt.empty?
121
+ rubyopt = [rubyopt.strip, REQUIRE_COVERED_AUTOSTART].join(' ')
122
+ else
123
+ rubyopt = REQUIRE_COVERED_AUTOSTART
124
+ end
125
+
126
+ ENV['RUBYOPT'] = rubyopt
127
+
128
+ unless ENV['COVERED_ROOT']
129
+ ENV['COVERED_ROOT'] = @root
130
+ end
131
+
132
+ # Don't report coverage in child processes:
133
+ ENV.delete('COVERAGE')
134
+ end
91
135
  end
92
136
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2018-2022, by Samuel Williams.
4
+ # Copyright, 2018-2023, by Samuel Williams.
5
5
 
6
6
  module Covered
7
7
  module Ratio
@@ -20,45 +20,76 @@ module Covered
20
20
  end
21
21
  end
22
22
 
23
- class Coverage
24
- Source = Struct.new(:path, :code, :line_offset) do
25
- def to_s
26
- "\#<#{self.class} path=#{path}>"
27
- end
28
-
29
- def read(&block)
30
- if block_given?
31
- File.open(self.path, "r", &block)
32
- else
33
- File.read(self.path)
34
- end
35
- end
36
-
37
- # The actual code which is being covered. If a template generates the source, this is the generated code, while the path refers to the template itself.
38
- def code!
39
- self.code || self.read
40
- end
41
-
42
- def code?
43
- !!self.code
23
+ class Source
24
+ def self.for(path, code, line_offset)
25
+ self.new(path, code: code, line_offset: line_offset)
26
+ end
27
+
28
+ def initialize(path, code: nil, line_offset: 1, modified_time: nil)
29
+ @path = path
30
+ @code = code
31
+ @line_offset = line_offset
32
+ @modified_time = modified_time
33
+ end
34
+
35
+ attr_accessor :path
36
+ attr :code
37
+ attr :line_offset
38
+ attr :modified_time
39
+
40
+ def to_s
41
+ "\#<#{self.class} path=#{path}>"
42
+ end
43
+
44
+ def read(&block)
45
+ if block_given?
46
+ File.open(self.path, "r", &block)
47
+ else
48
+ File.read(self.path)
44
49
  end
45
50
  end
46
51
 
47
- def self.source(path, code = nil, line_offset = 1)
48
- Source.new(path, code, line_offset)
52
+ # The actual code which is being covered. If a template generates the source, this is the generated code, while the path refers to the template itself.
53
+ def code!
54
+ self.code || self.read
49
55
  end
50
56
 
51
- def self.for(path, code = nil, line_offset = 1)
52
- self.new(Source.new(path, code, line_offset))
57
+ def code?
58
+ !!self.code
53
59
  end
54
60
 
55
- def initialize(source, counts = [])
61
+ def serialize(packer)
62
+ packer.write(self.path)
63
+ packer.write(self.code)
64
+ packer.write(self.line_offset)
65
+ packer.write(self.modified_time)
66
+ end
67
+
68
+ def self.deserialize(unpacker)
69
+ path = unpacker.read
70
+ code = unpacker.read
71
+ line_offset = unpacker.read
72
+ modified_time = unpacker.read
73
+
74
+ self.new(path, code: code, line_offset: line_offset, modified_time: modified_time)
75
+ end
76
+ end
77
+
78
+ class Coverage
79
+ include Ratio
80
+
81
+ def self.for(path, **options)
82
+ self.new(Source.new(path, **options))
83
+ end
84
+
85
+ def initialize(source, counts = [], annotations = {}, total = nil)
56
86
  @source = source
57
87
  @counts = counts
58
- @total = 0
88
+ @annotations = annotations
59
89
 
60
- @annotations = {}
90
+ @total = total || counts.sum{|count| count || 0}
61
91
 
92
+ # Memoized metrics:
62
93
  @executable_lines = nil
63
94
  @executed_lines = nil
64
95
  end
@@ -67,6 +98,29 @@ module Covered
67
98
  @source.path
68
99
  end
69
100
 
101
+ def path= value
102
+ @source.path = value
103
+ end
104
+
105
+ def fresh?
106
+ if @source.modified_time.nil?
107
+ # We don't know when the file was last modified, so we assume it is fresh:
108
+ return true
109
+ end
110
+
111
+ unless File.exist?(@source.path)
112
+ # The file no longer exists, so we assume it is stale:
113
+ return false
114
+ end
115
+
116
+ if @source.modified_time >= File.mtime(@source.path)
117
+ # The file has not been modified since we last processed it, so we assume it is fresh:
118
+ return true
119
+ end
120
+
121
+ return false
122
+ end
123
+
70
124
  attr_accessor :source
71
125
 
72
126
  attr :counts
@@ -84,9 +138,6 @@ module Covered
84
138
  @counts.freeze
85
139
  @annotations.freeze
86
140
 
87
- executable_lines
88
- executed_lines
89
-
90
141
  super
91
142
  end
92
143
 
@@ -102,21 +153,6 @@ module Covered
102
153
  @counts[lineno]
103
154
  end
104
155
 
105
- def annotate(lineno, annotation)
106
- @annotations[lineno] ||= []
107
- @annotations[lineno] << annotation
108
- end
109
-
110
- def mark(lineno, value = 1)
111
- @total += value
112
-
113
- if @counts[lineno]
114
- @counts[lineno] += value
115
- else
116
- @counts[lineno] = value
117
- end
118
- end
119
-
120
156
  def executable_lines
121
157
  @executable_lines ||= @counts.compact
122
158
  end
@@ -137,14 +173,28 @@ module Covered
137
173
  executable_count - executed_count
138
174
  end
139
175
 
140
- include Ratio
141
-
142
176
  def print(output)
143
177
  output.puts "** #{executed_count}/#{executable_count} lines executed; #{percentage.to_f.round(2)}% covered."
144
178
  end
145
179
 
146
180
  def to_s
147
- "\#<#{self.class} path=#{@path} #{percentage.to_f.round(2)}% covered>"
181
+ "\#<#{self.class} path=#{self.path} #{self.summary.percentage.to_f.round(2)}% covered>"
182
+ end
183
+
184
+ def serialize(packer)
185
+ packer.write(@source)
186
+ packer.write(@counts)
187
+ packer.write(@annotations)
188
+ packer.write(@total)
189
+ end
190
+
191
+ def self.deserialize(unpacker)
192
+ source = unpacker.read
193
+ counts = unpacker.read
194
+ annotations = unpacker.read
195
+ total = unpacker.read
196
+
197
+ self.new(source, counts, annotations, total)
148
198
  end
149
199
  end
150
200
  end
data/lib/covered/files.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2018-2022, by Samuel Williams.
4
+ # Copyright, 2018-2023, by Samuel Williams.
5
5
 
6
6
  require_relative 'coverage'
7
7
  require_relative 'wrapper'
@@ -10,6 +10,55 @@ require 'set'
10
10
 
11
11
  module Covered
12
12
  class Files < Base
13
+ class State
14
+ def self.for(path, **options)
15
+ self.new(Source.new(path, **options))
16
+ end
17
+
18
+ def initialize(source)
19
+ @source = source
20
+ @counts = []
21
+ @annotations = {}
22
+ end
23
+
24
+ def [](lineno)
25
+ @counts[lineno]
26
+ end
27
+
28
+ attr :counts
29
+ attr :annotations
30
+
31
+ def annotate(lineno, annotation)
32
+ @annotations[lineno] ||= []
33
+ @annotations[lineno] << annotation
34
+ end
35
+
36
+ def mark(lineno, value = 1)
37
+ if @counts[lineno]
38
+ @counts[lineno] += value
39
+ else
40
+ @counts[lineno] = value
41
+ end
42
+ end
43
+
44
+ def merge!(coverage)
45
+ coverage.counts.each_with_index do |count, index|
46
+ if count
47
+ @counts[index] ||= 0
48
+ @counts[index] += count
49
+ end
50
+ end
51
+
52
+ @annotations.merge!(coverage.annotations) do |lineno, a, b|
53
+ Array(a) + Array(b)
54
+ end
55
+ end
56
+
57
+ def coverage
58
+ Coverage.new(@source, @counts, @annotations)
59
+ end
60
+ end
61
+
13
62
  def initialize(*)
14
63
  super
15
64
 
@@ -19,7 +68,7 @@ module Covered
19
68
  attr_accessor :paths
20
69
 
21
70
  def [](path)
22
- @paths[path]
71
+ @paths[path] ||= State.for(path)
23
72
  end
24
73
 
25
74
  def empty?
@@ -27,19 +76,27 @@ module Covered
27
76
  end
28
77
 
29
78
  def mark(path, lineno, value)
30
- coverage = (@paths[path] ||= Coverage.for(path))
31
-
32
- coverage.mark(lineno, value)
33
-
34
- return coverage
79
+ self[path].mark(lineno, value)
35
80
  end
36
81
 
37
- def add(source)
38
- @paths[source.path] ||= Coverage.new(source)
82
+ def annotate(path, lineno, value)
83
+ self[path].annotate(lineno, value)
39
84
  end
40
85
 
41
- def each(&block)
42
- @paths.each_value(&block)
86
+ def add(coverage)
87
+ self[coverage.path].merge!(coverage)
88
+ end
89
+
90
+ def each
91
+ return to_enum unless block_given?
92
+
93
+ @paths.each_value do |state|
94
+ yield state.coverage
95
+ end
96
+ end
97
+
98
+ def clear
99
+ @paths.clear
43
100
  end
44
101
  end
45
102
 
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2023, by Samuel Williams.
5
+
6
+ require_relative 'wrapper'
7
+
8
+ module Covered
9
+ class Forks < Wrapper
10
+ def start
11
+ super
12
+
13
+ Handler.start(self)
14
+ end
15
+
16
+ def finish
17
+ Handler.finish
18
+
19
+ super
20
+ end
21
+
22
+ module Handler
23
+ LOCK = Mutex.new
24
+
25
+ class << self
26
+ attr :coverage
27
+
28
+ def start(coverage)
29
+ LOCK.synchronize do
30
+ if @coverage
31
+ raise ArgumentError, "Coverage is already being tracked!"
32
+ end
33
+
34
+ @coverage = coverage
35
+ end
36
+ end
37
+
38
+ def finish
39
+ LOCK.synchronize do
40
+ @coverage = nil
41
+ end
42
+ end
43
+
44
+ def after_fork
45
+ return unless coverage = Handler.coverage
46
+ pid = Process.pid
47
+
48
+ # Any pre-existing coverage is being tracked by the parent process, so discard it.
49
+ coverage.clear
50
+
51
+ at_exit do
52
+ # Don't break forked children:
53
+ if Process.pid == pid
54
+ coverage.finish
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def _fork
61
+ pid = super
62
+
63
+ if pid.zero?
64
+ Handler.after_fork
65
+ end
66
+
67
+ return pid
68
+ end
69
+
70
+ ::Process.singleton_class.prepend(self)
71
+ end
72
+
73
+ private_constant :Handler
74
+ end
75
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2022, by Samuel Williams.
4
+ # Copyright, 2021-2023, by Samuel Williams.
5
5
 
6
6
  require_relative 'statistics'
7
7
  require_relative 'wrapper'
@@ -54,7 +54,7 @@ module Covered
54
54
  end
55
55
  end
56
56
 
57
- # 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).
57
+ # A coverage array gives, for each line, the number of line execution by the interpreter. A nil value means coverage is finishd for this line (lines like else and end).
58
58
  def call(wrapper, output = $stdout)
59
59
  output.puts '# Coverage Report'
60
60
  output.puts
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
4
+ # Copyright, 2019-2023, by Samuel Williams.
5
5
  # Copyright, 2022, by Adam Daniels.
6
6
 
7
7
  require_relative 'config'
@@ -13,7 +13,7 @@ $covered = Covered::Config.load
13
13
  module Covered
14
14
  module Minitest
15
15
  def run(*)
16
- $covered.enable
16
+ $covered.start
17
17
 
18
18
  super
19
19
  end
@@ -26,7 +26,7 @@ if $covered.record?
26
26
  end
27
27
 
28
28
  Minitest.after_run do
29
- $covered.disable
29
+ $covered.finish
30
30
  $covered.call($stderr)
31
31
  end
32
32
  end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
4
+ # Copyright, 2019-2023, by Samuel Williams.
5
+ # Copyright, 2023, by Stephen Ierodiaconou.
5
6
 
6
7
  require_relative 'wrapper'
7
8
 
8
9
  require 'msgpack'
9
10
  require 'time'
10
- require 'set'
11
11
 
12
12
  module Covered
13
13
  class Persist < Wrapper
@@ -17,34 +17,16 @@ module Covered
17
17
  super(output)
18
18
 
19
19
  @path = self.expand_path(path)
20
- @touched = Set.new
21
20
  end
22
21
 
23
22
  def apply(record, ignore_mtime: false)
24
- # The file must still exist:
25
- return unless path = expand_path(record[:path])
26
-
27
- unless File.exist?(path)
28
- # Ignore this coverage, the file no longer exists.
29
- return
30
- end
31
-
32
- # If the file has been modified since... we can't use the coverage.
33
- return unless mtime = record[:mtime]
34
-
35
- unless ignore_mtime
36
- if File.mtime(path).to_f > record[:mtime]
37
- # Ignore this coverage, the file has been modified since it was recorded.
38
- return
23
+ if coverage = record[:coverage]
24
+ if path = record[:path]
25
+ path = self.expand_path(path)
26
+ coverage.path = path
39
27
  end
40
- end
41
-
42
- if source = record[:source]
43
- @output.add(source)
44
- end
45
-
46
- record[:counts].each_with_index do |count, index|
47
- @output.mark(path, index, count) if count
28
+
29
+ add(coverage)
48
30
  end
49
31
  end
50
32
 
@@ -53,9 +35,8 @@ module Covered
53
35
  # We want to use relative paths so that moving the repo won't break everything:
54
36
  pid: Process.pid,
55
37
  path: relative_path(coverage.path),
56
- mtime: File.mtime(coverage.path).to_f,
57
- counts: coverage.counts,
58
- source: coverage.source,
38
+ # relative_path: relative_path(coverage.path),
39
+ coverage: coverage,
59
40
  }
60
41
  end
61
42
 
@@ -67,21 +48,23 @@ module Covered
67
48
  file.flock(File::LOCK_SH)
68
49
 
69
50
  make_unpacker(file).each do |record|
51
+ # pp load: record
70
52
  self.apply(record, **options)
71
53
  end
72
54
  end
73
- rescue => error
55
+ rescue
74
56
  raise LoadError, "Failed to load coverage from #{@path}, maybe old format or corrupt!"
75
57
  end
76
58
 
77
59
  def save!
78
60
  # Dump all coverage:
79
- File.open(@path, "wb") do |file|
61
+ File.open(@path, "ab") do |file|
80
62
  file.flock(File::LOCK_EX)
81
63
 
82
64
  packer = make_packer(file)
83
65
 
84
- self.each do |coverage|
66
+ @output.each do |coverage|
67
+ # pp save: coverage
85
68
  packer.write(serialize(coverage))
86
69
  end
87
70
 
@@ -89,64 +72,52 @@ module Covered
89
72
  end
90
73
  end
91
74
 
92
- def mark(file, line, count)
93
- @touched << file
94
-
95
- super
96
- end
97
-
98
- def enable
75
+ def finish
99
76
  super
100
77
 
101
- load!
78
+ self.save!
102
79
  end
103
80
 
104
- def flush
105
- load!
81
+ def each(&block)
82
+ return to_enum unless block_given?
83
+
84
+ @output.clear
85
+ self.load!
106
86
 
107
87
  super
108
88
  end
109
89
 
110
- def disable
111
- super
90
+ def make_factory
91
+ factory = MessagePack::Factory.new
92
+
93
+ factory.register_type(0x00, Symbol)
94
+
95
+ factory.register_type(0x01, Time,
96
+ packer: MessagePack::Time::Packer,
97
+ unpacker: MessagePack::Time::Unpacker
98
+ )
99
+
100
+ factory.register_type(0x20, Source,
101
+ recursive: true,
102
+ packer: :serialize,
103
+ unpacker: :deserialize,
104
+ )
112
105
 
113
- # @touched.each do |path|
114
- # if @output.accept?(path)
115
- # puts "Updated #{path} coverage."
116
- # end
117
- # end
106
+ factory.register_type(0x21, Coverage,
107
+ recursive: true,
108
+ packer: :serialize,
109
+ unpacker: :deserialize,
110
+ )
118
111
 
119
- save!
112
+ return factory
120
113
  end
121
114
 
122
- # def each
123
- # super do |coverage|
124
- # if @touched.include?(coverage.path)
125
- # yield coverage
126
- # end
127
- # end
128
- # end
129
-
130
115
  def make_packer(io)
131
- packer = MessagePack::Packer.new(io)
132
- packer.register_type(0x00, Symbol, :to_msgpack_ext)
133
- packer.register_type(0x01, Time) {|object| object.to_s}
134
- packer.register_type(0x0F, Coverage::Source) do |object|
135
- object.to_a.to_msgpack
136
- end
137
-
138
- return packer
116
+ return make_factory.packer(io)
139
117
  end
140
118
 
141
119
  def make_unpacker(io)
142
- unpacker = MessagePack::Unpacker.new(io)
143
- unpacker.register_type(0x00, Symbol, :from_msgpack_ext)
144
- unpacker.register_type(0x01, Time, :parse)
145
- unpacker.register_type(0x0F) do |data|
146
- Coverage::Source.new(*MessagePack.unpack(data))
147
- end
148
-
149
- return unpacker
120
+ return make_factory.unpacker(io)
150
121
  end
151
122
  end
152
123
  end
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2018-2022, by Samuel Williams.
4
+ # Copyright, 2018-2023, by Samuel Williams.
5
5
 
6
6
  require_relative "summary"
7
7
  require_relative "files"
8
8
  require_relative "capture"
9
9
  require_relative "persist"
10
+ require_relative "forks"
10
11
 
11
12
  module Covered
12
13
  class Policy < Wrapper
@@ -14,6 +15,7 @@ module Covered
14
15
  super(Files.new)
15
16
 
16
17
  @reports = []
18
+ @capture = nil
17
19
  end
18
20
 
19
21
  attr :output
@@ -48,19 +50,17 @@ module Covered
48
50
  end
49
51
 
50
52
  def capture
51
- @capture ||= Capture.new(@output)
53
+ @capture ||= Forks.new(
54
+ Capture.new(@output)
55
+ )
52
56
  end
53
57
 
54
- def enable
55
- capture.enable
58
+ def start
59
+ capture.start
56
60
  end
57
61
 
58
- def disable
59
- capture.disable
60
- end
61
-
62
- def flush
63
- @output.flush
62
+ def finish
63
+ capture.finish
64
64
  end
65
65
 
66
66
  attr :reports
data/lib/covered/rspec.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2018-2022, by Samuel Williams.
4
+ # Copyright, 2018-2023, by Samuel Williams.
5
5
 
6
6
  require_relative 'config'
7
7
  require 'rspec/core/formatters'
@@ -12,7 +12,7 @@ module Covered
12
12
  module RSpec
13
13
  module Policy
14
14
  def load_spec_files
15
- $covered.enable
15
+ $covered.start
16
16
 
17
17
  super
18
18
  end
@@ -33,7 +33,7 @@ if $covered.record?
33
33
 
34
34
  RSpec.configure do |config|
35
35
  config.after(:suite) do
36
- $covered.disable
36
+ $covered.finish
37
37
  $covered.call(config.output_stream)
38
38
  end
39
39
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2018-2022, by Samuel Williams.
4
+ # Copyright, 2018-2023, by Samuel Williams.
5
5
 
6
6
  require_relative 'wrapper'
7
7
  require_relative 'coverage'
@@ -28,6 +28,7 @@ module Covered
28
28
 
29
29
  def << coverage
30
30
  @count += 1
31
+
31
32
  @executable_count += coverage.executable_count
32
33
  @executed_count += coverage.executed_count
33
34
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2018-2022, by Samuel Williams.
4
+ # Copyright, 2018-2023, by Samuel Williams.
5
5
 
6
6
  require_relative 'statistics'
7
7
  require_relative 'wrapper'
@@ -82,7 +82,7 @@ module Covered
82
82
  end
83
83
  end
84
84
 
85
- # 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).
85
+ # A coverage array gives, for each line, the number of line execution by the interpreter. A nil value means coverage is finishd for this line (lines like else and end).
86
86
  def call(wrapper, output = $stdout)
87
87
  terminal = self.terminal(output)
88
88
 
@@ -116,6 +116,12 @@ module Covered
116
116
  end
117
117
  end
118
118
 
119
+ class FullSummary < Summary
120
+ def initialize
121
+ super(threshold: nil)
122
+ end
123
+ end
124
+
119
125
  class BriefSummary < Summary
120
126
  def call(wrapper, output = $stdout, before: 4, after: 4)
121
127
  terminal = self.terminal(output)
data/lib/covered/sus.rb CHANGED
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022, by Samuel Williams.
4
+ # Copyright, 2022-2023, by Samuel Williams.
5
5
 
6
6
  module Covered
7
7
  module Sus
8
8
  def initialize(...)
9
9
  super
10
10
 
11
- # Defer loading the coverage configuration unless we are actually running with coverage enabled to avoid performance cost/overhead.
11
+ # Defer loading the coverage configuration unless we are actually running with coverage startd to avoid performance cost/overhead.
12
12
  if ENV['COVERAGE']
13
13
  require_relative 'config'
14
14
 
15
15
  @covered = Covered::Config.load(root: self.root)
16
16
  if @covered.record?
17
- @covered.enable
17
+ @covered.start
18
18
  end
19
19
  else
20
20
  @covered = nil
@@ -25,7 +25,7 @@ module Covered
25
25
  super(assertions)
26
26
 
27
27
  if @covered&.record?
28
- @covered.disable
28
+ @covered.finish
29
29
  @covered.call(self.output.io)
30
30
  end
31
31
  end
@@ -4,5 +4,5 @@
4
4
  # Copyright, 2018-2022, by Samuel Williams.
5
5
 
6
6
  module Covered
7
- VERSION = "0.19.1"
7
+ VERSION = "0.20.0"
8
8
  end
@@ -1,17 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2018-2022, by Samuel Williams.
4
+ # Copyright, 2018-2023, by Samuel Williams.
5
5
 
6
6
  module Covered
7
7
  class Base
8
- def enable
8
+ # Start tracking coverage.
9
+ def start
9
10
  end
10
11
 
11
- def disable
12
+ # Discard any coverage data and restart tracking.
13
+ def clear
12
14
  end
13
15
 
14
- def flush
16
+ # Stop tracking coverage.
17
+ def finish
15
18
  end
16
19
 
17
20
  def accept?(path)
@@ -21,9 +24,12 @@ module Covered
21
24
  def mark(path, lineno, value)
22
25
  end
23
26
 
24
- def add(source)
27
+ def add(coverage)
25
28
  end
26
29
 
30
+ # Enumerate the coverage data.
31
+ # @yields {|coverage| ...}
32
+ # @parameter coverage [Coverage] The coverage data, including the source file and execution counts.
27
33
  def each
28
34
  end
29
35
 
@@ -43,16 +49,16 @@ module Covered
43
49
 
44
50
  attr :output
45
51
 
46
- def enable
47
- @output.enable
52
+ def start
53
+ @output.start
48
54
  end
49
55
 
50
- def disable
51
- @output.disable
56
+ def clear
57
+ @output.clear
52
58
  end
53
59
 
54
- def flush
55
- @output.flush
60
+ def finish
61
+ @output.finish
56
62
  end
57
63
 
58
64
  def accept?(path)
@@ -63,8 +69,8 @@ module Covered
63
69
  @output.mark(path, lineno, value)
64
70
  end
65
71
 
66
- def add(source)
67
- @output.add(source)
72
+ def add(coverage)
73
+ @output.add(coverage)
68
74
  end
69
75
 
70
76
  # @yield [Coverage] the path to the file, and the execution counts.
@@ -81,7 +87,7 @@ module Covered
81
87
  end
82
88
 
83
89
  def to_h
84
- @output.to_enum(:each).collect{|coverage| [coverage.path, coverage]}.to_h
90
+ to_enum(:each).collect{|coverage| [coverage.path, coverage]}.to_h
85
91
  end
86
92
  end
87
93
 
data/license.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # MIT License
2
2
 
3
- Copyright, 2018-2022, by Samuel Williams.
3
+ Copyright, 2018-2023, by Samuel Williams.
4
4
  Copyright, 2018, by Shannon Skipper.
5
5
  Copyright, 2018, by chocolateboy.
6
6
  Copyright, 2019, by Cyril Roelandt.
7
7
  Copyright, 2022, by Adam Daniels.
8
+ Copyright, 2022, by Felix Yan.
9
+ Copyright, 2023, by Stephen Ierodiaconou.
8
10
 
9
11
  Permission is hereby granted, free of charge, to any person obtaining a copy
10
12
  of this software and associated documentation files (the "Software"), to deal
data/readme.md CHANGED
@@ -59,7 +59,7 @@ When running `rspec`, you can specify the kind of coverage analysis you would li
59
59
 
60
60
  COVERAGE=Summary rspec
61
61
 
62
- If no `COVERAGE` is specified, coverage tracking will be disabled.
62
+ If no `COVERAGE` is specified, coverage tracking will be finishd.
63
63
 
64
64
  ### Partial Summary
65
65
 
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,13 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: covered
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.1
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  - Adam Daniels
9
9
  - Cyril Roelandt
10
+ - Felix Yan
10
11
  - Shannon Skipper
12
+ - Stephen Ierodiaconou
11
13
  - chocolateboy
12
14
  autorequire:
13
15
  bindir: bin
@@ -41,7 +43,7 @@ cert_chain:
41
43
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
42
44
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
43
45
  -----END CERTIFICATE-----
44
- date: 2022-12-30 00:00:00.000000000 Z
46
+ date: 2023-02-10 00:00:00.000000000 Z
45
47
  dependencies:
46
48
  - !ruby/object:Gem::Dependency
47
49
  name: console
@@ -136,10 +138,12 @@ files:
136
138
  - bake/covered/debug.rb
137
139
  - bake/covered/validate.rb
138
140
  - lib/covered.rb
141
+ - lib/covered/autostart.rb
139
142
  - lib/covered/capture.rb
140
143
  - lib/covered/config.rb
141
144
  - lib/covered/coverage.rb
142
145
  - lib/covered/files.rb
146
+ - lib/covered/forks.rb
143
147
  - lib/covered/markdown_summary.rb
144
148
  - lib/covered/minitest.rb
145
149
  - lib/covered/persist.rb
@@ -172,7 +176,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
176
  - !ruby/object:Gem::Version
173
177
  version: '0'
174
178
  requirements: []
175
- rubygems_version: 3.4.1
179
+ rubygems_version: 3.4.6
176
180
  signing_key:
177
181
  specification_version: 4
178
182
  summary: A modern approach to code coverage.
metadata.gz.sig CHANGED
Binary file