rcov 0.8.1.2.0 → 0.9.3

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 (68) hide show
  1. data/BLURB +2 -40
  2. data/LICENSE +2 -5
  3. data/Rakefile +32 -106
  4. data/THANKS +14 -0
  5. data/bin/rcov +277 -1090
  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/{rcov.el → editor-extensions/rcov.el} +0 -0
  11. data/{rcov.vim → editor-extensions/rcov.vim} +0 -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 +12 -2
  17. data/lib/rcov.rb +13 -968
  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 +173 -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 +274 -0
  28. data/lib/rcov/formatters/html_erb_template.rb +62 -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 +16 -17
  33. data/lib/rcov/rcovtask.rb +21 -22
  34. data/lib/rcov/templates/detail.html.erb +64 -0
  35. data/lib/rcov/templates/index.html.erb +93 -0
  36. data/lib/rcov/templates/jquery-1.3.2.min.js +19 -0
  37. data/lib/rcov/templates/jquery.tablesorter.min.js +15 -0
  38. data/lib/rcov/templates/print.css +12 -0
  39. data/lib/rcov/templates/rcov.js +42 -0
  40. data/lib/rcov/templates/screen.css +270 -0
  41. data/lib/rcov/version.rb +5 -8
  42. data/setup.rb +5 -2
  43. data/test/{sample_01.rb → assets/sample_01.rb} +0 -0
  44. data/test/{sample_02.rb → assets/sample_02.rb} +0 -0
  45. data/test/{sample_03.rb → assets/sample_03.rb} +0 -0
  46. data/test/{sample_04.rb → assets/sample_04.rb} +0 -0
  47. data/test/{sample_05-new.rb → assets/sample_05-new.rb} +0 -0
  48. data/test/{sample_05-old.rb → assets/sample_05-old.rb} +0 -0
  49. data/test/{sample_05.rb → assets/sample_05.rb} +0 -0
  50. data/test/{test_CallSiteAnalyzer.rb → call_site_analyzer_test.rb} +57 -81
  51. data/test/{test_CodeCoverageAnalyzer.rb → code_coverage_analyzer_test.rb} +71 -35
  52. data/test/{test_FileStatistics.rb → file_statistics_test.rb} +34 -36
  53. data/test/{test_functional.rb → functional_test.rb} +21 -35
  54. metadata +91 -69
  55. data/CHANGES +0 -177
  56. data/LEGAL +0 -36
  57. data/README.API +0 -42
  58. data/README.emacs +0 -64
  59. data/README.en +0 -130
  60. data/README.rake +0 -62
  61. data/README.rant +0 -68
  62. data/README.vim +0 -47
  63. data/Rantfile +0 -76
  64. data/ext/rcovrt/callsite.c +0 -242
  65. data/ext/rcovrt/rcovrt.c +0 -329
  66. data/lib/rcov/rant.rb +0 -87
  67. data/lib/rcov/report.rb +0 -1236
  68. data/mingw-rbconfig.rb +0 -174
@@ -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
data/lib/rcov/lowlevel.rb CHANGED
@@ -6,15 +6,16 @@ require 'rcov/version'
6
6
 
7
7
  module Rcov
8
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.
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
+
18
19
  module RCOV__
19
20
  COVER = {}
20
21
  CALLSITES = {}
@@ -59,19 +60,19 @@ One Click Installer and mswin32 builds) at http://eigenclass.org/hiki.rb?rcov .
59
60
  (methods & sklass.instance_methods).each do |meth|
60
61
  sklass.class_eval{ remove_method meth }
61
62
  end
62
-
63
+
63
64
  @coverage_hook_activated = @callsite_hook_activated = false
64
65
 
65
66
  def self.install_coverage_hook # :nodoc:
66
67
  install_common_hook
67
68
  @coverage_hook_activated = true
68
69
  end
69
-
70
+
70
71
  def self.install_callsite_hook # :nodoc:
71
72
  install_common_hook
72
73
  @callsite_hook_activated = true
73
74
  end
74
-
75
+
75
76
  def self.install_common_hook # :nodoc:
76
77
  set_trace_func lambda {|event, file, line, id, binding, klass|
77
78
  next unless SCRIPT_LINES__.has_key? file
@@ -109,7 +110,7 @@ One Click Installer and mswin32 builds) at http://eigenclass.org/hiki.rb?rcov .
109
110
  @coverage_hook_activated = false
110
111
  set_trace_func(nil) if !@callsite_hook_activated
111
112
  end
112
-
113
+
113
114
  def self.remove_callsite_hook # :nodoc:
114
115
  @callsite_hook_activated = false
115
116
  set_trace_func(nil) if !@coverage_hook_activated
@@ -140,8 +141,6 @@ One Click Installer and mswin32 builds) at http://eigenclass.org/hiki.rb?rcov .
140
141
  end
141
142
  end
142
143
  end
143
- end # RCOV__
144
-
145
- end # Rcov
144
+ end
146
145
 
147
- # vi: set sw=2:
146
+ end
data/lib/rcov/rcovtask.rb CHANGED
@@ -12,9 +12,9 @@ module Rcov
12
12
  # coverage reports.
13
13
  #
14
14
  # Example:
15
- #
15
+ #
16
16
  # require 'rcov/rcovtask'
17
- #
17
+ #
18
18
  # Rcov::RcovTask.new do |t|
19
19
  # t.libs << "test"
20
20
  # t.test_files = FileList['test/test*.rb']
@@ -103,27 +103,27 @@ module Rcov
103
103
  (@name==:rcov ? "" : " for #{actual_name}")
104
104
  end
105
105
  task @name do
106
- run_code = ''
107
- RakeFileUtils.verbose(@verbose) do
108
- run_code =
109
- case rcov_path
110
- when nil, ''
111
- "-S rcov"
112
- else %!"#{rcov_path}"!
113
- end
106
+ run_code = ''
107
+ RakeFileUtils.verbose(@verbose) do
108
+ run_code =
109
+ case rcov_path
110
+ when nil, ''
111
+ "-S rcov"
112
+ else %!"#{rcov_path}"!
113
+ end
114
114
  ruby_opts = @ruby_opts.clone
115
115
  ruby_opts.push( "-I#{lib_path}" )
116
116
  ruby_opts.push run_code
117
- ruby_opts.push( "-w" ) if @warning
118
- ruby ruby_opts.join(" ") + " " + option_list +
119
- %[ -o "#{@output_dir}" ] +
120
- file_list.collect { |fn| %["#{fn}"] }.join(' ')
121
- end
117
+ ruby_opts.push( "-w" ) if @warning
118
+ ruby ruby_opts.join(" ") + " " + option_list +
119
+ %[ -o "#{@output_dir}" ] +
120
+ file_list.collect { |fn| %["#{fn}"] }.join(' ')
121
+ end
122
122
  end
123
123
 
124
124
  desc "Remove rcov products for #{actual_name}"
125
125
  task paste("clobber_", actual_name) do
126
- rm_r @output_dir rescue nil
126
+ rm_r @output_dir rescue nil
127
127
  end
128
128
 
129
129
  clobber_task = paste("clobber_", actual_name)
@@ -143,14 +143,13 @@ module Rcov
143
143
 
144
144
  def file_list # :nodoc:
145
145
  if ENV['TEST']
146
- FileList[ ENV['TEST'] ]
146
+ FileList[ ENV['TEST'] ]
147
147
  else
148
- result = []
149
- result += @test_files.to_a if @test_files
150
- result += FileList[ @pattern ].to_a if @pattern
151
- FileList[result]
148
+ result = []
149
+ result += @test_files.to_a if @test_files
150
+ result += FileList[ @pattern ].to_a if @pattern
151
+ FileList[result]
152
152
  end
153
153
  end
154
154
  end
155
155
  end
156
-
@@ -0,0 +1,64 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html lang='en' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'>
3
+ <head>
4
+ <title><%= page_title %></title>
5
+ <link href="screen.css" media="all" rel="stylesheet" type="text/css" />
6
+ <link href="print.css" media="print" rel="stylesheet" type="text/css" />
7
+ <% if css %><link href="custom.css" media="all" rel="stylesheet" type="text/css" /><% end %>
8
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
9
+ <script type="text/javascript" src="rcov.js"></script>
10
+ </head>
11
+ <body>
12
+ <h1><%= "#{project_name} C0 Coverage Information - RCov" %></h1>
13
+ <h2><%= page_title %></h2>
14
+
15
+ <% if output_threshold != 101 %>
16
+ <!-- Note: 101 is the default threshold if you don't enter one -->
17
+ <p>Threshold: <%= output_threshold %>%</p>
18
+ <% end %>
19
+
20
+ <div class="report_table_wrapper">
21
+ <table class='report' id='report_table'>
22
+ <thead>
23
+ <tr>
24
+ <th class="left_align">Name</th>
25
+ <th class="right_align">Total Lines</th>
26
+ <th class="right_align">Lines of Code</th>
27
+ <th class="left_align">Total Coverage</th>
28
+ <th class="left_align">Code Coverage</th>
29
+ </tr>
30
+ </thead>
31
+ <tbody>
32
+ <tr>
33
+ <td class="left_align"><a href="<%= relative_filename(fileinfo.name) %>"><%= fileinfo.name %></a></td>
34
+ <td class='right_align'><tt><%= fileinfo.num_lines %></tt></td>
35
+ <td class='right_align'><tt><%= fileinfo.num_code_lines %></tt></td>
36
+ <td class="left_align"><%= code_coverage_html(fileinfo.total_coverage_for_report) %></td>
37
+ <td class="left_align"><%= code_coverage_html(fileinfo.code_coverage_for_report) %></td>
38
+ </tr>
39
+ </tbody>
40
+ </table>
41
+ </div>
42
+
43
+ <h3>Key</h3>
44
+
45
+ <div class="key"><pre><span class='marked'>Code reported as executed by Ruby looks like this...</span><span class='marked1'>and this: this line is also marked as covered.</span><span class='inferred'>Lines considered as run by rcov, but not reported by Ruby, look like this,</span><span class='inferred1'>and this: these lines were inferred by rcov (using simple heuristics).</span><span class='uncovered'>Finally, here's a line marked as not executed.</span></pre></div>
46
+
47
+ <h3>Coverage Details</h3>
48
+
49
+ <table class="details">
50
+ <tbody>
51
+ <% fileinfo.num_lines.times do |i| %>
52
+ <% line = fileinfo.lines[i].chomp %>
53
+ <% count = fileinfo.counts[i] %>
54
+ <tr class="<%= line_css(i) %>">
55
+ <td><pre><a name="line<%= i.next %>"><%= i.next %></a> <%= CGI::escapeHTML(line) %></pre></td>
56
+ </tr>
57
+ <% end %>
58
+ </tbody>
59
+ </table>
60
+
61
+ <p>Generated on <%= generated_on %> with <a href="<%= rcov::UPSTREAM_URL %>">rcov <%= rcov::VERSION %></a></p>
62
+
63
+ </body>
64
+ </html>