A CodeCoverageAnalyzer is responsible for tracing code execution and returning code coverage and execution count information.
Note that you must require 'rcov' before the code you want to analyze is parsed (i.e. before it gets loaded or required). You can do that by either invoking ruby with the -rrcov command-line option or just:
require 'rcov' require 'mycode' # ....
analyzer = Rcov::CodeCoverageAnalyzer.new analyzer.run_hooked do do_foo # all the code executed as a result of this method call is traced end # .... analyzer.run_hooked do do_bar # the code coverage information generated in this run is aggregated # to the previously recorded one end analyzer.analyzed_files # => ["foo.rb", "bar.rb", ... ] lines, marked_info, count_info = analyzer.data("foo.rb")
In this example, two pieces of code are monitored, and the data generated in both runs are aggregated. lines is an array of strings representing the source code of foo.rb. marked_info is an array holding false, true values indicating whether the corresponding lines of code were reported as executed by Ruby. count_info is an array of integers representing how many times each line of code has been executed (more precisely, how many events where reported by Ruby — a single line might correspond to several events, e.g. many method calls).
You can have several CodeCoverageAnalyzer objects at a time, and it is possible to nest the # / #/# blocks: each analyzer will manage its data separately. Note however that no special provision is taken to ignore code executed “inside” the CodeCoverageAnalyzer class. At any rate this will not pose a problem since it’s easy to ignore it manually: just don’t do
lines, coverage, counts = analyzer.data("/path/to/lib/rcov.rb")
if you’re not interested in that information.
Return an array with the names of the files whose code was executed inside the block given to # or between # and #.
# File lib/rcov/code_coverage_analyzer.rb, line 67 67: def analyzed_files 68: update_script_lines__ 69: raw_data_relative.select do |file, lines| 70: @script_lines__.has_key?(file) 71: end.map{|fname,| fname} 72: end
Return the available data about the requested file, or nil if none of its code was executed or it cannot be found. The return value is an array with three elements:
lines, marked_info, count_info = analyzer.data("foo.rb")
lines is an array of strings representing the source code of foo.rb. marked_info is an array holding false, true values indicating whether the corresponding lines of code were reported as executed by Ruby. count_info is an array of integers representing how many times each line of code has been executed (more precisely, how many events where reported by Ruby — a single line might correspond to several events, e.g. many method calls).
The returned data corresponds to the aggregation of all the statistics collected in each # or #/# runs. You can reset the data at any time with # to start from scratch.
# File lib/rcov/code_coverage_analyzer.rb, line 89 89: def data(filename) 90: raw_data = raw_data_relative 91: update_script_lines__ 92: unless @script_lines__.has_key?(filename) && 93: raw_data.has_key?(filename) 94: return nil 95: end 96: refine_coverage_info(@script_lines__[filename], raw_data[filename]) 97: end
Data for the first file matching the given regexp. See #.
# File lib/rcov/code_coverage_analyzer.rb, line 101 101: def data_matching(filename_re) 102: raw_data = raw_data_relative 103: update_script_lines__ 104: 105: match = raw_data.keys.sort.grep(filename_re).first 106: return nil unless match 107: 108: refine_coverage_info(@script_lines__[match], raw_data[match]) 109: end
Remove the data collected so far. The coverage and execution count “history” will be erased, and further collection will start from scratch: no code is considered executed, and therefore all execution counts are 0. Right after #, # will return an empty array, and # will return nil.
# File lib/rcov/code_coverage_analyzer.rb, line 131 131: def reset; super end
# File lib/rcov/code_coverage_analyzer.rb, line 157 157: def aggregate_data(aggregated_data, delta) 158: delta.each_pair do |file, cov_arr| 159: dest = (aggregated_data[file] ||= Array.new(cov_arr.size, 0)) 160: cov_arr.each_with_index do |x,i| 161: dest[i] ||= 0 162: dest[i] += x.to_i 163: end 164: end 165: end
# File lib/rcov/code_coverage_analyzer.rb, line 230 230: def braindead_factorize(num) 231: return [0] if num == 0 232: return [1] + braindead_factorize(-num) if num < 0 233: factors = [] 234: while num % 2 == 0 235: factors << 2 236: num /= 2 237: end 238: size = num 239: n = 3 240: max = Math.sqrt(num) 241: while n <= max && n <= size 242: while size % n == 0 243: size /= n 244: factors << n 245: end 246: n += 2 247: end 248: factors << size if size != 1 249: factors 250: end
# File lib/rcov/code_coverage_analyzer.rb, line 167 167: def compute_raw_data_difference(first, last) 168: difference = {} 169: last.each_pair do |fname, cov_arr| 170: unless first.has_key?(fname) 171: difference[fname] = cov_arr.clone 172: else 173: orig_arr = first[fname] 174: diff_arr = Array.new(cov_arr.size, 0) 175: changed = false 176: cov_arr.each_with_index do |x, i| 177: diff_arr[i] = diff = (x || 0) - (orig_arr[i] || 0) 178: changed = true if diff != 0 179: end 180: difference[fname] = diff_arr if changed 181: end 182: end 183: difference 184: end
# File lib/rcov/code_coverage_analyzer.rb, line 151 151: def data_default; {} end
# File lib/rcov/code_coverage_analyzer.rb, line 153 153: def raw_data_absolute 154: Rcov::RCOV__.generate_coverage_info 155: end
# File lib/rcov/code_coverage_analyzer.rb, line 186 186: def refine_coverage_info(lines, covers) 187: marked_info = [] 188: count_info = [] 189: lines.size.times do |i| 190: c = covers[i] 191: marked_info << ((c && c > 0) ? true : false) 192: count_info << (c || 0) 193: end 194: 195: script_lines_workaround(lines, marked_info, count_info) 196: end
Try to detect repeated data, based on observed repetitions in line_info: this is a workaround for SCRIPT_LINES__[filename] including as many copies of the file as the number of times it was parsed.
# File lib/rcov/code_coverage_analyzer.rb, line 201 201: def script_lines_workaround(line_info, coverage_info, count_info) 202: is_repeated = lambda do |div| 203: n = line_info.size / div 204: break false unless line_info.size % div == 0 && n > 1 205: different = false 206: n.times do |i| 207: 208: things = (0...div).map { |j| line_info[i + j * n] } 209: if things.uniq.size != 1 210: different = true 211: break 212: end 213: end 214: 215: ! different 216: end 217: 218: factors = braindead_factorize(line_info.size) 219: factors.each do |n| 220: if is_repeated[n] 221: line_info = line_info[0, line_info.size / n] 222: coverage_info = coverage_info[0, coverage_info.size / n] 223: count_info = count_info[0, count_info.size / n] 224: end 225: end if factors.size > 1 # don't even try if it's prime 226: 227: [line_info, coverage_info, count_info] 228: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.