valo-rcov 0.8.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/BLURB +111 -0
  2. data/LICENSE +53 -0
  3. data/Rakefile +88 -0
  4. data/THANKS +96 -0
  5. data/bin/rcov +500 -0
  6. data/doc/readme_for_api +41 -0
  7. data/doc/readme_for_emacs +64 -0
  8. data/doc/readme_for_rake +62 -0
  9. data/doc/readme_for_vim +47 -0
  10. data/editor-extensions/rcov.el +131 -0
  11. data/editor-extensions/rcov.vim +38 -0
  12. data/ext/rcovrt/1.8/callsite.c +216 -0
  13. data/ext/rcovrt/1.8/rcovrt.c +287 -0
  14. data/ext/rcovrt/1.9/callsite.c +234 -0
  15. data/ext/rcovrt/1.9/rcovrt.c +264 -0
  16. data/ext/rcovrt/extconf.rb +21 -0
  17. data/lib/rcov.rb +1009 -0
  18. data/lib/rcov/formatters.rb +15 -0
  19. data/lib/rcov/formatters/base_formatter.rb +168 -0
  20. data/lib/rcov/formatters/full_text_report.rb +55 -0
  21. data/lib/rcov/formatters/html_coverage.rb +255 -0
  22. data/lib/rcov/formatters/html_erb_template.rb +128 -0
  23. data/lib/rcov/formatters/text_coverage_diff.rb +199 -0
  24. data/lib/rcov/formatters/text_report.rb +36 -0
  25. data/lib/rcov/formatters/text_summary.rb +15 -0
  26. data/lib/rcov/lowlevel.rb +145 -0
  27. data/lib/rcov/rcovtask.rb +156 -0
  28. data/lib/rcov/templates/detail.html.erb +78 -0
  29. data/lib/rcov/templates/index.html.erb +76 -0
  30. data/lib/rcov/templates/screen.css +165 -0
  31. data/lib/rcov/version.rb +10 -0
  32. data/setup.rb +1588 -0
  33. data/test/assets/sample_01.rb +7 -0
  34. data/test/assets/sample_02.rb +5 -0
  35. data/test/assets/sample_03.rb +20 -0
  36. data/test/assets/sample_04.rb +10 -0
  37. data/test/assets/sample_05-new.rb +17 -0
  38. data/test/assets/sample_05-old.rb +13 -0
  39. data/test/assets/sample_05.rb +17 -0
  40. data/test/call_site_analyzer_test.rb +171 -0
  41. data/test/code_coverage_analyzer_test.rb +184 -0
  42. data/test/file_statistics_test.rb +471 -0
  43. data/test/functional_test.rb +89 -0
  44. data/test/turn_off_rcovrt.rb +4 -0
  45. metadata +107 -0
