coverband 0.0.9 → 0.0.10
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/README.md +63 -2
- data/Rakefile +8 -0
- data/coverband.gemspec +3 -0
- data/lib/coverband.rb +25 -0
- data/lib/coverband/middleware.rb +19 -12
- data/lib/coverband/reporter.rb +20 -12
- data/lib/coverband/version.rb +1 -1
- data/test/test_helper.rb +34 -0
- data/test/unit/middleware_test.rb +25 -0
- data/test/unit/reporter_test.rb +63 -0
- metadata +59 -5
data/README.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
# Coverband
|
2
2
|
|
3
|
-
Rack middleware to
|
3
|
+
Rack middleware to measure production code coverage. Coverband allows easy configuration to collect and report on production code coverage.
|
4
|
+
|
5
|
+
* Allow sampleing to avoid the perf overhead on every request.
|
6
|
+
* Ignore directories to avoid data collection on vendor, lib, etc
|
7
|
+
* Take a baseline to get inital app loading coverage.
|
8
|
+
|
9
|
+
At the momement, Coverband relies on Ruby's `set_trace_func` hook. I attempted to use the standard lib's `Coverage` support but it proved buggy when stampling or stoping and starting collection. When [Coverage is patched](https://www.ruby-forum.com/topic/1811306) in future Ruby versions it would likely be better. Using `set_trace_func` has some limitations where it doesn't collect covered lines, but I have been impressed with the coverage it shows for both Sinatra and Rails applications.
|
4
10
|
|
5
11
|
## Installation
|
6
12
|
|
@@ -18,7 +24,61 @@ Or install it yourself as:
|
|
18
24
|
|
19
25
|
## Usage
|
20
26
|
|
21
|
-
|
27
|
+
After installing the gem you likely want to get the rake tasks configured as well as the rack middle ware.
|
28
|
+
|
29
|
+
#### Configuring Rake
|
30
|
+
|
31
|
+
Either add the below to your `Rakefile` or to a file included in you Rakefile
|
32
|
+
|
33
|
+
require 'coverband'
|
34
|
+
Coverband.configure do |config|
|
35
|
+
config.redis = Redis.new
|
36
|
+
# merge in lines to consider covered manually to override any misses
|
37
|
+
# existing_coverage = {'./cover_band_server/app.rb' => Array.new(31,1)}
|
38
|
+
# JSON.parse(File.read('./tmp/coverband_baseline.json')).merge(existing_coverage)
|
39
|
+
config.coverage_baseline = JSON.parse(File.read('./tmp/coverband_baseline.json'))
|
40
|
+
config.root_paths = ['/app/']
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "report unused lines"
|
44
|
+
task :coverband => :environment do
|
45
|
+
baseline = JSON.parse(File.read('./tmp/coverband_baseline.json'))
|
46
|
+
|
47
|
+
root_paths = ['/app/']
|
48
|
+
coverband_options = {:existing_coverage => baseline, :roots => root_paths}
|
49
|
+
Coverband::Reporter.report(Redis.new, coverband_options)
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "get coverage baseline"
|
53
|
+
task :coverband_baseline do
|
54
|
+
Coverband::Reporter.baseline {
|
55
|
+
#rails
|
56
|
+
require File.expand_path("../config/environment", __FILE__)
|
57
|
+
#sinatra
|
58
|
+
#require File.expand_path("./app", __FILE__)
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
#### Configure rack middleware
|
63
|
+
|
64
|
+
For the best coverage you want this loaded as early as possible. I have been putting it directly in my `config.ru` but you could use an initializer you may just end up missing some boot up coverage.
|
65
|
+
|
66
|
+
require File.dirname(__FILE__) + '/config/environment'
|
67
|
+
|
68
|
+
require 'coverband'
|
69
|
+
|
70
|
+
Coverband.configure do |config|
|
71
|
+
config.root = Dir.pwd
|
72
|
+
config.redis = Redis.new
|
73
|
+
config.coverage_baseline = JSON.parse(File.read('./tmp/coverband_baseline.json'))
|
74
|
+
config.root_paths = ['/app/']
|
75
|
+
config.ignore = ['vendor']
|
76
|
+
config.percentage = 100.0
|
77
|
+
end
|
78
|
+
|
79
|
+
use Coverband::Middleware
|
80
|
+
run ActionController::Dispatcher.new
|
81
|
+
|
22
82
|
|
23
83
|
## TODO
|
24
84
|
|
@@ -43,6 +103,7 @@ TODO: Write usage instructions here
|
|
43
103
|
* [Ruby Coverage docs](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/coverage/rdoc/Coverage.html)
|
44
104
|
* [simplecov walk through](http://www.tamingthemindmonkey.com/2011/09/27/ruby-code-coverage-using-simplecov) copy some of the syntax sugar setup for cover band
|
45
105
|
* [Jruby coverage bug](http://jira.codehaus.org/browse/JRUBY-6106?page=com.atlassian.jira.plugin.system.issuetabpanels:changehistory-tabpanel)
|
106
|
+
* [learn from oboe ruby code](https://github.com/appneta/oboe-ruby#writing-custom-instrumentation)
|
46
107
|
|
47
108
|
## Contributing
|
48
109
|
|
data/Rakefile
CHANGED
data/coverband.gemspec
CHANGED
@@ -20,6 +20,9 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
22
|
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "mocha"
|
24
|
+
spec.add_development_dependency "shoulda"
|
23
25
|
spec.add_runtime_dependency "simplecov"
|
24
26
|
spec.add_runtime_dependency "json"
|
27
|
+
spec.add_runtime_dependency "redis"
|
25
28
|
end
|
data/lib/coverband.rb
CHANGED
@@ -5,5 +5,30 @@ require "coverband/reporter"
|
|
5
5
|
require "coverband/middleware"
|
6
6
|
|
7
7
|
module Coverband
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_accessor :configuration
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.configure
|
14
|
+
self.configuration ||= Configuration.new
|
15
|
+
yield(configuration)
|
16
|
+
end
|
17
|
+
|
18
|
+
class Configuration
|
19
|
+
attr_accessor :redis, :coverage_baseline, :root_paths, :root, :ignore, :percentage, :verbose, :reporter
|
20
|
+
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@root = Dir.pwd
|
24
|
+
@redis = nil
|
25
|
+
@coverage_baseline = {}
|
26
|
+
@root_paths = []
|
27
|
+
@ignore = []
|
28
|
+
@percentage = 100.0
|
29
|
+
@verbose = false
|
30
|
+
@reporter = 'rcov'
|
31
|
+
end
|
32
|
+
end
|
8
33
|
|
9
34
|
end
|
data/lib/coverband/middleware.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
module Coverband
|
2
2
|
class Middleware
|
3
3
|
|
4
|
-
def initialize(app
|
4
|
+
def initialize(app)
|
5
5
|
@app = app
|
6
|
-
root =
|
6
|
+
root = Coverband.configuration.root
|
7
7
|
@project_directory = File.expand_path(root)
|
8
8
|
@enabled = true
|
9
9
|
@function_set = false
|
10
10
|
@files = {}
|
11
11
|
|
12
|
-
@ignore_patterns =
|
13
|
-
@sample_percentage =
|
14
|
-
@reporter =
|
12
|
+
@ignore_patterns = Coverband.configuration.ignore
|
13
|
+
@sample_percentage = Coverband.configuration.percentage
|
14
|
+
@reporter = Coverband.configuration.redis
|
15
|
+
@verbose = Coverband.configuration.verbose
|
15
16
|
end
|
16
17
|
|
17
18
|
def call(env)
|
@@ -68,20 +69,26 @@ module Coverband
|
|
68
69
|
old_files = @files.dup
|
69
70
|
@files = {}
|
70
71
|
old_files.each_pair do |key, values|
|
71
|
-
@reporter.sadd "coverband", key
|
72
|
-
@reporter.
|
72
|
+
@reporter.sadd "coverband", key
|
73
|
+
if @reporter.inspect.match(/v2/)
|
74
|
+
values.each do |value|
|
75
|
+
@reporter.sadd "coverband.#{key}", value
|
76
|
+
end
|
77
|
+
else
|
78
|
+
@reporter.sadd "coverband.#{key}", values
|
79
|
+
end
|
73
80
|
end
|
74
81
|
end
|
75
82
|
else
|
76
|
-
puts "coverage report: "
|
77
|
-
puts @files.inspect
|
83
|
+
puts "coverage report: " if @verbose
|
84
|
+
puts @files.inspect if @verbose
|
78
85
|
end
|
79
86
|
else
|
80
|
-
puts "coverage disabled" if @
|
87
|
+
puts "coverage disabled" if @verbose
|
81
88
|
end
|
82
89
|
rescue RuntimeError => err
|
83
|
-
if @
|
84
|
-
puts "coverage missing"
|
90
|
+
if @verbose
|
91
|
+
puts "coverage missing"
|
85
92
|
puts "error: #{err.inspect} #{err.message}"
|
86
93
|
end
|
87
94
|
end
|
data/lib/coverband/reporter.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
require 'simplecov'
|
2
2
|
|
3
3
|
module Coverband
|
4
|
-
|
5
4
|
class Reporter
|
6
5
|
|
7
6
|
def self.baseline
|
8
7
|
require 'coverage'
|
9
8
|
Coverage.start
|
10
9
|
yield
|
11
|
-
@project_directory = File.expand_path(
|
10
|
+
@project_directory = File.expand_path(Coverband.configuration.root)
|
12
11
|
results = Coverage.result
|
13
12
|
results = results.reject{|key, val| !key.match(@project_directory)}
|
14
13
|
puts results.inspect
|
@@ -16,18 +15,28 @@ module Coverband
|
|
16
15
|
File.open('./tmp/coverband_baseline.json', 'w') {|f| f.write(results.to_json) }
|
17
16
|
end
|
18
17
|
|
19
|
-
def self.report
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
def self.report
|
19
|
+
redis = Coverband.configuration.redis
|
20
|
+
roots = Coverband.configuration.root_paths
|
21
|
+
existing_coverage = Coverband.configuration.coverage_baseline
|
22
|
+
roots << "#{current_root}/"
|
23
23
|
puts "fixing root: #{roots.join(', ')}"
|
24
|
-
if
|
24
|
+
if Coverband.configuration.reporter=='rcov'
|
25
25
|
report_rcov(redis, existing_coverage, roots)
|
26
26
|
else
|
27
|
-
redis.smembers('coverband').
|
27
|
+
lines = redis.smembers('coverband').map{|key| report_line(redis, key) }
|
28
|
+
puts lines.join("\n")
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
32
|
+
def self.clear_coverage(redis)
|
33
|
+
Coverband.configuration.redis.smembers('coverband').each{|key| redis.del("coverband.#{key}")}
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.current_root
|
37
|
+
File.expand_path(Coverband.configuration.root)
|
38
|
+
end
|
39
|
+
|
31
40
|
private
|
32
41
|
|
33
42
|
def self.fix_file_names(report_hash, roots)
|
@@ -73,11 +82,11 @@ module Coverband
|
|
73
82
|
# /Users/danmayer/projects/cover_band_server/app/rb: ["54", "55"]
|
74
83
|
# /Users/danmayer/projects/cover_band_server/views/layout/erb: ["0", "33", "36", "37", "38", "39", "40", "62", "63", "66", "65532", "65533"]
|
75
84
|
def self.report_line(redis, key)
|
76
|
-
|
85
|
+
"#{key}: #{redis.smembers("coverband.#{key}").inspect}"
|
77
86
|
end
|
78
87
|
|
79
88
|
def self.filename_from_key(key, roots)
|
80
|
-
filename = key
|
89
|
+
filename = key
|
81
90
|
roots.each do |root|
|
82
91
|
filename = filename.gsub(/^#{root}/, './')
|
83
92
|
end
|
@@ -90,7 +99,7 @@ module Coverband
|
|
90
99
|
def self.line_hash(redis, key, roots)
|
91
100
|
filename = filename_from_key(key, roots)
|
92
101
|
if File.exists?(filename)
|
93
|
-
lines_hit = redis.smembers("coverband
|
102
|
+
lines_hit = redis.smembers("coverband.#{key}")
|
94
103
|
count = File.foreach(filename).inject(0) {|c, line| c+1}
|
95
104
|
if filename.match(/\.erb/)
|
96
105
|
line_array = Array.new(count, nil)
|
@@ -105,5 +114,4 @@ module Coverband
|
|
105
114
|
end
|
106
115
|
|
107
116
|
end
|
108
|
-
|
109
117
|
end
|
data/lib/coverband/version.rb
CHANGED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'simplecov'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'shoulda'
|
5
|
+
require 'mocha'
|
6
|
+
require 'ostruct'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
SimpleCov.start do
|
10
|
+
add_filter 'specs/ruby/1.9.1/gems/'
|
11
|
+
add_filter '/test/'
|
12
|
+
add_filter '/config/'
|
13
|
+
end
|
14
|
+
|
15
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
16
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
17
|
+
Mocha::Configuration.prevent(:stubbing_non_existent_method)
|
18
|
+
|
19
|
+
require 'coverband'
|
20
|
+
|
21
|
+
unless File.exist?('./tmp/coverband_baseline.json')
|
22
|
+
`mkdir -p ./tmp`
|
23
|
+
`touch ./tmp/coverband_baseline.json`
|
24
|
+
end
|
25
|
+
|
26
|
+
Coverband.configure do |config|
|
27
|
+
config.root = Dir.pwd
|
28
|
+
config.redis = Redis.new
|
29
|
+
#config.coverage_baseline = JSON.parse(File.read('./tmp/coverband_baseline.json'))
|
30
|
+
config.root_paths = ['/app/']
|
31
|
+
config.ignore = ['vendor']
|
32
|
+
config.percentage = 100.0
|
33
|
+
config.reporter = 'std_out'
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path('../test_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class MiddlewareTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
FAKE_RESULTS = 'results'
|
6
|
+
|
7
|
+
should "call app" do
|
8
|
+
middleware = Coverband::Middleware.new(fake_app)
|
9
|
+
results = middleware.call({})
|
10
|
+
assert_equal FAKE_RESULTS, results
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def fake_app
|
16
|
+
@app ||= begin
|
17
|
+
my_app = OpenStruct.new()
|
18
|
+
def my_app.call(env)
|
19
|
+
FAKE_RESULTS
|
20
|
+
end
|
21
|
+
my_app
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require File.expand_path('../test_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class ReporterTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
should "record baseline" do
|
6
|
+
Coverage.expects(:start).at_least_once
|
7
|
+
Coverage.expects(:result).returns({'fake' => [0,1]}).at_least_once
|
8
|
+
File.expects(:open).once
|
9
|
+
|
10
|
+
Coverband::Reporter.stubs(:puts)
|
11
|
+
|
12
|
+
Coverband::Reporter.baseline{
|
13
|
+
#nothing
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
should "report data" do
|
18
|
+
Coverband.configure do |config|
|
19
|
+
config.redis = fake_redis
|
20
|
+
config.reporter = 'std_out'
|
21
|
+
end
|
22
|
+
|
23
|
+
Coverband::Reporter.expects(:current_root).returns('/root_dir')
|
24
|
+
fake_redis.expects(:smembers).with('coverband').returns(fake_coverband_members)
|
25
|
+
Coverband::Reporter.expects('puts').with("fixing root: /app/, /root_dir/")
|
26
|
+
|
27
|
+
fake_coverband_members.each do |key|
|
28
|
+
fake_redis.expects(:smembers).with("coverband.#{key}").returns(["54", "55"])
|
29
|
+
end
|
30
|
+
|
31
|
+
matchers = [regexp_matches(/tester/),
|
32
|
+
regexp_matches(/application_controller/),
|
33
|
+
regexp_matches(/account/),
|
34
|
+
regexp_matches(/54/)]
|
35
|
+
|
36
|
+
Coverband::Reporter.expects('puts').with(all_of(*matchers))
|
37
|
+
|
38
|
+
Coverband::Reporter.report
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def fake_redis
|
44
|
+
@redis ||= begin
|
45
|
+
redis = OpenStruct.new()
|
46
|
+
def redis.smembers(key)
|
47
|
+
end
|
48
|
+
redis
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def fake_coverband_members
|
53
|
+
["/Users/danmayer/projects/hearno/script/tester.rb",
|
54
|
+
"/Users/danmayer/projects/hearno/app/controllers/application_controller.rb",
|
55
|
+
"/Users/danmayer/projects/hearno/app/models/account.rb"
|
56
|
+
]
|
57
|
+
end
|
58
|
+
|
59
|
+
def fake_coverage_report
|
60
|
+
{"/Users/danmayer/projects/hearno/script/tester.rb"=>[1, nil, 1, 1, nil, nil, nil]}
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coverband
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-12-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -43,6 +43,38 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mocha
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: shoulda
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
46
78
|
- !ruby/object:Gem::Dependency
|
47
79
|
name: simplecov
|
48
80
|
requirement: !ruby/object:Gem::Requirement
|
@@ -75,6 +107,22 @@ dependencies:
|
|
75
107
|
- - ! '>='
|
76
108
|
- !ruby/object:Gem::Version
|
77
109
|
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: redis
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
78
126
|
description: Rack middleware to help measure production code coverage
|
79
127
|
email:
|
80
128
|
- dan@mayerdan.com
|
@@ -93,6 +141,9 @@ files:
|
|
93
141
|
- lib/coverband/middleware.rb
|
94
142
|
- lib/coverband/reporter.rb
|
95
143
|
- lib/coverband/version.rb
|
144
|
+
- test/test_helper.rb
|
145
|
+
- test/unit/middleware_test.rb
|
146
|
+
- test/unit/reporter_test.rb
|
96
147
|
homepage: ''
|
97
148
|
licenses:
|
98
149
|
- MIT
|
@@ -108,7 +159,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
108
159
|
version: '0'
|
109
160
|
segments:
|
110
161
|
- 0
|
111
|
-
hash:
|
162
|
+
hash: 1293128324123628513
|
112
163
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
164
|
none: false
|
114
165
|
requirements:
|
@@ -117,11 +168,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
168
|
version: '0'
|
118
169
|
segments:
|
119
170
|
- 0
|
120
|
-
hash:
|
171
|
+
hash: 1293128324123628513
|
121
172
|
requirements: []
|
122
173
|
rubyforge_project:
|
123
174
|
rubygems_version: 1.8.25
|
124
175
|
signing_key:
|
125
176
|
specification_version: 3
|
126
177
|
summary: Rack middleware to help measure production code coverage
|
127
|
-
test_files:
|
178
|
+
test_files:
|
179
|
+
- test/test_helper.rb
|
180
|
+
- test/unit/middleware_test.rb
|
181
|
+
- test/unit/reporter_test.rb
|