gcovtools 1.0.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8314cd41c5ead47cae359e181b5021016ddb7e23
4
+ data.tar.gz: 565783cb5e96dedff1a27c229da4b86aa2fa6bad
5
+ SHA512:
6
+ metadata.gz: 13767da67d715e9cda49872f549f1ee415113c82cefaff960e43e239149eae0e32faeeb5054b661733899a5013892c6d50b581b58fec43675c0afaa5699a56d4
7
+ data.tar.gz: 5aa48a5638137f9613de783e944a41100b5cdc5283e6230f46a04bb811daa2ed2a44c348b253721eca06161ee3de9ad235bc51ca774755f4b0c8567001d9a392
data/.gitignore ADDED
@@ -0,0 +1,37 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ Gemfile.lock
30
+ .ruby-version
31
+ .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
35
+
36
+ ## Emacs
37
+ *~
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Mattias Bergbom
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ gcovtools
2
+ ======
3
+
4
+ ![Travis CI Status](https://travis-ci.org/mattiasbergbom/gcovtools.svg)
5
+
6
+ **gcovtools** provides various tools and utilities for processing coverage data emitted from [llvm-cov](http://llvm.org/docs/CommandGuide/llvm-cov.html).
7
+
8
+ Quick Start
9
+ -----------
10
+
11
+ First, unless you haven't already done so, compile your project using ```clang --coverage``` and digest the output using ```llvm-cov``` (see the `src` dir for examples). You should end up with one or more ```.gcov``` files.
12
+
13
+ Next, install the gem:
14
+
15
+ ```gem install gcovtools```
16
+
17
+ This should furnish you with a ```gcovtools``` executable in your path.
18
+
19
+ Finally, execute ```gcovtools``` in one of multiple possible ways. For instance, generate a HTML coverage report:
20
+
21
+ ```gcovtools convert --format html *.gcov > coverage.html```
22
+
23
+ Disclaimer
24
+ ----------
25
+ This is a work in progress.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ Dir['tasks/**/*.rake'].sort.each { |lib| load lib }
2
+
data/bin/gcovtools ADDED
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "thor"
4
+ require 'mixlib/cli'
5
+
6
+ require_relative '../lib/logging'
7
+ require_relative '../lib/gcovtools'
8
+ require_relative '../lib/ansii_formatter'
9
+ require_relative '../lib/xml_formatter'
10
+ require_relative '../lib/html_formatter'
11
+ require_relative '../lib/json_formatter'
12
+ require_relative '../lib/version'
13
+
14
+ class Util
15
+ def Util.opt_wrap(s, width=61)
16
+ s.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n # ")
17
+ end
18
+ end
19
+
20
+ class MyCLI < Thor
21
+
22
+ class_option :verbose, :type => :boolean, :desc => "Be verbose"
23
+ desc "report FILES", "generate a coverage report from one or more gcov FILES"
24
+
25
+ method_option :recursive, :type => :boolean, :desc => "Search for input files recursively in the directory/ies specified."
26
+
27
+ method_option :include,
28
+ :desc => ::Util.opt_wrap("A list of regex filters which specify which files to include in the report. Will filter on the actual filename (taking into account any path mangling done by llvm-cov) as well as the 'Source' meta attribute in the GCOVTOOLS data.
29
+ Available presets:
30
+ :xcode - Xcode system headers
31
+ :linux - Linux system headers
32
+ "),
33
+ :type => :array
34
+
35
+ method_option :exclude,
36
+ :desc => ::Util.opt_wrap("A list of regex filters which specify which files NOT to include in the report. Will filter on the actual filename (taking into account any path mangling done by llvm-cov) as well as the 'Source' meta attribute in the GCOVTOOLS data. The filters will be applied in the order inclusive-exclusive, where an empty/unspecified filter means to include all files encountered.
37
+
38
+ Available presets:
39
+ :xcode - Xcode system headers
40
+ :linux - Linux system headers
41
+ "),
42
+ :type => :array
43
+
44
+ method_option :inclusive,
45
+ :desc => ::Util.opt_wrap("Filter inclusively instead of exclusively (which is the default)."),
46
+ :type => :boolean
47
+
48
+ method_option :format,
49
+ :aliases => "-f",
50
+ :desc => ::Util.opt_wrap("The output format (ascii, html, xml, json)"),
51
+ :default => "ascii"
52
+
53
+ method_option :css,
54
+ :aliases => "-c",
55
+ :desc => ::Util.opt_wrap("CSS file to reference in HTML output.")
56
+
57
+ method_option :xsl,
58
+ :aliases => "-x",
59
+ :desc => ::Util.opt_wrap("XSL file to reference in XML output.")
60
+
61
+ method_option :recursive,
62
+ :aliases => "-r",
63
+ :desc => ::Util.opt_wrap("Recursively load all .gcov files in the given directory"),
64
+ :type => :boolean
65
+
66
+ method_option :verbose,
67
+ :aliases => "-v",
68
+ :desc => ::Util.opt_wrap("Verbose output"),
69
+ :type => :boolean
70
+
71
+ # --------------------------------------------------------------------------------------- #
72
+ # Generate report
73
+ # --------------------------------------------------------------------------------------- #
74
+ def report(*filenames)
75
+
76
+ fail "Got no filename" unless filenames
77
+
78
+ if options[:verbose]
79
+ GCOVTOOLS::logger.level = Logger::INFO
80
+ end
81
+
82
+ GCOVTOOLS::logger.info "gcovtools v#{GCOVTOOLS::VERSION}"
83
+
84
+ proj = GCOVTOOLS::Project.new
85
+
86
+ filter = {
87
+ :include => [],
88
+ :exclude => []
89
+ }
90
+
91
+ [:include,:exclude].each do |verb|
92
+
93
+ if !options[verb].nil?
94
+ options[verb].each do |f|
95
+ filter[verb] << case f
96
+ when ":xcode" then /Developer\/(Toolchains|Platforms)\/.*\/usr\/include\//
97
+ when ":linux" then /\/usr\/include\//
98
+ when nil then nil
99
+ else /#{f}/
100
+ end
101
+ end # each f
102
+ end
103
+ GCOVTOOLS::logger.info "#{verb.to_s}: #{filter[verb]}"
104
+ end
105
+
106
+ filenames.each do |filename|
107
+ GCOVTOOLS::logger.info "processing: #{filename}"
108
+ if File.directory? filename
109
+ proj.add_dir filename, :recursive => options[:recursive], :include => filter[:include], :exclude => filter[:exclude]
110
+ elsif File.file? filename
111
+ proj.add_file filename, :include => filter[:include], :exclude => filter[:exclude]
112
+ end
113
+ end
114
+
115
+ GCOVTOOLS::logger.info "project has #{proj.files.count} files after filtering"
116
+ GCOVTOOLS::logger.info "#{proj.files.map(&:name).join("\n")}"
117
+
118
+ case options[:format].to_sym
119
+ when :ascii then
120
+ formatter = GCOVTOOLS::ANSIIFormatter.new proj
121
+ formatter.print
122
+ when :html then
123
+ formatter = GCOVTOOLS::HTMLFormatter.new( proj, :css => options[:css] )
124
+ formatter.print
125
+ when :xml then
126
+ formatter = GCOVTOOLS::XMLFormatter.new( proj, :xsl => options[:xsl] )
127
+ formatter.print
128
+ when :json then
129
+ formatter = GCOVTOOLS::JSONFormatter.new proj
130
+ formatter.print
131
+ else
132
+ fail "Invalid output format: #{options[:format]}"
133
+ end
134
+ end
135
+
136
+ end
137
+
138
+ MyCLI.start(ARGV)
data/gcovtools.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ require File.expand_path('../lib/version.rb', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+
5
+ gem.name = 'gcovtools'
6
+ gem.version = GCOVTOOLS::VERSION
7
+ gem.date = '2014-12-09'
8
+ gem.summary = "gcov parser and formatter"
9
+ gem.description = "gcovtools digests .gcov files generated by llvm-cov and translates them into various common formats"
10
+ gem.authors = ["Mattias Bergbom"]
11
+ gem.email = 'mattias.bergbom@gmail.com'
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = ["gcovtools"]
14
+ gem.homepage = 'http://rubygems.org/gems/gcovtools'
15
+ gem.license = 'MIT'
16
+ gem.required_ruby_version = '>= 2.0'
17
+
18
+ gem.add_development_dependency 'rspec'
19
+ gem.add_development_dependency 'rake'
20
+ gem.add_development_dependency 'rubygems-tasks'
21
+ gem.add_development_dependency 'simplecov'
22
+
23
+ gem.add_runtime_dependency 'terminal-table'
24
+ gem.add_runtime_dependency 'term-ansicolor'
25
+ gem.add_runtime_dependency 'thor'
26
+ gem.add_runtime_dependency 'gyoku', '~> 1.0'
27
+
28
+ end
@@ -0,0 +1,34 @@
1
+ require 'term/ansicolor'
2
+ include Term::ANSIColor
3
+
4
+ require_relative './project'
5
+ require_relative './file'
6
+ require_relative './line'
7
+
8
+ module GCOVTOOLS
9
+
10
+ class ANSIIFormatter
11
+
12
+ def initialize project
13
+ @project = project
14
+ end
15
+
16
+ def print
17
+
18
+ @project.files.each do |file|
19
+ puts "#{white}#{bold}=== #{file.meta['Source']} ===#{reset}"
20
+ file.lines.select{|line| line.number > 0}.each do |line|
21
+ col = case line.count
22
+ when :none then dark+white
23
+ when :missed then black+on_red
24
+ else green
25
+ end
26
+ puts col + line.text + reset
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
data/lib/file.rb ADDED
@@ -0,0 +1,149 @@
1
+ require_relative './line'
2
+ require 'pathname'
3
+
4
+ class TrueClass
5
+ def to_i
6
+ return 1
7
+ end
8
+ end
9
+
10
+ class FalseClass
11
+ def to_i
12
+ return 0
13
+ end
14
+ end
15
+
16
+ module GCOVTOOLS
17
+
18
+ class File
19
+
20
+ attr_reader :name, :lines, :meta, :stats
21
+
22
+ def initialize name
23
+ fail "name required" unless name and name.is_a? String
24
+ @name = name
25
+ @lines = {}
26
+ @meta = {}
27
+ @stats = {}
28
+ _update_stats
29
+ end
30
+
31
+ # return lines as array, in order by number
32
+ def lines
33
+ @lines.sort.map{|key,val|val}
34
+ end
35
+
36
+ def add_lines &block
37
+ fail "add_lines requires a block" unless block_given?
38
+ @adding = true
39
+ yield
40
+ @adding = false
41
+ _update_stats
42
+ end
43
+
44
+ def _update_stats
45
+ @stats = {
46
+ :missed_lines => 0,
47
+ :exec_lines => 0,
48
+ :empty_lines => 0,
49
+ :total_exec => 0,
50
+ :total_lines => 0,
51
+ :lines => 0,
52
+ :coverage => 0.0,
53
+ :hits_per_line => 0.0
54
+ }
55
+
56
+ @lines.each do |index,line|
57
+ @stats[:missed_lines] += (line.state == :missed).to_i
58
+ @stats[:exec_lines] += (line.state == :exec).to_i
59
+ @stats[:total_exec] += (line.count.is_a?(Integer) ? line.count : 0 )
60
+ @stats[:empty_lines] += (line.state == :none).to_i
61
+ end
62
+
63
+ @stats[:lines] = @stats[:exec_lines] + @stats[:missed_lines]
64
+ @stats[:total_lines] = @stats[:lines] + @stats[:empty_lines]
65
+
66
+ if @stats[:lines] > 0
67
+ @stats[:coverage] = @stats[:exec_lines].to_f / @stats[:lines].to_f
68
+ @stats[:hits_per_line] = @stats[:total_exec].to_f / @stats[:lines]
69
+ else
70
+ @stats[:coverage] = 1
71
+ @stats[:hits_per_line] = 0
72
+ end
73
+
74
+ @stats[:coverage_s] = sprintf("%0.01f%",100.0*@stats[:coverage])
75
+ @stats[:hits_per_line_s] = sprintf("%0.02f",@stats[:hits_per_line])
76
+
77
+ end
78
+
79
+ def _add_line line
80
+ if line.number == 0
81
+ key,val = /([^:]+):(.*)$/.match(line.text).to_a[1..2]
82
+ @meta[key] = val
83
+ else
84
+ @lines[line.number] = line
85
+ end
86
+ end
87
+
88
+ def <<(line)
89
+ fail "need to be in add_lines block" unless @adding
90
+ _add_line line
91
+ end
92
+
93
+ def self.load filename
94
+ files = []
95
+ file = nil
96
+ ::File.open(filename, "r") do |file_handle|
97
+ file_handle.each_line do |line_|
98
+ line = GCOVTOOLS::Line.parse(line_)
99
+ if line.number == 0
100
+ key,val = /([^:]+):(.*)$/.match(line.text).to_a[1..2]
101
+ if key == 'Source'
102
+ if !file.nil?
103
+ file._update_stats
104
+ files << file
105
+ end # if
106
+ file = GCOVTOOLS::File.new val
107
+ end # if source
108
+ end # if line == 0
109
+ file._add_line line
110
+ end # each line
111
+ end# file_handle
112
+ if file
113
+ file._update_stats
114
+ files << file
115
+ end # if file
116
+ files
117
+ end
118
+
119
+ def self.demangle filename
120
+ result = filename.dup
121
+ if start = result.index(/###/)
122
+ result = result[start..-1]
123
+ end
124
+
125
+ result.gsub!( /(###|#|\^|\.gcov$)/, {"###"=>"/","#"=>"/","^"=>"..",".gcov"=>""} )
126
+ result = ::Pathname.new(result).cleanpath.to_s
127
+ result
128
+ end
129
+
130
+ def merge! other
131
+ other.lines.each do |line|
132
+ if @lines.has_key? line.number
133
+ @lines[line.number].merge! line
134
+ else
135
+ @lines[line.number] = line
136
+ end
137
+ end
138
+ _update_stats
139
+ end
140
+
141
+ def merge other
142
+ result = self.dup
143
+ result.merge! other
144
+ result
145
+ end
146
+
147
+ end # class File
148
+
149
+ end
data/lib/gcovtools.rb ADDED
@@ -0,0 +1,4 @@
1
+ require_relative './line'
2
+ require_relative './file'
3
+ require_relative './project'
4
+ require_relative './version'
@@ -0,0 +1,84 @@
1
+
2
+ require_relative './project'
3
+ require_relative './file'
4
+ require_relative './line'
5
+
6
+ require 'erb'
7
+ require 'cgi'
8
+
9
+ module GCOVTOOLS
10
+
11
+ class HTMLFormatter
12
+
13
+ def get_binding # this is only a helper method to access the objects binding method
14
+ binding
15
+ end
16
+
17
+ def initialize project, va={}
18
+ @project = project
19
+ @css = va[:css]
20
+
21
+ if !@css
22
+ @csslink = <<EOF
23
+ <style>
24
+
25
+ </style>
26
+ EOF
27
+ else
28
+ @csslink = "<link rel=\"stylesheet\" href=\"#{@css}\" />"
29
+ end
30
+
31
+ @template = ::File.read(::File.join(::File.dirname(__FILE__),"html_view.html.erb"))
32
+ end # initialize
33
+
34
+ def class_of line
35
+ case line.count
36
+ when :none then "irrelevant"
37
+ when :missed then "missed"
38
+ else "ok"
39
+ end
40
+ end
41
+
42
+ def class_of_stat value, error_comp, warning_comp=nil
43
+ if eval("#{value}#{error_comp}")
44
+ return "value_error"
45
+ elsif !warning_comp.nil? and eval("#{value}#{warning_comp}")
46
+ return "value_warning"
47
+ else
48
+ return "value_ok"
49
+ end
50
+ end
51
+
52
+ def class_of_file file, error_level, warning_level=nil
53
+ if file.stats[:coverage] <= error_level
54
+ return "header error"
55
+ elsif !warning_level.nil? and file.stats[:coverage] < warning_level
56
+ return "header warning"
57
+ else
58
+ return "header ok"
59
+ end
60
+ end
61
+
62
+ def count_of line
63
+ case line.count
64
+ when :none then ""
65
+ when :missed then "<strong>miss</strong>"
66
+ else line.count
67
+ end
68
+ end
69
+
70
+ def encode text
71
+ CGI.escapeHTML( text )
72
+ end
73
+
74
+ def print
75
+
76
+ renderer = ERB.new( @template )
77
+
78
+ puts renderer.result(self.get_binding)
79
+
80
+ end # HTMLFormatter#print
81
+
82
+ end # HTMLFormatter
83
+
84
+ end # GCOVTOOLS