rcov 0.9.3-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/BLURB +111 -0
  2. data/LICENSE +53 -0
  3. data/Rakefile +103 -0
  4. data/THANKS +110 -0
  5. data/bin/rcov +514 -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/java/src/CallsiteHook.java +137 -0
  13. data/ext/java/src/CoverageHook.java +117 -0
  14. data/ext/java/src/RcovHook.java +9 -0
  15. data/ext/java/src/RcovrtService.java +130 -0
  16. data/lib/rcov.rb +33 -0
  17. data/lib/rcov/call_site_analyzer.rb +225 -0
  18. data/lib/rcov/code_coverage_analyzer.rb +268 -0
  19. data/lib/rcov/coverage_info.rb +36 -0
  20. data/lib/rcov/differential_analyzer.rb +116 -0
  21. data/lib/rcov/file_statistics.rb +334 -0
  22. data/lib/rcov/formatters.rb +13 -0
  23. data/lib/rcov/formatters/base_formatter.rb +173 -0
  24. data/lib/rcov/formatters/failure_report.rb +15 -0
  25. data/lib/rcov/formatters/full_text_report.rb +48 -0
  26. data/lib/rcov/formatters/html_coverage.rb +274 -0
  27. data/lib/rcov/formatters/html_erb_template.rb +62 -0
  28. data/lib/rcov/formatters/text_coverage_diff.rb +193 -0
  29. data/lib/rcov/formatters/text_report.rb +32 -0
  30. data/lib/rcov/formatters/text_summary.rb +11 -0
  31. data/lib/rcov/lowlevel.rb +146 -0
  32. data/lib/rcov/rcovtask.rb +155 -0
  33. data/lib/rcov/templates/detail.html.erb +64 -0
  34. data/lib/rcov/templates/index.html.erb +93 -0
  35. data/lib/rcov/templates/jquery-1.3.2.min.js +19 -0
  36. data/lib/rcov/templates/jquery.tablesorter.min.js +15 -0
  37. data/lib/rcov/templates/print.css +12 -0
  38. data/lib/rcov/templates/rcov.js +42 -0
  39. data/lib/rcov/templates/screen.css +270 -0
  40. data/lib/rcov/version.rb +10 -0
  41. data/lib/rcovrt.jar +0 -0
  42. data/setup.rb +1588 -0
  43. data/test/assets/sample_01.rb +7 -0
  44. data/test/assets/sample_02.rb +5 -0
  45. data/test/assets/sample_03.rb +20 -0
  46. data/test/assets/sample_04.rb +10 -0
  47. data/test/assets/sample_05-new.rb +17 -0
  48. data/test/assets/sample_05-old.rb +13 -0
  49. data/test/assets/sample_05.rb +17 -0
  50. data/test/assets/sample_06.rb +8 -0
  51. data/test/call_site_analyzer_test.rb +171 -0
  52. data/test/code_coverage_analyzer_test.rb +219 -0
  53. data/test/file_statistics_test.rb +471 -0
  54. data/test/functional_test.rb +91 -0
  55. data/test/turn_off_rcovrt.rb +4 -0
  56. metadata +120 -0
