Geminstaller C0 Coverage Information - RCov

/Users/woolley/.rvm/gems/ruby-1.8.7-p174@geminstaller/gems/rcov-0.9.8/lib/rcov/code_coverage_analyzer.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
/Users/woolley/.rvm/gems/ruby-1.8.7-p174@geminstaller/gems/rcov-0.9.8/lib/rcov/code_coverage_analyzer.rb 271 156
7.01%
5.13%

Key

Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.

Coverage Details

1 module Rcov
2   # A CodeCoverageAnalyzer is responsible for tracing code execution and
3   # returning code coverage and execution count information.
4   #
5   # Note that you must <tt>require 'rcov'</tt> before the code you want to
6   # analyze is parsed (i.e. before it gets loaded or required). You can do that
7   # by either invoking ruby with the <tt>-rrcov</tt> command-line option or
8   # just:
9   #  require 'rcov'
10   #  require 'mycode'
11   #  # ....
12   #
13   # == Example
14   #
15   #  analyzer = Rcov::CodeCoverageAnalyzer.new
16   #  analyzer.run_hooked do 
17   #    do_foo  
18   #    # all the code executed as a result of this method call is traced
19   #  end
20   #  # ....
21   #  
22   #  analyzer.run_hooked do 
23   #    do_bar
24   #    # the code coverage information generated in this run is aggregated
25   #    # to the previously recorded one
26   #  end
27   #
28   #  analyzer.analyzed_files   # => ["foo.rb", "bar.rb", ... ]
29   #  lines, marked_info, count_info = analyzer.data("foo.rb")
30   #
31   # In this example, two pieces of code are monitored, and the data generated in
32   # both runs are aggregated. +lines+ is an array of strings representing the 
33   # source code of <tt>foo.rb</tt>. +marked_info+ is an array holding false,
34   # true values indicating whether the corresponding lines of code were reported
35   # as executed by Ruby. +count_info+ is an array of integers representing how
36   # many times each line of code has been executed (more precisely, how many
37   # events where reported by Ruby --- a single line might correspond to several
38   # events, e.g. many method calls).
39   #
40   # You can have several CodeCoverageAnalyzer objects at a time, and it is
41   # possible to nest the #run_hooked / #install_hook/#remove_hook blocks: each
42   # analyzer will manage its data separately. Note however that no special
43   # provision is taken to ignore code executed "inside" the CodeCoverageAnalyzer
44   # class. At any rate this will not pose a problem since it's easy to ignore it
45   # manually: just don't do
46   #   lines, coverage, counts = analyzer.data("/path/to/lib/rcov.rb")
47   # if you're not interested in that information.
48   class CodeCoverageAnalyzer < DifferentialAnalyzer
49     @hook_level = 0
50     # defined this way instead of attr_accessor so that it's covered
51     def self.hook_level      # :nodoc:
52       @hook_level 
53     end
54      
55     def self.hook_level=(x)  # :nodoc: 
56       @hook_level = x 
57     end 
58 
59     def initialize
60       @script_lines__ = SCRIPT_LINES__
61       super(:install_coverage_hook, :remove_coverage_hook,
62             :reset_coverage)
63     end
64   
65     # Return an array with the names of the files whose code was executed inside
66     # the block given to #run_hooked or between #install_hook and #remove_hook.
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
73 
74     # Return the available data about the requested file, or nil if none of its
75     # code was executed or it cannot be found.
76     # The return value is an array with three elements:
77     #  lines, marked_info, count_info = analyzer.data("foo.rb")
78     # +lines+ is an array of strings representing the 
79     # source code of <tt>foo.rb</tt>. +marked_info+ is an array holding false,
80     # true values indicating whether the corresponding lines of code were reported
81     # as executed by Ruby. +count_info+ is an array of integers representing how
82     # many times each line of code has been executed (more precisely, how many
83     # events where reported by Ruby --- a single line might correspond to several
84     # events, e.g. many method calls).
85     #
86     # The returned data corresponds to the aggregation of all the statistics
87     # collected in each #run_hooked or #install_hook/#remove_hook runs. You can
88     # reset the data at any time with #reset to start from scratch.
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
98 
99     # Data for the first file matching the given regexp.
100     # See #data.
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
110 
111     # Execute the code in the given block, monitoring it in order to gather
112     # information about which code was executed.
113     def run_hooked; super end
114 
115     # Start monitoring execution to gather code coverage and execution count
116     # information. Such data will be collected until #remove_hook is called.
117     #
118     # Use #run_hooked instead if possible.
119     def install_hook; super end
120 
121     # Stop collecting code coverage and execution count information.
122     # #remove_hook will also stop collecting info if it is run inside a
123     # #run_hooked block.
124     def remove_hook; super end
125 
126     # Remove the data collected so far. The coverage and execution count
127     # "history" will be erased, and further collection will start from scratch:
128     # no code is considered executed, and therefore all execution counts are 0.
129     # Right after #reset, #analyzed_files will return an empty array, and
130     # #data(filename) will return nil.
131     def reset; super end
132 
133     def dump_coverage_info(formatters) # :nodoc:
134       update_script_lines__
135       raw_data_relative.each do |file, lines|
136         next if @script_lines__.has_key?(file) == false
137         lines = @script_lines__[file]
138         raw_coverage_array = raw_data_relative[file]
139 
140         line_info, marked_info, 
141           count_info = refine_coverage_info(lines, raw_coverage_array)
142         formatters.each do |formatter|
143           formatter.add_file(file, line_info, marked_info, count_info)
144         end
145       end
146       formatters.each{|formatter| formatter.execute}
147     end
148 
149     private
150 
151     def data_default; {} end
152 
153     def raw_data_absolute
154       Rcov::RCOV__.generate_coverage_info
155     end
156 
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
166 
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
185 
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
197 
198     # Try to detect repeated data, based on observed repetitions in line_info:
199     # this is a workaround for SCRIPT_LINES__[filename] including as many copies
200     # of the file as the number of times it was parsed.
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
229 
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
251 
252     def update_script_lines__
253       @script_lines__ = @script_lines__.merge(SCRIPT_LINES__)
254     end
255 
256     public
257 
258     def marshal_dump # :nodoc:
259       # @script_lines__ is updated just before serialization so as to avoid
260       # missing files in SCRIPT_LINES__
261       ivs = {}
262       update_script_lines__
263       instance_variables.each{|iv| ivs[iv] = instance_variable_get(iv)}
264       ivs
265     end
266 
267     def marshal_load(ivs) # :nodoc:
268       ivs.each_pair{|iv, val| instance_variable_set(iv, val)}
269     end
270   end # CodeCoverageAnalyzer
271 end

Generated on Mon May 10 23:40:27 -0700 2010 with rcov 0.9.8