covered 0.28.1 → 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.
@@ -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)}%!"
@@ -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
- # 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).
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 startd to avoid performance cost/overhead.
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
@@ -3,6 +3,7 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2018-2025, by Samuel Williams.
5
5
 
6
+ # @namespace
6
7
  module Covered
7
- VERSION = "0.28.1"
8
+ VERSION = "0.28.2"
8
9
  end
@@ -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
data/license.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MIT License
2
2
 
3
- Copyright, 2018-2025, by Samuel Williams.
3
+ Copyright, 2018-2026, by Samuel Williams.
4
4
  Copyright, 2018, by Shannon Skipper.
5
5
  Copyright, 2019, by Cyril Roelandt.
6
6
  Copyright, 2022, by Adam Daniels.
data/readme.md CHANGED
@@ -34,6 +34,10 @@ Please see the [project documentation](https://socketry.github.io/covered/) for
34
34
 
35
35
  Please see the [project releases](https://socketry.github.io/covered/releases/index) for all releases.
36
36
 
37
+ ### v0.28.2
38
+
39
+ - Ensure bake coverage validation loads persisted coverage using project configuration.
40
+
37
41
  ### v0.28.0
38
42
 
39
43
  - List files that have 100% coverage in `PartialSummary` output.
@@ -58,6 +62,22 @@ We welcome contributions to this project.
58
62
  4. Push to the branch (`git push origin my-new-feature`).
59
63
  5. Create new Pull Request.
60
64
 
65
+ ### Running Tests
66
+
67
+ To run the test suite:
68
+
69
+ ``` shell
70
+ bundle exec sus
71
+ ```
72
+
73
+ ### Making Releases
74
+
75
+ To make a new release:
76
+
77
+ ``` shell
78
+ bundle exec bake gem:release:patch # or minor or major
79
+ ```
80
+
61
81
  ### Developer Certificate of Origin
62
82
 
63
83
  In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
data/releases.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Releases
2
2
 
3
+ ## v0.28.2
4
+
5
+ - Ensure bake coverage validation loads persisted coverage using project configuration.
6
+
3
7
  ## v0.28.0
4
8
 
5
9
  - List files that have 100% coverage in `PartialSummary` output.
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.28.1
4
+ version: 0.28.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -121,14 +121,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: '3.2'
124
+ version: '3.3'
125
125
  required_rubygems_version: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - ">="
128
128
  - !ruby/object:Gem::Version
129
129
  version: '0'
130
130
  requirements: []
131
- rubygems_version: 3.6.9
131
+ rubygems_version: 4.0.10
132
132
  specification_version: 4
133
133
  summary: A modern approach to code coverage.
134
134
  test_files: []
metadata.gz.sig CHANGED
Binary file