@@ -0,0 +1,62 @@
1
+ module Rcov
2
+ module Formatters
3
+ class HtmlErbTemplate
4
+ attr_accessor :local_variables
5
+
6
+ def initialize(template_file, locals={})
7
+ require "erb"
8
+
9
+ template_path = File.expand_path("#{File.dirname(__FILE__)}/../templates/#{template_file}")
10
+ @template = ERB.new(File.read(template_path))
11
+ @local_variables = locals
12
+ @path_relativizer = Hash.new{|h,base|
13
+ h[base] = Pathname.new(base).cleanpath.to_s.gsub(%r{^\w:[/\\]}, "").gsub(/\./, "_").gsub(/[\\\/]/, "-") + ".html"
14
+ }
15
+ end
16
+
17
+ def render
18
+ @template.result(get_binding)
19
+ end
20
+
21
+ def coverage_threshold_classes(percentage)
22
+ return 110 if percentage == 100
23
+ return (1..10).find_all{|i| i * 10 > percentage}.map{|i| i.to_i * 10} * " "
24
+ end
25
+
26
+ def code_coverage_html(code_coverage_percentage, is_total=false)
27
+ %{<div class="percent_graph_legend"><tt class='#{ is_total ? 'coverage_total' : ''}'>#{ "%3.2f" % code_coverage_percentage }%</tt></div>
28
+ <div class="percent_graph">
29
+ <div class="covered" style="width:#{ code_coverage_percentage.round }px"></div>
30
+ <div class="uncovered" style="width:#{ 100 - code_coverage_percentage.round }px"></div>
31
+ </div>}
32
+ end
33
+
34
+ def file_filter_classes(file_path)
35
+ file_path.split('/')[0..-2] * " "
36
+ end
37
+
38
+ def relative_filename(path)
39
+ @path_relativizer[path]
40
+ end
41
+
42
+ def line_css(line_number)
43
+ case fileinfo.coverage[line_number]
44
+ when true
45
+ "marked"
46
+ when :inferred
47
+ "inferred"
48
+ else
49
+ "uncovered"
50
+ end
51
+ end
52
+
53
+ def method_missing(key, *args)
54
+ local_variables.has_key?(key) ? local_variables[key] : super
55
+ end
56
+
57
+ def get_binding
58
+ binding
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,193 @@
1
+ module Rcov
2
+ class TextCoverageDiff < BaseFormatter # :nodoc:
3
+ FORMAT_VERSION = [0, 1, 0]
4
+ DEFAULT_OPTS = { :textmode => :coverage_diff, :coverage_diff_mode => :record,
5
+ :coverage_diff_file => "coverage.info", :diff_cmd => "diff",
6
+ :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
+ new_stderr = Tempfile.new("rcov_check_diff")
130
+ STDERR.reopen new_stderr.path
131
+ STDOUT.reopen new_stderr.path
132
+
133
+ retval = system "#{@diff_cmd} --version"
134
+ unless retval
135
+ old_stderr.puts <<EOF
136
+ The '#{@diff_cmd}' executable seems not to be available.
137
+ You can specify which diff executable should be used with --diff-cmd.
138
+ If your system doesn't have one, you might want to use Diff::LCS's:
139
+ gem install diff-lcs
140
+ and use --diff-cmd=ldiff.
141
+ EOF
142
+ return false
143
+ end
144
+ true
145
+ ensure
146
+ STDOUT.reopen old_stdout
147
+ STDERR.reopen old_stderr
148
+ new_stderr.close!
149
+ end
150
+
151
+ def process_unified_diff(filename, diff)
152
+ current_hunk = []
153
+ current_hunk_start = 0
154
+ keep_current_hunk = false
155
+ state = :init
156
+ interesting_hunks = []
157
+ diff.each_with_index do |line, i|
158
+ #puts "#{state} %5d #{line}" % i
159
+ case state
160
+ when :init
161
+ if md = HUNK_HEADER.match(line)
162
+ current_hunk = []
163
+ current_hunk_start = md[1].to_i
164
+ state = :body
165
+ end
166
+ when :body
167
+ case line
168
+ when HUNK_HEADER
169
+ new_start = $1.to_i
170
+ if keep_current_hunk
171
+ interesting_hunks << [current_hunk_start, current_hunk]
172
+ end
173
+ current_hunk_start = new_start
174
+ current_hunk = []
175
+ keep_current_hunk = false
176
+ when /^-/
177
+ # ignore
178
+ when /^\+!! /
179
+ keep_current_hunk = true
180
+ current_hunk << line[1..-1]
181
+ else
182
+ current_hunk << line[1..-1]
183
+ end
184
+ end
185
+ end
186
+ if keep_current_hunk
187
+ interesting_hunks << [current_hunk_start, current_hunk]
188
+ end
189
+
190
+ interesting_hunks
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,32 @@
1
+ module Rcov
2
+ class TextReport < TextSummary # :nodoc:
3
+ def execute
4
+ print_lines
5
+ print_header
6
+ print_lines
7
+
8
+ each_file_pair_sorted do |fname, finfo|
9
+ name = fname.size < 52 ? fname : "..." + fname[-48..-1]
10
+ print_info(name, finfo.num_lines, finfo.num_code_lines,
11
+ finfo.code_coverage)
12
+ end
13
+
14
+ print_lines
15
+ print_info("Total", num_lines, num_code_lines, code_coverage)
16
+ print_lines
17
+ puts summary
18
+ end
19
+
20
+ def print_info(name, lines, loc, coverage)
21
+ puts "|%-51s | %5d | %5d | %5.1f%% |" % [name, lines, loc, 100 * coverage]
22
+ end
23
+
24
+ def print_lines
25
+ puts "+----------------------------------------------------+-------+-------+--------+"
26
+ end
27
+
28
+ def print_header
29
+ puts "| File | Lines | LOC | COV |"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ module Rcov
2
+ class TextSummary < BaseFormatter # :nodoc:
3
+ def execute
4
+ puts summary
5
+ end
6
+
7
+ def summary
8
+ "%.1f%% %d file(s) %d Lines %d LOC" % [code_coverage * 100, @files.size, num_lines, num_code_lines]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,146 @@
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
+
19
+ module RCOV__
20
+ COVER = {}
21
+ CALLSITES = {}
22
+ DEFSITES = {}
23
+ pure_ruby_impl_needed = true
24
+ unless defined? $rcov_do_not_use_rcovrt
25
+ begin
26
+ require 'rcovrt'
27
+ abi = [0,0,0]
28
+ begin
29
+ abi = RCOV__.ABI
30
+ raise if abi[0] != RCOVRT_ABI[0] || abi[1] < RCOVRT_ABI[1]
31
+ pure_ruby_impl_needed = false
32
+ rescue
33
+ $stderr.puts <<-EOF
34
+ The rcovrt extension I found was built for a different version of rcov.
35
+ The required ABI is: #{RCOVRT_ABI.join(".")}
36
+ Your current rcovrt extension is: #{abi.join(".")}
37
+
38
+ Please delete rcovrt.{so,bundle,dll,...} and install the required one.
39
+ EOF
40
+ raise LoadError
41
+ end
42
+ rescue LoadError
43
+ $stderr.puts <<-EOF
44
+
45
+ Since the rcovrt extension couldn't be loaded, rcov will run in pure-Ruby
46
+ mode, which is about two orders of magnitude slower.
47
+
48
+ If you're on win32, you can find a pre-built extension (usable with recent
49
+ One Click Installer and mswin32 builds) at http://eigenclass.org/hiki.rb?rcov .
50
+
51
+ EOF
52
+ end
53
+ end
54
+
55
+ if pure_ruby_impl_needed
56
+ methods = %w[install_coverage_hook remove_coverage_hook reset_coverage
57
+ install_callsite_hook remove_callsite_hook reset_callsite
58
+ generate_coverage_info generate_callsite_info]
59
+ sklass = class << self; self end
60
+ (methods & sklass.instance_methods).each do |meth|
61
+ sklass.class_eval{ remove_method meth }
62
+ end
63
+
64
+ @coverage_hook_activated = @callsite_hook_activated = false
65
+
66
+ def self.install_coverage_hook # :nodoc:
67
+ install_common_hook
68
+ @coverage_hook_activated = true
69
+ end
70
+
71
+ def self.install_callsite_hook # :nodoc:
72
+ install_common_hook
73
+ @callsite_hook_activated = true
74
+ end
75
+
76
+ def self.install_common_hook # :nodoc:
77
+ set_trace_func lambda {|event, file, line, id, binding, klass|
78
+ next unless SCRIPT_LINES__.has_key? file
79
+ case event
80
+ when 'call'
81
+ if @callsite_hook_activated
82
+ receiver = eval("self", binding)
83
+ klass = class << klass; self end unless klass === receiver
84
+ begin
85
+ DEFSITES[[klass.to_s, id.to_s]] = [file, line]
86
+ rescue Exception
87
+ end
88
+ caller_arr = self.format_backtrace_array(caller[1,1])
89
+ begin
90
+ hash = CALLSITES[[klass.to_s, id.to_s]] ||= {}
91
+ hash[caller_arr] ||= 0
92
+ hash[caller_arr] += 1
93
+ #puts "#{event} #{file} #{line} #{klass.inspect} " +
94
+ # "#{klass.object_id} #{id} #{eval('self', binding)}"
95
+ rescue Exception
96
+ end
97
+ end
98
+ when 'c-call', 'c-return', 'class'
99
+ return
100
+ end
101
+ if @coverage_hook_activated
102
+ COVER[file] ||= Array.new(SCRIPT_LINES__[file].size, 0)
103
+ COVER[file][line - 1] ||= 0
104
+ COVER[file][line - 1] += 1
105
+ end
106
+ }
107
+ end
108
+
109
+ def self.remove_coverage_hook # :nodoc:
110
+ @coverage_hook_activated = false
111
+ set_trace_func(nil) if !@callsite_hook_activated
112
+ end
113
+
114
+ def self.remove_callsite_hook # :nodoc:
115
+ @callsite_hook_activated = false
116
+ set_trace_func(nil) if !@coverage_hook_activated
117
+ end
118
+
119
+ def self.reset_coverage # :nodoc:
120
+ COVER.replace({})
121
+ end
122
+
123
+ def self.reset_callsite # :nodoc:
124
+ CALLSITES.replace({})
125
+ DEFSITES.replace({})
126
+ end
127
+
128
+ def self.generate_coverage_info # :nodoc:
129
+ Marshal.load(Marshal.dump(COVER))
130
+ end
131
+
132
+ def self.generate_callsite_info # :nodoc:
133
+ [CALLSITES, DEFSITES]
134
+ end
135
+
136
+ def self.format_backtrace_array(backtrace)
137
+ backtrace.map do |line|
138
+ md = /^([^:]*)(?::(\d+)(?::in `(.*)'))?/.match(line)
139
+ raise "Bad backtrace format" unless md
140
+ [nil, md[3] ? md[3].to_sym : nil, md[1], (md[2] || '').to_i]
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ end