Rcov::CodeCoverageAnalyzer

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'
 # ....

Example

 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.

Public Class Methods

new() click to toggle source
    # File lib/rcov/code_coverage_analyzer.rb, line 59
59:     def initialize
60:       @script_lines__ = SCRIPT_LINES__
61:       super(:install_coverage_hook, :remove_coverage_hook,
62:             :reset_coverage)
63:     end

Public Instance Methods

analyzed_files() click to toggle source

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
data(filename) click to toggle source

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_matching(filename_re) click to toggle source

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
install_hook() click to toggle source

Start monitoring execution to gather code coverage and execution count information. Such data will be collected until # is called.

Use # instead if possible.

     # File lib/rcov/code_coverage_analyzer.rb, line 119
119:     def install_hook; super end
remove_hook() click to toggle source

Stop collecting code coverage and execution count information. # will also stop collecting info if it is run inside a # block.

     # File lib/rcov/code_coverage_analyzer.rb, line 124
124:     def remove_hook; super end
reset() click to toggle source

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
run_hooked() click to toggle source

Execute the code in the given block, monitoring it in order to gather information about which code was executed.

     # File lib/rcov/code_coverage_analyzer.rb, line 113
113:     def run_hooked; super end

Private Instance Methods

aggregate_data(aggregated_data, delta) click to toggle source
     # 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
braindead_factorize(num) click to toggle source
     # 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
compute_raw_data_difference(first, last) click to toggle source
     # 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
data_default() click to toggle source
     # File lib/rcov/code_coverage_analyzer.rb, line 151
151:     def data_default; {} end
raw_data_absolute() click to toggle source
     # File lib/rcov/code_coverage_analyzer.rb, line 153
153:     def raw_data_absolute
154:       Rcov::RCOV__.generate_coverage_info
155:     end
refine_coverage_info(lines, covers) click to toggle source
     # 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
script_lines_workaround(line_info, coverage_info, count_info) click to toggle source

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
update_script_lines__() click to toggle source
     # File lib/rcov/code_coverage_analyzer.rb, line 252
252:     def update_script_lines__
253:       @script_lines__ = @script_lines__.merge(SCRIPT_LINES__)
254:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.