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.
@@ -6,53 +6,83 @@
6
6
  require_relative "source"
7
7
 
8
8
  module Covered
9
+ # Computes common coverage ratios from executed and executable line counts.
9
10
  module Ratio
11
+ # The fraction of executable lines that were executed.
12
+ # @returns [Rational | Float] The executed-to-executable line ratio, or `1.0` when there are no executable lines.
10
13
  def ratio
11
14
  return 1.0 if executable_count.zero?
12
15
 
13
16
  Rational(executed_count, executable_count)
14
17
  end
15
18
 
19
+ # Whether all executable lines were executed.
20
+ # @returns [Boolean] Whether `executed_count` equals `executable_count`.
16
21
  def complete?
17
22
  executed_count == executable_count
18
23
  end
19
24
 
25
+ # The coverage ratio as a percentage.
26
+ # @returns [Numeric] The coverage ratio multiplied by `100`.
20
27
  def percentage
21
28
  ratio * 100
22
29
  end
23
30
  end
24
31
 
32
+ # Stores line execution counts and source metadata for a single file.
25
33
  class Coverage
26
34
  include Ratio
27
35
 
36
+ # Build coverage for the given source path.
37
+ # @parameter path [String] The source path.
38
+ # @parameter options [Hash] Options forwarded to {Covered::Source.for}.
39
+ # @returns [Covered::Coverage] The coverage object for the source path.
28
40
  def self.for(path, **options)
29
41
  self.new(Source.for(path, **options))
30
42
  end
31
43
 
44
+ # Initialize coverage with the given source, line counts and annotations.
45
+ # @parameter source [Covered::Source] The covered source metadata.
46
+ # @parameter counts [Array(Integer | Nil)] Line execution counts indexed by line number.
47
+ # @parameter annotations [Hash(Integer, Array(String))] Line annotations indexed by line number.
32
48
  def initialize(source, counts = [], annotations = {})
33
49
  @source = source
34
50
  @counts = counts
35
51
  @annotations = annotations
36
52
  end
37
53
 
54
+ # @attribute [Covered::Source] The covered source metadata.
38
55
  attr_accessor :source
56
+
57
+ # @attribute [Array(Integer | Nil)] Line execution counts indexed by line number.
39
58
  attr :counts
59
+
60
+ # @attribute [Hash(Integer, Array(String))] Line annotations indexed by line number.
40
61
  attr :annotations
41
62
 
63
+ # The total number of executions across all tracked lines.
64
+ # @returns [Integer] The sum of all non-`nil` execution counts.
42
65
  def total
43
66
  counts.sum{|count| count || 0}
44
67
  end
45
68
 
46
69
  # Create an empty coverage with the same source.
70
+ # @returns [Covered::Coverage] Coverage with the same source and `nil` counts.
47
71
  def empty
48
72
  self.class.new(@source, [nil] * @counts.size)
49
73
  end
50
74
 
75
+ # Add an annotation to the given line number.
76
+ # @parameter line_number [Integer] The line number to annotate.
77
+ # @parameter annotation [String] The annotation text.
51
78
  def annotate(line_number, annotation)
52
79
  @annotations[line_number] ||= []
53
80
  @annotations[line_number] << annotation
54
81
  end
55
82
 
83
+ # Add the given execution count to one or more line numbers.
84
+ # @parameter line_number [Integer] The first line number to mark.
85
+ # @parameter value [Integer | Array(Integer)] The execution count or counts to add.
56
86
  def mark(line_number, value = 1)
57
87
  # As currently implemented, @counts is base-zero rather than base-one.
58
88
  # 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.
@@ -66,6 +96,8 @@ module Covered
66
96
  end
67
97
  end
68
98
 
99
+ # Merge another coverage object into this coverage object.
100
+ # @parameter other [Covered::Coverage] The coverage object to merge.
69
101
  def merge!(other)
70
102
  # If the counts are non-zero and don't match, that can indicate a problem.
71
103
 
@@ -83,6 +115,7 @@ module Covered
83
115
 
