covered 0.19.1 → 0.20.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: 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