coverage-helpers 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b8f770dd30dc496298673d765e63f2d8409396bae8abaf5dba9578a9f759a305
4
+ data.tar.gz: 782fb6b6d21f0fbfd9919c89929b15e369cccc7936d77f43cfb339519ebee8f9
5
+ SHA512:
6
+ metadata.gz: 899b809986fe68cacfc819a86ac87fd3df1270cb8454ac4985d1156f8e7e3c6a8d9f40e415905dc5f7d558f1a42a892fc3ab76b444682a15f820230ce31d121d
7
+ data.tar.gz: 42c51e1457028b8fcf90de08117098d24157956918c7c335f2bc7e9ae62b1b1f8c81680dd1c8610e26761fa3dd192894844668ea4a7daeb8323963c05c42e155
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ coverage-helpers-1.0.0.gem
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ gemspec
@@ -0,0 +1,22 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ coverage-helpers (1.0.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ minitest (5.11.3)
10
+ rake (10.5.0)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ bundler (~> 1.16)
17
+ coverage-helpers!
18
+ minitest (~> 5.0)
19
+ rake (~> 10.0)
20
+
21
+ BUNDLED WITH
22
+ 1.16.1
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Yusuke Endoh
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
13
+ all 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
21
+ THE SOFTWARE.
@@ -0,0 +1,77 @@
1
+ # Coverage::Helpers
2
+
3
+ This gem provides some helper methods to manipulate the coverage results measured by `Coverage.result` of the `coverage.so` library.
4
+
5
+ ## APIs
6
+
7
+ Currently, the following methods are available:
8
+
9
+ * `Coverage::Helpers.merge(*covs)`: Sum up all coverage results.
10
+ * `Coverage::Helpers.diff(cov1, cov2)`: Extract the coverage results that is covered by `cov1` but not covered by `cov2`.
11
+ * `Coverage::Helpers.sanitize(cov)`: Make the covearge result able to `Marshal#dump`. (See the Notes section.)
12
+ * `Coverage::Helpers.save(filename, cov)`: Save the coverage result to a file.
13
+ * `Coverage::Helpers.load(filename)`: Load the coverage result from a file.
14
+ * `Coverage::Helpers.to_lcov_info(cov)`: Translate the result into LCOV info format.
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'coverage-helpers'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install coverage-helpers
31
+
32
+ ## Usage
33
+
34
+ Here is an example:
35
+
36
+ ```
37
+ $ cd example
38
+ $ ruby measure.rb -o cov1.dat -l cov1.info -- file.rb 1
39
+ $ genhtml --branch-coverage cov1.info -o cov1-lcov
40
+ $ open cov1-lcov/index.html
41
+ ```
42
+
43
+ Note that `genhtml` is an executable of LCOV that visualizes LCOV info data by HTML view.
44
+
45
+ You can merge the coverage results of multiple runs:
46
+
47
+ ```
48
+ $ ruby measure.rb -o cov1.dat -l cov1.info -- file.rb 1
49
+ $ ruby measure.rb -o cov2.dat -l cov2.info -a cov1.dat -- file.rb 2
50
+ $ genhtml --branch-coverage cov2.info -o cov2-lcov
51
+ $ open cov2-lcov/index.html
52
+ ```
53
+
54
+ You can also check the newly covered code:
55
+
56
+ ```
57
+ $ ruby measure.rb -o cov1.dat -l cov1.info -- file.rb 1
58
+ $ ruby measure.rb -o cov2.dat -l cov2.info -b cov1.dat -- file.rb 2
59
+ $ genhtml --branch-coverage cov2.info -o cov2-lcov
60
+ $ open cov2-lcov/index.html
61
+ ```
62
+
63
+ See `example/measure.rb` in detail.
64
+
65
+ ## Notes
66
+
67
+ There are two formats of `Coverage.result`. One is an old format, which Ruby 2.4 or before returns. It can contain only line coverage. The other is a new format, which Ruby 2.5 or later generates. It can contain line, branch, and method covearge. All methods of this gem support both format.
68
+
69
+ Method coverage data contains a class defined a measured method. The class may be a singleton class, which `Marshal.dump` cannot handle. This is why this gem provides `sanitize`. The method replaces all singleton classes with its string representation by using `to_s`.
70
+
71
+ ## Contributing
72
+
73
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mame/coverage-helpers.
74
+
75
+ ## License
76
+
77
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "coverage/helpers"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,26 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "coverage/helpers"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "coverage-helpers"
7
+ spec.version = Coverage::Helpers::VERSION
8
+ spec.authors = ["Yusuke Endoh"]
9
+ spec.email = ["mame@ruby-lang.org"]
10
+
11
+ spec.summary = %q{Helper methods for the Coverage library.}
12
+ spec.description = %q{You can merge and diff coverage results, and convert them to LCOV info format.}
13
+ spec.homepage = "https://github.com/mame/coverage-helpers"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.16"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "minitest", "~> 5.0"
26
+ end
@@ -0,0 +1,3 @@
1
+ *.dat
2
+ *.info
3
+ *-lcov/
@@ -0,0 +1,22 @@
1
+ def foo(n)
2
+ if n == 0
3
+ :zero
4
+ else
5
+ :non_zero
6
+ end
7
+ end
8
+
9
+ def bar
10
+ :bar
11
+ end
12
+
13
+ def baz
14
+ raise
15
+ end
16
+
17
+ case ARGV[0]
18
+ when "1"
19
+ foo(0)
20
+ when "2"
21
+ bar
22
+ end
@@ -0,0 +1,42 @@
1
+ require "coverage"
2
+ require "coverage/helpers"
3
+ require "optparse"
4
+
5
+ # setup
6
+ output_marshal = "cov.dat"
7
+ output_lcov_info = "cov.info"
8
+ add = nil
9
+ base = nil
10
+ o = OptionParser.new
11
+ o.on("-o DAT", "output coverage data (in Marshal format)") {|v| output_marshal = v }
12
+ o.on("-l LCOVINFO", "output coverage data (in LCOV info format)") {|v| output_lcov_info = v }
13
+ o.on("-a DAT", "measure total covearge with this coverage") {|v| add = v }
14
+ o.on("-b DAT", "measure diff from this coverage") {|v| base = v }
15
+ o.parse!
16
+
17
+ # measure
18
+ Coverage.start(:all)
19
+ load ARGV.shift
20
+ cov = Coverage.result
21
+
22
+ # merge
23
+ if add
24
+ add = Coverage::Helpers.load(add)
25
+ cov = Coverage::Helpers.sanitize(cov)
26
+ cov = Coverage::Helpers.merge(cov, add)
27
+ end
28
+
29
+ # diff
30
+ if base
31
+ base = Coverage::Helpers.load(base)
32
+ cov = Coverage::Helpers.sanitize(cov)
33
+ cov = Coverage::Helpers.diff(cov, base)
34
+ end
35
+
36
+ # save the result in Marshal format
37
+ Coverage::Helpers.save(output_marshal, cov)
38
+
39
+ # save the result in LCOV info format
40
+ open(output_lcov_info, "w") do |f|
41
+ Coverage::Helpers.to_lcov_info(cov, out: f)
42
+ end
@@ -0,0 +1,275 @@
1
+ require "coverage"
2
+
3
+ module Coverage
4
+ module Helpers
5
+ VERSION = "1.0.0"
6
+
7
+ # helper methods
8
+ using(Module.new { refine(Kernel) do
9
+ def old2new!(cov)
10
+ cov.each do |path, runs|
11
+ cov[path] = { :lines => runs }
12
+ end
13
+ end
14
+
15
+ def merge_lines(dst, src)
16
+ dst.concat([nil] * [src.size - dst.size, 0].max)
17
+ src.each_with_index do |run, i|
18
+ next unless run
19
+ dst[i] ||= 0
20
+ dst[i] += run
21
+ end
22
+ end
23
+
24
+ def merge_branches(dst, src)
25
+ src.each do |base, targets|
26
+ dst[base] ||= {}
27
+ targets.each do |target, run|
28
+ dst[base][target] ||= 0
29
+ dst[base][target] += run
30
+ end
31
+ end
32
+ end
33
+
34
+ def merge_methods(dst, src)
35
+ src.each do |mthd, run|
36
+ dst[mthd] ||= 0
37
+ dst[mthd] += run
38
+ end
39
+ end
40
+
41
+ def diff_lines(min, sub)
42
+ min += [nil] * [sub.size - min.size, 0].max
43
+ sub.each_with_index do |run, i|
44
+ next unless run
45
+ min[i] = [min[i] - run, 0].max if min[i]
46
+ end
47
+ min
48
+ end
49
+
50
+ def diff_branches(min, sub)
51
+ nruns = {}
52
+ min.each do |base, targets|
53
+ nruns[base] ||= {}
54
+ targets.each do |target, run|
55
+ run2 = sub.dig(base, target) || 0
56
+ nruns[base][target] = [run - run2, 0].max
57
+ end
58
+ end
59
+ nruns
60
+ end
61
+
62
+ def diff_methods(min, sub)
63
+ nruns = {}
64
+ min.each do |mthd, run|
65
+ run2 = sub[mthd] || 0
66
+ nruns[mthd] = [run - run2, 0].max
67
+ end
68
+ nruns
69
+ end
70
+ end })
71
+
72
+ module_function
73
+
74
+ # Sum up all coverage results.
75
+ def self.merge(*covs)
76
+ ncov = {}
77
+ old_format = true
78
+
79
+ covs.each do |cov|
80
+ cov.each do |path, runs|
81
+ if runs.is_a?(Array) && old_format
82
+ # merge two old-format (ruby 2.4 or before) coverage results
83
+ merge_lines(ncov[path] ||= [], runs)
84
+ next
85
+ end
86
+
87
+ # promotes from old format to new one
88
+ if runs.is_a?(Array)
89
+ runs = { :lines => runs }
90
+ end
91
+ if old_format
92
+ old_format = false
93
+ old2new!(ncov)
94
+ end
95
+
96
+ # merge two new-format (ruby 2.5 or later) coverage results
97
+ ncov[path] ||= {}
98
+ [
99
+ [:lines, :merge_lines, []],
100
+ [:branches, :merge_branches, {}],
101
+ [:methods, :merge_methods, {}],
102
+ ].each do |type, merge_func, default|
103
+ if runs[type]
104
+ send(merge_func, ncov[path][type] ||= default, runs[type])
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ ncov
111
+ end
112
+
113
+ # Extract the coverage results that is covered by `cov1` but not covered by `cov2`.
114
+ def diff(cov1, cov2)
115
+ ncov = {}
116
+ old_format = true
117
+
118
+ cov1.each do |path1, runs1|
119
+ if cov2[path1]
120
+ runs2 = cov2[path1]
121
+
122
+ if runs1.is_a?(Array) && runs2.is_a?(Array) && old_format
123
+ # diff two old-format (ruby 2.4 or before) coverage results
124
+ ncov[path1] = diff_lines(runs1, runs2)
125
+ next
126
+ end
127
+
128
+ # promotes from old format to new one
129
+ if runs1.is_a?(Array)
130
+ runs1 = { :lines => runs1 }
131
+ end
132
+ if runs2.is_a?(Array)
133
+ runs2 = { :lines => runs2 }
134
+ end
135
+ if old_format
136
+ old_format = false
137
+ old2new!(ncov)
138
+ end
139
+
140
+ # diff two new-format (ruby 2.5 or later) coverage results
141
+ ncov[path1] = {}
142
+ [
143
+ [:lines, :diff_lines],
144
+ [:branches, :diff_branches],
145
+ [:methods, :diff_methods],
146
+ ].each do |type, diff_func|
147
+ if runs1[type]
148
+ if runs2[type]
149
+ ncov[path1][type] = send(diff_func, runs1[type], runs2[type])
150
+ else
151
+ ncov[path1][type] = runs1[type]
152
+ end
153
+ end
154
+ end
155
+ else
156
+ if runs1.is_a?(Array) && old_format
157
+ ncov[path1] = runs1
158
+ next
159
+ end
160
+
161
+ # promotes from old format to new one
162
+ if runs1.is_a?(Array)
163
+ runs1 = { :lines => runs1 }
164
+ end
165
+ if old_format
166
+ old_format = false
167
+ old2new!(ncov)
168
+ end
169
+ ncov[path1] = runs1
170
+ end
171
+ end
172
+
173
+ ncov
174
+ end
175
+
176
+ # Make the covearge result able to marshal.
177
+ def sanitize(cov)
178
+ ncov = {}
179
+ cov.each do |path, runs|
180
+ if runs.is_a?(Array)
181
+ ncov[path] = runs
182
+ next
183
+ end
184
+
185
+ ncov[path] = {}
186
+ ncov[path][:lines] = runs[:lines] if runs[:lines]
187
+ ncov[path][:branches] = runs[:branches] if runs[:branches]
188
+
189
+ if runs[:methods]
190
+ ncov[path][:methods] = methods = {}
191
+ runs[:methods].each do |mthd, run|
192
+ klass =
193
+ begin
194
+ Marshal.dump(mthd[0])
195
+ mthd[0]
196
+ rescue
197
+ mthd[0].to_s
198
+ end
199
+ methods[[klass] + mthd.drop(1)] = run
200
+ end
201
+ end
202
+ end
203
+ ncov
204
+ end
205
+
206
+ # Save the coverage result to a file.
207
+ def save(path, cov)
208
+ File.binwrite(path, Marshal.dump(sanitize(cov)))
209
+ end
210
+
211
+ # Load the coverage result from a file.
212
+ def load(path)
213
+ Marshal.load(File.binread(path))
214
+ end
215
+
216
+ # Translate the result into LCOV info format.
217
+ def to_lcov_info(cov, out: "", test_name: nil)
218
+ out << "TN:#{ test_name }\n"
219
+ cov.each do |path, runs|
220
+ out << "SF:#{ path }\n"
221
+
222
+ # function coverage
223
+ if runs.is_a?(Hash) && runs[:methods]
224
+ total = covered = 0
225
+ runs[:methods].each do |(klass, name, lineno), run|
226
+ out << "FN:#{ lineno },#{ klass }##{ name }\n"
227
+ total += 1
228
+ covered += 1 if run > 0
229
+ end
230
+ out << "FNF:#{ total }\n"
231
+ out << "FNF:#{ covered }\n"
232
+ runs[:methods].each do |(klass, name, _), run|
233
+ out << "FNDA:#{ run },#{ klass }##{ name }\n"
234
+ end
235
+ end
236
+
237
+ # line coverage
238
+ if runs.is_a?(Array) || (runs.is_a?(Hash) && runs[:lines])
239
+ total = covered = 0
240
+ lines = runs.is_a?(Array) ? runs : runs[:lines]
241
+ lines.each_with_index do |run, lineno|
242
+ next unless run
243
+ out << "DA:#{ lineno + 1 },#{ run }\n"
244
+ total += 1
245
+ covered += 1 if run > 0
246
+ end
247
+ out << "LF:#{ total }\n"
248
+ out << "LH:#{ covered }\n"
249
+ end
250
+
251
+ # branch coverage
252
+ if runs.is_a?(Hash) && runs[:branches]
253
+ total = covered = 0
254
+ id = 0
255
+ runs[:branches].each do |(_base_type, _, base_lineno), targets|
256
+ i = 0
257
+ targets.each do |(_target_type, _target_lineno), run|
258
+ out << "BRDA:#{ base_lineno },#{ id },#{ i },#{ run }\n"
259
+ total += 1
260
+ covered += 1 if run > 0
261
+ i += 1
262
+ end
263
+ id += 1
264
+ end
265
+ out << "BRF:#{ total }\n"
266
+ out << "BRH:#{ covered }\n"
267
+ end
268
+
269
+ out << "end_of_record\n"
270
+ end
271
+
272
+ out
273
+ end
274
+ end
275
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: coverage-helpers
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Yusuke Endoh
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-02-04 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.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
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: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ description: You can merge and diff coverage results, and convert them to LCOV info
56
+ format.
57
+ email:
58
+ - mame@ruby-lang.org
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - Gemfile.lock
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - bin/console
71
+ - bin/setup
72
+ - coverage-helpers.gemspec
73
+ - example/.gitignore
74
+ - example/file.rb
75
+ - example/measure.rb
76
+ - lib/coverage/helpers.rb
77
+ homepage: https://github.com/mame/coverage-helpers
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.7.3
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Helper methods for the Coverage library.
101
+ test_files: []