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 +4 -4
- checksums.yaml.gz.sig +5 -4
- data/bake/covered/debug.rb +3 -3
- data/bake/covered/validate.rb +1 -3
- data/lib/covered/autostart.rb +32 -0
- data/lib/covered/capture.rb +15 -8
- data/lib/covered/config.rb +57 -13
- data/lib/covered/coverage.rb +100 -50
- data/lib/covered/files.rb +68 -11
- data/lib/covered/forks.rb +75 -0
- data/lib/covered/markdown_summary.rb +2 -2
- data/lib/covered/minitest.rb +3 -3
- data/lib/covered/persist.rb +45 -74
- data/lib/covered/policy.rb +10 -10
- data/lib/covered/rspec.rb +3 -3
- data/lib/covered/statistics.rb +2 -1
- data/lib/covered/summary.rb +8 -2
- data/lib/covered/sus.rb +4 -4
- data/lib/covered/version.rb +1 -1
- data/lib/covered/wrapper.rb +20 -14
- data/license.md +3 -1
- data/readme.md +1 -1
- data.tar.gz.sig +0 -0
- metadata +7 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e865810f0e3c42f28a7bf1569449240d46f6304a0803b2d34006c3d8ecb4b17a
|
4
|
+
data.tar.gz: b0da723e52175e6766fae5c9d4e9c61b4619fd19867ee6ce0970008c61bc7330
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4858522284e9e50cff92558194a7caf5a08604b45602bd83b6137d71e0a6537a3569bae82270efde1f3ee6fefa58015fbcac036c8eba36a72f52f3f41cfc1078
|
7
|
+
data.tar.gz: ee3779802478b0d9097e8ec00c93a2cc25c6a01e72963c762f0e463759b5284b43820d3213b0927e9402db77c31a639f04ac4fc3beceab5b9f199a3b84028c30
|
checksums.yaml.gz.sig
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
��j�}�Xo7�)��iD���:J��
|
2
|
+
�!kG^]��*��;�8e~����>��|�~�\(�mZ��Ed��
|
3
|
+
B�
|
4
|
+
a��ƛ���N�؇�kS1
|
5
|
+
����ʃ2�
|
data/bake/covered/debug.rb
CHANGED
@@ -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.
|
26
|
+
capture.start
|
27
27
|
paths.each do |path|
|
28
28
|
load path
|
29
29
|
end
|
30
|
-
capture.
|
30
|
+
capture.finish
|
31
31
|
|
32
32
|
files.paths = files.paths.slice(*paths)
|
33
33
|
end
|
data/bake/covered/validate.rb
CHANGED
@@ -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!
|
data/lib/covered/capture.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2018-
|
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
|
12
|
+
def start
|
13
13
|
super
|
14
14
|
|
15
15
|
::Coverage.start(lines: true, eval: true)
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
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
|
-
|
40
|
+
start
|
34
41
|
|
35
42
|
eval(source.code!, binding, source.path, source.line_offset)
|
36
43
|
ensure
|
37
|
-
|
44
|
+
finish
|
38
45
|
end
|
39
46
|
end
|
40
47
|
end
|
data/lib/covered/config.rb
CHANGED
@@ -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.
|
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:
|
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
|
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
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
65
|
-
|
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(
|
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
|
data/lib/covered/coverage.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2018-
|
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
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
48
|
-
|
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
|
52
|
-
self.
|
57
|
+
def code?
|
58
|
+
!!self.code
|
53
59
|
end
|
54
60
|
|
55
|
-
def
|
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
|
-
@
|
88
|
+
@annotations = annotations
|
59
89
|
|
60
|
-
@
|
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=#{
|
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-
|
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
|
-
|
31
|
-
|
32
|
-
coverage.mark(lineno, value)
|
33
|
-
|
34
|
-
return coverage
|
79
|
+
self[path].mark(lineno, value)
|
35
80
|
end
|
36
81
|
|
37
|
-
def
|
38
|
-
|
82
|
+
def annotate(path, lineno, value)
|
83
|
+
self[path].annotate(lineno, value)
|
39
84
|
end
|
40
85
|
|
41
|
-
def
|
42
|
-
|
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-
|
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
|
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
|
data/lib/covered/minitest.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
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.
|
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.
|
29
|
+
$covered.finish
|
30
30
|
$covered.call($stderr)
|
31
31
|
end
|
32
32
|
end
|
data/lib/covered/persist.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
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
|
-
|
57
|
-
|
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
|
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, "
|
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
|
-
|
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
|
93
|
-
@touched << file
|
94
|
-
|
95
|
-
super
|
96
|
-
end
|
97
|
-
|
98
|
-
def enable
|
75
|
+
def finish
|
99
76
|
super
|
100
77
|
|
101
|
-
|
78
|
+
self.save!
|
102
79
|
end
|
103
80
|
|
104
|
-
def
|
105
|
-
|
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
|
111
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
106
|
+
factory.register_type(0x21, Coverage,
|
107
|
+
recursive: true,
|
108
|
+
packer: :serialize,
|
109
|
+
unpacker: :deserialize,
|
110
|
+
)
|
118
111
|
|
119
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/covered/policy.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2018-
|
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 ||=
|
53
|
+
@capture ||= Forks.new(
|
54
|
+
Capture.new(@output)
|
55
|
+
)
|
52
56
|
end
|
53
57
|
|
54
|
-
def
|
55
|
-
capture.
|
58
|
+
def start
|
59
|
+
capture.start
|
56
60
|
end
|
57
61
|
|
58
|
-
def
|
59
|
-
capture.
|
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-
|
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.
|
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.
|
36
|
+
$covered.finish
|
37
37
|
$covered.call(config.output_stream)
|
38
38
|
end
|
39
39
|
end
|
data/lib/covered/statistics.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2018-
|
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
|
data/lib/covered/summary.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2018-
|
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
|
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
|
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.
|
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.
|
28
|
+
@covered.finish
|
29
29
|
@covered.call(self.output.io)
|
30
30
|
end
|
31
31
|
end
|
data/lib/covered/version.rb
CHANGED
data/lib/covered/wrapper.rb
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2018-
|
4
|
+
# Copyright, 2018-2023, by Samuel Williams.
|
5
5
|
|
6
6
|
module Covered
|
7
7
|
class Base
|
8
|
-
|
8
|
+
# Start tracking coverage.
|
9
|
+
def start
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
+
# Discard any coverage data and restart tracking.
|
13
|
+
def clear
|
12
14
|
end
|
13
15
|
|
14
|
-
|
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(
|
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
|
47
|
-
@output.
|
52
|
+
def start
|
53
|
+
@output.start
|
48
54
|
end
|
49
55
|
|
50
|
-
def
|
51
|
-
@output.
|
56
|
+
def clear
|
57
|
+
@output.clear
|
52
58
|
end
|
53
59
|
|
54
|
-
def
|
55
|
-
@output.
|
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(
|
67
|
-
@output.add(
|
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
|
-
|
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-
|
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
|
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.
|
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:
|
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.
|
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
|