covered 0.28.0 → 0.28.2
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 +4 -3
- data/bake/covered/policy.rb +2 -22
- data/bake/covered/validate.rb +4 -4
- data/context/configuration.md +10 -10
- data/context/getting-started.md +6 -6
- data/context/usage.md +6 -6
- data/lib/covered/autostart.rb +3 -0
- data/lib/covered/brief_summary.rb +6 -0
- data/lib/covered/capture.rb +10 -1
- data/lib/covered/config.rb +51 -2
- data/lib/covered/coverage.rb +73 -0
- data/lib/covered/files.rb +63 -1
- data/lib/covered/forks.rb +12 -0
- data/lib/covered/markdown_summary.rb +23 -1
- data/lib/covered/minitest.rb +3 -0
- data/lib/covered/partial_summary.rb +16 -3
- data/lib/covered/persist.rb +29 -0
- data/lib/covered/policy.rb +33 -0
- data/lib/covered/rspec.rb +8 -0
- data/lib/covered/source.rb +32 -0
- data/lib/covered/statistics.rb +42 -0
- data/lib/covered/summary.rb +40 -1
- data/lib/covered/sus.rb +8 -1
- data/lib/covered/version.rb +2 -1
- data/lib/covered/wrapper.rb +51 -0
- data/license.md +1 -1
- data/readme.md +20 -0
- data/releases.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +3 -3
- metadata.gz.sig +3 -3
data/lib/covered/policy.rb
CHANGED
|
@@ -10,7 +10,9 @@ require_relative "persist"
|
|
|
10
10
|
require_relative "forks"
|
|
11
11
|
|
|
12
12
|
module Covered
|
|
13
|
+
# Configures coverage collection, filtering, persistence and reports.
|
|
13
14
|
class Policy < Wrapper
|
|
15
|
+
# Initialize a policy with an empty file collection.
|
|
14
16
|
def initialize
|
|
15
17
|
super(Files.new)
|
|
16
18
|
|
|
@@ -18,8 +20,11 @@ module Covered
|
|
|
18
20
|
@capture = nil
|
|
19
21
|
end
|
|
20
22
|
|
|
23
|
+
# @attribute [Covered::Base] The current output pipeline.
|
|
21
24
|
attr :output
|
|
22
25
|
|
|
26
|
+
# Freeze the policy and eagerly build the capture pipeline.
|
|
27
|
+
# @returns [Covered::Policy] This frozen policy.
|
|
23
28
|
def freeze
|
|
24
29
|
return self if frozen?
|
|
25
30
|
|
|
@@ -29,47 +34,67 @@ module Covered
|
|
|
29
34
|
super
|
|
30
35
|
end
|
|
31
36
|
|
|
37
|
+
# Include files matching the given pattern in coverage results.
|
|
38
|
+
# Arguments are forwarded to {Covered::Include#initialize}.
|
|
32
39
|
def include(...)
|
|
33
40
|
@output = Include.new(@output, ...)
|
|
34
41
|
end
|
|
35
42
|
|
|
43
|
+
# Exclude files matching the given pattern from coverage results.
|
|
44
|
+
# Arguments are forwarded to {Covered::Skip#initialize}.
|
|
36
45
|
def skip(...)
|
|
37
46
|
@output = Skip.new(@output, ...)
|
|
38
47
|
end
|
|
39
48
|
|
|
49
|
+
# Restrict coverage results to files matching the given pattern.
|
|
50
|
+
# Arguments are forwarded to {Covered::Only#initialize}.
|
|
40
51
|
def only(...)
|
|
41
52
|
@output = Only.new(@output, ...)
|
|
42
53
|
end
|
|
43
54
|
|
|
55
|
+
# Restrict coverage results to the given project root.
|
|
56
|
+
# Arguments are forwarded to {Covered::Root#initialize}.
|
|
44
57
|
def root(...)
|
|
45
58
|
@output = Root.new(@output, ...)
|
|
46
59
|
end
|
|
47
60
|
|
|
61
|
+
# Persist coverage results to a database.
|
|
62
|
+
# Arguments are forwarded to {Covered::Persist#initialize}.
|
|
48
63
|
def persist!(...)
|
|
49
64
|
@output = Persist.new(@output, ...)
|
|
50
65
|
end
|
|
51
66
|
|
|
67
|
+
# The runtime capture pipeline for this policy.
|
|
68
|
+
# @returns [Covered::Forks] The memoized capture pipeline.
|
|
52
69
|
def capture
|
|
53
70
|
@capture ||= Forks.new(
|
|
54
71
|
Capture.new(@output)
|
|
55
72
|
)
|
|
56
73
|
end
|
|
57
74
|
|
|
75
|
+
# Start collecting coverage.
|
|
58
76
|
def start
|
|
59
77
|
capture.start
|
|
60
78
|
end
|
|
61
79
|
|
|
80
|
+
# Finish collecting coverage.
|
|
62
81
|
def finish
|
|
63
82
|
capture.finish
|
|
64
83
|
end
|
|
65
84
|
|
|
85
|
+
# @attribute [Array] The configured report objects or autoloaders.
|
|
66
86
|
attr :reports
|
|
67
87
|
|
|
88
|
+
# Lazily loads a report class when it is first used.
|
|
68
89
|
class Autoload
|
|
90
|
+
# Initialize an autoloaded report with the given constant name.
|
|
91
|
+
# @parameter name [String] The report class name under {Covered}.
|
|
69
92
|
def initialize(name)
|
|
70
93
|
@name = name
|
|
71
94
|
end
|
|
72
95
|
|
|
96
|
+
# Instantiate the report class.
|
|
97
|
+
# @returns [Object] A new report instance.
|
|
73
98
|
def new
|
|
74
99
|
begin
|
|
75
100
|
klass = Covered.const_get(@name)
|
|
@@ -82,10 +107,14 @@ module Covered
|
|
|
82
107
|
return klass.new
|
|
83
108
|
end
|
|
84
109
|
|
|
110
|
+
# Instantiate the report and call it.
|
|
111
|
+
# Arguments are forwarded to the report.
|
|
85
112
|
def call(...)
|
|
86
113
|
self.new.call(...)
|
|
87
114
|
end
|
|
88
115
|
|
|
116
|
+
# A human-readable representation of this autoloaded report.
|
|
117
|
+
# @returns [String] A debug representation of the autoloader.
|
|
89
118
|
def to_s
|
|
90
119
|
"\#<#{self.class} loading #{@name}>"
|
|
91
120
|
end
|
|
@@ -101,6 +130,8 @@ module Covered
|
|
|
101
130
|
end
|
|
102
131
|
end
|
|
103
132
|
|
|
133
|
+
# Configure reports from names, booleans, arrays or report objects.
|
|
134
|
+
# @parameter reports [String | Boolean | Array | Object | Nil] The reports to configure.
|
|
104
135
|
def reports!(reports)
|
|
105
136
|
if reports.is_a?(String)
|
|
106
137
|
names = reports.split(",")
|
|
@@ -124,6 +155,8 @@ module Covered
|
|
|
124
155
|
end
|
|
125
156
|
end
|
|
126
157
|
|
|
158
|
+
# Generate all configured reports.
|
|
159
|
+
# Arguments are forwarded to each report.
|
|
127
160
|
def call(...)
|
|
128
161
|
@reports.each do |report|
|
|
129
162
|
report.call(self, ...)
|
data/lib/covered/rspec.rb
CHANGED
|
@@ -9,18 +9,26 @@ require "rspec/core/formatters"
|
|
|
9
9
|
$covered = Covered::Config.load
|
|
10
10
|
|
|
11
11
|
module Covered
|
|
12
|
+
# Integrates coverage tracking with RSpec.
|
|
12
13
|
module RSpec
|
|
14
|
+
# Extends RSpec configuration with coverage tracking.
|
|
13
15
|
module Policy
|
|
16
|
+
# Start coverage before loading spec files.
|
|
17
|
+
# @returns [Object] The result of RSpec's original `load_spec_files` method.
|
|
14
18
|
def load_spec_files
|
|
15
19
|
$covered.start
|
|
16
20
|
|
|
17
21
|
super
|
|
18
22
|
end
|
|
19
23
|
|
|
24
|
+
# The active coverage configuration.
|
|
25
|
+
# @returns [Covered::Config] The active coverage configuration.
|
|
20
26
|
def covered
|
|
21
27
|
$covered
|
|
22
28
|
end
|
|
23
29
|
|
|
30
|
+
# Assign the active coverage configuration.
|
|
31
|
+
# @parameter policy [Covered::Config] The replacement coverage configuration.
|
|
24
32
|
def covered= policy
|
|
25
33
|
$covered = policy
|
|
26
34
|
end
|
data/lib/covered/source.rb
CHANGED
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
# Copyright, 2018-2023, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
module Covered
|
|
7
|
+
# Source code metadata for a covered file or generated template.
|
|
7
8
|
class Source
|
|
9
|
+
# Build source metadata for the given path.
|
|
10
|
+
# Records the current file modification time when the path exists.
|
|
11
|
+
# @parameter path [String] The source path.
|
|
12
|
+
# @parameter options [Hash] Options forwarded to {initialize}.
|
|
13
|
+
# @returns [Covered::Source] The source metadata.
|
|
8
14
|
def self.for(path, **options)
|
|
9
15
|
if File.exist?(path)
|
|
10
16
|
# options[:code] ||= File.read(path)
|
|
@@ -14,6 +20,11 @@ module Covered
|
|
|
14
20
|
self.new(path, **options)
|
|
15
21
|
end
|
|
16
22
|
|
|
23
|
+
# Initialize source metadata.
|
|
24
|
+
# @parameter path [String] The source path.
|
|
25
|
+
# @parameter code [String | Nil] Optional generated source code.
|
|
26
|
+
# @parameter line_offset [Integer] The starting line offset.
|
|
27
|
+
# @parameter modified_time [Time | Nil] The source modification time.
|
|
17
28
|
def initialize(path, code: nil, line_offset: 1, modified_time: nil)
|
|
18
29
|
@path = path
|
|
19
30
|
@code = code
|
|
@@ -21,15 +32,28 @@ module Covered
|
|
|
21
32
|
@modified_time = modified_time
|
|
22
33
|
end
|
|
23
34
|
|
|
35
|
+
# @attribute [String] The source path.
|
|
24
36
|
attr_accessor :path
|
|
37
|
+
|
|
38
|
+
# @attribute [String | Nil] Optional generated source code.
|
|
25
39
|
attr :code
|
|
40
|
+
|
|
41
|
+
# @attribute [Integer] The starting line offset for generated source code.
|
|
26
42
|
attr :line_offset
|
|
43
|
+
|
|
44
|
+
# @attribute [Time | Nil] The recorded source modification time.
|
|
27
45
|
attr :modified_time
|
|
28
46
|
|
|
47
|
+
# A human-readable representation of this source.
|
|
48
|
+
# @returns [String] A summary containing the source path.
|
|
29
49
|
def to_s
|
|
30
50
|
"\#<#{self.class} path=#{path}>"
|
|
31
51
|
end
|
|
32
52
|
|
|
53
|
+
# Read the source code from disk.
|
|
54
|
+
# @yields {|file| ...} If a block is given, yields an open source file.
|
|
55
|
+
# @parameter file [File] The open source file.
|
|
56
|
+
# @returns [String | Object] The source contents without a block, or the block result with a block.
|
|
33
57
|
def read(&block)
|
|
34
58
|
if block_given?
|
|
35
59
|
File.open(self.path, "r", &block)
|
|
@@ -39,14 +63,19 @@ module Covered
|
|
|
39
63
|
end
|
|
40
64
|
|
|
41
65
|
# 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.
|
|
66
|
+
# @returns [String] The generated code when present, otherwise the file contents.
|
|
42
67
|
def code!
|
|
43
68
|
self.code || self.read
|
|
44
69
|
end
|
|
45
70
|
|
|
71
|
+
# Whether generated source code is present.
|
|
72
|
+
# @returns [Boolean] Whether this source has generated code.
|
|
46
73
|
def code?
|
|
47
74
|
!!self.code
|
|
48
75
|
end
|
|
49
76
|
|
|
77
|
+
# Serialize this source with the given packer.
|
|
78
|
+
# @parameter packer [Object] The MessagePack-compatible packer.
|
|
50
79
|
def serialize(packer)
|
|
51
80
|
packer.write(self.path)
|
|
52
81
|
packer.write(self.code)
|
|
@@ -54,6 +83,9 @@ module Covered
|
|
|
54
83
|
packer.write(self.modified_time)
|
|
55
84
|
end
|
|
56
85
|
|
|
86
|
+
# Deserialize a source from the given unpacker.
|
|
87
|
+
# @parameter unpacker [Object] The MessagePack-compatible unpacker.
|
|
88
|
+
# @returns [Covered::Source] The deserialized source metadata.
|
|
57
89
|
def self.deserialize(unpacker)
|
|
58
90
|
path = unpacker.read
|
|
59
91
|
code = unpacker.read
|
data/lib/covered/statistics.rb
CHANGED
|
@@ -7,21 +7,28 @@ require_relative "wrapper"
|
|
|
7
7
|
require_relative "coverage"
|
|
8
8
|
|
|
9
9
|
module Covered
|
|
10
|
+
# Raised when coverage does not meet the configured minimum.
|
|
10
11
|
class CoverageError < StandardError
|
|
11
12
|
end
|
|
12
13
|
|
|
14
|
+
# Aggregates coverage statistics across files.
|
|
13
15
|
class Statistics
|
|
14
16
|
include Ratio
|
|
15
17
|
|
|
18
|
+
# Build statistics for a single coverage object.
|
|
19
|
+
# @parameter coverage [Covered::Coverage] The coverage object to summarize.
|
|
20
|
+
# @returns [Covered::Statistics] Statistics containing the given coverage.
|
|
16
21
|
def self.for(coverage)
|
|
17
22
|
self.new.tap do |statistics|
|
|
18
23
|
statistics << coverage
|
|
19
24
|
end
|
|
20
25
|
end
|
|
21
26
|
|
|
27
|
+
# Aggregate coverage totals.
|
|
22
28
|
class Aggregate
|
|
23
29
|
include Ratio
|
|
24
30
|
|
|
31
|
+
# Initialize empty aggregate statistics.
|
|
25
32
|
def initialize
|
|
26
33
|
@count = 0
|
|
27
34
|
@executable_count = 0
|
|
@@ -29,14 +36,19 @@ module Covered
|
|
|
29
36
|
end
|
|
30
37
|
|
|
31
38
|
# Total number of files added.
|
|
39
|
+
# @returns [Integer] The number of coverage objects added.
|
|
32
40
|
attr :count
|
|
33
41
|
|
|
34
42
|
# The number of lines which could have been executed.
|
|
43
|
+
# @returns [Integer] The executable line count.
|
|
35
44
|
attr :executable_count
|
|
36
45
|
|
|
37
46
|
# The number of lines that were executed.
|
|
47
|
+
# @returns [Integer] The executed line count.
|
|
38
48
|
attr :executed_count
|
|
39
49
|
|
|
50
|
+
# A JSON-compatible representation of these aggregate statistics.
|
|
51
|
+
# @returns [Hash] The aggregate count, line counts and percentage.
|
|
40
52
|
def as_json
|
|
41
53
|
{
|
|
42
54
|
count: count,
|
|
@@ -46,10 +58,15 @@ module Covered
|
|
|
46
58
|
}
|
|
47
59
|
end
|
|
48
60
|
|
|
61
|
+
# Convert these aggregate statistics to JSON.
|
|
62
|
+
# @parameter options [Hash] Options forwarded to `to_json`.
|
|
63
|
+
# @returns [String] The JSON representation.
|
|
49
64
|
def to_json(options)
|
|
50
65
|
as_json.to_json(options)
|
|
51
66
|
end
|
|
52
67
|
|
|
68
|
+
# Add coverage to these aggregate statistics.
|
|
69
|
+
# @parameter coverage [Covered::Coverage] The coverage object to add.
|
|
53
70
|
def << coverage
|
|
54
71
|
@count += 1
|
|
55
72
|
|
|
@@ -58,35 +75,52 @@ module Covered
|
|
|
58
75
|
end
|
|
59
76
|
end
|
|
60
77
|
|
|
78
|
+
# Initialize empty coverage statistics.
|
|
61
79
|
def initialize
|
|
62
80
|
@total = Aggregate.new
|
|
63
81
|
@paths = Hash.new
|
|
64
82
|
end
|
|
65
83
|
|
|
84
|
+
# @attribute [Covered::Statistics::Aggregate] The total aggregate statistics.
|
|
66
85
|
attr :total
|
|
86
|
+
|
|
87
|
+
# @attribute [Hash(String, Covered::Coverage)] Coverage statistics indexed by path.
|
|
67
88
|
attr :paths
|
|
68
89
|
|
|
90
|
+
# The number of unique paths with coverage data.
|
|
91
|
+
# @returns [Integer] The number of unique paths.
|
|
69
92
|
def count
|
|
70
93
|
@paths.size
|
|
71
94
|
end
|
|
72
95
|
|
|
96
|
+
# The total number of executable lines.
|
|
97
|
+
# @returns [Integer] The total executable line count.
|
|
73
98
|
def executable_count
|
|
74
99
|
@total.executable_count
|
|
75
100
|
end
|
|
76
101
|
|
|
102
|
+
# The total number of executed lines.
|
|
103
|
+
# @returns [Integer] The total executed line count.
|
|
77
104
|
def executed_count
|
|
78
105
|
@total.executed_count
|
|
79
106
|
end
|
|
80
107
|
|
|
108
|
+
# Add coverage to these statistics.
|
|
109
|
+
# @parameter coverage [Covered::Coverage] The coverage object to add.
|
|
81
110
|
def << coverage
|
|
82
111
|
@total << coverage
|
|
83
112
|
(@paths[coverage.path] ||= coverage.empty).merge!(coverage)
|
|
84
113
|
end
|
|
85
114
|
|
|
115
|
+
# Get coverage for the given path.
|
|
116
|
+
# @parameter path [String] The source path.
|
|
117
|
+
# @returns [Covered::Coverage | Nil] The merged coverage for the path.
|
|
86
118
|
def [](path)
|
|
87
119
|
@paths[path]
|
|
88
120
|
end
|
|
89
121
|
|
|
122
|
+
# A JSON-compatible representation of these statistics.
|
|
123
|
+
# @returns [Hash] The total statistics and path statistics.
|
|
90
124
|
def as_json
|
|
91
125
|
{
|
|
92
126
|
total: total.as_json,
|
|
@@ -94,6 +128,9 @@ module Covered
|
|
|
94
128
|
}
|
|
95
129
|
end
|
|
96
130
|
|
|
131
|
+
# Convert these statistics to JSON.
|
|
132
|
+
# @parameter options [Hash] Options forwarded to `to_json`.
|
|
133
|
+
# @returns [String] The JSON representation.
|
|
97
134
|
def to_json(options)
|
|
98
135
|
as_json.to_json(options)
|
|
99
136
|
end
|
|
@@ -111,6 +148,8 @@ module Covered
|
|
|
111
148
|
"100% code coverage: Zen achieved! Bugs in harmony, code at peace.",
|
|
112
149
|
]
|
|
113
150
|
|
|
151
|
+
# Print a human-readable coverage summary.
|
|
152
|
+
# @parameter output [IO] The output stream.
|
|
114
153
|
def print(output)
|
|
115
154
|
output.puts "#{count} files checked; #{@total.executed_count}/#{@total.executable_count} lines executed; #{@total.percentage.to_f.round(2)}% covered."
|
|
116
155
|
|
|
@@ -119,6 +158,9 @@ module Covered
|
|
|
119
158
|
end
|
|
120
159
|
end
|
|
121
160
|
|
|
161
|
+
# Validate that coverage meets the given minimum ratio.
|
|
162
|
+
# @parameter minimum [Numeric] The minimum accepted coverage ratio.
|
|
163
|
+
# @raises [Covered::CoverageError] If coverage is below the minimum ratio.
|
|
122
164
|
def validate!(minimum = 1.0)
|
|
123
165
|
if total.ratio < minimum
|
|
124
166
|
raise CoverageError, "Coverage of #{self.percentage.to_f.round(2)}% is less than required minimum of #{(minimum * 100.0).round(2)}%!"
|
data/lib/covered/summary.rb
CHANGED
|
@@ -7,11 +7,17 @@ require_relative "statistics"
|
|
|
7
7
|
require_relative "wrapper"
|
|
8
8
|
|
|
9
9
|
module Covered
|
|
10
|
+
# Generates a detailed terminal coverage report.
|
|
10
11
|
class Summary
|
|
12
|
+
# Initialize the report with an optional coverage threshold.
|
|
13
|
+
# @parameter threshold [Numeric | Nil] The minimum ratio a file must meet to be omitted from the detailed output.
|
|
11
14
|
def initialize(threshold: 1.0)
|
|
12
15
|
@threshold = threshold
|
|
13
16
|
end
|
|
14
17
|
|
|
18
|
+
# Build a styled terminal for the given output.
|
|
19
|
+
# @parameter output [IO] The output stream.
|
|
20
|
+
# @returns [Console::Terminal] The styled terminal wrapper.
|
|
15
21
|
def terminal(output)
|
|
16
22
|
require "console/terminal"
|
|
17
23
|
|
|
@@ -33,6 +39,11 @@ module Covered
|
|
|
33
39
|
end
|
|
34
40
|
end
|
|
35
41
|
|
|
42
|
+
# Enumerate coverage below the threshold and return aggregate statistics.
|
|
43
|
+
# @parameter wrapper [Covered::Base] The coverage wrapper to enumerate.
|
|
44
|
+
# @yields {|coverage| ...} Coverage whose ratio is below the configured threshold.
|
|
45
|
+
# @parameter coverage [Covered::Coverage] The coverage object below the threshold.
|
|
46
|
+
# @returns [Covered::Statistics] Statistics for all coverage objects, including omitted ones.
|
|
36
47
|
def each(wrapper)
|
|
37
48
|
statistics = Statistics.new
|
|
38
49
|
|
|
@@ -47,6 +58,11 @@ module Covered
|
|
|
47
58
|
return statistics
|
|
48
59
|
end
|
|
49
60
|
|
|
61
|
+
# Print any annotations for the given line.
|
|
62
|
+
# @parameter terminal [Console::Terminal] The terminal to write to.
|
|
63
|
+
# @parameter coverage [Covered::Coverage] The coverage being rendered.
|
|
64
|
+
# @parameter line [String] The source line.
|
|
65
|
+
# @parameter line_offset [Integer] The current line number.
|
|
50
66
|
def print_annotations(terminal, coverage, line, line_offset)
|
|
51
67
|
if annotations = coverage.annotations[line_offset]
|
|
52
68
|
prefix = "#{line_offset}|".rjust(8) + "*|".rjust(8)
|
|
@@ -57,12 +73,19 @@ module Covered
|
|
|
57
73
|
end
|
|
58
74
|
end
|
|
59
75
|
|
|
76
|
+
# Print the line and hit-count header.
|
|
77
|
+
# @parameter terminal [Console::Terminal] The terminal to write to.
|
|
60
78
|
def print_line_header(terminal)
|
|
61
79
|
prefix = "Line|".rjust(8) + "Hits|".rjust(8)
|
|
62
80
|
|
|
63
81
|
terminal.puts prefix, style: :header_prefix
|
|
64
82
|
end
|
|
65
83
|
|
|
84
|
+
# Print a single source line with coverage styling.
|
|
85
|
+
# @parameter terminal [Console::Terminal] The terminal to write to.
|
|
86
|
+
# @parameter line [String] The source line.
|
|
87
|
+
# @parameter line_offset [Integer] The current line number.
|
|
88
|
+
# @parameter count [Integer | Nil] The execution count for the line.
|
|
66
89
|
def print_line(terminal, line, line_offset, count)
|
|
67
90
|
prefix = "#{line_offset}|".rjust(8) + "#{count}|".rjust(8)
|
|
68
91
|
|
|
@@ -83,6 +106,9 @@ module Covered
|
|
|
83
106
|
end
|
|
84
107
|
end
|
|
85
108
|
|
|
109
|
+
# Print line-by-line coverage for one source file.
|
|
110
|
+
# @parameter terminal [Console::Terminal] The terminal to write to.
|
|
111
|
+
# @parameter coverage [Covered::Coverage] The coverage to render.
|
|
86
112
|
def print_coverage(terminal, coverage)
|
|
87
113
|
line_offset = 1
|
|
88
114
|
counts = coverage.counts
|
|
@@ -102,12 +128,19 @@ module Covered
|
|
|
102
128
|
end
|
|
103
129
|
end
|
|
104
130
|
|
|
131
|
+
# Print an error raised while rendering coverage.
|
|
132
|
+
# @parameter terminal [Console::Terminal] The terminal to write to.
|
|
133
|
+
# @parameter error [Exception] The rendering error.
|
|
105
134
|
def print_error(terminal, error)
|
|
106
135
|
terminal.puts "Error: #{error.message}", style: :error
|
|
107
136
|
terminal.puts error.backtrace
|
|
108
137
|
end
|
|
109
138
|
|
|
110
|
-
#
|
|
139
|
+
# Print the detailed coverage report.
|
|
140
|
+
# A coverage array gives, for each line, the number of line executions by the interpreter. A `nil` value means coverage is finished for this line (lines like `else` and `end`).
|
|
141
|
+
# @parameter wrapper [Covered::Base] The coverage wrapper to report.
|
|
142
|
+
# @parameter output [IO] The output stream.
|
|
143
|
+
# @parameter options [Hash] Options forwarded to {print_coverage}.
|
|
111
144
|
def call(wrapper, output = $stdout, **options)
|
|
112
145
|
terminal = self.terminal(output)
|
|
113
146
|
|
|
@@ -130,13 +163,19 @@ module Covered
|
|
|
130
163
|
end
|
|
131
164
|
end
|
|
132
165
|
|
|
166
|
+
# Generates a detailed report without applying a coverage threshold.
|
|
133
167
|
class FullSummary < Summary
|
|
168
|
+
# Initialize a full summary report.
|
|
134
169
|
def initialize
|
|
135
170
|
super(threshold: nil)
|
|
136
171
|
end
|
|
137
172
|
end
|
|
138
173
|
|
|
174
|
+
# Suppresses coverage report output.
|
|
139
175
|
class Quiet
|
|
176
|
+
# Generate no output.
|
|
177
|
+
# @parameter wrapper [Covered::Base] The coverage wrapper to ignore.
|
|
178
|
+
# @parameter output [IO] The output stream to ignore.
|
|
140
179
|
def call(wrapper, output = $stdout)
|
|
141
180
|
# Silent.
|
|
142
181
|
end
|
data/lib/covered/sus.rb
CHANGED
|
@@ -4,11 +4,14 @@
|
|
|
4
4
|
# Copyright, 2019-2025, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
module Covered
|
|
7
|
+
# Integrates coverage tracking with Sus.
|
|
7
8
|
module Sus
|
|
9
|
+
# Initialize Sus with optional coverage tracking.
|
|
10
|
+
# Loads and starts coverage only when the `COVERAGE` environment variable is set.
|
|
8
11
|
def initialize(...)
|
|
9
12
|
super
|
|
10
13
|
|
|
11
|
-
# Defer loading the coverage configuration unless we are actually running with coverage
|
|
14
|
+
# Defer loading the coverage configuration unless we are actually running with coverage started to avoid performance cost/overhead:
|
|
12
15
|
if ENV["COVERAGE"]
|
|
13
16
|
require_relative "config"
|
|
14
17
|
|
|
@@ -21,6 +24,8 @@ module Covered
|
|
|
21
24
|
end
|
|
22
25
|
end
|
|
23
26
|
|
|
27
|
+
# Finish coverage tracking after tests complete.
|
|
28
|
+
# @parameter assertions [Object] The assertions object passed through to Sus.
|
|
24
29
|
def after_tests(assertions)
|
|
25
30
|
super(assertions)
|
|
26
31
|
|
|
@@ -30,6 +35,8 @@ module Covered
|
|
|
30
35
|
end
|
|
31
36
|
end
|
|
32
37
|
|
|
38
|
+
# The active coverage configuration.
|
|
39
|
+
# @returns [Covered::Config | Nil] The active coverage configuration when coverage is enabled.
|
|
33
40
|
def covered
|
|
34
41
|
@covered
|
|
35
42
|
end
|
data/lib/covered/version.rb
CHANGED
data/lib/covered/wrapper.rb
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# Copyright, 2018-2023, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
module Covered
|
|
7
|
+
# The base coverage output interface.
|
|
7
8
|
class Base
|
|
8
9
|
# Start tracking coverage.
|
|
9
10
|
def start
|
|
@@ -17,13 +18,22 @@ module Covered
|
|
|
17
18
|
def finish
|
|
18
19
|
end
|
|
19
20
|
|
|
21
|
+
# Whether the given path should be accepted.
|
|
22
|
+
# @parameter path [String] The source path.
|
|
23
|
+
# @returns [Boolean] Whether the path should be included.
|
|
20
24
|
def accept?(path)
|
|
21
25
|
true
|
|
22
26
|
end
|
|
23
27
|
|
|
28
|
+
# Mark coverage for the given path and line number.
|
|
29
|
+
# @parameter path [String] The source path.
|
|
30
|
+
# @parameter lineno [Integer] The starting line number.
|
|
31
|
+
# @parameter value [Integer | Array(Integer)] The execution count or counts to add.
|
|
24
32
|
def mark(path, lineno, value)
|
|
25
33
|
end
|
|
26
34
|
|
|
35
|
+
# Add a coverage object.
|
|
36
|
+
# @parameter coverage [Covered::Coverage] The coverage object to add.
|
|
27
37
|
def add(coverage)
|
|
28
38
|
end
|
|
29
39
|
|
|
@@ -33,42 +43,64 @@ module Covered
|
|
|
33
43
|
def each
|
|
34
44
|
end
|
|
35
45
|
|
|
46
|
+
# Convert a path to a relative path.
|
|
47
|
+
# @parameter path [String] The source path.
|
|
48
|
+
# @returns [String] The relative path.
|
|
36
49
|
def relative_path(path)
|
|
37
50
|
path
|
|
38
51
|
end
|
|
39
52
|
|
|
53
|
+
# Expand a path relative to this output.
|
|
54
|
+
# @parameter path [String] The path to expand.
|
|
55
|
+
# @returns [String] The expanded path.
|
|
40
56
|
def expand_path(path)
|
|
41
57
|
path
|
|
42
58
|
end
|
|
43
59
|
end
|
|
44
60
|
|
|
61
|
+
# Wraps another coverage output.
|
|
45
62
|
class Wrapper < Base
|
|
63
|
+
# Initialize the wrapper with the given output.
|
|
64
|
+
# @parameter output [Covered::Base] The output to wrap.
|
|
46
65
|
def initialize(output = Base.new)
|
|
47
66
|
@output = output
|
|
48
67
|
end
|
|
49
68
|
|
|
69
|
+
# @attribute [Covered::Base] The wrapped output.
|
|
50
70
|
attr :output
|
|
51
71
|
|
|
72
|
+
# Start tracking coverage on the wrapped output.
|
|
52
73
|
def start
|
|
53
74
|
@output.start
|
|
54
75
|
end
|
|
55
76
|
|
|
77
|
+
# Clear coverage on the wrapped output.
|
|
56
78
|
def clear
|
|
57
79
|
@output.clear
|
|
58
80
|
end
|
|
59
81
|
|
|
82
|
+
# Finish tracking coverage on the wrapped output.
|
|
60
83
|
def finish
|
|
61
84
|
@output.finish
|
|
62
85
|
end
|
|
63
86
|
|
|
87
|
+
# Whether the wrapped output accepts the given path.
|
|
88
|
+
# @parameter path [String] The source path.
|
|
89
|
+
# @returns [Boolean] Whether the wrapped output accepts the path.
|
|
64
90
|
def accept?(path)
|
|
65
91
|
@output.accept?(path)
|
|
66
92
|
end
|
|
67
93
|
|
|
94
|
+
# Mark coverage on the wrapped output.
|
|
95
|
+
# @parameter path [String] The source path.
|
|
96
|
+
# @parameter lineno [Integer] The starting line number.
|
|
97
|
+
# @parameter value [Integer | Array(Integer)] The execution count or counts to add.
|
|
68
98
|
def mark(path, lineno, value)
|
|
69
99
|
@output.mark(path, lineno, value)
|
|
70
100
|
end
|
|
71
101
|
|
|
102
|
+
# Add coverage to the wrapped output.
|
|
103
|
+
# @parameter coverage [Covered::Coverage] The coverage object to add.
|
|
72
104
|
def add(coverage)
|
|
73
105
|
@output.add(coverage)
|
|
74
106
|
end
|
|
@@ -78,20 +110,33 @@ module Covered
|
|
|
78
110
|
@output.each(&block)
|
|
79
111
|
end
|
|
80
112
|
|
|
113
|
+
# Convert a path using the wrapped output.
|
|
114
|
+
# @parameter path [String] The source path.
|
|
115
|
+
# @returns [String] The converted path.
|
|
81
116
|
def relative_path(path)
|
|
82
117
|
@output.relative_path(path)
|
|
83
118
|
end
|
|
84
119
|
|
|
120
|
+
# Expand a path using the wrapped output.
|
|
121
|
+
# @parameter path [String] The path to expand.
|
|
122
|
+
# @returns [String] The expanded path.
|
|
85
123
|
def expand_path(path)
|
|
86
124
|
@output.expand_path(path)
|
|
87
125
|
end
|
|
88
126
|
|
|
127
|
+
# Convert all coverage data to a hash keyed by path.
|
|
128
|
+
# @returns [Hash(String, Covered::Coverage)] Coverage keyed by path.
|
|
89
129
|
def to_h
|
|
90
130
|
to_enum(:each).collect{|coverage| [coverage.path, coverage]}.to_h
|
|
91
131
|
end
|
|
92
132
|
end
|
|
93
133
|
|
|
134
|
+
# Filters coverage before forwarding it to another output.
|
|
94
135
|
class Filter < Wrapper
|
|
136
|
+
# Mark coverage if the path is accepted by this filter.
|
|
137
|
+
# @parameter path [String] The source path.
|
|
138
|
+
# @parameter lineno [Integer] The starting line number.
|
|
139
|
+
# @parameter value [Integer | Array(Integer)] The execution count or counts to add.
|
|
95
140
|
def mark(path, lineno, value)
|
|
96
141
|
@output.mark(path, lineno, value) if accept?(path)
|
|
97
142
|
end
|
|
@@ -103,10 +148,16 @@ module Covered
|
|
|
103
148
|
end
|
|
104
149
|
end
|
|
105
150
|
|
|
151
|
+
# Whether the given path is accepted by this filter and its output.
|
|
152
|
+
# @parameter path [String] The source path.
|
|
153
|
+
# @returns [Boolean] Whether this filter and the wrapped output accept the path.
|
|
106
154
|
def accept?(path)
|
|
107
155
|
match?(path) and super
|
|
108
156
|
end
|
|
109
157
|
|
|
158
|
+
# Whether the given path matches this filter.
|
|
159
|
+
# @parameter path [String] The source path.
|
|
160
|
+
# @returns [Boolean] Whether this filter matches the path.
|
|
110
161
|
def match?(path)
|
|
111
162
|
true
|
|
112
163
|
end
|