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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 289a32298170797803df03aec0f13ab6452bcc2787d38e85860ead1bca0b17b0
4
- data.tar.gz: 6f682d81566f970a93a4aed6b00e1db77603fcf2a982245e75c60e7c30f15652
3
+ metadata.gz: '009f56de49059187634630092d8e8256fc32e59a309c155d46676f3a87002581'
4
+ data.tar.gz: 3f91b876b34f65800ab6b92a6a991eba898131dee199d476c2f82620a864d3d1
5
5
  SHA512:
6
- metadata.gz: 4495fe21349a2cd25fbfc81e7de1b0d2c00465635f543b991cb8f740e8dad98653dd7a59552e065fb51a5224e39ce12601199ca912fde16033eb43b109f1197d
7
- data.tar.gz: e43586380b8587f803c1175f3a919eafd55a860dd61092057ecbf729cce902671e7046fe81fb0baacbd699f4968bdb571b3fe8a10017f80b6341365291584003
6
+ metadata.gz: d11ce0723dc200027f6a319972baab7492599d40fc383cc2c1deba7ef88c9e600231809ce6e4de701a11fec78c0b5de4ac136c82110b503ebf5f59a9c6d7591e
7
+ data.tar.gz: f848f0fde6e08847fa8194c8dc6854ecc80053279ea1f97cd99028840f9892f102964a52d901a63ffd9393e00bc917ecbfefb247db94ab0c65b59272e1a63188
checksums.yaml.gz.sig CHANGED
@@ -1,3 +1,4 @@
1
- `�#dpJ`6��ˑ��
2
- �#Ed�ӲO���1J#9T:� ������PK��5�׬�u�}f�?�P}�X+V2A
3
- i����b�83���)y��1�q��=���ɚ������z{۶~7CG�/�@�%��&�Ul�%�v����-H�_�e��>�|�Q�jȋ�Wx��윁Ɉ�
1
+ a��0����(M\$�*�~s��B�Xa'�BD����[��[1���z
2
+ %�9+����nk�x��ˢ*f{�{���x��q%�W������B[f3�H�}Ȑ��6��]h<�"Е�d�`!w���<�� T����G�T.q��!�f�c�qz����7����
3
+ B@k!��,��aX <^���{Ѳ
4
+ ���1K�gK9"�TY.��pb|�2Jn�y��b�i���{����@���gd���y�)!н��̞!�#Bͫ�,���-�R�+En���vZ�/�o��Yosc�NMSX�'�$l�P�d����Uwm��ht����.4����^+'@y�^[֩�l�"�'Yt�Ga�=�Зɏ�����ԙM���/C��e�
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2023-2025, by Samuel Williams.
4
+ # Copyright, 2023-2026, by Samuel Williams.
5
5
  # Copyright, 2023, by Michael Adams.
6
6
 
7
7
  def initialize(context)
@@ -14,27 +14,7 @@ end
14
14
  # Defaults to the default coverage path if no paths are specified.
15
15
  # @parameter paths [Array(String)] The coverage database paths.
16
16
  def current(paths: nil, reports: Covered::Config.reports)
17
- policy = Covered::Policy.new
18
-
19
- # Load the default path if no paths are specified:
20
- paths ||= Dir.glob(Covered::Persist::DEFAULT_PATH, base: context.root)
21
-
22
- # If no paths are specified, raise an error:
23
- if paths.empty?
24
- raise ArgumentError, "No coverage paths specified!"
25
- end
26
-
27
- # Load all coverage information:
28
- paths.each do |path|
29
- # It would be nice to have a better algorithm here than just ignoring mtime - perhaps using checksums?
30
- Covered::Persist.new(policy.output, path).load!(ignore_mtime: true)
31
- end
32
-
33
- if reports
34
- policy.reports!(reports)
35
- end
36
-
37
- return policy
17
+ return Covered::Config.load(root: context.root, reports: reports).policy_for(paths)
38
18
  end
39
19
 
40
20
  # Validate the coverage of multiple test runs.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022-2025, by Samuel Williams.
4
+ # Copyright, 2022-2026, by Samuel Williams.
5
5
 
6
6
  def initialize(context)
7
7
  super
@@ -14,19 +14,19 @@ end
14
14
  # @parameter minimum [Float] The minimum required coverage in order to pass.
15
15
  # @parameter input [Covered::Policy] The input policy to validate.
16
16
  def validate(paths: nil, minimum: 1.0, input:)
17
- policy ||= context.lookup("covered:policy:current").call(paths: paths)
17
+ input ||= context.lookup("covered:policy:current").call(paths: paths)
18
18
 
19
19
  # Calculate statistics:
20
20
  statistics = Covered::Statistics.new
21
21
 
22
- policy.each do |coverage|
22
+ input.each do |coverage|
23
23
  statistics << coverage
24
24
  end
25
25
 
26
26
  # Print statistics:
27
27
  statistics.print($stderr)
28
28
 
29
- policy.call(STDOUT)
29
+ input.call(STDOUT)
30
30
 
31
31
  # Validate statistics and raise an error if they are not met:
32
32
  statistics.validate!(minimum)
@@ -146,13 +146,13 @@ def make_policy(policy)
146
146
  policy.only(/^app\//)
147
147
 
148
148
  # Set custom root
149
- policy.root(File.expand_path('..', __dir__))
149
+ policy.root(File.expand_path("..", __dir__))
150
150
 
151
151
  # Enable persistent coverage across runs
152
152
  policy.persist!
153
153
 
154
154
  # Configure reports programmatically
155
- if ENV['CI']
155
+ if ENV["CI"]
156
156
  policy.reports << Covered::MarkdownSummary.new
157
157
  else
158
158
  policy.reports << Covered::PartialSummary.new
@@ -245,7 +245,7 @@ def make_policy(policy)
245
245
  super
246
246
 
247
247
  # Ensure template coverage is enabled
248
- require 'covered/erb' if defined?(ERB)
248
+ require "covered/erb" if defined?(ERB)
249
249
  end
250
250
  ```
251
251
 
@@ -257,14 +257,14 @@ end
257
257
  def make_policy(policy)
258
258
  super
259
259
 
260
- if ENV['GITHUB_ACTIONS']
260
+ if ENV["GITHUB_ACTIONS"]
261
261
  # Use markdown format for GitHub
262
262
  policy.reports << Covered::MarkdownSummary.new
263
263
 
264
264
  # Fail build on low coverage
265
265
  policy.reports << Class.new do
266
266
  def call(wrapper, output = $stdout)
267
- statistics = wrapper.each.inject(Statistics.new) { |s, c| s << c }
267
+ statistics = wrapper.each.inject(Statistics.new){|s, c| s << c}
268
268
  if statistics.ratio < 0.90
269
269
  exit 1
270
270
  end
@@ -281,9 +281,9 @@ def make_policy(policy)
281
281
  super
282
282
 
283
283
  # Different thresholds for different environments
284
- threshold = case ENV['RAILS_ENV']
285
- when 'production' then 0.95
286
- when 'staging' then 0.90
284
+ threshold = case ENV["RAILS_ENV"]
285
+ when "production" then 0.95
286
+ when "staging" then 0.90
287
287
  else 0.80
288
288
  end
289
289
 
@@ -305,7 +305,7 @@ def make_policy(policy)
305
305
  policy.skip(/\/coverage\//)
306
306
 
307
307
  # Use more efficient reports for large projects
308
- if Dir['**/*.rb'].length > 1000
308
+ if Dir["**/*.rb"].length > 1000
309
309
  policy.reports << Covered::BriefSummary.new
310
310
  else
311
311
  policy.reports << Covered::PartialSummary.new
@@ -344,7 +344,7 @@ def make_policy(policy)
344
344
  super
345
345
 
346
346
  # Debug: print current configuration
347
- if ENV['DEBUG_COVERAGE']
347
+ if ENV["DEBUG_COVERAGE"]
348
348
  puts "Ignore paths: #{ignore_paths}"
349
349
  puts "Include patterns: #{include_patterns}"
350
350
  puts "Root: #{@root}"
@@ -7,7 +7,7 @@ This guide explains how to get started with `covered` and integrate it with your
7
7
  Add this line to your application's `Gemfile`:
8
8
 
9
9
  ``` ruby
10
- gem 'covered'
10
+ gem "covered"
11
11
  ```
12
12
 
13
13
  ### Sus Integration
@@ -15,7 +15,7 @@ gem 'covered'
15
15
  In your `config/sus.rb` add the following:
16
16
 
17
17
  ``` ruby
18
- require 'covered/sus'
18
+ require "covered/sus"
19
19
  include Covered::Sus
20
20
  ```
21
21
 
@@ -24,7 +24,7 @@ include Covered::Sus
24
24
  In your `spec/spec_helper.rb` add the following before loading any other code:
25
25
 
26
26
  ``` ruby
27
- require 'covered/rspec'
27
+ require "covered/rspec"
28
28
  ```
29
29
 
30
30
  Ensure that you have a `.rspec` file with `--require spec_helper`:
@@ -38,14 +38,14 @@ Ensure that you have a `.rspec` file with `--require spec_helper`:
38
38
  In your `test/test_helper.rb` add the following before loading any other code:
39
39
 
40
40
  ``` ruby
41
- require 'covered/minitest'
42
- require 'minitest/autorun'
41
+ require "covered/minitest"
42
+ require "minitest/autorun"
43
43
  ```
44
44
 
45
45
  In your test files, e.g. `test/dummy_test.rb` add the following at the top:
46
46
 
47
47
  ``` ruby
48
- require_relative 'test_helper'
48
+ require_relative "test_helper"
49
49
  ```
50
50
 
51
51
  ### Template Coverage
data/context/usage.md CHANGED
@@ -13,7 +13,7 @@ more accurate than classic line-count tools like SimpleCov.
13
13
  Add this line to your application's `Gemfile`:
14
14
 
15
15
  ```ruby
16
- gem 'covered'
16
+ gem "covered"
17
17
  ```
18
18
 
19
19
  ## Configuration
@@ -65,7 +65,7 @@ One possibly helpful functionality to take note of is that you can override the
65
65
  In your `config/sus.rb` add the following:
66
66
 
67
67
  ```ruby
68
- require 'covered/sus'
68
+ require "covered/sus"
69
69
  include Covered::Sus
70
70
  ```
71
71
  ### RSpec Integration
@@ -73,7 +73,7 @@ include Covered::Sus
73
73
  In your `spec/spec_helper.rb` add the following before loading any other code:
74
74
 
75
75
  ```ruby
76
- require 'covered/rspec'
76
+ require "covered/rspec"
77
77
  ```
78
78
 
79
79
  Ensure that you have a `.rspec` file with `--require spec_helper`:
@@ -89,14 +89,14 @@ Ensure that you have a `.rspec` file with `--require spec_helper`:
89
89
  In your `test/test_helper.rb` add the following before loading any other code:
90
90
 
91
91
  ```ruby
92
- require 'covered/minitest'
93
- require 'minitest/autorun'
92
+ require "covered/minitest"
93
+ require "minitest/autorun"
94
94
  ```
95
95
 
96
96
  In your test files, e.g. `test/dummy_test.rb` add the following at the top:
97
97
 
98
98
  ```ruby
99
- require_relative 'test_helper'
99
+ require_relative "test_helper"
100
100
  ```
101
101
 
102
102
 
@@ -5,10 +5,13 @@
5
5
 
6
6
  require_relative "config"
7
7
 
8
+ # @namespace
8
9
  module Coverage
10
+ # Integrates `covered` with Ruby's `Coverage.autostart!` hook.
9
11
  module Autostart
10
12
  # Start recording coverage information.
11
13
  # Usage: RUBYOPT=-rcovered/autostart ruby my_script.rb
14
+ # Registers an `at_exit` hook which finishes coverage and writes reports for the original process.
12
15
  def self.autostart!
13
16
  config = Covered::Config.load
14
17
  config.start
@@ -6,7 +6,13 @@
6
6
  require_relative "summary"
7
7
 
8
8
  module Covered
9
+ # Generates a short coverage report with the least-covered files.
9
10
  class BriefSummary < Summary
11
+ # Print aggregate statistics and the files with the most missing lines.
12
+ # @parameter wrapper [Covered::Base] The coverage wrapper to report.
13
+ # @parameter output [IO] The output stream.
14
+ # @parameter before [Integer] Reserved for compatibility with other summaries.
15
+ # @parameter after [Integer] Reserved for compatibility with other summaries.
10
16
  def call(wrapper, output = $stdout, before: 4, after: 4)
11
17
  terminal = self.terminal(output)
12
18
 
@@ -8,13 +8,16 @@ require_relative "wrapper"
8
8
  require "coverage"
9
9
 
10
10
  module Covered
11
+ # Captures Ruby coverage data and forwards it to another coverage output.
11
12
  class Capture < Wrapper
13
+ # Start Ruby coverage collection.
12
14
  def start
13
15
  super
14
16
 
15
17
  ::Coverage.start(lines: true, eval: true)
16
18
  end
17
19
 
20
+ # Clear any collected coverage data without stopping coverage.
18
21
  def clear
19
22
  super
20
23
 
@@ -27,6 +30,8 @@ module Covered
27
30
  "eval" => true
28
31
  }
29
32
 
33
+ # Stop coverage collection and add the collected results to the output.
34
+ # Ignores Ruby's anonymous eval paths and files that no longer exist.
30
35
  def finish
31
36
  results = ::Coverage.result
32
37
 
@@ -35,7 +40,7 @@ module Covered
35
40
 
36
41
  path = self.expand_path(path)
37
42
 
38
- # Skip files which don't exist. This can happen if `eval` is used with an invalid/incorrect path.
43
+ # Skip files which don't exist. This can happen if `eval` is used with an invalid/incorrect path:
39
44
  if File.exist?(path)
40
45
  @output.mark(path, 1, result[:lines])
41
46
  else
@@ -47,6 +52,10 @@ module Covered
47
52
  super
48
53
  end
49
54
 
55
+ # Execute the given source while capturing coverage for it.
56
+ # @parameter source [Covered::Source] The source to execute.
57
+ # @parameter binding [Binding] The binding used to evaluate the source.
58
+ # @returns [Object] The result of evaluating the source.
50
59
  def execute(source, binding: TOPLEVEL_BINDING)
51
60
  start
52
61
 
@@ -1,18 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2025, by Samuel Williams.
4
+ # Copyright, 2019-2026, by Samuel Williams.
5
5
 
6
6
  require_relative "policy"
7
7
 
8
8
  module Covered
9
+ # Loads project coverage configuration and controls a configured policy.
9
10
  class Config
10
11
  PATH = "config/covered.rb"
11
12
 
13
+ # The root directory used for coverage configuration.
14
+ # @returns [String] The value of `COVERED_ROOT`, or the current working directory.
12
15
  def self.root
13
16
  ENV["COVERED_ROOT"] || Dir.pwd
14
17
  end
15
18
 
19
+ # The coverage configuration path under the given root.
20
+ # @parameter root [String] The project root.
21
+ # @returns [String | Nil] The expanded configuration path if it exists.
16
22
  def self.path(root)
17
23
  path = ::File.expand_path(PATH, root)
18
24
 
@@ -21,10 +27,16 @@ module Covered
21
27
  end
22
28
  end
23
29
 
30
+ # The report names requested by the environment.
31
+ # @returns [String | Nil] The `COVERAGE` environment value.
24
32
  def self.reports
25
33
  ENV["COVERAGE"]
26
34
  end
27
35
 
36
+ # Load the project coverage configuration for the given root.
37
+ # @parameter root [String] The project root.
38
+ # @parameter reports [String | Boolean | Array | Object | Nil] The report configuration.
39
+ # @returns [Covered::Config] The loaded configuration instance.
28
40
  def self.load(root: self.root, reports: self.reports)
29
41
  derived = Class.new(self)
30
42
 
@@ -37,6 +49,9 @@ module Covered
37
49
  return derived.new(root, reports)
38
50
  end
39
51
 
52
+ # Initialize the configuration for a project root and reports.
53
+ # @parameter root [String] The project root.
54
+ # @parameter reports [String | Boolean | Array | Object | Nil] The report configuration.
40
55
  def initialize(root, reports)
41
56
  @root = root
42
57
  @reports = reports
@@ -45,23 +60,31 @@ module Covered
45
60
  @environment = nil
46
61
  end
47
62
 
63
+ # Whether reports should be generated.
64
+ # @returns [Boolean] Whether reporting is enabled.
48
65
  def report?
49
66
  !!@reports
50
67
  end
51
68
 
52
69
  alias :record? :report?
53
70
 
71
+ # @attribute [Covered::Policy | Nil] The active coverage policy, if assigned by an integration.
54
72
  attr :coverage
55
73
 
74
+ # The configured coverage policy.
75
+ # @returns [Covered::Policy] The memoized, frozen policy.
56
76
  def policy
57
77
  @policy ||= Policy.new.tap{|policy| make_policy(policy)}.freeze
58
78
  end
59
79
 
80
+ # The configured policy output wrapper.
81
+ # @returns [Covered::Base] The output wrapper at the end of the policy pipeline.
60
82
  def output
61
83
  policy.output
62
84
  end
63
85
 
64
86
  # Start coverage tracking.
87
+ # Stores the current environment, configures child process autostart, and starts the policy capture pipeline.
65
88
  def start
66
89
  # Save and setup the environment:
67
90
  @environment = ENV.to_h
@@ -72,6 +95,7 @@ module Covered
72
95
  end
73
96
 
74
97
  # Finish coverage tracking.
98
+ # Stops the policy capture pipeline and restores the environment saved by {start}.
75
99
  def finish
76
100
  # Finish coverage tracking:
77
101
  policy.finish
@@ -82,15 +106,39 @@ module Covered
82
106
  end
83
107
 
84
108
  # Generate coverage reports to the given output.
85
- # @param output [IO] The output stream to write the coverage report to.
109
+ # @parameter output [IO] The output stream to write the coverage report to.
86
110
  def call(output)
87
111
  policy.call(output)
88
112
  end
89
113
 
114
+ # Enumerate the coverage data from the configured policy.
115
+ # @yields {|coverage| ...} Each coverage object from the policy.
116
+ # @parameter coverage [Covered::Coverage] The current coverage object.
90
117
  def each(&block)
91
118
  policy.each(&block)
92
119
  end
93
120
 
121
+ # Build a configured policy using coverage data from persistent storage.
122
+ #
123
+ # @parameter paths [Array(String)] The coverage database paths.
124
+ # @parameter ignore_mtime [Boolean] Whether to ignore source file modification times.
125
+ # @returns [Covered::Policy] The configured policy with loaded coverage data.
126
+ def policy_for(paths = nil, ignore_mtime: true)
127
+ paths ||= Dir.glob(Persist::DEFAULT_PATH, base: @root)
128
+ paths = Array(paths)
129
+
130
+ if paths.empty?
131
+ raise ArgumentError, "No coverage paths specified!"
132
+ end
133
+
134
+ paths.each do |path|
135
+ # It would be nice to have a better algorithm here than just ignoring mtime - perhaps using checksums:
136
+ Persist.new(policy.output, path).load!(ignore_mtime: ignore_mtime)
137
+ end
138
+
139
+ return policy
140
+ end
141
+
94
142
  # Which paths to ignore when computing coverage for a given project.
95
143
  # @returns [Array(String)] An array of relative paths to ignore.
96
144
  def ignore_paths
@@ -104,6 +152,7 @@ module Covered
104
152
  end
105
153
 
106
154
  # Override this method to implement your own policy.
155
+ # @parameter policy [Covered::Policy] The policy to configure.
107
156
  def make_policy(policy)
108
157
  # Only files in the root would be tracked:
109
158
  policy.root(@root)