@@ -0,0 +1,15 @@
1
+ require 'rcov/formatters/html_erb_template'
2
+ require 'rcov/formatters/base_formatter'
3
+ require 'rcov/formatters/text_summary'
4
+ require 'rcov/formatters/text_report'
5
+ require 'rcov/formatters/text_coverage_diff'
6
+ require 'rcov/formatters/full_text_report'
7
+ require 'rcov/formatters/html_coverage'
8
+
9
+ module Rcov
10
+
11
+ module Formatters
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,168 @@
1
+ module Rcov
2
+
3
+ class BaseFormatter # :nodoc:
4
+ require 'pathname'
5
+ require 'mkmf'
6
+ ignore_files = [/\A#{Regexp.escape(Pathname.new(::Config::CONFIG['libdir']).cleanpath.to_s)}/, /\btc_[^.]*.rb/, /_test\.rb\z/, /\btest\//, /\bvendor\//, /\A#{Regexp.escape(__FILE__)}\z/]
7
+
8
+ DEFAULT_OPTS = {:ignore => ignore_files, :sort => :name, :sort_reverse => false,
9
+ :output_threshold => 101, :dont_ignore => [], :callsite_analyzer => nil, :comments_run_by_default => false}
10
+
11
+ def initialize(opts = {})
12
+ options = DEFAULT_OPTS.clone.update(opts)
13
+ @files = {}
14
+ @ignore_files = options[:ignore]
15
+ @dont_ignore_files = options[:dont_ignore]
16
+ @sort_criterium = case options[:sort]
17
+ when :loc then lambda{|fname, finfo| finfo.num_code_lines}
18
+ when :coverage then lambda{|fname, finfo| finfo.code_coverage}
19
+ else lambda{|fname, finfo| fname}
20
+ end
21
+ @sort_reverse = options[:sort_reverse]
22
+ @output_threshold = options[:output_threshold]
23
+ @callsite_analyzer = options[:callsite_analyzer]
24
+ @comments_run_by_default = options[:comments_run_by_default]
25
+ @callsite_index = nil
26
+
27
+ @mangle_filename = Hash.new{|h,base|
28
+ h[base] = Pathname.new(base).cleanpath.to_s.gsub(%r{^\w:[/\\]}, "").gsub(/\./, "_").gsub(/[\\\/]/, "-") + ".html"
29
+ }
30
+ end
31
+
32
+ def add_file(filename, lines, coverage, counts)
33
+ old_filename = filename
34
+ filename = normalize_filename(filename)
35
+ SCRIPT_LINES__[filename] = SCRIPT_LINES__[old_filename]
36
+ if @ignore_files.any?{|x| x === filename} &&
37
+ !@dont_ignore_files.any?{|x| x === filename}
38
+ return nil
39
+ end
40
+ if @files[filename]
41
+ @files[filename].merge(lines, coverage, counts)
42
+ else
43
+ @files[filename] = FileStatistics.new(filename, lines, counts,
44
+ @comments_run_by_default)
45
+ end
46
+ end
47
+
48
+ def normalize_filename(filename)
49
+ File.expand_path(filename).gsub(/^#{Regexp.escape(Dir.getwd)}\//, '')
50
+ end
51
+
52
+ def mangle_filename(base)
53
+ @mangle_filename[base]
54
+ end
55
+
56
+ def each_file_pair_sorted(&b)
57
+ return sorted_file_pairs unless block_given?
58
+ sorted_file_pairs.each(&b)
59
+ end
60
+
61
+ def sorted_file_pairs
62
+ pairs = @files.sort_by do |fname, finfo|
63
+ @sort_criterium.call(fname, finfo)
64
+ end.select{|_, finfo| 100 * finfo.code_coverage < @output_threshold}
65
+ @sort_reverse ? pairs.reverse : pairs
66
+ end
67
+
68
+ def total_coverage
69
+ lines = 0
70
+ total = 0.0
71
+ @files.each do |k,f|
72
+ total += f.num_lines * f.total_coverage
73
+ lines += f.num_lines
74
+ end
75
+ return 0 if lines == 0
76
+ total / lines
77
+ end
78
+
79
+ def code_coverage
80
+ lines = 0
81
+ total = 0.0
82
+ @files.each do |k,f|
83
+ total += f.num_code_lines * f.code_coverage
84
+ lines += f.num_code_lines
85
+ end
86
+ return 0 if lines == 0
87
+ total / lines
88
+ end
89
+
90
+ def num_code_lines
91
+ lines = 0
92
+ @files.each{|k, f| lines += f.num_code_lines }
93
+ lines
94
+ end
95
+
96
+ def num_lines
97
+ lines = 0
98
+ @files.each{|k, f| lines += f.num_lines }
99
+ lines
100
+ end
101
+
102
+ private
103
+ def cross_references_for(filename, lineno)
104
+ return nil unless @callsite_analyzer
105
+ @callsite_index ||= build_callsite_index
106
+ @callsite_index[normalize_filename(filename)][lineno]
107
+ end
108
+
109
+ def reverse_cross_references_for(filename, lineno)
110
+ return nil unless @callsite_analyzer
111
+ @callsite_reverse_index ||= build_reverse_callsite_index
112
+ @callsite_reverse_index[normalize_filename(filename)][lineno]
113
+ end
114
+
115
+ def build_callsite_index
116
+ index = Hash.new{|h,k| h[k] = {}}
117
+ @callsite_analyzer.analyzed_classes.each do |classname|
118
+ @callsite_analyzer.analyzed_methods(classname).each do |methname|
119
+ defsite = @callsite_analyzer.defsite(classname, methname)
120
+ index[normalize_filename(defsite.file)][defsite.line] =
121
+ @callsite_analyzer.callsites(classname, methname)
122
+ end
123
+ end
124
+ index
125
+ end
126
+
127
+ def build_reverse_callsite_index
128
+ index = Hash.new{|h,k| h[k] = {}}
129
+ @callsite_analyzer.analyzed_classes.each do |classname|
130
+ @callsite_analyzer.analyzed_methods(classname).each do |methname|
131
+ callsites = @callsite_analyzer.callsites(classname, methname)
132
+ defsite = @callsite_analyzer.defsite(classname, methname)
133
+ callsites.each_pair do |callsite, count|
134
+ next unless callsite.file
135
+ fname = normalize_filename(callsite.file)
136
+ (index[fname][callsite.line] ||= []) << [classname, methname, defsite, count]
137
+ end
138
+ end
139
+ end
140
+ index
141
+ end
142
+
143
+ class XRefHelper < Struct.new(:file, :line, :klass, :mid, :count) # :nodoc:
144
+ end
145
+
146
+ def _get_defsites(ref_blocks, filename, lineno, linetext, label, &format_call_ref)
147
+ if @do_cross_references and
148
+ (rev_xref = reverse_cross_references_for(filename, lineno))
149
+ refs = rev_xref.map do |classname, methodname, defsite, count|
150
+ XRefHelper.new(defsite.file, defsite.line, classname, methodname, count)
151
+ end.sort_by{|r| r.count}.reverse
152
+ ref_blocks << [refs, label, format_call_ref]
153
+ end
154
+ end
155
+
156
+ def _get_callsites(ref_blocks, filename, lineno, linetext, label, &format_called_ref)
157
+ if @do_callsites and
158
+ (refs = cross_references_for(filename, lineno))
159
+ refs = refs.sort_by{|k,count| count}.map do |ref, count|
160
+ XRefHelper.new(ref.file, ref.line, ref.calling_class, ref.calling_method, count)
161
+ end.reverse
162
+ ref_blocks << [refs, label, format_called_ref]
163
+ end
164
+ end
165
+
166
+ end
167
+
168
+ end
@@ -0,0 +1,55 @@
1
+ module Rcov
2
+
3
+ class FullTextReport < BaseFormatter # :nodoc:
4
+ DEFAULT_OPTS = {:textmode => :coverage}
5
+
6
+ def initialize(opts = {})
7
+ options = DEFAULT_OPTS.clone.update(opts)
8
+ @textmode = options[:textmode]
9
+ @color = options[:color]
10
+ super(options)
11
+ end
12
+
13
+ def execute
14
+ each_file_pair_sorted do |filename, fileinfo|
15
+ puts "=" * 80
16
+ puts filename
17
+ puts "=" * 80
18
+ lines = SCRIPT_LINES__[filename]
19
+
20
+ unless lines
21
+ # try to get the source code from the global code coverage
22
+ # analyzer
23
+ re = /#{Regexp.escape(filename)}\z/
24
+ if $rcov_code_coverage_analyzer and
25
+ (data = $rcov_code_coverage_analyzer.data_matching(re))
26
+ lines = data[0]
27
+ end
28
+ end
29
+
30
+ (lines || []).each_with_index do |line, i|
31
+
32
+ case @textmode
33
+ when :counts
34
+ puts "%-70s| %6d" % [line.chomp[0,70], fileinfo.counts[i]]
35
+ when :gcc
36
+ puts "%s:%d:%s" % [filename, i+1, line.chomp] unless fileinfo.coverage[i]
37
+ when :coverage
38
+ if @color
39
+ prefix = fileinfo.coverage[i] ? "\e[32;40m" : "\e[31;40m"
40
+ puts "#{prefix}%s\e[37;40m" % line.chomp
41
+ else
42
+ prefix = fileinfo.coverage[i] ? " " : "!! "
43
+ puts "#{prefix}#{line}"
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,255 @@
1
+ module Rcov
2
+
3
+ class HTMLCoverage < BaseFormatter # :nodoc:
4
+ require 'fileutils'
5
+
6
+ DEFAULT_OPTS = {:color => false, :fsr => 30, :destdir => "coverage",
7
+ :callsites => false, :cross_references => false,
8
+ :charset => nil }
9
+
10
+ def initialize(opts = {})
11
+ options = DEFAULT_OPTS.clone.update(opts)
12
+ super(options)
13
+ @dest = options[:destdir]
14
+ @color = options[:color]
15
+ @fsr = options[:fsr]
16
+ @do_callsites = options[:callsites]
17
+ @do_cross_references = options[:cross_references]
18
+ @span_class_index = 0
19
+ @charset = options[:charset]
20
+ end
21
+
22
+ def execute
23
+ return if @files.empty?
24
+ FileUtils.mkdir_p @dest
25
+ css_file = File.expand_path("#{File.dirname(__FILE__)}/../templates/screen.css")
26
+ FileUtils.cp(css_file, File.join(@dest, "screen.css"))
27
+
28
+ create_index(File.join(@dest, "index.html"))
29
+
30
+ each_file_pair_sorted do |filename, fileinfo|
31
+ create_file(File.join(@dest, mangle_filename(filename)), fileinfo)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ class SummaryFileInfo # :nodoc:
38
+
39
+ def initialize(obj)
40
+ @o = obj
41
+ end
42
+
43
+ def num_lines
44
+ @o.num_lines
45
+ end
46
+
47
+ def num_code_lines
48
+ @o.num_code_lines
49
+ end
50
+
51
+ def code_coverage
52
+ @o.code_coverage
53
+ end
54
+
55
+ def code_coverage_for_report
56
+ code_coverage * 100
57
+ end
58
+
59
+ def total_coverage
60
+ @o.total_coverage
61
+ end
62
+
63
+ def total_coverage_for_report
64
+ total_coverage * 100
65
+ end
66
+
67
+ def name
68
+ "TOTAL"
69
+ end
70
+
71
+ end
72
+
73
+ def create_index(destname)
74
+ files = [SummaryFileInfo.new(self)] + each_file_pair_sorted.map{|k,v| v}
75
+ doc = Rcov::Formatters::HtmlErbTemplate.new('index.html.erb', :title => 'C0 Coverage Information - RCov',
76
+ :generated_on => Time.now,
77
+ :rcov => Rcov,
78
+ :formatter => self,
79
+ :output_threshold => @output_threshold,
80
+ :files => files)
81
+
82
+ File.open(destname, "w") { |f| f.puts doc.render }
83
+ end
84
+
85
+
86
+ def create_file(destfile, fileinfo)
87
+ doc = Rcov::Formatters::HtmlErbTemplate.new('detail.html.erb', :title => fileinfo.name,
88
+ :generated_on => Time.now,
89
+ :rcov => Rcov,
90
+ :formatter => self,
91
+ :output_threshold => @output_threshold,
92
+ :fileinfo => fileinfo)
93
+ File.open(destfile, "w") { |f| f.puts doc.render }
94
+ end
95
+
96
+ end
97
+
98
+ class HTMLProfiling < HTMLCoverage # :nodoc:
99
+
100
+ DEFAULT_OPTS = {:destdir => "profiling"}
101
+ def initialize(opts = {})
102
+ options = DEFAULT_OPTS.clone.update(opts)
103
+ super(options)
104
+ @max_cache = {}
105
+ @median_cache = {}
106
+ end
107
+
108
+ def default_title
109
+ "Bogo-profile information"
110
+ end
111
+
112
+ def default_color
113
+ if @color
114
+ "rgb(179,205,255)"
115
+ else
116
+ "rgb(255, 255, 255)"
117
+ end
118
+ end
119
+
120
+ def output_color_table?
121
+ false
122
+ end
123
+
124
+ def span_class(sourceinfo, marked, count)
125
+ full_scale_range = @fsr # dB
126
+ nz_count = sourceinfo.counts.select{|x| x && x != 0}
127
+ nz_count << 1 # avoid div by 0
128
+ max = @max_cache[sourceinfo] ||= nz_count.max
129
+ #avg = @median_cache[sourceinfo] ||= 1.0 *
130
+ # nz_count.inject{|a,b| a+b} / nz_count.size
131
+ median = @median_cache[sourceinfo] ||= 1.0 * nz_count.sort[nz_count.size/2]
132
+ max ||= 2
133
+ max = 2 if max == 1
134
+ if marked == true
135
+ count = 1 if !count || count == 0
136
+ idx = 50 + 1.0 * (500/full_scale_range) * Math.log(count/median) / Math.log(10)
137
+ idx = idx.to_i
138
+ idx = 0 if idx < 0
139
+ idx = 100 if idx > 100
140
+ "run#{idx}"
141
+ else
142
+ nil
143
+ end
144
+ end
145
+
146
+ end
147
+
148
+ class RubyAnnotation < BaseFormatter # :nodoc:
149
+ DEFAULT_OPTS = { :destdir => "coverage" }
150
+ def initialize(opts = {})
151
+ options = DEFAULT_OPTS.clone.update(opts)
152
+ super(options)
153
+ @dest = options[:destdir]
154
+ @do_callsites = true
155
+ @do_cross_references = true
156
+
157
+ @mangle_filename = Hash.new{|h,base|
158
+ h[base] = Pathname.new(base).cleanpath.to_s.gsub(%r{^\w:[/\\]}, "").gsub(/\./, "_").gsub(/[\\\/]/, "-") + ".rb"
159
+ }
160
+ end
161
+
162
+ def execute
163
+ return if @files.empty?
164
+ FileUtils.mkdir_p @dest
165
+ each_file_pair_sorted do |filename, fileinfo|
166
+ create_file(File.join(@dest, mangle_filename(filename)), fileinfo)
167
+ end
168
+ end
169
+
170
+ private
171
+
172
+ def format_lines(file)
173
+ result = ""
174
+ format_line = "%#{file.num_lines.to_s.size}d"
175
+ file.num_lines.times do |i|
176
+ line = file.lines[i].chomp
177
+ marked = file.coverage[i]
178
+ count = file.counts[i]
179
+ result << create_cross_refs(file.name, i+1, line, marked) + "\n"
180
+ end
181
+ result
182
+ end
183
+
184
+ def create_cross_refs(filename, lineno, linetext, marked)
185
+ return linetext unless @callsite_analyzer && @do_callsites
186
+ ref_blocks = []
187
+ _get_defsites(ref_blocks, filename, lineno, linetext, ">>") do |ref|
188
+ if ref.file
189
+ ref.file.sub!(%r!^./!, '')
190
+ where = "at #{mangle_filename(ref.file)}:#{ref.line}"
191
+ else
192
+ where = "(C extension/core)"
193
+ end
194
+ "#{ref.klass}##{ref.mid} " + where + ""
195
+ end
196
+ _get_callsites(ref_blocks, filename, lineno, linetext, "<<") do |ref| # "
197
+ ref.file.sub!(%r!^./!, '')
198
+ "#{mangle_filename(ref.file||'C code')}:#{ref.line} " +
199
+ "in #{ref.klass}##{ref.mid}"
200
+ end
201
+
202
+ create_cross_reference_block(linetext, ref_blocks, marked)
203
+ end
204
+
205
+ def create_cross_reference_block(linetext, ref_blocks, marked)
206
+ codelen = 75
207
+ if ref_blocks.empty?
208
+ if marked
209
+ return "%-#{codelen}s #o" % linetext
210
+ else
211
+ return linetext
212
+ end
213
+ end
214
+ ret = ""
215
+ @cross_ref_idx ||= 0
216
+ @known_files ||= sorted_file_pairs.map{|fname, finfo| normalize_filename(fname)}
217
+ ret << "%-#{codelen}s # " % linetext
218
+ ref_blocks.each do |refs, toplabel, label_proc|
219
+ unless !toplabel || toplabel.empty?
220
+ ret << toplabel << " "
221
+ end
222
+ refs.each do |dst|
223
+ dstfile = normalize_filename(dst.file) if dst.file
224
+ dstline = dst.line
225
+ label = label_proc.call(dst)
226
+ if dst.file && @known_files.include?(dstfile)
227
+ ret << "[[" << label << "]], "
228
+ else
229
+ ret << label << ", "
230
+ end
231
+ end
232
+ end
233
+
234
+ ret
235
+ end
236
+
237
+ def create_file(destfile, fileinfo)
238
+ #body = format_lines(fileinfo)
239
+ #File.open(destfile, "w") do |f|
240
+ #f.puts body
241
+ #f.puts footer(fileinfo)
242
+ #end
243
+ end
244
+
245
+ def footer(fileinfo)
246
+ s = "# Total lines : %d\n" % fileinfo.num_lines
247
+ s << "# Lines of code : %d\n" % fileinfo.num_code_lines
248
+ s << "# Total coverage : %3.1f%%\n" % [ fileinfo.total_coverage*100 ]
249
+ s << "# Code coverage : %3.1f%%\n\n" % [ fileinfo.code_coverage*100 ]
250
+ # prevents false positives on Emacs
251
+ s << "# Local " "Variables:\n" "# mode: " "rcov-xref\n" "# End:\n"
252
+ end
253
+ end
254
+
255
+ end