84
116
  # Construct a new coverage object for the given line numbers. Only the given line numbers will be considered for the purposes of computing coverage.
85
117
  # @parameter line_numbers [Array(Integer)] The line numbers to include in the new coverage object.
118
+ # @returns [Covered::Coverage] A coverage object containing counts for the selected lines.
86
119
  def for_lines(line_numbers)
87
120
  counts = [nil] * @counts.size
88
121
  line_numbers.each do |line_number|
@@ -92,14 +125,20 @@ module Covered
92
125
  self.class.new(@source, counts, @annotations)
93
126
  end
94
127
 
128
+ # The covered source path.
129
+ # @returns [String] The covered source path.
95
130
  def path
96
131
  @source.path
97
132
  end
98
133
 
134
+ # Assign the covered source path.
135
+ # @parameter value [String] The new covered source path.
99
136
  def path= value
100
137
  @source.path = value
101
138
  end
102
139
 
140
+ # Whether the source file has not changed since this coverage was recorded.
141
+ # @returns [Boolean] Whether the source exists and its modification time is not newer than the recorded time.
103
142
  def fresh?
104
143
  if @source.modified_time.nil?
105
144
  # We don't know when the file was last modified, so we assume it is stale:
@@ -119,10 +158,16 @@ module Covered
119
158
  return false
120
159
  end
121
160
 
161
+ # Read the covered source.
162
+ # @yields {|file| ...} If a block is given, yields an open source file.
163
+ # @parameter file [File] The open source file.
164
+ # @returns [String | Object] The source contents without a block, or the block result with a block.
122
165
  def read(&block)
123
166
  @source.read(&block)
124
167
  end
125
168
 
169
+ # Freeze this coverage and its mutable collections.
170
+ # @returns [Covered::Coverage] This frozen coverage object.
126
171
  def freeze
127
172
  return self if frozen?
128
173
 
@@ -132,46 +177,69 @@ module Covered
132
177
  super
133
178
  end
134
179
 
180
+ # The raw coverage counts array.
181
+ # @returns [Array(Integer | Nil)] The raw coverage counts.
135
182
  def to_a
136
183
  @counts
137
184
  end
138
185
 
186
+ # Whether this coverage has no executions.
187
+ # @returns [Boolean] Whether the total execution count is zero.
139
188
  def zero?
140
189
  total.zero?
141
190
  end
142
191
 
192
+ # The raw coverage count for the given line number.
193
+ # @parameter line_number [Integer] The line number to query.
194
+ # @returns [Integer | Nil] The execution count for the line.
143
195
  def [] line_number
144
196
  @counts[line_number]
145
197
  end
146
198
 
199
+ # Counts for lines that are executable.
200
+ # @returns [Array(Integer)] Execution counts for executable lines.
147
201
  def executable_lines
148
202
  @counts.compact
149
203
  end
150
204
 
205
+ # The number of executable lines.
206
+ # @returns [Integer] The number of executable lines.
151
207
  def executable_count
152
208
  executable_lines.count
153
209
  end
154
210
 
211
+ # Counts for executable lines that were executed.
212
+ # @returns [Array(Integer)] Non-zero execution counts for executable lines.
155
213
  def executed_lines
156
214
  executable_lines.reject(&:zero?)
157
215
  end
158
216
 
217
+ # The number of executable lines that were executed.
218
+ # @returns [Integer] The number of executed lines.
159
219
  def executed_count
160
220
  executed_lines.count
161
221
  end
162
222
 
223
+ # The number of executable lines that were not executed.
224
+ # @returns [Integer] The number of missing executable lines.
163
225
  def missing_count
164
226
  executable_count - executed_count
165
227
  end
166
228
 
229
+ # Print a human-readable coverage summary.
230
+ # @parameter output [IO] The output stream.
167
231
  def print(output)
168
232
  output.puts "** #{executed_count}/#{executable_count} lines executed; #{percentage.to_f.round(2)}% covered."
169
233
  end
170
234
 
235
+ # A human-readable representation of this coverage object.
236
+ # @returns [String] A summary including the source path and percentage.
171
237
  def to_s
