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.
# File lib/rcov/file_statistics.rb, line 29 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
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.
# File lib/rcov/file_statistics.rb, line 79 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
# File lib/rcov/file_statistics.rb, line 87 def code_coverage_for_report code_coverage * 100 end
Returns true if the given line number corresponds to code, as opposed to a comment (either # or =begin/=end blocks).
# File lib/rcov/file_statistics.rb, line 107 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| line.force_encoding("utf-8") if line.respond_to?(:force_encoding) 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 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 the coverage array
considered :inferred if the neither self nor the coverage array indicate that it was definitely executed, but it was inferred in either one
not covered (false) if it was uncovered in both
Execution counts are just summated on a per-line basis.
# File lib/rcov/file_statistics.rb, line 55 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
Number of lines of code (loc).
# File lib/rcov/file_statistics.rb, line 96 def num_code_lines (0...@lines.size).select{|i| is_code? i}.size end
Total number of lines.
# File lib/rcov/file_statistics.rb, line 101 def num_lines @lines.size end
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.
# File lib/rcov/file_statistics.rb, line 72 def total_coverage return 0 if @coverage.size == 0 @coverage.inject(0.0) {|s,a| s + (a ? 1:0) } / @coverage.size end
Generated with the Darkfish Rdoc Generator 2.