valo-rcov 0.8.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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