covered 0.23.0 → 0.24.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 +0 -0
- data/bake/covered/policy.rb +17 -0
- data/lib/covered/coverage.rb +67 -24
- data/lib/covered/files.rb +7 -61
- data/lib/covered/statistics.rb +83 -25
- data/lib/covered/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +1 -1
- 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
|
data/bake/covered/policy.rb
CHANGED
@@ -29,3 +29,20 @@ def current(paths: nil)
|
|
29
29
|
|
30
30
|
return policy
|
31
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/lib/covered/coverage.rb
CHANGED
@@ -8,7 +8,7 @@ require_relative 'source'
|
|
8
8
|
module Covered
|
9
9
|
module Ratio
|
10
10
|
def ratio
|
11
|
-
return 0 if executable_count.zero?
|
11
|
+
return 1.0 if executable_count.zero?
|
12
12
|
|
13
13
|
Rational(executed_count, executable_count)
|
14
14
|
end
|
@@ -29,22 +29,65 @@ module Covered
|
|
29
29
|
self.new(Source.for(path, **options))
|
30
30
|
end
|
31
31
|
|
32
|
-
def initialize(source, counts = [], annotations = {}
|
32
|
+
def initialize(source, counts = [], annotations = {})
|
33
33
|
@source = source
|
34
34
|
@counts = counts
|
35
35
|
@annotations = annotations
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_accessor :source
|
39
|
+
attr :counts
|
40
|
+
attr :annotations
|
41
|
+
|
42
|
+
def total
|
43
|
+
counts.sum{|count| count || 0}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Create an empty coverage with the same source.
|
47
|
+
def empty
|
48
|
+
self.class.new(@source, [nil] * @counts.size)
|
49
|
+
end
|
50
|
+
|
51
|
+
def annotate(line_number, annotation)
|
52
|
+
@annotations[line_number] ||= []
|
53
|
+
@annotations[line_number] << annotation
|
54
|
+
end
|
55
|
+
|
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
|
67
|
+
end
|
68
|
+
|
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
|
36
76
|
|
37
|
-
@
|
38
|
-
|
39
|
-
|
40
|
-
@executable_lines = nil
|
41
|
-
@executed_lines = nil
|
77
|
+
@annotations.merge!(other.annotations) do |line_number, a, b|
|
78
|
+
Array(a) + Array(b)
|
79
|
+
end
|
42
80
|
end
|
43
81
|
|
44
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.
|
45
83
|
# @parameter line_numbers [Array(Integer)] The line numbers to include in the new coverage object.
|
46
84
|
def for_lines(line_numbers)
|
47
|
-
|
85
|
+
counts = [nil] * @counts.size
|
86
|
+
line_numbers.each do |line_number|
|
87
|
+
counts[line_number] = @counts[line_number]
|
88
|
+
end
|
89
|
+
|
90
|
+
self.class.new(@source, counts, @annotations)
|
48
91
|
end
|
49
92
|
|
50
93
|
def path
|
@@ -74,13 +117,6 @@ module Covered
|
|
74
117
|
return false
|
75
118
|
end
|
76
119
|
|
77
|
-
attr_accessor :source
|
78
|
-
|
79
|
-
attr :counts
|
80
|
-
attr :total
|
81
|
-
|
82
|
-
attr :annotations
|
83
|
-
|
84
120
|
def read(&block)
|
85
121
|
@source.read(&block)
|
86
122
|
end
|
@@ -99,15 +135,15 @@ module Covered
|
|
99
135
|
end
|
100
136
|
|
101
137
|
def zero?
|
102
|
-
|
138
|
+
total.zero?
|
103
139
|
end
|
104
140
|
|
105
|
-
def []
|
106
|
-
@counts[
|
141
|
+
def [] line_number
|
142
|
+
@counts[line_number]
|
107
143
|
end
|
108
144
|
|
109
145
|
def executable_lines
|
110
|
-
@
|
146
|
+
@counts.compact
|
111
147
|
end
|
112
148
|
|
113
149
|
def executable_count
|
@@ -115,7 +151,7 @@ module Covered
|
|
115
151
|
end
|
116
152
|
|
117
153
|
def executed_lines
|
118
|
-
|
154
|
+
executable_lines.reject(&:zero?)
|
119
155
|
end
|
120
156
|
|
121
157
|
def executed_count
|
@@ -131,23 +167,30 @@ module Covered
|
|
131
167
|
end
|
132
168
|
|
133
169
|
def to_s
|
134
|
-
"\#<#{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
|
+
}
|
135
180
|
end
|
136
181
|
|
137
182
|
def serialize(packer)
|
138
183
|
packer.write(@source)
|
139
184
|
packer.write(@counts)
|
140
185
|
packer.write(@annotations)
|
141
|
-
packer.write(@total)
|
142
186
|
end
|
143
187
|
|
144
188
|
def self.deserialize(unpacker)
|
145
189
|
source = unpacker.read
|
146
190
|
counts = unpacker.read
|
147
191
|
annotations = unpacker.read
|
148
|
-
total = unpacker.read
|
149
192
|
|
150
|
-
self.new(source, counts, annotations
|
193
|
+
self.new(source, counts, annotations)
|
151
194
|
end
|
152
195
|
end
|
153
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.for(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/statistics.rb
CHANGED
@@ -10,34 +10,86 @@ module Covered
|
|
10
10
|
class CoverageError < StandardError
|
11
11
|
end
|
12
12
|
|
13
|
-
class Statistics
|
13
|
+
class Statistics
|
14
|
+
include Ratio
|
15
|
+
|
14
16
|
def self.for(coverage)
|
15
17
|
self.new.tap do |statistics|
|
16
18
|
statistics << coverage
|
17
19
|
end
|
18
20
|
end
|
19
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
|
+
|
20
61
|
def initialize
|
21
|
-
@
|
22
|
-
@
|
23
|
-
@executed_count = 0
|
62
|
+
@total = Aggregate.new
|
63
|
+
@paths = Hash.new
|
24
64
|
end
|
25
65
|
|
26
|
-
|
27
|
-
|
66
|
+
def count
|
67
|
+
@paths.size
|
68
|
+
end
|
69
|
+
|
70
|
+
def executable_count
|
71
|
+
@total.executable_count
|
72
|
+
end
|
73
|
+
|
74
|
+
def executed_count
|
75
|
+
@total.executed_count
|
76
|
+
end
|
28
77
|
|
29
|
-
|
30
|
-
|
78
|
+
def << coverage
|
79
|
+
@total << coverage
|
80
|
+
(@paths[coverage.path] ||= coverage.empty).merge!(coverage)
|
81
|
+
end
|
31
82
|
|
32
|
-
|
33
|
-
|
83
|
+
attr :total
|
84
|
+
|
85
|
+
def [](path)
|
86
|
+
@paths[path]
|
87
|
+
end
|
34
88
|
|
35
89
|
def as_json
|
36
90
|
{
|
37
|
-
|
38
|
-
|
39
|
-
executed_count: executed_count,
|
40
|
-
percentage: percentage.to_f.round(2),
|
91
|
+
total: total.as_json,
|
92
|
+
paths: @paths.map{|path, coverage| [path, coverage.as_json]}.to_h,
|
41
93
|
}
|
42
94
|
end
|
43
95
|
|
@@ -45,23 +97,29 @@ module Covered
|
|
45
97
|
as_json.to_json(options)
|
46
98
|
end
|
47
99
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
+
]
|
56
112
|
|
57
113
|
def print(output)
|
58
|
-
output.puts "
|
114
|
+
output.puts "#{count} files checked; #{@total.executed_count}/#{@total.executable_count} lines executed; #{@total.percentage.to_f.round(2)}% covered."
|
59
115
|
|
60
|
-
|
116
|
+
if self.complete?
|
117
|
+
output.puts "🧘 #{COMPLETE.sample}"
|
118
|
+
end
|
61
119
|
end
|
62
120
|
|
63
121
|
def validate!(minimum = 1.0)
|
64
|
-
if
|
122
|
+
if total.ratio < minimum
|
65
123
|
raise CoverageError, "Coverage of #{self.percentage.to_f.round(2)}% is less than required minimum of #{(minimum * 100.0).round(2)}%!"
|
66
124
|
end
|
67
125
|
end
|
data/lib/covered/version.rb
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
Binary file
|