172
238
  "\#<#{self.class} path=#{self.path} #{self.percentage.to_f.round(2)}% covered>"
173
239
  end
174
240
 
241
+ # A JSON-compatible representation of this coverage object.
242
+ # @returns [Hash] The coverage counts and summary statistics.
175
243
  def as_json
176
244
  {
177
245
  counts: counts,
@@ -181,12 +249,17 @@ module Covered
181
249
  }
182
250
  end
183
251
 
252
+ # Serialize this coverage object with the given packer.
253
+ # @parameter packer [Object] The MessagePack-compatible packer.
184
254
  def serialize(packer)
185
255
  packer.write(@source)
186
256
  packer.write(@counts)
187
257
  packer.write(@annotations)
188
258
  end
189
259
 
260
+ # Deserialize a coverage object from the given unpacker.
261
+ # @parameter unpacker [Object] The MessagePack-compatible unpacker.
262
+ # @returns [Covered::Coverage] The deserialized coverage object.
190
263
  def self.deserialize(unpacker)
191
264
  source = unpacker.read
192
265
  counts = unpacker.read
data/lib/covered/files.rb CHANGED
@@ -9,35 +9,57 @@ require_relative "wrapper"
9
9
  require "set"
10
10
 
11
11
  module Covered
12
+ # Collects coverage information keyed by source path.
12
13
  class Files < Base
14
+ # Initialize an empty coverage collection.
13
15
  def initialize(*)
14
16
  super
15
17
 
16
18
  @paths = {}
17
19
  end
18
20
 
21
+ # @attribute [Hash(String, Covered::Coverage)] Coverage indexed by expanded source path.
19
22
  attr_accessor :paths
20
23
 
24
+ # Get or create coverage for the given path.
25
+ # @parameter path [String] The source path.
26
+ # @returns [Covered::Coverage] The coverage object for the path.
21
27
  def [](path)
22
28
  @paths[path] ||= Coverage.for(path)
23
29
  end
24
30
 
31
+ # Whether there are no tracked paths.
32
+ # @returns [Boolean] Whether no coverage paths are tracked.
25
33
  def empty?
26
34
  @paths.empty?
27
35
  end
28
36
 
37
+ # Mark a line in the given path as executed.
38
+ # @parameter path [String] The source path.
39
+ # @parameter line_number [Integer] The line number to mark.
40
+ # @parameter value [Integer | Array(Integer)] The execution count or counts to add.
29
41
  def mark(path, line_number, value)
30
42
  self[path].mark(line_number, value)
31
43
  end
32
44
 
45
+ # Add an annotation to a line in the given path.
46
+ # @parameter path [String] The source path.
47
+ # @parameter line_number [Integer] The line number to annotate.
48
+ # @parameter value [String] The annotation text.
33
49
  def annotate(path, line_number, value)
34
50
  self[path].annotate(line_number, value)
35
51
  end
36
52
 
53
+ # Merge coverage for the given path into this collection.
54
+ # @parameter coverage [Covered::Coverage] The coverage object to merge.
37
55
  def add(coverage)
38
56
  self[coverage.path].merge!(coverage)
39
57
  end
40
58
 
59
+ # Enumerate tracked coverage objects.
60
+ # @yields {|coverage| ...} Each tracked coverage object.
61
+ # @parameter coverage [Covered::Coverage] The current coverage object.
62
+ # @returns [Enumerator | Nil] An enumerator without a block.
41
63
  def each
42
64
  return to_enum unless block_given?
43
65
 
@@ -46,12 +68,19 @@ module Covered
46
68
  end
47
69
  end
48
70
 
71
+ # Remove all tracked coverage data.
72
+ # @returns [Hash] The cleared path map.
49
73
  def clear
50
74
  @paths.clear
51
75
  end
52
76
  end
53
77
 
78
+ # Includes coverage for files matching a glob pattern.
54
79
  class Include < Wrapper
80
+ # Initialize an include filter for the given glob pattern.
81
+ # @parameter output [Covered::Base] The output to wrap.
82
+ # @parameter pattern [String] The glob pattern to include.
83
+ # @parameter base [String] The base path used to expand the glob.
55
84
  def initialize(output, pattern, base = "")
56
85
  super(output)
57
86
 
@@ -59,8 +88,11 @@ module Covered
59
88
  @base = base
60
89
  end
61
90
 
91
+ # @attribute [String] The glob pattern to include.
62
92
  attr :pattern
63
93
 
94
+ # Resolve the include pattern to real file paths.
95
+ # @returns [Set(String)] The real paths matched by the include pattern.
64
96
  def glob
65
97
  paths = Set.new
66
98
  root = self.expand_path(@base)
@@ -75,6 +107,9 @@ module Covered
75
107
  return paths
76
108
  end
77
109
 
110
+ # Enumerate existing coverage and synthesize empty coverage for unmatched included files.
111
+ # @yields {|coverage| ...} Each existing or synthesized coverage object.
112
+ # @parameter coverage [Covered::Coverage] The current coverage object.
78
113
  def each(&block)
79
114
  paths = glob
80
115
 
@@ -90,17 +125,22 @@ module Covered
90
125
  end
91
126
  end
92
127
 
128
+ # Excludes coverage for paths matching a pattern.
93
129
  class Skip < Filter
130
+ # Initialize a skip filter for the given pattern.
131
+ # @parameter output [Covered::Base] The output to wrap.
132
+ # @parameter pattern [Regexp] The pattern to exclude.
94
133
  def initialize(output, pattern)
95
134
  super(output)
96
135
 
97
136
  @pattern = pattern
98
137
  end
99
138
 
139
+ # @attribute [Regexp] The pattern to exclude.
100
140
  attr :pattern
101
141
 
102
142
  if Regexp.instance_methods.include? :match?
103
- # This is better as it doesn't allocate a MatchData instance which is essentially useless.
143
+ # This is better as it doesn't allocate a MatchData instance which is essentially useless:
104
144
  def match? path
105
145
  !@pattern.match?(path)
106
146
  end
@@ -111,33 +151,52 @@ module Covered
111
151
  end
112
152
  end
113
153
 
154
+ # Only includes coverage for paths matching a pattern.
114
155
  class Only < Filter
156
+ # Initialize an only filter for the given pattern.
157
+ # @parameter output [Covered::Base] The output to wrap.
158
+ # @parameter pattern [Object] The pattern matched with `===`.
115
159
  def initialize(output, pattern)
116
160
  super(output)
117
161
 
118
162
  @pattern = pattern
119
163
  end
120
164
 
165
+ # @attribute [Object] The pattern matched with `===`.
121
166
  attr :pattern
122
167
 
168
+ # Whether the given path matches the only pattern.
169
+ # @parameter path [String] The source path.
170
+ # @returns [Boolean] Whether the path matches the pattern.
123
171
  def match?(path)
124
172
  @pattern === path
125
173
  end
126
174
  end
127
175
 
176
+ # Restricts coverage to a project root and converts paths relative to it.
128
177
  class Root < Filter
178
+ # Initialize a root filter for the given path.
179
+ # @parameter output [Covered::Base] The output to wrap.
180
+ # @parameter path [String] The root path.
129
181
  def initialize(output, path)
130
182
  super(output)
131
183
 
132
184
  @path = path
133
185
  end
134
186
 
187
+ # @attribute [String] The root path.
135
188
  attr :path
136
189
 
190
+ # Expand a path relative to this root.
191
+ # @parameter path [String] The path to expand.
192
+ # @returns [String] The expanded path.
137
193
  def expand_path(path)
138
194
  File.expand_path(super, @path)
139
195
  end
140
196
 
197
+ # Convert a path under this root to a relative path.
198
+ # @parameter path [String] The path to relativize.
199
+ # @returns [String] The relative path when under this root, otherwise the wrapped output result.
141
200
  def relative_path(path)
142
201
  if path.start_with?(@path)
143
202
  path.slice(@path.size+1, path.size)
