covered 0.22.1 → 0.24.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/bake/covered/policy.rb +48 -0
- data/bake/covered/validate.rb +3 -16
- data/lib/covered/coverage.rb +70 -74
- data/lib/covered/files.rb +7 -61
- data/lib/covered/minitest.rb +1 -3
- data/lib/covered/persist.rb +6 -1
- data/lib/covered/source.rb +66 -0
- data/lib/covered/statistics.rb +95 -18
- data/lib/covered/version.rb +1 -1
- data/readme.md +24 -68
- data.tar.gz.sig +0 -0
- metadata +4 -2
- 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: 6101920cc470784c2969e8d78252c81678104f6cd882038a306e633f3ec34267
|
4
|
+
data.tar.gz: 213bc88224aba333120b869360a663f02355ace3f1dcb4106c5fcdbebd61cd7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aabfd2999173af2de001ce8a4718483c9801497bb064b27c6892d8c6cb80dc8b2207db0de41a0eb8e4116deac5a2eaf8f68cc2febe8acbea84c4795b666992c1
|
7
|
+
data.tar.gz: 302d49b96fcad976a40bc86422976e1526d0b410ed72112190787a8c1ea75309015fe0c6c2fa8359b8d3cf60f9b93f63b021dab063b8e3c1a08d24cb9b79fb59
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2023, by Samuel Williams.
|
5
|
+
|
6
|
+
def initialize(context)
|
7
|
+
super
|
8
|
+
|
9
|
+
require_relative '../../lib/covered/config'
|
10
|
+
end
|
11
|
+
|
12
|
+
# Load the current coverage policy.
|
13
|
+
def current(paths: nil)
|
14
|
+
policy = Covered::Policy.new
|
15
|
+
|
16
|
+
# Load the default path if no paths are specified:
|
17
|
+
paths ||= Dir.glob(Covered::Persist::DEFAULT_PATH, base: context.root)
|
18
|
+
|
19
|
+
# If no paths are specified, raise an error:
|
20
|
+
if paths.empty?
|
21
|
+
raise ArgumentError, "No coverage paths specified!"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Load all coverage information:
|
25
|
+
paths.each do |path|
|
26
|
+
# It would be nice to have a better algorithm here than just ignoring mtime - perhaps using checksums?
|
27
|
+
Covered::Persist.new(policy.output, path).load!(ignore_mtime: true)
|
28
|
+
end
|
29
|
+
|
30
|
+
return policy
|
31
|
+
end
|
32
|
+
|
33
|
+
# Validate the coverage of multiple test runs.
|
34
|
+
# @parameter paths [Array(String)] The coverage database paths.
|
35
|
+
# @parameter minimum [Float] The minimum required coverage in order to pass.
|
36
|
+
# @parameter input [Covered::Policy] The input policy to validate.
|
37
|
+
def statistics(paths: nil, minimum: 1.0, input:)
|
38
|
+
policy ||= context.lookup("covered:policy:current").call(paths: paths)
|
39
|
+
|
40
|
+
# Calculate statistics:
|
41
|
+
statistics = Covered::Statistics.new
|
42
|
+
|
43
|
+
policy.each do |coverage|
|
44
|
+
statistics << coverage
|
45
|
+
end
|
46
|
+
|
47
|
+
return statistics
|
48
|
+
end
|
data/bake/covered/validate.rb
CHANGED
@@ -12,22 +12,9 @@ end
|
|
12
12
|
# Validate the coverage of multiple test runs.
|
13
13
|
# @parameter paths [Array(String)] The coverage database paths.
|
14
14
|
# @parameter minimum [Float] The minimum required coverage in order to pass.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
# Load the default path if no paths are specified:
|
19
|
-
paths ||= Dir.glob(Covered::Persist::DEFAULT_PATH, base: context.root)
|
20
|
-
|
21
|
-
# If no paths are specified, raise an error:
|
22
|
-
if paths.empty?
|
23
|
-
raise ArgumentError, "No coverage paths specified!"
|
24
|
-
end
|
25
|
-
|
26
|
-
# Load all coverage information:
|
27
|
-
paths.each do |path|
|
28
|
-
# It would be nice to have a better algorithm here than just ignoring mtime - perhaps using checksums?
|
29
|
-
Covered::Persist.new(policy.output, path).load!(ignore_mtime: true)
|
30
|
-
end
|
15
|
+
# @parameter input [Covered::Policy] The input policy to validate.
|
16
|
+
def validate(paths: nil, minimum: 1.0, input:)
|
17
|
+
policy ||= context.lookup("covered:policy:current").call(paths: paths)
|
31
18
|
|
32
19
|
# Calculate statistics:
|
33
20
|
statistics = Covered::Statistics.new
|
data/lib/covered/coverage.rb
CHANGED
@@ -3,10 +3,12 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2018-2023, by Samuel Williams.
|
5
5
|
|
6
|
+
require_relative 'source'
|
7
|
+
|
6
8
|
module Covered
|
7
9
|
module Ratio
|
8
10
|
def ratio
|
9
|
-
return 0 if executable_count.zero?
|
11
|
+
return 1.0 if executable_count.zero?
|
10
12
|
|
11
13
|
Rational(executed_count, executable_count)
|
12
14
|
end
|
@@ -20,78 +22,72 @@ module Covered
|
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
|
-
class
|
24
|
-
|
25
|
-
self.new(path, code: code, line_offset: line_offset)
|
26
|
-
end
|
25
|
+
class Coverage
|
26
|
+
include Ratio
|
27
27
|
|
28
|
-
def
|
29
|
-
|
30
|
-
@code = code
|
31
|
-
@line_offset = line_offset
|
32
|
-
@modified_time = modified_time
|
28
|
+
def self.for(path, **options)
|
29
|
+
self.new(Source.for(path, **options))
|
33
30
|
end
|
34
31
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
def to_s
|
41
|
-
"\#<#{self.class} path=#{path}>"
|
32
|
+
def initialize(source, counts = [], annotations = {})
|
33
|
+
@source = source
|
34
|
+
@counts = counts
|
35
|
+
@annotations = annotations
|
42
36
|
end
|
43
37
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
else
|
48
|
-
File.read(self.path)
|
49
|
-
end
|
50
|
-
end
|
38
|
+
attr_accessor :source
|
39
|
+
attr :counts
|
40
|
+
attr :annotations
|
51
41
|
|
52
|
-
|
53
|
-
|
54
|
-
self.code || self.read
|
42
|
+
def total
|
43
|
+
counts.sum{|count| count || 0}
|
55
44
|
end
|
56
45
|
|
57
|
-
|
58
|
-
|
46
|
+
# Create an empty coverage with the same source.
|
47
|
+
def empty
|
48
|
+
self.class.new(@source, [nil] * @counts.size)
|
59
49
|
end
|
60
50
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
packer.write(self.line_offset)
|
65
|
-
packer.write(self.modified_time)
|
51
|
+
def annotate(line_number, annotation)
|
52
|
+
@annotations[line_number] ||= []
|
53
|
+
@annotations[line_number] << annotation
|
66
54
|
end
|
67
55
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
56
|
+
def mark(line_number, value = 1)
|
57
|
+
# As currently implemented, @counts is base-zero rather than base-one.
|
58
|
+
# Line numbers generally start at line 1, so the first line, line 1, is at index 1. This means that index[0] is usually nil.
|
59
|
+
Array(value).each_with_index do |value, index|
|
60
|
+
offset = line_number + index
|
61
|
+
if @counts[offset]
|
62
|
+
@counts[offset] += value
|
63
|
+
else
|
64
|
+
@counts[offset] = value
|
65
|
+
end
|
66
|
+
end
|
75
67
|
end
|
76
|
-
end
|
77
|
-
|
78
|
-
class Coverage
|
79
|
-
include Ratio
|
80
68
|
|
81
|
-
def
|
82
|
-
|
69
|
+
def merge!(other)
|
70
|
+
other.counts.each_with_index do |count, index|
|
71
|
+
if count
|
72
|
+
@counts[index] ||= 0
|
73
|
+
@counts[index] += count
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
@annotations.merge!(other.annotations) do |line_number, a, b|
|
78
|
+
Array(a) + Array(b)
|
79
|
+
end
|
83
80
|
end
|
84
81
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
82
|
+
# Construct a new coverage object for the given line numbers. Only the given line numbers will be considered for the purposes of computing coverage.
|
83
|
+
# @parameter line_numbers [Array(Integer)] The line numbers to include in the new coverage object.
|
84
|
+
def for_lines(line_numbers)
|
85
|
+
counts = [nil] * @counts.size
|
86
|
+
line_numbers.each do |line_number|
|
87
|
+
counts[line_number] = @counts[line_number]
|
88
|
+
end
|
91
89
|
|
92
|
-
|
93
|
-
@executable_lines = nil
|
94
|
-
@executed_lines = nil
|
90
|
+
self.class.new(@source, counts, @annotations)
|
95
91
|
end
|
96
92
|
|
97
93
|
def path
|
@@ -104,8 +100,8 @@ module Covered
|
|
104
100
|
|
105
101
|
def fresh?
|
106
102
|
if @source.modified_time.nil?
|
107
|
-
# We don't know when the file was last modified, so we assume it is
|
108
|
-
return
|
103
|
+
# We don't know when the file was last modified, so we assume it is stale:
|
104
|
+
return false
|
109
105
|
end
|
110
106
|
|
111
107
|
unless File.exist?(@source.path)
|
@@ -121,13 +117,6 @@ module Covered
|
|
121
117
|
return false
|
122
118
|
end
|
123
119
|
|
124
|
-
attr_accessor :source
|
125
|
-
|
126
|
-
attr :counts
|
127
|
-
attr :total
|
128
|
-
|
129
|
-
attr :annotations
|
130
|
-
|
131
120
|
def read(&block)
|
132
121
|
@source.read(&block)
|
133
122
|
end
|
@@ -146,15 +135,15 @@ module Covered
|
|
146
135
|
end
|
147
136
|
|
148
137
|
def zero?
|
149
|
-
|
138
|
+
total.zero?
|
150
139
|
end
|
151
140
|
|
152
|
-
def []
|
153
|
-
@counts[
|
141
|
+
def [] line_number
|
142
|
+
@counts[line_number]
|
154
143
|
end
|
155
144
|
|
156
145
|
def executable_lines
|
157
|
-
@
|
146
|
+
@counts.compact
|
158
147
|
end
|
159
148
|
|
160
149
|
def executable_count
|
@@ -162,7 +151,7 @@ module Covered
|
|
162
151
|
end
|
163
152
|
|
164
153
|
def executed_lines
|
165
|
-
|
154
|
+
executable_lines.reject(&:zero?)
|
166
155
|
end
|
167
156
|
|
168
157
|
def executed_count
|
@@ -178,23 +167,30 @@ module Covered
|
|
178
167
|
end
|
179
168
|
|
180
169
|
def to_s
|
181
|
-
"\#<#{self.class} path=#{self.path} #{self.
|
170
|
+
"\#<#{self.class} path=#{self.path} #{self.percentage.to_f.round(2)}% covered>"
|
171
|
+
end
|
172
|
+
|
173
|
+
def as_json
|
174
|
+
{
|
175
|
+
counts: counts,
|
176
|
+
executable_count: executable_count,
|
177
|
+
executed_count: executed_count,
|
178
|
+
percentage: percentage.to_f.round(2),
|
179
|
+
}
|
182
180
|
end
|
183
181
|
|
184
182
|
def serialize(packer)
|
185
183
|
packer.write(@source)
|
186
184
|
packer.write(@counts)
|
187
185
|
packer.write(@annotations)
|
188
|
-
packer.write(@total)
|
189
186
|
end
|
190
187
|
|
191
188
|
def self.deserialize(unpacker)
|
192
189
|
source = unpacker.read
|
193
190
|
counts = unpacker.read
|
194
191
|
annotations = unpacker.read
|
195
|
-
total = unpacker.read
|
196
192
|
|
197
|
-
self.new(source, counts, annotations
|
193
|
+
self.new(source, counts, annotations)
|
198
194
|
end
|
199
195
|
end
|
200
196
|
end
|
data/lib/covered/files.rb
CHANGED
@@ -10,60 +10,6 @@ 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
|
-
# As currently implemented, @counts is base-zero rather than base-one.
|
38
|
-
# Line numbers generally start at line 1, so the first line, line 1, is at index 1. This means that index[0] is usually nil.
|
39
|
-
Array(value).each_with_index do |value, index|
|
40
|
-
offset = lineno + index
|
41
|
-
if @counts[offset]
|
42
|
-
@counts[offset] += value
|
43
|
-
else
|
44
|
-
@counts[offset] = value
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def merge!(coverage)
|
50
|
-
coverage.counts.each_with_index do |count, index|
|
51
|
-
if count
|
52
|
-
@counts[index] ||= 0
|
53
|
-
@counts[index] += count
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
@annotations.merge!(coverage.annotations) do |lineno, a, b|
|
58
|
-
Array(a) + Array(b)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def coverage
|
63
|
-
Coverage.new(@source, @counts, @annotations)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
13
|
def initialize(*)
|
68
14
|
super
|
69
15
|
|
@@ -73,19 +19,19 @@ module Covered
|
|
73
19
|
attr_accessor :paths
|
74
20
|
|
75
21
|
def [](path)
|
76
|
-
@paths[path] ||=
|
22
|
+
@paths[path] ||= Coverage.for(path)
|
77
23
|
end
|
78
24
|
|
79
25
|
def empty?
|
80
26
|
@paths.empty?
|
81
27
|
end
|
82
28
|
|
83
|
-
def mark(path,
|
84
|
-
self[path].mark(
|
29
|
+
def mark(path, line_number, value)
|
30
|
+
self[path].mark(line_number, value)
|
85
31
|
end
|
86
32
|
|
87
|
-
def annotate(path,
|
88
|
-
self[path].annotate(
|
33
|
+
def annotate(path, line_number, value)
|
34
|
+
self[path].annotate(line_number, value)
|
89
35
|
end
|
90
36
|
|
91
37
|
def add(coverage)
|
@@ -95,8 +41,8 @@ module Covered
|
|
95
41
|
def each
|
96
42
|
return to_enum unless block_given?
|
97
43
|
|
98
|
-
@paths.each_value do |
|
99
|
-
yield
|
44
|
+
@paths.each_value do |coverage|
|
45
|
+
yield coverage
|
100
46
|
end
|
101
47
|
end
|
102
48
|
|
data/lib/covered/minitest.rb
CHANGED
data/lib/covered/persist.rb
CHANGED
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2018-2023, by Samuel Williams.
|
5
|
+
|
6
|
+
module Covered
|
7
|
+
class Source
|
8
|
+
def self.for(path, **options)
|
9
|
+
if File.exist?(path)
|
10
|
+
options[:code] ||= File.read(path)
|
11
|
+
options[:modified_time] ||= File.mtime(path)
|
12
|
+
end
|
13
|
+
|
14
|
+
self.new(path, **options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(path, code: nil, line_offset: 1, modified_time: nil)
|
18
|
+
@path = path
|
19
|
+
@code = code
|
20
|
+
@line_offset = line_offset
|
21
|
+
@modified_time = modified_time
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_accessor :path
|
25
|
+
attr :code
|
26
|
+
attr :line_offset
|
27
|
+
attr :modified_time
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
"\#<#{self.class} path=#{path}>"
|
31
|
+
end
|
32
|
+
|
33
|
+
def read(&block)
|
34
|
+
if block_given?
|
35
|
+
File.open(self.path, "r", &block)
|
36
|
+
else
|
37
|
+
File.read(self.path)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# 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.
|
42
|
+
def code!
|
43
|
+
self.code || self.read
|
44
|
+
end
|
45
|
+
|
46
|
+
def code?
|
47
|
+
!!self.code
|
48
|
+
end
|
49
|
+
|
50
|
+
def serialize(packer)
|
51
|
+
packer.write(self.path)
|
52
|
+
packer.write(self.code)
|
53
|
+
packer.write(self.line_offset)
|
54
|
+
packer.write(self.modified_time)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.deserialize(unpacker)
|
58
|
+
path = unpacker.read
|
59
|
+
code = unpacker.read
|
60
|
+
line_offset = unpacker.read
|
61
|
+
modified_time = unpacker.read
|
62
|
+
|
63
|
+
self.new(path, code: code, line_offset: line_offset, modified_time: modified_time)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/covered/statistics.rb
CHANGED
@@ -10,39 +10,116 @@ module Covered
|
|
10
10
|
class CoverageError < StandardError
|
11
11
|
end
|
12
12
|
|
13
|
-
class Statistics
|
13
|
+
class Statistics
|
14
|
+
include Ratio
|
15
|
+
|
16
|
+
def self.for(coverage)
|
17
|
+
self.new.tap do |statistics|
|
18
|
+
statistics << coverage
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Aggregate
|
23
|
+
include Ratio
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@count = 0
|
27
|
+
@executable_count = 0
|
28
|
+
@executed_count = 0
|
29
|
+
end
|
30
|
+
|
31
|
+
# Total number of files added.
|
32
|
+
attr :count
|
33
|
+
|
34
|
+
# The number of lines which could have been executed.
|
35
|
+
attr :executable_count
|
36
|
+
|
37
|
+
# The number of lines that were executed.
|
38
|
+
attr :executed_count
|
39
|
+
|
40
|
+
def as_json
|
41
|
+
{
|
42
|
+
count: count,
|
43
|
+
executable_count: executable_count,
|
44
|
+
executed_count: executed_count,
|
45
|
+
percentage: percentage.to_f.round(2),
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_json(options)
|
50
|
+
as_json.to_json(options)
|
51
|
+
end
|
52
|
+
|
53
|
+
def << coverage
|
54
|
+
@count += 1
|
55
|
+
|
56
|
+
@executable_count += coverage.executable_count
|
57
|
+
@executed_count += coverage.executed_count
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
14
61
|
def initialize
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@executed_count = 0
|
62
|
+
@total = Aggregate.new
|
63
|
+
@paths = Hash.new
|
18
64
|
end
|
19
65
|
|
20
|
-
|
21
|
-
|
66
|
+
def count
|
67
|
+
@paths.size
|
68
|
+
end
|
22
69
|
|
23
|
-
|
24
|
-
|
70
|
+
def executable_count
|
71
|
+
@total.executable_count
|
72
|
+
end
|
25
73
|
|
26
|
-
|
27
|
-
|
74
|
+
def executed_count
|
75
|
+
@total.executed_count
|
76
|
+
end
|
28
77
|
|
29
78
|
def << coverage
|
30
|
-
@
|
31
|
-
|
32
|
-
@executable_count += coverage.executable_count
|
33
|
-
@executed_count += coverage.executed_count
|
79
|
+
@total << coverage
|
80
|
+
(@paths[coverage.path] ||= coverage.empty).merge!(coverage)
|
34
81
|
end
|
35
82
|
|
36
|
-
|
83
|
+
attr :total
|
84
|
+
|
85
|
+
def [](path)
|
86
|
+
@paths[path]
|
87
|
+
end
|
88
|
+
|
89
|
+
def as_json
|
90
|
+
{
|
91
|
+
total: total.as_json,
|
92
|
+
paths: @paths.map{|path, coverage| [path, coverage.as_json]}.to_h,
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_json(options)
|
97
|
+
as_json.to_json(options)
|
98
|
+
end
|
99
|
+
|
100
|
+
COMPLETE = [
|
101
|
+
"Enter the code dojo: 100% coverage attained, bugs defeated with one swift strike.",
|
102
|
+
"Nirvana reached: 100% code coverage, where bugs meditate and vanish like a passing cloud.",
|
103
|
+
"With 100% coverage, your code has unlocked the path to enlightenment – bugs have no place to hide.",
|
104
|
+
"In the realm of code serenity, 100% coverage is your ticket to coding enlightenment.",
|
105
|
+
"100% coverage, where code and bugs coexist in perfect harmony, like Yin and Yang.",
|
106
|
+
"Achieving the Zen of code coverage, your code is a peaceful garden where bugs find no shelter.",
|
107
|
+
"Congratulations on coding enlightenment! 100% coverage means your code is one with the universe.",
|
108
|
+
"With 100% coverage, your code is a tranquil pond where bugs cause no ripples.",
|
109
|
+
"At the peak of code mastery: 100% coverage, where bugs bow down before the wisdom of your code.",
|
110
|
+
"100% code coverage: Zen achieved! Bugs in harmony, code at peace.",
|
111
|
+
]
|
37
112
|
|
38
113
|
def print(output)
|
39
|
-
output.puts "
|
114
|
+
output.puts "#{count} files checked; #{@total.executed_count}/#{@total.executable_count} lines executed; #{@total.percentage.to_f.round(2)}% covered."
|
40
115
|
|
41
|
-
|
116
|
+
if self.complete?
|
117
|
+
output.puts "🧘 #{COMPLETE.sample}"
|
118
|
+
end
|
42
119
|
end
|
43
120
|
|
44
121
|
def validate!(minimum = 1.0)
|
45
|
-
if
|
122
|
+
if total.ratio < minimum
|
46
123
|
raise CoverageError, "Coverage of #{self.percentage.to_f.round(2)}% is less than required minimum of #{(minimum * 100.0).round(2)}%!"
|
47
124
|
end
|
48
125
|
end
|
data/lib/covered/version.rb
CHANGED
data/readme.md
CHANGED
@@ -1,86 +1,40 @@
|
|
1
1
|
# Covered
|
2
2
|
|
3
|
-
|
3
|
+
![Screenshot](media/example.png)
|
4
4
|
|
5
|
-
Covered uses modern Ruby features to generate comprehensive coverage, including support for templates which are compiled
|
5
|
+
Covered uses modern Ruby features to generate comprehensive coverage, including support for templates which are compiled
|
6
|
+
into Ruby.
|
6
7
|
|
7
|
-
- Incremental coverage - if you run your full test suite, and the run a subset, it will still report the correct
|
8
|
-
|
8
|
+
- Incremental coverage - if you run your full test suite, and the run a subset, it will still report the correct
|
9
|
+
coverage - so you can incrementally work on improving coverage.
|
10
|
+
- Integration with Sus, Git, RSpec and Minitest- no need to configure anything - out of the box support for these
|
11
|
+
platforms.
|
9
12
|
- Supports coverage of views - templates compiled to Ruby code can be tracked for coverage reporting.
|
10
13
|
|
11
|
-
![
|
14
|
+
[![Development
|
15
|
+
Status](https://github.com/ioquatix/covered/workflows/Test/badge.svg)](https://github.com/ioquatix/covered/actions?workflow=Test)
|
12
16
|
|
13
17
|
## Motivation
|
14
18
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
## Installation
|
20
|
-
|
21
|
-
Add this line to your application's `Gemfile`:
|
22
|
-
|
23
|
-
``` ruby
|
24
|
-
gem 'covered'
|
25
|
-
```
|
26
|
-
|
27
|
-
### RSpec Integration
|
28
|
-
|
29
|
-
In your `spec/spec_helper.rb` add the following before loading any other code:
|
30
|
-
|
31
|
-
``` ruby
|
32
|
-
require 'covered/rspec'
|
33
|
-
```
|
34
|
-
|
35
|
-
Ensure that you have a `.rspec` file with `--require spec_helper`:
|
36
|
-
|
37
|
-
--require spec_helper
|
38
|
-
--format documentation
|
39
|
-
--warnings
|
40
|
-
|
41
|
-
### Minitest Integration
|
19
|
+
Originally, Ruby coverage tools were unable to handle `eval`ed code. This is because the `coverage` module built into
|
20
|
+
Ruby doesn't expose the necessary hooks to capture it. Using the [parser](https://github.com/whitequark/parser) gem and
|
21
|
+
trace points allows us to do our own source code analysis to compute executable lines, thus making it possible to
|
22
|
+
compute coverage for "templates".
|
42
23
|
|
43
|
-
|
44
|
-
|
45
|
-
``` ruby
|
46
|
-
require 'covered/minitest'
|
47
|
-
require 'minitest/autorun'
|
48
|
-
```
|
49
|
-
|
50
|
-
In your test files, e.g. `test/dummy_test.rb` add the following at the top:
|
51
|
-
|
52
|
-
``` ruby
|
53
|
-
require_relative 'test_helper'
|
54
|
-
```
|
24
|
+
After this concept prooved useful, [it was integrated directly into Ruby](https://bugs.ruby-lang.org/issues/19008).
|
55
25
|
|
56
26
|
## Usage
|
57
27
|
|
58
|
-
|
59
|
-
|
60
|
-
COVERAGE=Summary rspec
|
61
|
-
|
62
|
-
If no `COVERAGE` is specified, coverage tracking will be finishd.
|
63
|
-
|
64
|
-
### Template Coverage
|
65
|
-
|
66
|
-
Covered supports coverage of templates which are compiled into Ruby code. This is only supported on Ruby 3.2+ due to enhancements in the coverage interface.
|
67
|
-
|
68
|
-
### Partial Summary
|
69
|
-
|
70
|
-
COVERAGE=PartialSummary rspec
|
71
|
-
|
72
|
-
This report only shows snippets of source code with incomplete coverage.
|
73
|
-
|
74
|
-
### Brief Summary
|
75
|
-
|
76
|
-
COVERAGE=BriefSummary rspec
|
28
|
+
Please see the [project documentation](https://github.com/ioquatix/covered) for more details.
|
77
29
|
|
78
|
-
|
30
|
+
- [Getting Started](https://github.com/ioquatix/coveredguides/getting-started/index) - This guide explains how to get
|
31
|
+
started with `covered` and integrate it with your test suite.
|
79
32
|
|
80
33
|
## See Also
|
81
34
|
|
82
|
-
- [
|
83
|
-
|
35
|
+
- [simplecov](https://github.com/colszowka/simplecov) – one of the original coverage implementations for Ruby, uses
|
36
|
+
the built-in `coverage` library.
|
37
|
+
- [sus](https://github.com/ioquatix/sus) - a test framework which uses `covered` to generate coverage reports.
|
84
38
|
|
85
39
|
## Contributing
|
86
40
|
|
@@ -94,8 +48,10 @@ We welcome contributions to this project.
|
|
94
48
|
|
95
49
|
### Developer Certificate of Origin
|
96
50
|
|
97
|
-
This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this
|
51
|
+
This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this
|
52
|
+
project must agree to this document to have their contributions accepted.
|
98
53
|
|
99
54
|
### Contributor Covenant
|
100
55
|
|
101
|
-
This project is governed by [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and
|
56
|
+
This project is governed by [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and
|
57
|
+
participants agree to abide by its terms.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: covered
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.24.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -42,7 +42,7 @@ cert_chain:
|
|
42
42
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
43
43
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
44
44
|
-----END CERTIFICATE-----
|
45
|
-
date: 2023-07-
|
45
|
+
date: 2023-07-20 00:00:00.000000000 Z
|
46
46
|
dependencies:
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: console
|
@@ -79,6 +79,7 @@ extensions: []
|
|
79
79
|
extra_rdoc_files: []
|
80
80
|
files:
|
81
81
|
- bake/covered/debug.rb
|
82
|
+
- bake/covered/policy.rb
|
82
83
|
- bake/covered/validate.rb
|
83
84
|
- lib/covered.rb
|
84
85
|
- lib/covered/autostart.rb
|
@@ -92,6 +93,7 @@ files:
|
|
92
93
|
- lib/covered/persist.rb
|
93
94
|
- lib/covered/policy.rb
|
94
95
|
- lib/covered/rspec.rb
|
96
|
+
- lib/covered/source.rb
|
95
97
|
- lib/covered/statistics.rb
|
96
98
|
- lib/covered/summary.rb
|
97
99
|
- lib/covered/sus.rb
|
metadata.gz.sig
CHANGED
Binary file
|