gcovinator 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/assets/file_report.html.erb +140 -0
- data/assets/index_report.html.erb +134 -0
- data/exe/gcovinator +3 -0
- data/gcovinator.gemspec +25 -0
- data/lib/gcovinator.rb +49 -0
- data/lib/gcovinator/cli.rb +60 -0
- data/lib/gcovinator/file_coverage.rb +36 -0
- data/lib/gcovinator/file_report.rb +67 -0
- data/lib/gcovinator/gcov_parser.rb +51 -0
- data/lib/gcovinator/index_report.rb +36 -0
- data/lib/gcovinator/version.rb +3 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6080a3f5203d898c659bb2698dd9dcb301414866
|
4
|
+
data.tar.gz: 3b7f30bccae512a97e9a252282cbdd4a26c8652b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 00327a796279fa7d8307065cfcfe2460097ebcae866aa98a8f64fbf163b5d25ecf0c22b74bca2dab939b74bfb2062fa36322d6457f39e03af9304b258bce0a9d
|
7
|
+
data.tar.gz: 50e1da2ffd76f1cb75027f5f4b33ccd2b59f1511baf830f7cf8169d6f3ffe64b9e7823c7583e96291ff30e90d3a619a2860a1a0687d623bceadac4904e100a2d
|
@@ -0,0 +1,140 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Coverage for <%= @source_file_name %></title>
|
4
|
+
<style>
|
5
|
+
body {
|
6
|
+
margin-left: 0px;
|
7
|
+
margin-right: 0px;
|
8
|
+
padding: 0px;
|
9
|
+
}
|
10
|
+
h2 {
|
11
|
+
text-align: center;
|
12
|
+
}
|
13
|
+
#overall_table {
|
14
|
+
margin-left: auto;
|
15
|
+
margin-right: auto;
|
16
|
+
}
|
17
|
+
#overall_table th {
|
18
|
+
text-align: right;
|
19
|
+
}
|
20
|
+
#overall_table td {
|
21
|
+
padding-left: 1ex;
|
22
|
+
}
|
23
|
+
#code_table {
|
24
|
+
border-collapse: collapse;
|
25
|
+
margin-left: auto;
|
26
|
+
margin-right: auto;
|
27
|
+
}
|
28
|
+
#code_table th, #code_table td {
|
29
|
+
margin: 0px;
|
30
|
+
padding: 0px;
|
31
|
+
}
|
32
|
+
#code_table .padded {
|
33
|
+
padding: 0px 0.5ex;
|
34
|
+
}
|
35
|
+
#code_table th {
|
36
|
+
border-bottom: 1px solid black;
|
37
|
+
}
|
38
|
+
table th, table td {
|
39
|
+
vertical-align: top;
|
40
|
+
}
|
41
|
+
.code {
|
42
|
+
font-family: monospace;
|
43
|
+
}
|
44
|
+
.alignright {
|
45
|
+
text-align: right;
|
46
|
+
}
|
47
|
+
.borderright {
|
48
|
+
border-right: 1px solid black;
|
49
|
+
}
|
50
|
+
.normal-odd {
|
51
|
+
background-color: #bbffff;
|
52
|
+
}
|
53
|
+
.normal-even {
|
54
|
+
background-color: #99ffff;
|
55
|
+
}
|
56
|
+
.covered-odd {
|
57
|
+
background-color: #bbffbb;
|
58
|
+
}
|
59
|
+
.covered-even {
|
60
|
+
background-color: #99ff99;
|
61
|
+
}
|
62
|
+
.uncovered-odd {
|
63
|
+
background-color: #ffbbbb;
|
64
|
+
}
|
65
|
+
.uncovered-even {
|
66
|
+
background-color: #ff9999;
|
67
|
+
}
|
68
|
+
</style>
|
69
|
+
</head>
|
70
|
+
<body>
|
71
|
+
<h2>Coverage for <%= @source_file_name %></h2>
|
72
|
+
<table id="overall_table">
|
73
|
+
<tr>
|
74
|
+
<th>Line Coverage:</th>
|
75
|
+
<% if @total_lines > 0 %>
|
76
|
+
<td><%= @covered_lines %> / <%= @total_lines %> = <%= (100.0 * @covered_lines / @total_lines).to_i %>%</td>
|
77
|
+
<% else %>
|
78
|
+
<td>-</td>
|
79
|
+
<% end %>
|
80
|
+
</tr>
|
81
|
+
<tr>
|
82
|
+
<th>Branch Coverage:</th>
|
83
|
+
<% if @total_branches > 0 %>
|
84
|
+
<td><%= @covered_branches %> / <%= @total_branches %> = <%= (100.0 * @covered_branches / @total_branches).to_i %>%</td>
|
85
|
+
<% else %>
|
86
|
+
<td>-</td>
|
87
|
+
<% end %>
|
88
|
+
</tr>
|
89
|
+
</table>
|
90
|
+
<br/>
|
91
|
+
<table id="code_table">
|
92
|
+
<tr>
|
93
|
+
<th><span title="Branch Coverage">BC</span></th>
|
94
|
+
<th><span title="Line Coverage">LC</th></th>
|
95
|
+
<th>Line</th>
|
96
|
+
<th>Source</th>
|
97
|
+
</tr>
|
98
|
+
<% source_file_lines.each_with_index do |line, i| %>
|
99
|
+
<% line_number = i + 1 %>
|
100
|
+
<% line_execution_count = file_coverage.get_line_count(line_number) %>
|
101
|
+
<% if line_execution_count %>
|
102
|
+
<% if line_execution_count > 0 %>
|
103
|
+
<% row_coverage_style = "covered" %>
|
104
|
+
<% else %>
|
105
|
+
<% row_coverage_style = "uncovered" %>
|
106
|
+
<% end %>
|
107
|
+
<% else %>
|
108
|
+
<% row_coverage_style = "normal" %>
|
109
|
+
<% end %>
|
110
|
+
<% evenodd = i & 1 == 0 ? 'even' : 'odd' %>
|
111
|
+
<tr class="<%= row_coverage_style %>-<%= evenodd %>">
|
112
|
+
<td class="borderright">
|
113
|
+
<% if branches = file_coverage.get_branches(line_number) %>
|
114
|
+
<table class="alignright" style="position: relative; width: 100%; height: 100%; margin: 0px; padding: 0px;">
|
115
|
+
<% branches.each do |branch_id, branch_coverage| %>
|
116
|
+
<% if branch_coverage[:taken_count] > 0 %>
|
117
|
+
<% branch_coverage_style = "covered" %>
|
118
|
+
<% else %>
|
119
|
+
<% branch_coverage_style = "uncovered" %>
|
120
|
+
<% end %>
|
121
|
+
<tr class="<%= branch_coverage_style %>-<%= evenodd %>">
|
122
|
+
<td>
|
123
|
+
<% if branch_coverage[:branch_info] %>
|
124
|
+
(<%= branch_coverage[:branch_info] %>)
|
125
|
+
<% end %>
|
126
|
+
<%= branch_coverage[:taken_count] %>
|
127
|
+
</td>
|
128
|
+
</tr>
|
129
|
+
<% end %>
|
130
|
+
</table>
|
131
|
+
<% end %>
|
132
|
+
</td>
|
133
|
+
<td class="padded alignright borderright"><%= line_execution_count %></td>
|
134
|
+
<td class="padded alignright borderright"><%= line_number %></td>
|
135
|
+
<td class="padded code"><%= CGI.escape_html(line.chomp).gsub("\t", " " * 4).gsub(" ", " ") %></td>
|
136
|
+
</tr>
|
137
|
+
<% end %>
|
138
|
+
</table>
|
139
|
+
</body>
|
140
|
+
</html>
|
@@ -0,0 +1,134 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>gcovinator Coverage Report</title>
|
4
|
+
<style>
|
5
|
+
body {
|
6
|
+
margin-left: 0px;
|
7
|
+
margin-right: 0px;
|
8
|
+
padding: 0px;
|
9
|
+
}
|
10
|
+
h2 {
|
11
|
+
text-align: center;
|
12
|
+
}
|
13
|
+
#overall_table {
|
14
|
+
margin-left: auto;
|
15
|
+
margin-right: auto;
|
16
|
+
}
|
17
|
+
#overall_table th {
|
18
|
+
text-align: right;
|
19
|
+
}
|
20
|
+
#overall_table td {
|
21
|
+
padding-left: 1ex;
|
22
|
+
}
|
23
|
+
#code_table {
|
24
|
+
border-collapse: collapse;
|
25
|
+
margin-left: auto;
|
26
|
+
margin-right: auto;
|
27
|
+
}
|
28
|
+
#code_table th, #code_table td {
|
29
|
+
margin: 0px;
|
30
|
+
padding: 0px;
|
31
|
+
}
|
32
|
+
#code_table .padded {
|
33
|
+
padding: 0px 0.5ex;
|
34
|
+
}
|
35
|
+
#code_table th {
|
36
|
+
border-bottom: 1px solid black;
|
37
|
+
}
|
38
|
+
table th, table td {
|
39
|
+
vertical-align: top;
|
40
|
+
}
|
41
|
+
.alignright {
|
42
|
+
text-align: right;
|
43
|
+
}
|
44
|
+
.borderright {
|
45
|
+
border-right: 1px solid black;
|
46
|
+
}
|
47
|
+
.normal-odd {
|
48
|
+
background-color: #bbffff;
|
49
|
+
}
|
50
|
+
.normal-even {
|
51
|
+
background-color: #99ffff;
|
52
|
+
}
|
53
|
+
.covered-odd {
|
54
|
+
background-color: #bbffbb;
|
55
|
+
}
|
56
|
+
.covered-even {
|
57
|
+
background-color: #99ff99;
|
58
|
+
}
|
59
|
+
.uncovered-odd {
|
60
|
+
background-color: #ffbbbb;
|
61
|
+
}
|
62
|
+
.uncovered-even {
|
63
|
+
background-color: #ff9999;
|
64
|
+
}
|
65
|
+
</style>
|
66
|
+
</head>
|
67
|
+
<body>
|
68
|
+
<h2>gcovinator Coverage Report</h2>
|
69
|
+
<table id="overall_table">
|
70
|
+
<tr>
|
71
|
+
<th>Line Coverage:</th>
|
72
|
+
<% if @total_lines > 0 %>
|
73
|
+
<td><%= @covered_lines %> / <%= @total_lines %> = <%= (100.0 * @covered_lines / @total_lines).to_i %>%</td>
|
74
|
+
<% else %>
|
75
|
+
<td>-</td>
|
76
|
+
<% end %>
|
77
|
+
</tr>
|
78
|
+
<tr>
|
79
|
+
<th>Branch Coverage:</th>
|
80
|
+
<% if @total_branches > 0 %>
|
81
|
+
<td><%= @covered_branches %> / <%= @total_branches %> = <%= (100.0 * @covered_branches / @total_branches).to_i %>%</td>
|
82
|
+
<% else %>
|
83
|
+
<td>-</td>
|
84
|
+
<% end %>
|
85
|
+
</tr>
|
86
|
+
</table>
|
87
|
+
<br/>
|
88
|
+
<table id="code_table">
|
89
|
+
<tr>
|
90
|
+
<th><span title="Branch Coverage">BC</span></th>
|
91
|
+
<th><span title="Line Coverage">LC</th></th>
|
92
|
+
<th>File</th>
|
93
|
+
</tr>
|
94
|
+
<% file_reports.sort_by(&:source_file_name).each_with_index do |file_report, i| %>
|
95
|
+
<% if file_report.total_lines > 0 %>
|
96
|
+
<% if file_report.covered_lines == file_report.total_lines %>
|
97
|
+
<% row_coverage_style = "covered" %>
|
98
|
+
<% else %>
|
99
|
+
<% row_coverage_style = "uncovered" %>
|
100
|
+
<% end %>
|
101
|
+
<% else %>
|
102
|
+
<% row_coverage_style = "normal" %>
|
103
|
+
<% end %>
|
104
|
+
<% evenodd = i & 1 == 0 ? 'even' : 'odd' %>
|
105
|
+
<tr class="<%= row_coverage_style %>-<%= evenodd %>">
|
106
|
+
<% if file_report.total_branches > 0 %>
|
107
|
+
<% if file_report.covered_branches == file_report.total_branches %>
|
108
|
+
<% branch_coverage_style = "covered-#{evenodd}" %>
|
109
|
+
<% else %>
|
110
|
+
<% branch_coverage_style = "uncovered-#{evenodd}" %>
|
111
|
+
<% end %>
|
112
|
+
<% else %>
|
113
|
+
<% branch_coverage_style = nil %>
|
114
|
+
<% end %>
|
115
|
+
<td class="padded alignright borderright <%= branch_coverage_style %>">
|
116
|
+
<% if file_report.total_branches > 0 %>
|
117
|
+
<%= file_report.covered_branches %> / <%= file_report.total_branches %> = <%= (100.0 * file_report.covered_branches / file_report.total_branches).to_i %>%
|
118
|
+
<% else %>
|
119
|
+
-
|
120
|
+
<% end %>
|
121
|
+
</td>
|
122
|
+
<td class="padded alignright borderright">
|
123
|
+
<% if file_report.total_lines > 0 %>
|
124
|
+
<%= file_report.covered_lines %> / <%= file_report.total_lines %> = <%= (100.0 * file_report.covered_lines / file_report.total_lines).to_i %>%
|
125
|
+
<% else %>
|
126
|
+
-
|
127
|
+
<% end %>
|
128
|
+
</td>
|
129
|
+
<td class="padded"><a href="<%= file_report.report_file_name %>"><%= file_report.source_file_name %></a></td>
|
130
|
+
</tr>
|
131
|
+
<% end %>
|
132
|
+
</table>
|
133
|
+
</body>
|
134
|
+
</html>
|
data/exe/gcovinator
ADDED
data/gcovinator.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gcovinator/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "gcovinator"
|
8
|
+
spec.version = Gcovinator::VERSION
|
9
|
+
spec.authors = ["Josh Holtrop"]
|
10
|
+
spec.email = ["jholtrop@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{gcovinator generates HTML reports for gcov coverage data}
|
13
|
+
spec.description = %q{gcovinator generates HTML reports for gcov coverage data. It provides a command-line executable that can be run after the .gcda and .gcno files are present. It executes gcov, reads the .gcov output files, and combines the original source files with the coverage data into HTML reports.}
|
14
|
+
spec.homepage = "https://github.com/holtrop/gcovinator"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = Dir["{assets,exe,lib}/**/*", "*.gemspec"]
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
25
|
+
end
|
data/lib/gcovinator.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative "gcovinator/file_coverage"
|
2
|
+
require_relative "gcovinator/file_report"
|
3
|
+
require_relative "gcovinator/gcov_parser"
|
4
|
+
require_relative "gcovinator/index_report"
|
5
|
+
require_relative "gcovinator/version"
|
6
|
+
require "fileutils"
|
7
|
+
require "open3"
|
8
|
+
require "pathname"
|
9
|
+
require "tmpdir"
|
10
|
+
|
11
|
+
module Gcovinator
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
def run(build_dir, source_dirs, files, output_dir, prefix)
|
16
|
+
build_dir = Pathname.new(File.expand_path(build_dir)).cleanpath.to_s
|
17
|
+
source_dirs = ["."] if source_dirs.empty?
|
18
|
+
source_dirs = source_dirs.map do |s|
|
19
|
+
Pathname.new(File.expand_path(s)).cleanpath.to_s
|
20
|
+
end
|
21
|
+
files = Dir["#{build_dir}/**/*.gcda"] if files.empty?
|
22
|
+
files = files.map do |f|
|
23
|
+
Pathname.new(File.expand_path(f)).cleanpath.to_s
|
24
|
+
end
|
25
|
+
output_dir = Pathname.new(File.expand_path(output_dir)).cleanpath.to_s
|
26
|
+
prefix = Pathname.new(File.expand_path(prefix)).cleanpath.to_s
|
27
|
+
file_coverages = {}
|
28
|
+
Dir.mktmpdir do |dir|
|
29
|
+
Dir.chdir(dir) do
|
30
|
+
files.each do |f|
|
31
|
+
stdout, stderr, status = Open3.capture3("gcov", "-bc", f)
|
32
|
+
gcov_files = Dir["*.gcov"]
|
33
|
+
gcov_files.each do |gcov_file|
|
34
|
+
GcovParser.parse(gcov_file, file_coverages, build_dir, source_dirs)
|
35
|
+
File.unlink(gcov_file)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
FileUtils.mkdir_p(output_dir)
|
41
|
+
file_reports = file_coverages.each_with_index.map do |(source_file_name, file_coverage), i|
|
42
|
+
FileReport.new(source_file_name, file_coverage, prefix, output_dir, sprintf("s%04d.html", i))
|
43
|
+
end
|
44
|
+
IndexReport.new(output_dir, file_reports)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "gcovinator"
|
2
|
+
require "optparse"
|
3
|
+
|
4
|
+
module Gcovinator
|
5
|
+
module Cli
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def run(argv)
|
10
|
+
argv = argv.dup
|
11
|
+
build_dir = "."
|
12
|
+
source_dirs = []
|
13
|
+
output_dir = "coverage"
|
14
|
+
prefix = "."
|
15
|
+
|
16
|
+
OptionParser.new do |opts|
|
17
|
+
|
18
|
+
opts.banner = "Usage: #{$0} [options] [FILES]"
|
19
|
+
|
20
|
+
opts.separator ""
|
21
|
+
opts.separator "Pass paths to .gcda files as FILES."
|
22
|
+
opts.separator "If no FILES are specified, gcovinator looks for all .gcda files recursively under the build directory."
|
23
|
+
opts.separator ""
|
24
|
+
opts.separator "Options:"
|
25
|
+
|
26
|
+
opts.on("-b BUILDDIR", "--build-dir BUILDDIR", "Specify the build directory. Source file paths in object/gcov files will be relative to this directory. Defaults to '.' if not specified.") do |b|
|
27
|
+
build_dir = b
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on_tail("-h", "--help", "Show this help.") do
|
31
|
+
puts opts
|
32
|
+
exit 0
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("-o OUTPUTDIR", "--output-dir OUTPUTDIR", "Specify output directory. HTML reports will be written to this directory. Defaults to 'coverage' if not specified.") do |o|
|
36
|
+
output_dir = o
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("-p PREFIX", "--prefix PREFIX", "Prefix path to strip from source file paths. Defaults to '.' if not specified.") do |p|
|
40
|
+
prefix = p
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("-s SRCDIR", "--source-dir SRCDIR", "Specify a source directory. Reports will only be generated for sources under a specified source directory. Multiple source directories may be specified. Defaults to '.' if not specified.") do |s|
|
44
|
+
source_dirs << s
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on_tail("--version", "Show version") do
|
48
|
+
puts "gcovinator, version #{Gcovinator::VERSION}"
|
49
|
+
exit 0
|
50
|
+
end
|
51
|
+
|
52
|
+
end.parse!(argv)
|
53
|
+
|
54
|
+
Gcovinator.run(build_dir, source_dirs, argv, output_dir, prefix)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Gcovinator
|
2
|
+
class FileCoverage
|
3
|
+
|
4
|
+
attr_reader :line_counts
|
5
|
+
attr_reader :branches
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@line_counts = {}
|
9
|
+
@branches = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def log_line_count(line_number, count)
|
13
|
+
@line_counts[line_number] ||= 0
|
14
|
+
@line_counts[line_number] += count
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_line_count(line_number)
|
18
|
+
@line_counts[line_number]
|
19
|
+
end
|
20
|
+
|
21
|
+
def log_branch(line_number, branch_id, taken_count, branch_info)
|
22
|
+
@branches[line_number] ||= {}
|
23
|
+
@branches[line_number][branch_id] ||= {
|
24
|
+
taken_count: 0,
|
25
|
+
branch_info: nil,
|
26
|
+
}
|
27
|
+
@branches[line_number][branch_id][:taken_count] += taken_count
|
28
|
+
@branches[line_number][branch_id][:branch_info] = branch_info
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_branches(line_number)
|
32
|
+
@branches[line_number]
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Gcovinator
|
2
|
+
class FileReport
|
3
|
+
|
4
|
+
attr_reader :total_lines
|
5
|
+
attr_reader :covered_lines
|
6
|
+
attr_reader :total_branches
|
7
|
+
attr_reader :covered_branches
|
8
|
+
attr_reader :source_file_name
|
9
|
+
attr_reader :report_file_name
|
10
|
+
|
11
|
+
def initialize(source_file_name, file_coverage, prefix, output_dir, report_file_name)
|
12
|
+
@source_file_name = clean_source_file_name(source_file_name, prefix)
|
13
|
+
@report_file_name = report_file_name
|
14
|
+
@total_lines = 0
|
15
|
+
@covered_lines = 0
|
16
|
+
@total_branches = 0
|
17
|
+
@covered_branches = 0
|
18
|
+
run(source_file_name, File.join(output_dir, report_file_name), file_coverage)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def clean_source_file_name(source_file_name, prefix)
|
24
|
+
prefix_test = "#{prefix}/"
|
25
|
+
if source_file_name.start_with?(prefix_test)
|
26
|
+
return source_file_name[prefix_test.size, source_file_name.size]
|
27
|
+
end
|
28
|
+
source_file_name
|
29
|
+
end
|
30
|
+
|
31
|
+
def run(source_file_name, output_file_name, file_coverage)
|
32
|
+
analyze(file_coverage)
|
33
|
+
require "cgi"
|
34
|
+
require "erb"
|
35
|
+
source_file = read_source_file(source_file_name)
|
36
|
+
source_file_lines = source_file.lines.to_a
|
37
|
+
file_report_template = File.read(File.join(File.dirname(__FILE__), "../../assets/file_report.html.erb"))
|
38
|
+
erb = ERB.new(file_report_template, nil, "<>")
|
39
|
+
report = erb.result(binding.clone)
|
40
|
+
File.open(output_file_name, "w") do |fh|
|
41
|
+
fh.write(report)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def analyze(file_coverage)
|
46
|
+
file_coverage.line_counts.each do |line_number, count|
|
47
|
+
@total_lines += 1
|
48
|
+
@covered_lines += 1 if count > 0
|
49
|
+
end
|
50
|
+
file_coverage.branches.each do |line_number, branches|
|
51
|
+
branches.each do |branch_id, branch_coverage|
|
52
|
+
@total_branches += 1
|
53
|
+
@covered_branches += 1 if branch_coverage[:taken_count] > 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def read_source_file(source_file_name)
|
59
|
+
begin
|
60
|
+
File.read(source_file_name)
|
61
|
+
rescue
|
62
|
+
%[<Could not open #{source_file_name}>]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Gcovinator
|
2
|
+
module GcovParser
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def parse(gcov_file, file_coverages, build_dir, source_dirs)
|
6
|
+
gcov_file = File.read(gcov_file)
|
7
|
+
file_coverage = nil
|
8
|
+
current_line_number = nil
|
9
|
+
gcov_file.each_line do |line|
|
10
|
+
if line =~ /^\s+\S*:\s*\d*:Source:(.*)$/
|
11
|
+
filename = $1
|
12
|
+
unless Pathname.new(filename).absolute?
|
13
|
+
filename = Pathname.new(File.join(build_dir, filename)).cleanpath.to_s
|
14
|
+
end
|
15
|
+
filename = filename.gsub("\\", "/")
|
16
|
+
unless source_dirs.any? {|s| filename.start_with?("#{s}/")}
|
17
|
+
return
|
18
|
+
end
|
19
|
+
file_coverages[filename] ||= FileCoverage.new
|
20
|
+
file_coverage = file_coverages[filename]
|
21
|
+
next
|
22
|
+
end
|
23
|
+
next unless file_coverage
|
24
|
+
if line =~ /^\s*(\S+):\s*(\d+):/
|
25
|
+
line_coverage, current_line_number = $1, $2.to_i
|
26
|
+
if (line_coverage =~ /^#+$/) or
|
27
|
+
(line_coverage =~ /^\$+$/) or
|
28
|
+
(line_coverage =~ /^=+$/)
|
29
|
+
file_coverage.log_line_count(current_line_number, 0)
|
30
|
+
elsif line_coverage =~ /^\d+$/
|
31
|
+
file_coverage.log_line_count(current_line_number, line_coverage.to_i)
|
32
|
+
end
|
33
|
+
next
|
34
|
+
end
|
35
|
+
if line =~ /^branch\s+(\d+)\s+(.*)$/
|
36
|
+
branch_id, taken_extra = $1.to_i, $2
|
37
|
+
branch_info = nil
|
38
|
+
if taken_extra =~ /never.executed/
|
39
|
+
taken_count = 0
|
40
|
+
elsif taken_extra =~ /taken\s+(\d+)(?:.*\((.*)\))?/
|
41
|
+
taken_count = $1.to_i
|
42
|
+
branch_info = $2
|
43
|
+
end
|
44
|
+
file_coverage.log_branch(current_line_number, branch_id, taken_count, branch_info)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Gcovinator
|
2
|
+
class IndexReport
|
3
|
+
|
4
|
+
def initialize(output_dir, file_reports)
|
5
|
+
@total_lines = 0
|
6
|
+
@covered_lines = 0
|
7
|
+
@total_branches = 0
|
8
|
+
@covered_branches = 0
|
9
|
+
run(File.join(output_dir, "index.html"), file_reports)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def run(output_file_name, file_reports)
|
15
|
+
analyze(file_reports)
|
16
|
+
require "cgi"
|
17
|
+
require "erb"
|
18
|
+
index_report_template = File.read(File.join(File.dirname(__FILE__), "../../assets/index_report.html.erb"))
|
19
|
+
erb = ERB.new(index_report_template, nil, "<>")
|
20
|
+
report = erb.result(binding.clone)
|
21
|
+
File.open(output_file_name, "w") do |fh|
|
22
|
+
fh.write(report)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def analyze(file_reports)
|
27
|
+
file_reports.each do |file_report|
|
28
|
+
@total_lines += file_report.total_lines
|
29
|
+
@covered_lines += file_report.covered_lines
|
30
|
+
@total_branches += file_report.total_branches
|
31
|
+
@covered_branches += file_report.covered_branches
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gcovinator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Josh Holtrop
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-01-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description: gcovinator generates HTML reports for gcov coverage data. It provides
|
56
|
+
a command-line executable that can be run after the .gcda and .gcno files are present.
|
57
|
+
It executes gcov, reads the .gcov output files, and combines the original source
|
58
|
+
files with the coverage data into HTML reports.
|
59
|
+
email:
|
60
|
+
- jholtrop@gmail.com
|
61
|
+
executables:
|
62
|
+
- gcovinator
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- assets/file_report.html.erb
|
67
|
+
- assets/index_report.html.erb
|
68
|
+
- exe/gcovinator
|
69
|
+
- gcovinator.gemspec
|
70
|
+
- lib/gcovinator.rb
|
71
|
+
- lib/gcovinator/cli.rb
|
72
|
+
- lib/gcovinator/file_coverage.rb
|
73
|
+
- lib/gcovinator/file_report.rb
|
74
|
+
- lib/gcovinator/gcov_parser.rb
|
75
|
+
- lib/gcovinator/index_report.rb
|
76
|
+
- lib/gcovinator/version.rb
|
77
|
+
homepage: https://github.com/holtrop/gcovinator
|
78
|
+
licenses:
|
79
|
+
- MIT
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.5.1
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: gcovinator generates HTML reports for gcov coverage data
|
101
|
+
test_files: []
|