gigpark-rcov 0.8.6

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