relevance-rcov 0.9.3-java
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.
- data/BLURB +111 -0
- data/LICENSE +53 -0
- data/Rakefile +94 -0
- data/THANKS +110 -0
- data/bin/rcov +514 -0
- data/doc/readme_for_api.markdown +22 -0
- data/doc/readme_for_emacs.markdown +52 -0
- data/doc/readme_for_rake.markdown +51 -0
- data/doc/readme_for_vim.markdown +34 -0
- data/editor-extensions/rcov.el +131 -0
- data/editor-extensions/rcov.vim +38 -0
- data/ext/java/src/CallsiteHook.java +137 -0
- data/ext/java/src/CoverageHook.java +117 -0
- data/ext/java/src/RcovHook.java +9 -0
- data/ext/java/src/RcovrtService.java +130 -0
- data/lib/rcov.rb +33 -0
- data/lib/rcov/call_site_analyzer.rb +225 -0
- data/lib/rcov/code_coverage_analyzer.rb +268 -0
- data/lib/rcov/coverage_info.rb +36 -0
- data/lib/rcov/differential_analyzer.rb +116 -0
- data/lib/rcov/file_statistics.rb +334 -0
- data/lib/rcov/formatters.rb +13 -0
- data/lib/rcov/formatters/base_formatter.rb +173 -0
- data/lib/rcov/formatters/failure_report.rb +15 -0
- data/lib/rcov/formatters/full_text_report.rb +48 -0
- data/lib/rcov/formatters/html_coverage.rb +274 -0
- data/lib/rcov/formatters/html_erb_template.rb +62 -0
- data/lib/rcov/formatters/text_coverage_diff.rb +193 -0
- data/lib/rcov/formatters/text_report.rb +32 -0
- data/lib/rcov/formatters/text_summary.rb +11 -0
- data/lib/rcov/lowlevel.rb +146 -0
- data/lib/rcov/rcovtask.rb +155 -0
- data/lib/rcov/templates/detail.html.erb +64 -0
- data/lib/rcov/templates/index.html.erb +93 -0
- data/lib/rcov/templates/jquery-1.3.2.min.js +19 -0
- data/lib/rcov/templates/jquery.tablesorter.min.js +15 -0
- data/lib/rcov/templates/print.css +12 -0
- data/lib/rcov/templates/rcov.js +42 -0
- data/lib/rcov/templates/screen.css +270 -0
- data/lib/rcov/version.rb +10 -0
- data/lib/rcovrt.jar +0 -0
- data/setup.rb +1588 -0
- data/test/assets/sample_01.rb +7 -0
- data/test/assets/sample_02.rb +5 -0
- data/test/assets/sample_03.rb +20 -0
- data/test/assets/sample_04.rb +10 -0
- data/test/assets/sample_05-new.rb +17 -0
- data/test/assets/sample_05-old.rb +13 -0
- data/test/assets/sample_05.rb +17 -0
- data/test/assets/sample_06.rb +8 -0
- data/test/call_site_analyzer_test.rb +171 -0
- data/test/code_coverage_analyzer_test.rb +219 -0
- data/test/file_statistics_test.rb +471 -0
- data/test/functional_test.rb +91 -0
- data/test/turn_off_rcovrt.rb +4 -0
- metadata +115 -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,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
|