Class: Rcov::FileStatistics
- Inherits:
-
Object
- Object
- Rcov::FileStatistics
- Defined in:
- lib/rcov.rb
Overview
A FileStatistics object associates a filename to:
-
its source code
-
the per-line coverage information after correction using rcov’s heuristics
-
the per-line execution counts
A FileStatistics object can be therefore be built given the filename, the associated source code, and an array holding execution counts (i.e. how many times each line has been executed).
FileStatistics is relatively intelligent: it handles normal comments, =begin/=end
, heredocs, many multiline-expressions… It uses a number of heuristics to determine what is code and what is a comment, and to refine the initial (incomplete) coverage information.
Basic usage is as follows:
sf = FileStatistics.new("foo.rb", ["puts 1", "if true &&", " false",
"puts 2", "end"], [1, 1, 0, 0, 0])
sf.num_lines # => 5
sf.num_code_lines # => 5
sf.coverage[2] # => true
sf.coverage[3] # => :inferred
sf.code_coverage # => 0.6
The array of strings representing the source code and the array of execution counts would normally be obtained from a Rcov::CodeCoverageAnalyzer.
Instance Attribute Summary collapse
-
#counts ⇒ Object
readonly
Returns the value of attribute counts.
-
#coverage ⇒ Object
readonly
Returns the value of attribute coverage.
-
#lines ⇒ Object
readonly
Returns the value of attribute lines.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
Instance Method Summary collapse
-
#code_coverage ⇒ Object
Code coverage rate: fraction of lines of code executed, relative to the total amount of lines of code (loc).
-
#initialize(name, lines, counts, comments_run_by_default = false) ⇒ FileStatistics
constructor
A new instance of FileStatistics.
-
#is_code?(lineno) ⇒ Boolean
Returns true if the given line number corresponds to code, as opposed to a comment (either # or =begin/=end blocks).
-
#merge(lines, coverage, counts) ⇒ Object
Merge code coverage and execution count information.
-
#num_code_lines ⇒ Object
Number of lines of code (loc).
-
#num_lines ⇒ Object
Total number of lines.
-
#total_coverage ⇒ Object
Total coverage rate if comments are also considered “executable”, given as a fraction, i.e.
Constructor Details
#initialize(name, lines, counts, comments_run_by_default = false) ⇒ FileStatistics
Returns a new instance of FileStatistics.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/rcov.rb', line 83 def initialize(name, lines, counts, comments_run_by_default = false) @name = name @lines = lines initial_coverage = counts.map{|x| (x || 0) > 0 ? true : false } @coverage = CoverageInfo.new initial_coverage @counts = counts @is_begin_comment = nil # points to the line defining the heredoc identifier # but only if it was marked (we don't care otherwise) @heredoc_start = Array.new(lines.size, false) @multiline_string_start = Array.new(lines.size, false) extend_heredocs find_multiline_strings precompute_coverage comments_run_by_default end |
Instance Attribute Details
#counts ⇒ Object (readonly)
Returns the value of attribute counts.
82 83 84 |
# File 'lib/rcov.rb', line 82 def counts @counts end |
#coverage ⇒ Object (readonly)
Returns the value of attribute coverage.
82 83 84 |
# File 'lib/rcov.rb', line 82 def coverage @coverage end |
#lines ⇒ Object (readonly)
Returns the value of attribute lines.
82 83 84 |
# File 'lib/rcov.rb', line 82 def lines @lines end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
82 83 84 |
# File 'lib/rcov.rb', line 82 def name @name end |
Instance Method Details
#code_coverage ⇒ Object
Code coverage rate: fraction of lines of code executed, relative to the total amount of lines of code (loc). Returns a float from 0 to 1.0.
133 134 135 136 137 138 139 |
# File 'lib/rcov.rb', line 133 def code_coverage indices = (0...@lines.size).select{|i| is_code? i } return 0 if indices.size == 0 count = 0 indices.each {|i| count += 1 if @coverage[i] } 1.0 * count / indices.size end |
#is_code?(lineno) ⇒ Boolean
Returns true if the given line number corresponds to code, as opposed to a comment (either # or =begin/=end blocks).
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/rcov.rb', line 153 def is_code?(lineno) unless @is_begin_comment @is_begin_comment = Array.new(@lines.size, false) pending = [] state = :code @lines.each_with_index do |line, index| case state when :code if /^=begin\b/ =~ line state = :comment pending << index end when :comment pending << index if /^=end\b/ =~ line state = :code pending.each{|idx| @is_begin_comment[idx] = true} pending.clear end end end end @lines[lineno] && !@is_begin_comment[lineno] && @lines[lineno] !~ /^\s*(#|$)/ end |
#merge(lines, coverage, counts) ⇒ Object
Merge code coverage and execution count information. As for code coverage, a line will be considered
-
covered for sure (true) if it is covered in either
self
or in thecoverage
array -
considered
:inferred
if the neitherself
nor thecoverage
array indicate that it was definitely executed, but it wasinferred
in either one -
not covered (
false
) if it was uncovered in both
Execution counts are just summated on a per-line basis.
109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/rcov.rb', line 109 def merge(lines, coverage, counts) coverage.each_with_index do |v, idx| case @coverage[idx] when :inferred @coverage[idx] = v || @coverage[idx] when false @coverage[idx] ||= v end end counts.each_with_index{|v, idx| @counts[idx] += v } precompute_coverage false end |
#num_code_lines ⇒ Object
Number of lines of code (loc).
142 143 144 |
# File 'lib/rcov.rb', line 142 def num_code_lines (0...@lines.size).select{|i| is_code? i}.size end |
#num_lines ⇒ Object
Total number of lines.
147 148 149 |
# File 'lib/rcov.rb', line 147 def num_lines @lines.size end |
#total_coverage ⇒ Object
Total coverage rate if comments are also considered “executable”, given as a fraction, i.e. from 0 to 1.0. A comment is attached to the code following it (RDoc-style): it will be considered executed if the the next statement was executed.
126 127 128 129 |
# File 'lib/rcov.rb', line 126 def total_coverage return 0 if @coverage.size == 0 @coverage.inject(0.0) {|s,a| s + (a ? 1:0) } / @coverage.size end |