@@ -146,6 +205,9 @@ module Covered
146
205
  end
147
206
  end
148
207
 
208
+ # Whether the given path is under this root.
209
+ # @parameter path [String] The source path.
210
+ # @returns [Boolean] Whether the path starts with this root.
149
211
  def match?(path)
150
212
  path.start_with?(@path)
151
213
  end
data/lib/covered/forks.rb CHANGED
@@ -6,25 +6,33 @@
6
6
  require_relative "wrapper"
7
7
 
8
8
  module Covered
9
+ # Tracks coverage across forked child processes.
9
10
  class Forks < Wrapper
11
+ # Start tracking coverage and install the fork handler.
10
12
  def start
11
13
  super
12
14
 
13
15
  Handler.start(self)
14
16
  end
15
17
 
18
+ # Stop tracking coverage and remove the fork handler state.
16
19
  def finish
17
20
  Handler.finish
18
21
 
19
22
  super
20
23
  end
21
24
 
25
+ # Handles `Process.fork` so child processes record coverage independently.
22
26
  module Handler
23
27
  LOCK = Mutex.new
24
28
 
25
29
  class << self
30
+ # @attribute [Covered::Forks | Nil] The currently registered coverage wrapper.
26
31
  attr :coverage
27
32
 
33
+ # Register coverage for fork handling.
34
+ # @parameter coverage [Covered::Forks] The coverage wrapper to use in forked children.
35
+ # @raises [ArgumentError] If coverage is already registered.
28
36
  def start(coverage)
29
37
  LOCK.synchronize do
30
38
  if @coverage
@@ -35,12 +43,14 @@ module Covered
35
43
  end
36
44
  end
37
45
 
46
+ # Clear the registered coverage.
38
47
  def finish
39
48
  LOCK.synchronize do
40
49
  @coverage = nil
41
50
  end
42
51
  end
43
52
 
53
+ # Reset coverage in a forked child and save it at exit.
44
54
  def after_fork
45
55
  return unless coverage = Handler.coverage
46
56
  pid = Process.pid
@@ -57,6 +67,8 @@ module Covered
57
67
  end
58
68
  end
59
69
 
70
+ # Intercept `Process.fork` and initialize coverage in the child.
71
+ # @returns [Integer | Nil] The process ID in the parent, and `0` or `nil` in the child depending on Ruby's fork semantics.
60
72
  def _fork
61
73
  pid = super
62
74
 
@@ -9,11 +9,19 @@ require_relative "wrapper"
9
9
  require "console/output"
10
10
 
11
11
  module Covered
12
+ # Generates a Markdown coverage summary.
12
13
  class MarkdownSummary
14
+ # Initialize the report with an optional coverage threshold.
15
+ # @parameter threshold [Numeric | Nil] The minimum ratio a file must meet to be omitted from the detailed output.
13
16
  def initialize(threshold: 1.0)
14
17
  @threshold = threshold
15
18
  end
16
19
 
20
+ # Enumerate coverage below the threshold and return aggregate statistics.
21
+ # @parameter wrapper [Covered::Base] The coverage wrapper to enumerate.
22
+ # @yields {|coverage| ...} Coverage whose ratio is below the configured threshold.
23
+ # @parameter coverage [Covered::Coverage] The coverage object below the threshold.
24
+ # @returns [Covered::Statistics] Statistics for all coverage objects, including omitted ones.
17
25
  def each(wrapper)
18
26
  statistics = Statistics.new
19
27
 
@@ -28,6 +36,11 @@ module Covered
28
36
  return statistics
29
37
  end
30
38
 
39
+ # Print any annotations for the given line.
40
+ # @parameter output [IO] The output stream.
41
+ # @parameter coverage [Covered::Coverage] The coverage being rendered.
42
+ # @parameter line [String] The source line.
43
+ # @parameter line_offset [Integer] The current line number.
31
44
  def print_annotations(output, coverage, line, line_offset)
32
45
  if annotations = coverage.annotations[line_offset]
33
46
  prefix = "#{line_offset}|".rjust(8) + "*|".rjust(8)
