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,128 @@
1
+ module Rcov
2
+ module Formatters
3
+
4
+ class HtmlErbTemplate
5
+ attr_accessor :local_variables
6
+
7
+ def initialize(template_file, locals={})
8
+ require "erb"
9
+
10
+ template_path = File.expand_path("#{File.dirname(__FILE__)}/../templates/#{template_file}")
11
+ @template = ERB.new(File.read(template_path))
12
+ @local_variables = locals
13
+ @path_relativizer = Hash.new{|h,base|
14
+ # TODO: Waaaahhhhh?
15
+ h[base] = Pathname.new(base).cleanpath.to_s.gsub(%r{^\w:[/\\]}, "").gsub(/\./, "_").gsub(/[\\\/]/, "-") + ".html"
16
+ }
17
+ end
18
+
19
+ def render
20
+ @template.result(get_binding)
21
+ end
22
+
23
+ def relative_filename(path)
24
+ @path_relativizer[path]
25
+ end
26
+
27
+ #def create_cross_refs(filename, lineno, linetext)
28
+ #form = formatter
29
+ #return linetext unless @callsite_analyzer && @do_callsites
30
+
31
+ #ref_blocks = []
32
+ #form.send(:_get_defsites, ref_blocks, filename, lineno, "Calls", linetext) do |ref|
33
+ #if ref.file
34
+ #where = "at #{formatter.normalize_filename(ref.file)}:#{ref.line}"
35
+ #else
36
+ #where = "(C extension/core)"
37
+ #end
38
+ #CGI.escapeHTML("%7d %s" % [ref.count, "#{ref.klass}##{ref.mid} " + where])
39
+ #end
40
+
41
+ #form.send(:_get_callsites, ref_blocks, filename, lineno, "Called by", linetext) do |ref|
42
+ #r = "%7d %s" % [ref.count, "#{formatter.normalize_filename(ref.file||'C code')}:#{ref.line} " + "in '#{ref.klass}##{ref.mid}'"]
43
+ #CGI.escapeHTML(r)
44
+ #end
45
+
46
+ #create_cross_reference_block(linetext, ref_blocks)
47
+ #end
48
+
49
+ #def create_cross_reference_block(linetext, ref_blocks)
50
+ #return linetext if ref_blocks.empty?
51
+ #ret = ""
52
+ #@cross_ref_idx ||= 0
53
+ #@known_files ||= formatter.sorted_file_pairs.map{|fname, finfo| formatter.normalize_filename(fname)}
54
+ #ret << %[<a class="crossref-toggle" href="#" onclick="toggleCode('XREF-#{@cross_ref_idx+=1}'); return false;">#{linetext}</a>]
55
+ #ret << %[<span class="cross-ref" id="XREF-#{@cross_ref_idx}">]
56
+ #ret << "\n"
57
+ #ref_blocks.each do |refs, toplabel, label_proc|
58
+ #unless !toplabel || toplabel.empty?
59
+ #ret << %!<span class="cross-ref-title">#{toplabel}</span>\n!
60
+ #end
61
+ #refs.each do |dst|
62
+ #dstfile = formatter.normalize_filename(dst.file) if dst.file
63
+ #dstline = dst.line
64
+ #label = label_proc.call(dst)
65
+ #if dst.file && @known_files.include?(dstfile)
66
+ #ret << %[<a href="#{formatter.mangle_filename(dstfile)}#line#{dstline}">#{label}</a>]
67
+ #else
68
+ #ret << label
69
+ #end
70
+ #ret << "\n"
71
+ #end
72
+ #end
73
+ #ret << "</span>"
74
+ #end
75
+
76
+ def line_css(line_number)
77
+ case fileinfo.coverage[line_number]
78
+ when true
79
+ "marked"
80
+ when :inferred
81
+ "inferred"
82
+ else
83
+ "uncovered"
84
+ end
85
+ end
86
+
87
+
88
+ #def format_lines(file)
89
+ #result = ""
90
+ #last = nil
91
+ #end_of_span = ""
92
+ #format_line = "%#{file.num_lines.to_s.size}d"
93
+ #file.num_lines.times do |i|
94
+ #line = file.lines[i].chomp
95
+ #marked = file.coverage[i]
96
+ #count = file.counts[i]
97
+ #spanclass = span_class(file, marked, count)
98
+ #if spanclass != last
99
+ #result += end_of_span
100
+ #case spanclass
101
+ #when nil
102
+ #end_of_span = ""
103
+ #else
104
+ #result += %[<span class="#{spanclass}">]
105
+ #end_of_span = "</span>"
106
+ #end
107
+ #end
108
+ #result += %[<a name="line#{i+1}"></a>] + (format_line % (i+1)) +
109
+ #" " + create_cross_refs(file.name, i+1, CGI.escapeHTML(line)) + "\n"
110
+ #last = spanclass
111
+ #end
112
+ #result += end_of_span
113
+ #"<pre>#{result}</pre>"
114
+ #end
115
+
116
+
117
+ def method_missing(key, *args)
118
+ local_variables.has_key?(key) ? local_variables[key] : super
119
+ end
120
+
121
+ def get_binding
122
+ binding
123
+ end
124
+
125
+ end
126
+
127
+ end
128
+ end
@@ -0,0 +1,199 @@
1
+ module Rcov
2
+
3
+ class TextCoverageDiff < BaseFormatter # :nodoc:
4
+ FORMAT_VERSION = [0, 1, 0]
5
+ DEFAULT_OPTS = { :textmode => :coverage_diff, :coverage_diff_mode => :record,
6
+ :coverage_diff_file => "coverage.info", :diff_cmd => "diff", :comments_run_by_default => true }
7
+ HUNK_HEADER = /@@ -\d+,\d+ \+(\d+),(\d+) @@/
8
+
9
+ def SERIALIZER
10
+ # mfp> this was going to be YAML but I caught it failing at basic
11
+ # round-tripping, turning "\n" into "" and corrupting the data, so
12
+ # it must be Marshal for now
13
+ Marshal
14
+ end
15
+
16
+ def initialize(opts = {})
17
+ options = DEFAULT_OPTS.clone.update(opts)
18
+ @textmode = options[:textmode]
19
+ @color = options[:color]
20
+ @mode = options[:coverage_diff_mode]
21
+ @state_file = options[:coverage_diff_file]
22
+ @diff_cmd = options[:diff_cmd]
23
+ @gcc_output = options[:gcc_output]
24
+ super(options)
25
+ end
26
+
27
+ def execute
28
+ case @mode
29
+ when :record
30
+ record_state
31
+ when :compare
32
+ compare_state
33
+ else
34
+ raise "Unknown TextCoverageDiff mode: #{mode.inspect}."
35
+ end
36
+ end
37
+
38
+ def record_state
39
+ state = {}
40
+ each_file_pair_sorted do |filename, fileinfo|
41
+ state[filename] = {:lines => SCRIPT_LINES__[filename], :coverage => fileinfo.coverage.to_a,:counts => fileinfo.counts}
42
+ end
43
+ File.open(@state_file, "w") do |f|
44
+ self.SERIALIZER.dump([FORMAT_VERSION, state], f)
45
+ end
46
+ rescue
47
+ $stderr.puts <<-EOF
48
+ Couldn't save coverage data to #{@state_file}.
49
+ EOF
50
+ end # '
51
+
52
+ require 'tempfile'
53
+ def compare_state
54
+ return unless verify_diff_available
55
+ begin
56
+ format, prev_state = File.open(@state_file){|f| self.SERIALIZER.load(f) }
57
+ rescue
58
+ $stderr.puts <<-EOF
59
+ Couldn't load coverage data from #{@state_file}.
60
+ EOF
61
+ return # '
62
+ end
63
+ if !(Array === format) or
64
+ FORMAT_VERSION[0] != format[0] || FORMAT_VERSION[1] < format[1]
65
+ $stderr.puts <<-EOF
66
+ Couldn't load coverage data from #{@state_file}.
67
+ The file is saved in the format #{format.inspect[0..20]}.
68
+ This rcov executable understands #{FORMAT_VERSION.inspect}.
69
+ EOF
70
+ return # '
71
+ end
72
+ each_file_pair_sorted do |filename, fileinfo|
73
+ old_data = Tempfile.new("#{mangle_filename(filename)}-old")
74
+ new_data = Tempfile.new("#{mangle_filename(filename)}-new")
75
+ if prev_state.has_key? filename
76
+ old_code, old_cov = prev_state[filename].values_at(:lines, :coverage)
77
+ old_code.each_with_index do |line, i|
78
+ prefix = old_cov[i] ? " " : "!! "
79
+ old_data.write "#{prefix}#{line}"
80
+ end
81
+ else
82
+ old_data.write ""
83
+ end
84
+ old_data.close
85
+ SCRIPT_LINES__[filename].each_with_index do |line, i|
86
+ prefix = fileinfo.coverage[i] ? " " : "!! "
87
+ new_data.write "#{prefix}#{line}"
88
+ end
89
+ new_data.close
90
+
91
+ diff = `#{@diff_cmd} -u "#{old_data.path}" "#{new_data.path}"`
92
+ new_uncovered_hunks = process_unified_diff(filename, diff)
93
+ old_data.close!
94
+ new_data.close!
95
+ display_hunks(filename, new_uncovered_hunks)
96
+ end
97
+ end
98
+
99
+ def display_hunks(filename, hunks)
100
+ return if hunks.empty?
101
+ puts
102
+ puts "=" * 80
103
+ puts "!!!!! Uncovered code introduced in #{filename}"
104
+
105
+ hunks.each do |offset, lines|
106
+ if @gcc_output
107
+ lines.each_with_index do |line,i|
108
+ lineno = offset + i
109
+ flag = (/^!! / !~ line) ? "-" : ":"
110
+ prefix = "#{filename}#{flag}#{lineno}#{flag}"
111
+ puts "#{prefix}#{line[3..-1]}"
112
+ end
113
+ elsif @color
114
+ puts "### #{filename}:#{offset}"
115
+ lines.each do |line|
116
+ prefix = (/^!! / !~ line) ? "\e[32;40m" : "\e[31;40m"
117
+ puts "#{prefix}#{line[3..-1].chomp}\e[37;40m"
118
+ end
119
+ else
120
+ puts "### #{filename}:#{offset}"
121
+ puts lines
122
+ end
123
+ end
124
+ end
125
+
126
+ def verify_diff_available
127
+ old_stderr = STDERR.dup
128
+ old_stdout = STDOUT.dup
129
+ # TODO: should use /dev/null or NUL(?), but I don't want to add the
130
+ # win32 check right now
131
+ new_stderr = Tempfile.new("rcov_check_diff")
132
+ STDERR.reopen new_stderr.path
133
+ STDOUT.reopen new_stderr.path
134
+
135
+ retval = system "#{@diff_cmd} --version"
136
+ unless retval
137
+ old_stderr.puts <<EOF
138
+
139
+ The '#{@diff_cmd}' executable seems not to be available.
140
+ You can specify which diff executable should be used with --diff-cmd.
141
+ If your system doesn't have one, you might want to use Diff::LCS's:
142
+ gem install diff-lcs
143
+ and use --diff-cmd=ldiff.
144
+ EOF
145
+ return false
146
+ end
147
+ true
148
+ ensure
149
+ STDOUT.reopen old_stdout
150
+ STDERR.reopen old_stderr
151
+ new_stderr.close!
152
+ end
153
+
154
+
155
+ def process_unified_diff(filename, diff)
156
+ current_hunk = []
157
+ current_hunk_start = 0
158
+ keep_current_hunk = false
159
+ state = :init
160
+ interesting_hunks = []
161
+ diff.each_with_index do |line, i|
162
+ #puts "#{state} %5d #{line}" % i
163
+ case state
164
+ when :init
165
+ if md = HUNK_HEADER.match(line)
166
+ current_hunk = []
167
+ current_hunk_start = md[1].to_i
168
+ state = :body
169
+ end
170
+ when :body
171
+ case line
172
+ when HUNK_HEADER
173
+ new_start = $1.to_i
174
+ if keep_current_hunk
175
+ interesting_hunks << [current_hunk_start, current_hunk]
176
+ end
177
+ current_hunk_start = new_start
178
+ current_hunk = []
179
+ keep_current_hunk = false
180
+ when /^-/
181
+ # ignore
182
+ when /^\+!! /
183
+ keep_current_hunk = true
184
+ current_hunk << line[1..-1]
185
+ else
186
+ current_hunk << line[1..-1]
187
+ end
188
+ end
189
+ end
190
+ if keep_current_hunk
191
+ interesting_hunks << [current_hunk_start, current_hunk]
192
+ end
193
+
194
+ interesting_hunks
195
+ end
196
+
197
+ end
198
+
199
+ end
@@ -0,0 +1,36 @@
1
+ module Rcov
2
+
3
+ class TextReport < TextSummary # :nodoc:
4
+
5
+ def execute
6
+ print_lines
7
+ print_header
8
+ print_lines
9
+
10
+ each_file_pair_sorted do |fname, finfo|
11
+ name = fname.size < 52 ? fname : "..." + fname[-48..-1]
12
+ print_info(name, finfo.num_lines, finfo.num_code_lines,
13
+ finfo.code_coverage)
14
+ end
15
+
16
+ print_lines
17
+ print_info("Total", num_lines, num_code_lines, code_coverage)
18
+ print_lines
19
+ puts summary
20
+ end
21
+
22
+ def print_info(name, lines, loc, coverage)
23
+ puts "|%-51s | %5d | %5d | %5.1f%% |" % [name, lines, loc, 100 * coverage]
24
+ end
25
+
26
+ def print_lines
27
+ puts "+----------------------------------------------------+-------+-------+--------+"
28
+ end
29
+
30
+ def print_header
31
+ puts "| File | Lines | LOC | COV |"
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,15 @@
1
+ module Rcov
2
+
3
+ class TextSummary < BaseFormatter # :nodoc:
4
+
5
+ def execute
6
+ puts summary
7
+ end
8
+
9
+ def summary
10
+ "%.1f%% %d file(s) %d Lines %d LOC" % [code_coverage * 100, @files.size, num_lines, num_code_lines]
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,145 @@
1
+ # rcov Copyright (c) 2004-2006 Mauricio Fernandez <mfp@acm.org>
2
+ #
3
+ # See LEGAL and LICENSE for licensing information.
4
+
5
+ require 'rcov/version'
6
+
7
+ module Rcov
8
+
9
+ # RCOV__ performs the low-level tracing of the execution, gathering code
10
+ # coverage information in the process. The C core made available through the
11
+ # rcovrt extension will be used if possible. Otherwise the functionality
12
+ # will be emulated using set_trace_func, but this is very expensive and
13
+ # will fail if other libraries (e.g. breakpoint) change the trace_func.
14
+ #
15
+ # Do not use this module; it is very low-level and subject to frequent
16
+ # changes. Rcov::CodeCoverageAnalyzer offers a much more convenient and
17
+ # stable interface.
18
+ module RCOV__
19
+ COVER = {}
20
+ CALLSITES = {}
21
+ DEFSITES = {}
22
+ pure_ruby_impl_needed = true
23
+ unless defined? $rcov_do_not_use_rcovrt
24
+ begin
25
+ require 'rcovrt'
26
+ abi = [0,0,0]
27
+ begin
28
+ abi = RCOV__.ABI
29
+ raise if abi[0] != RCOVRT_ABI[0] || abi[1] < RCOVRT_ABI[1]
30
+ pure_ruby_impl_needed = false
31
+ rescue
32
+ $stderr.puts <<-EOF
33
+ The rcovrt extension I found was built for a different version of rcov.
34
+ The required ABI is: #{RCOVRT_ABI.join(".")}
35
+ Your current rcovrt extension is: #{abi.join(".")}
36
+
37
+ Please delete rcovrt.{so,bundle,dll,...} and install the required one.
38
+ EOF
39
+ raise LoadError
40
+ end
41
+ rescue LoadError
42
+ $stderr.puts <<-EOF
43
+
44
+ Since the rcovrt extension couldn't be loaded, rcov will run in pure-Ruby
45
+ mode, which is about two orders of magnitude slower.
46
+
47
+ If you're on win32, you can find a pre-built extension (usable with recent
48
+ One Click Installer and mswin32 builds) at http://eigenclass.org/hiki.rb?rcov .
49
+
50
+ EOF
51
+ end
52
+ end
53
+
54
+ if pure_ruby_impl_needed
55
+ methods = %w[install_coverage_hook remove_coverage_hook reset_coverage
56
+ install_callsite_hook remove_callsite_hook reset_callsite
57
+ generate_coverage_info generate_callsite_info]
58
+ sklass = class << self; self end
59
+ (methods & sklass.instance_methods).each do |meth|
60
+ sklass.class_eval{ remove_method meth }
61
+ end
62
+
63
+ @coverage_hook_activated = @callsite_hook_activated = false
64
+
65
+ def self.install_coverage_hook # :nodoc:
66
+ install_common_hook
67
+ @coverage_hook_activated = true
68
+ end
69
+
70
+ def self.install_callsite_hook # :nodoc:
71
+ install_common_hook
72
+ @callsite_hook_activated = true
73
+ end
74
+
75
+ def self.install_common_hook # :nodoc:
76
+ set_trace_func lambda {|event, file, line, id, binding, klass|
77
+ next unless SCRIPT_LINES__.has_key? file
78
+ case event
79
+ when 'call'
80
+ if @callsite_hook_activated
81
+ receiver = eval("self", binding)
82
+ klass = class << klass; self end unless klass === receiver
83
+ begin
84
+ DEFSITES[[klass.to_s, id.to_s]] = [file, line]
85
+ rescue Exception
86
+ end
87
+ caller_arr = self.format_backtrace_array(caller[1,1])
88
+ begin
89
+ hash = CALLSITES[[klass.to_s, id.to_s]] ||= {}
90
+ hash[caller_arr] ||= 0
91
+ hash[caller_arr] += 1
92
+ #puts "#{event} #{file} #{line} #{klass.inspect} " +
93
+ # "#{klass.object_id} #{id} #{eval('self', binding)}"
94
+ rescue Exception
95
+ end
96
+ end
97
+ when 'c-call', 'c-return', 'class'
98
+ return
99
+ end
100
+ if @coverage_hook_activated
101
+ COVER[file] ||= Array.new(SCRIPT_LINES__[file].size, 0)
102
+ COVER[file][line - 1] ||= 0
103
+ COVER[file][line - 1] += 1
104
+ end
105
+ }
106
+ end
107
+
108
+ def self.remove_coverage_hook # :nodoc:
109
+ @coverage_hook_activated = false
110
+ set_trace_func(nil) if !@callsite_hook_activated
111
+ end
112
+
113
+ def self.remove_callsite_hook # :nodoc:
114
+ @callsite_hook_activated = false
115
+ set_trace_func(nil) if !@coverage_hook_activated
116
+ end
117
+
118
+ def self.reset_coverage # :nodoc:
119
+ COVER.replace({})
120
+ end
121
+
122
+ def self.reset_callsite # :nodoc:
123
+ CALLSITES.replace({})
124
+ DEFSITES.replace({})
125
+ end
126
+
127
+ def self.generate_coverage_info # :nodoc:
128
+ Marshal.load(Marshal.dump(COVER))
129
+ end
130
+
131
+ def self.generate_callsite_info # :nodoc:
132
+ [CALLSITES, DEFSITES]
133
+ end
134
+
135
+ def self.format_backtrace_array(backtrace)
136
+ backtrace.map do |line|
137
+ md = /^([^:]*)(?::(\d+)(?::in `(.*)'))?/.match(line)
138
+ raise "Bad backtrace format" unless md
139
+ [nil, md[3] ? md[3].to_sym : nil, md[1], (md[2] || '').to_i]
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ end