covered 0.23.0 → 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 +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
|