@@ -38,10 +51,17 @@ module Covered
38
51
  end
39
52
  end
40
53
 
54
+ # Print the line and hit-count header.
55
+ # @parameter output [IO] The output stream.
41
56
  def print_line_header(output)
42
57
  output.puts "Line|".rjust(8) + "Hits|".rjust(8)
43
58
  end
44
59
 
60
+ # Print a single source line.
61
+ # @parameter output [IO] The output stream.
62
+ # @parameter line [String] The source line.
63
+ # @parameter line_offset [Integer] The current line number.
64
+ # @parameter count [Integer | Nil] The execution count for the line.
45
65
  def print_line(output, line, line_offset, count)
46
66
  prefix = "#{line_offset}|".rjust(8) + "#{count}|".rjust(8)
47
67
 
@@ -54,7 +74,9 @@ module Covered
54
74
  end
55
75
  end
56
76
 
57
- # 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).
77
+ # 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`).
78
+ # @parameter wrapper [Covered::Base] The coverage wrapper to report.
79
+ # @parameter output [IO] The output stream.
58
80
  def call(wrapper, output = $stdout)
59
81
  output.puts "# Coverage Report"
60
82
  output.puts
@@ -11,7 +11,10 @@ require "minitest"
11
11
  $covered = Covered::Config.load
12
12
 
13
13
  module Covered
14
+ # Integrates coverage tracking with Minitest.
14
15
  module Minitest
16
+ # Start coverage before running the Minitest suite.
17
+ # @returns [Object] The result of Minitest's original `run` method.
15
18
  def run(*)
16
19
  $covered.start
17
20
 
@@ -6,7 +6,13 @@
6
6
  require_relative "summary"
7
7
 
8
8
  module Covered
9
+ # Generates a report containing only lines near missing coverage.
9
10
  class PartialSummary < Summary
11
+ # Print coverage snippets around uncovered lines.
12
+ # @parameter terminal [Console::Terminal] The terminal to write to.
13
+ # @parameter coverage [Covered::Coverage] The coverage to render.
14
+ # @parameter before [Integer] The number of lines before an uncovered line to show.
15
+ # @parameter after [Integer] The number of lines after an uncovered line to show.
10
16
  def print_coverage(terminal, coverage, before: 4, after: 4)
11
17
  return if coverage.zero?
12
18
 
@@ -38,11 +44,18 @@ module Covered
38
44
  end
39
45
  end
40
46
 
47
+ # Print the partial coverage report.
48
+ # @parameter wrapper [Covered::Base] The coverage wrapper to report.
49
+ # @parameter output [IO] The output stream.
50
+ # @parameter options [Hash] Options forwarded to {print_coverage}.
41
51
  def call(wrapper, output = $stdout, **options)
42
52
  terminal = self.terminal(output)
43
53
  complete_files = []
54
+ partial_files_count = 0
44
55
 
45
56
  statistics = self.each(wrapper) do |coverage|
57
+ partial_files_count += 1
58
+
46
59
  path = wrapper.relative_path(coverage.path)
47
60
  terminal.puts ""
48
61
  terminal.puts path, style: :path
@@ -56,7 +69,7 @@ module Covered
56
69
  coverage.print(output)
57
70
  end
58
71
 
59
- # Collect files with 100% coverage that were not shown
72
+ # Collect files with 100% coverage that were not shown:
60
73
  wrapper.each do |coverage|
61
74
  if coverage.ratio >= 1.0
62
75
  complete_files << wrapper.relative_path(coverage.path)
@@ -66,8 +79,8 @@ module Covered
66
79
  terminal.puts
67
80
  statistics.print(output)
68
81
 
69
- # Show information about files with 100% coverage
70
- if complete_files.any?
82
+ # Only show information about files with 100% coverage if there were files with partial coverage shown above:
83
+ if complete_files.any? && partial_files_count > 0
71
84
  terminal.puts ""
72
85
  if complete_files.size == 1
73
86
  terminal.puts "1 file has 100% coverage and is not shown above:"
@@ -10,15 +10,24 @@ require "msgpack"
10
10
  require "time"
11
11
 
12
12
  module Covered
13
+ # Persists coverage records to a MessagePack database.
13
14
  class Persist < Wrapper
14
15
  DEFAULT_PATH = ".covered.db"
15
16
 
17
+ # Initialize persistence for the given output and database path.
18
+ # @parameter output [Covered::Base] The output to wrap.
19
+ # @parameter path [String] The coverage database path.
16
20
  def initialize(output, path = DEFAULT_PATH)
17
21
  super(output)
18
22
 
19
23
  @path = self.expand_path(path)
20
24
  end
21
25
 
26
+ # Apply a persisted record to the output.
27
+ # Records with stale source modification times are ignored unless `ignore_mtime` is true.
28
+ # @parameter record [Hash] The persisted coverage record.
29
+ # @parameter ignore_mtime [Boolean] Whether to apply records even if their source appears stale.
30
+ # @returns [Boolean] Whether the record was applied.
22
31
  def apply(record, ignore_mtime: false)
23
32
  if coverage = record[:coverage]
24
33
  if path = record[:path]
@@ -35,6 +44,9 @@ module Covered
35
44
  return false
36
45
  end
37
46
 
47
+ # Convert coverage into a database record.
48
+ # @parameter coverage [Covered::Coverage] The coverage object to serialize.
49
+ # @returns [Hash] A MessagePack-compatible record.
38
50
  def serialize(coverage)
39
51
  {
40
52
  # We want to use relative paths so that moving the repo won't break everything:
@@ -45,6 +57,9 @@ module Covered
45
57
  }
46
58
  end
47
59
 
60
+ # Load persisted coverage records into the output.
61
+ # @parameter options [Hash] Options forwarded to {apply}.
62
+ # @raises [LoadError] If the database exists but cannot be decoded.
48
63
  def load!(**options)
49
64
  return unless File.exist?(@path)
50
65
 
@@ -61,6 +76,7 @@ module Covered
61
76
  raise LoadError, "Failed to load coverage from #{@path}, maybe old format or corrupt!"
62
77
  end
63
78
 
79
+ # Save all output coverage records to the database.
64
80
  def save!
65
81
  # Dump all coverage:
66
82
  File.open(@path, "ab") do |file|
@@ -77,12 +93,17 @@ module Covered
77
93
  end
78
94
  end
79
95
 
96
+ # Finish the wrapped output and save the coverage database.
80
97
  def finish
81
98
  super
82
99
 
83
100
  self.save!
84
101
  end
85
102
 
103
+ # Reload persisted coverage and enumerate the wrapped output.
104
+ # @yields {|coverage| ...} Each coverage object from the reloaded output.
105
+ # @parameter coverage [Covered::Coverage] The current coverage object.
106
+ # @returns [Enumerator | Nil] An enumerator without a block.
86
107
  def each(&block)
87
108
  return to_enum unless block_given?
88
109
 
@@ -92,6 +113,8 @@ module Covered
92
113
  super
93
114
  end
94
115
 
116
+ # Build the MessagePack factory used for coverage records.
117
+ # @returns [MessagePack::Factory] The configured MessagePack factory.
95
118
  def make_factory
96
119
  factory = MessagePack::Factory.new
97
120
 
@@ -117,10 +140,16 @@ module Covered
117
140
  return factory
118
141
  end
119
142
 
143
+ # Build a MessagePack packer for the given IO.
144
+ # @parameter io [IO] The IO to write packed records to.
145
+ # @returns [MessagePack::Packer] A packer configured for coverage records.
120
146
  def make_packer(io)
121
147
  return make_factory.packer(io)
122
148
  end
123
149
 
150
+ # Build a MessagePack unpacker for the given IO.
151
+ # @parameter io [IO] The IO to read packed records from.
152
+ # @returns [MessagePack::Unpacker] An unpacker configured for coverage records.
124
153
  def make_unpacker(io)
125
154
  return make_factory.unpacker(io)
126
155
  end