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,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