class Coverage
Stores line execution counts and source metadata for a single file.
Definitions
def self.for(path, **options)
Build coverage for the given source path.
Signature
-
parameter
pathString The source path.
-
parameter
optionsHash Options forwarded to
Covered::Source.for.-
returns
Covered::Coverage The coverage object for the source path.
Implementation
def self.for(path, **options)
self.new(Source.for(path, **options))
end
def initialize(source, counts = [], annotations = {})
Initialize coverage with the given source, line counts and annotations.
Signature
-
parameter
sourceCovered::Source The covered source metadata.
-
parameter
countsArray(Integer | Nil) Line execution counts indexed by line number.
-
parameter
annotationsHash(Integer, Array(String)) Line annotations indexed by line number.
Implementation
def initialize(source, counts = [], annotations = {})
@source = source
@counts = counts
@annotations = annotations
end
attr_accessor :source
Signature
-
attribute
Covered::Source The covered source metadata.
attr :counts
Signature
-
attribute
Array(Integer | Nil) Line execution counts indexed by line number.
attr :annotations
Signature
-
attribute
Hash(Integer, Array(String)) Line annotations indexed by line number.
def total
The total number of executions across all tracked lines.
Signature
-
returns
Integer The sum of all non-
nilexecution counts.
Implementation
def total
counts.sum{|count| count || 0}
end
def empty
Create an empty coverage with the same source.
Signature
-
returns
Covered::Coverage Coverage with the same source and
nilcounts.
Implementation
def empty
self.class.new(@source, [nil] * @counts.size)
end
def annotate(line_number, annotation)
Add an annotation to the given line number.
Signature
-
parameter
line_numberInteger The line number to annotate.
-
parameter
annotationString The annotation text.
Implementation
def annotate(line_number, annotation)
@annotations[line_number] ||= []
@annotations[line_number] << annotation
end
def mark(line_number, value = 1)
Add the given execution count to one or more line numbers.
Signature
-
parameter
line_numberInteger The first line number to mark.
-
parameter
valueInteger | Array(Integer) The execution count or counts to add.
Implementation
def mark(line_number, value = 1)
Array(value).each_with_index do |value, index|
offset = line_number + index
next if offset < 1
if @counts[offset]
@counts[offset] += value
else
@counts[offset] = value
end
end
end
def merge!(other)
Merge another coverage object into this coverage object.
Signature
-
parameter
otherCovered::Coverage The coverage object to merge.
Implementation
def merge!(other)
# If the counts are non-zero and don't match, that can indicate a problem.
other.counts.each_with_index do |count, index|
if count
@counts[index] ||= 0
@counts[index] += count
end
end
@annotations.merge!(other.annotations) do |line_number, a, b|
Array(a) + Array(b)
end
end
def for_lines(line_numbers)
Construct a new coverage object for the given line numbers. Only the given line numbers will be considered for the purposes of computing coverage.
Signature
-
parameter
line_numbersArray(Integer) The line numbers to include in the new coverage object.
-
returns
Covered::Coverage A coverage object containing counts for the selected lines.
Implementation
def for_lines(line_numbers)
counts = [nil] * @counts.size
line_numbers.each do |line_number|
counts[line_number] = @counts[line_number]
end
self.class.new(@source, counts, @annotations)
end
def path
The covered source path.
Signature
-
returns
String The covered source path.
Implementation
def path
@source.path
end
def path=(value)
Assign the covered source path.
Signature
-
parameter
valueString The new covered source path.
Implementation
def path= value
@source.path = value
end
def fresh?
Whether the source file has not changed since this coverage was recorded.
Signature
-
returns
Boolean Whether the source exists and its modification time is not newer than the recorded time.
Implementation
def fresh?
if @source.modified_time.nil?
# We don't know when the file was last modified, so we assume it is stale:
return false
end
unless File.exist?(@source.path)
# The file no longer exists, so we assume it is stale:
return false
end
if @source.modified_time >= File.mtime(@source.path)
# The file has not been modified since we last processed it, so we assume it is fresh:
return true
end
return false
end
def read(&block)
Read the covered source.
Signature
-
yields
{|file| ...} If a block is given, yields an open source file.
-
parameter
fileFile The open source file.
-
parameter
-
returns
String | Object The source contents without a block, or the block result with a block.
Implementation
def read(&block)
@source.read(&block)
end
def freeze
Freeze this coverage and its mutable collections.
Signature
-
returns
Covered::Coverage This frozen coverage object.
Implementation
def freeze
return self if frozen?
@counts.freeze
@annotations.freeze
super
end
def to_a
The raw coverage counts array.
Signature
-
returns
Array(Integer | Nil) The raw coverage counts.
Implementation
def to_a
@counts
end
def zero?
Whether this coverage has no executions.
Signature
-
returns
Boolean Whether the total execution count is zero.
Implementation
def zero?
total.zero?
end
def [](line_number)
The raw coverage count for the given line number.
Signature
-
parameter
line_numberInteger The line number to query.
-
returns
Integer | Nil The execution count for the line.
Implementation
def [] line_number
@counts[line_number]
end
def executable_lines
Counts for lines that are executable.
Signature
-
returns
Array(Integer) Execution counts for executable lines.
Implementation
def executable_lines
@counts.compact
end
def executable_count
The number of executable lines.
Signature
-
returns
Integer The number of executable lines.
Implementation
def executable_count
executable_lines.count
end
def executed_lines
Counts for executable lines that were executed.
Signature
-
returns
Array(Integer) Non-zero execution counts for executable lines.
Implementation
def executed_lines
executable_lines.reject(&:zero?)
end
def executed_count
The number of executable lines that were executed.
Signature
-
returns
Integer The number of executed lines.
Implementation
def executed_count
executed_lines.count
end
def missing_count
The number of executable lines that were not executed.
Signature
-
returns
Integer The number of missing executable lines.
Implementation
def missing_count
executable_count - executed_count
end
def print(output)
Print a human-readable coverage summary.
Signature
-
parameter
outputIO The output stream.
Implementation
def print(output)
output.puts "** #{executed_count}/#{executable_count} lines executed; #{percentage.to_f.round(2)}% covered."
end
def to_s
A human-readable representation of this coverage object.
Signature
-
returns
String A summary including the source path and percentage.
Implementation
def to_s
"\#<#{self.class} path=#{self.path} #{self.percentage.to_f.round(2)}% covered>"
end
def as_json
A JSON-compatible representation of this coverage object.
Signature
-
returns
Hash The coverage counts and summary statistics.
Implementation
def as_json
{
counts: counts,
executable_count: executable_count,
executed_count: executed_count,
percentage: percentage.to_f.round(2),
}
end
def serialize(packer)
Serialize this coverage object with the given packer.
Signature
-
parameter
packerObject The MessagePack-compatible packer.
Implementation
def serialize(packer)
packer.write(@source)
packer.write(@counts)
packer.write(@annotations)
end
def self.deserialize(unpacker)
Deserialize a coverage object from the given unpacker.
Signature
-
parameter
unpackerObject The MessagePack-compatible unpacker.
-
returns
Covered::Coverage The deserialized coverage object.
Implementation
def self.deserialize(unpacker)
source = unpacker.read
counts = unpacker.read
annotations = unpacker.read
self.new(source, counts, annotations)
end