coverband 0.0.26 → 0.1.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -3
- data/lib/coverband/base.rb +78 -57
- data/lib/coverband/middleware.rb +5 -6
- data/lib/coverband/version.rb +1 -1
- data/test/unit/base_test.rb +5 -5
- data/test/unit/middleware_test.rb +33 -24
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ab2f18c3f5f5663bb29a2335e7cd1cdac55f27d
|
4
|
+
data.tar.gz: 74eb1c7207c254af8c269906d40d8b069a83c8cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13f92b3215f32799e915997294e39b80773d08b6b31a2ec9ad31fee19e2bee9e29f9ef3ebbb8f06ae0da3ae1ecdecd04dc543cac41ce01e1a85d20f5270b980d
|
7
|
+
data.tar.gz: 59bb9f499b735e26e21c0d340f7d5cb2a3965446491001ea7a359fb0742d06fecec6cd0cb186f4b8a0e1300c09c12cf45a86a4556e031aee5b5789228772a559
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ A gem to measure production code coverage. Coverband allows easy configuration t
|
|
6
6
|
* Ignore directories to avoid overhead data collection on vendor, lib, etc.
|
7
7
|
* Take a baseline to get initial app loading coverage.
|
8
8
|
|
9
|
-
At the moment, Coverband relies on Ruby's `set_trace_func` hook. I attempted to use the standard lib's `Coverage` support but it proved buggy when sampling or stoping and starting collection. When [Coverage is patched](https://
|
9
|
+
At the moment, Coverband relies on Ruby's `set_trace_func` hook. I attempted to use the standard lib's `Coverage` support but it proved buggy when sampling or stoping and starting collection. When [Coverage is patched](https://bugs.ruby-lang.org/issues/9572) 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.
|
10
10
|
|
11
11
|
###### Success:
|
12
12
|
After running in production for 30 minutes, we were able very easily delete 2000 LOC after looking through the data. We expect to be able to clean up much more after it has collected more data.
|
@@ -256,13 +256,19 @@ If you are trying to debug locally wondering what code is being run during a req
|
|
256
256
|
|
257
257
|
## Resources
|
258
258
|
|
259
|
+
##### Ruby Std-lib Coverage
|
260
|
+
|
261
|
+
* [Fixed bug causing segfaults on 1.9.X](https://www.ruby-forum.com/topic/1811306)
|
262
|
+
* [Current Coverage Bug causing issues on 2.1.1](https://bugs.ruby-lang.org/issues/9572)
|
263
|
+
* [Ruby Coverage docs](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/coverage/rdoc/Coverage.html)
|
264
|
+
|
265
|
+
##### other
|
266
|
+
|
259
267
|
* [erb code coverage](http://stackoverflow.com/questions/13030909/how-to-test-code-coverage-for-rails-erb-templates)
|
260
268
|
* [more erb code coverage](https://github.com/colszowka/simplecov/issues/38)
|
261
269
|
* [erb syntax](http://stackoverflow.com/questions/7996695/rails-erb-syntax) parse out and mark lines as important
|
262
270
|
* [ruby 2 tracer](https://github.com/brightbox/deb-ruby2.0/blob/master/lib/tracer.rb)
|
263
271
|
* [coveralls hosted code coverage tracking](https://coveralls.io/docs/ruby) currently for test coverage but might be a good partner for production coverage
|
264
|
-
* [bug in Ruby's stl-lib Coverage, needs to be fixed to be more accurate](https://www.ruby-forum.com/topic/1811306)
|
265
|
-
* [Ruby Coverage docs](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/coverage/rdoc/Coverage.html)
|
266
272
|
* [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
|
267
273
|
* [Jruby coverage bug](http://jira.codehaus.org/browse/JRUBY-6106?page=com.atlassian.jira.plugin.system.issuetabpanels:changehistory-tabpanel)
|
268
274
|
* [learn from oboe ruby code](https://github.com/appneta/oboe-ruby#writing-custom-instrumentation)
|
data/lib/coverband/base.rb
CHANGED
@@ -1,21 +1,6 @@
|
|
1
1
|
module Coverband
|
2
2
|
class Base
|
3
|
-
|
4
|
-
def initialize(options = {})
|
5
|
-
@project_directory = File.expand_path(Coverband.configuration.root)
|
6
|
-
@enabled = false
|
7
|
-
@tracer_set = false
|
8
|
-
@files = {}
|
9
|
-
@file_usage = Hash.new(0)
|
10
|
-
@file_line_usage = {}
|
11
|
-
@startup_delay = Coverband.configuration.startup_delay
|
12
|
-
@ignore_patterns = Coverband.configuration.ignore
|
13
|
-
@sample_percentage = Coverband.configuration.percentage
|
14
|
-
@reporter = Coverband::RedisStore.new(Coverband.configuration.redis) if Coverband.configuration.redis
|
15
|
-
@stats = Coverband.configuration.stats
|
16
|
-
@verbose = Coverband.configuration.verbose
|
17
|
-
@logger = Coverband.configuration.logger || Logger.new(STDOUT)
|
18
|
-
end
|
3
|
+
include Singleton
|
19
4
|
|
20
5
|
def start
|
21
6
|
@enabled = true
|
@@ -40,7 +25,26 @@ module Coverband
|
|
40
25
|
@enabled = false
|
41
26
|
end
|
42
27
|
|
43
|
-
|
28
|
+
def extended?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset_instance
|
33
|
+
@project_directory = File.expand_path(Coverband.configuration.root)
|
34
|
+
@enabled = false
|
35
|
+
@tracer_set = false
|
36
|
+
@files = {}
|
37
|
+
@file_usage = Hash.new(0)
|
38
|
+
@file_line_usage = {}
|
39
|
+
@startup_delay = Coverband.configuration.startup_delay
|
40
|
+
@ignore_patterns = Coverband.configuration.ignore
|
41
|
+
@sample_percentage = Coverband.configuration.percentage
|
42
|
+
@reporter = Coverband::RedisStore.new(Coverband.configuration.redis) if Coverband.configuration.redis
|
43
|
+
@stats = Coverband.configuration.stats
|
44
|
+
@verbose = Coverband.configuration.verbose
|
45
|
+
@logger = Coverband.configuration.logger || Logger.new(STDOUT)
|
46
|
+
self
|
47
|
+
end
|
44
48
|
|
45
49
|
def configure_sampling
|
46
50
|
if @startup_delay!=0 || (rand * 100.0) > @sample_percentage
|
@@ -65,46 +69,6 @@ module Coverband
|
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
68
|
-
def set_tracer
|
69
|
-
unless @tracer_set
|
70
|
-
set_trace_func proc { |event, file, line, id, binding, classname|
|
71
|
-
add_file(file, line)
|
72
|
-
}
|
73
|
-
@tracer_set = true
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def unset_tracer
|
78
|
-
if @tracer_set
|
79
|
-
set_trace_func(nil)
|
80
|
-
@tracer_set = false
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def add_file(file, line)
|
85
|
-
if !file.match(/(\/gems\/|internal\:prelude)/) && file.match(@project_directory) && !@ignore_patterns.any?{|pattern| file.match(/#{pattern}/) }
|
86
|
-
if @verbose
|
87
|
-
@file_usage[file] += 1
|
88
|
-
@file_line_usage[file] = Hash.new(0) unless @file_line_usage.include?(file)
|
89
|
-
@file_line_usage[file][line] += 1
|
90
|
-
end
|
91
|
-
if @files.include?(file)
|
92
|
-
@files[file] << line unless @files.include?(line)
|
93
|
-
else
|
94
|
-
@files[file] = [line]
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def output_file_line_usage
|
100
|
-
@logger.info "coverband debug coverband file:line usage:"
|
101
|
-
@file_line_usage.sort_by {|_key, value| value.length}.each do |pair|
|
102
|
-
file = pair.first
|
103
|
-
lines = pair.last
|
104
|
-
@logger.info "file: #{file} => #{lines.sort_by {|_key, value| value}}"
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
72
|
def report_coverage
|
109
73
|
unless @enabled
|
110
74
|
@logger.info "coverage disabled" if @verbose
|
@@ -141,5 +105,62 @@ module Coverband
|
|
141
105
|
@logger.info "error: #{err.inspect} #{err.message}"
|
142
106
|
end
|
143
107
|
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
|
111
|
+
def set_tracer
|
112
|
+
unless @tracer_set
|
113
|
+
set_trace_func proc { |event, file, line, id, binding, classname|
|
114
|
+
add_file(file, line)
|
115
|
+
}
|
116
|
+
@tracer_set = true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def unset_tracer
|
121
|
+
if @tracer_set
|
122
|
+
set_trace_func(nil)
|
123
|
+
@tracer_set = false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def add_from_tracepoint(trace_point)
|
128
|
+
add_file(trace_point.path, trace_point.lineno)
|
129
|
+
end
|
130
|
+
|
131
|
+
def add_file(file, line)
|
132
|
+
if !file.match(/(\/gems\/|internal\:prelude)/) && file.match(@project_directory) && !@ignore_patterns.any?{|pattern| file.match(/#{pattern}/) }
|
133
|
+
add_file_without_checks(file, line)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def add_file_without_checks(file, line)
|
138
|
+
if @verbose
|
139
|
+
@file_usage[file] += 1
|
140
|
+
@file_line_usage[file] = Hash.new(0) unless @file_line_usage.include?(file)
|
141
|
+
@file_line_usage[file][line] += 1
|
142
|
+
end
|
143
|
+
if @files.include?(file)
|
144
|
+
@files[file] << line unless @files.include?(line)
|
145
|
+
else
|
146
|
+
@files[file] = [line]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def output_file_line_usage
|
151
|
+
@logger.info "coverband debug coverband file:line usage:"
|
152
|
+
@file_line_usage.sort_by {|_key, value| value.length}.each do |pair|
|
153
|
+
file = pair.first
|
154
|
+
lines = pair.last
|
155
|
+
@logger.info "file: #{file} => #{lines.sort_by {|_key, value| value}}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def initialize
|
162
|
+
reset_instance
|
163
|
+
end
|
164
|
+
|
144
165
|
end
|
145
166
|
end
|
data/lib/coverband/middleware.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
module Coverband
|
2
|
-
class Middleware
|
3
|
-
|
2
|
+
class Middleware
|
3
|
+
|
4
4
|
def initialize(app)
|
5
5
|
@app = app
|
6
|
-
super
|
7
6
|
end
|
8
7
|
|
9
8
|
def call(env)
|
10
|
-
configure_sampling
|
11
|
-
record_coverage
|
9
|
+
Coverband::Base.instance.configure_sampling
|
10
|
+
Coverband::Base.instance.record_coverage
|
12
11
|
results = @app.call(env)
|
13
|
-
report_coverage
|
12
|
+
Coverband::Base.instance.report_coverage
|
14
13
|
results
|
15
14
|
end
|
16
15
|
|
data/lib/coverband/version.rb
CHANGED
data/test/unit/base_test.rb
CHANGED
@@ -3,7 +3,7 @@ require File.expand_path('../test_helper', File.dirname(__FILE__))
|
|
3
3
|
class BaseTest < Test::Unit::TestCase
|
4
4
|
|
5
5
|
should "start should enable coverage" do
|
6
|
-
coverband = Coverband::Base.
|
6
|
+
coverband = Coverband::Base.instance.reset_instance
|
7
7
|
assert_equal false, coverband.instance_variable_get("@enabled")
|
8
8
|
coverband.expects(:record_coverage).once
|
9
9
|
coverband.start
|
@@ -11,7 +11,7 @@ class BaseTest < Test::Unit::TestCase
|
|
11
11
|
end
|
12
12
|
|
13
13
|
should "stop should disable coverage" do
|
14
|
-
coverband = Coverband::Base.
|
14
|
+
coverband = Coverband::Base.instance.reset_instance
|
15
15
|
assert_equal false, coverband.instance_variable_get("@enabled")
|
16
16
|
coverband.expects(:record_coverage).once
|
17
17
|
coverband.start
|
@@ -23,7 +23,7 @@ class BaseTest < Test::Unit::TestCase
|
|
23
23
|
|
24
24
|
should "allow for sampling with a block and enable when 100 percent sample" do
|
25
25
|
logger = Logger.new(STDOUT)
|
26
|
-
coverband = Coverband::Base.
|
26
|
+
coverband = Coverband::Base.instance.reset_instance
|
27
27
|
coverband.instance_variable_set("@sample_percentage", 100.0)
|
28
28
|
coverband.instance_variable_set("@verbose", true)
|
29
29
|
coverband.instance_variable_set("@logger", logger)
|
@@ -36,7 +36,7 @@ class BaseTest < Test::Unit::TestCase
|
|
36
36
|
|
37
37
|
should "allow reporting with start stop save" do
|
38
38
|
logger = Logger.new(STDOUT)
|
39
|
-
coverband = Coverband::Base.
|
39
|
+
coverband = Coverband::Base.instance.reset_instance
|
40
40
|
coverband.instance_variable_set("@sample_percentage", 100.0)
|
41
41
|
coverband.instance_variable_set("@verbose", true)
|
42
42
|
coverband.instance_variable_set("@logger", logger)
|
@@ -50,7 +50,7 @@ class BaseTest < Test::Unit::TestCase
|
|
50
50
|
end
|
51
51
|
|
52
52
|
should "allow reporting to redis start stop save" do
|
53
|
-
coverband = Coverband::Base.
|
53
|
+
coverband = Coverband::Base.instance.reset_instance
|
54
54
|
coverband.instance_variable_set("@sample_percentage", 100.0)
|
55
55
|
coverband.instance_variable_set("@verbose", true)
|
56
56
|
store = Coverband::RedisStore.new(Redis.new)
|
@@ -1,18 +1,18 @@
|
|
1
1
|
require File.expand_path('../test_helper', File.dirname(__FILE__))
|
2
2
|
require 'rack'
|
3
|
-
FAKE_RACK_APP_PATH = File.expand_path('../fake_app/basic_rack.rb', File.dirname(__FILE__))
|
4
|
-
require FAKE_RACK_APP_PATH
|
5
3
|
|
6
4
|
class MiddlewareTest < Test::Unit::TestCase
|
7
5
|
|
8
6
|
should "call app" do
|
9
7
|
request = Rack::MockRequest.env_for("/anything.json")
|
8
|
+
Coverband::Base.instance.reset_instance
|
10
9
|
middleware = Coverband::Middleware.new(fake_app)
|
11
10
|
results = middleware.call(request)
|
12
11
|
assert_equal "/anything.json", results.last
|
13
12
|
end
|
14
13
|
|
15
14
|
should 'pass all rack lint checks' do
|
15
|
+
Coverband::Base.instance.reset_instance
|
16
16
|
app = Rack::Lint.new(Coverband::Middleware.new(fake_app))
|
17
17
|
env = Rack::MockRequest.env_for('/hello')
|
18
18
|
app.call(env)
|
@@ -20,62 +20,71 @@ class MiddlewareTest < Test::Unit::TestCase
|
|
20
20
|
|
21
21
|
should 'always be enabled with sample percentage of 100' do
|
22
22
|
request = Rack::MockRequest.env_for("/anything.json")
|
23
|
+
Coverband::Base.instance.reset_instance
|
23
24
|
middleware = Coverband::Middleware.new(fake_app)
|
24
|
-
assert_equal false,
|
25
|
-
|
25
|
+
assert_equal false, Coverband::Base.instance.instance_variable_get("@enabled")
|
26
|
+
Coverband::Base.instance.instance_variable_set("@sample_percentage", 100.0)
|
26
27
|
results = middleware.call(request)
|
27
|
-
assert_equal true,
|
28
|
+
assert_equal true, Coverband::Base.instance.instance_variable_get("@enabled")
|
28
29
|
end
|
29
30
|
|
30
31
|
should 'never be enabled with sample percentage of 0' do
|
31
32
|
request = Rack::MockRequest.env_for("/anything.json")
|
33
|
+
Coverband::Base.instance.reset_instance
|
32
34
|
middleware = Coverband::Middleware.new(fake_app)
|
33
|
-
assert_equal false,
|
34
|
-
|
35
|
+
assert_equal false, Coverband::Base.instance.instance_variable_get("@enabled")
|
36
|
+
Coverband::Base.instance.instance_variable_set("@sample_percentage", 0.0)
|
35
37
|
results = middleware.call(request)
|
36
|
-
assert_equal false,
|
38
|
+
assert_equal false, Coverband::Base.instance.instance_variable_get("@enabled")
|
37
39
|
end
|
38
40
|
|
39
41
|
should 'always unset function when sampling' do
|
40
42
|
request = Rack::MockRequest.env_for("/anything.json")
|
43
|
+
Coverband::Base.instance.reset_instance
|
41
44
|
middleware = Coverband::Middleware.new(fake_app)
|
42
|
-
assert_equal false,
|
43
|
-
|
45
|
+
assert_equal false, Coverband::Base.instance.instance_variable_get("@tracer_set")
|
46
|
+
Coverband::Base.instance.instance_variable_set("@sample_percentage", 100.0)
|
44
47
|
results = middleware.call(request)
|
45
|
-
assert_equal false,
|
48
|
+
assert_equal false, Coverband::Base.instance.instance_variable_get("@tracer_set")
|
46
49
|
end
|
47
50
|
|
48
51
|
should 'always unset function when not sampling' do
|
49
52
|
request = Rack::MockRequest.env_for("/anything.json")
|
53
|
+
Coverband::Base.instance.reset_instance
|
50
54
|
middleware = Coverband::Middleware.new(fake_app)
|
51
|
-
assert_equal false,
|
55
|
+
assert_equal false, Coverband::Base.instance.instance_variable_get("@tracer_set")
|
52
56
|
middleware.instance_variable_set("@sample_percentage", 0.0)
|
53
57
|
results = middleware.call(request)
|
54
|
-
assert_equal false,
|
58
|
+
assert_equal false, Coverband::Base.instance.instance_variable_get("@tracer_set")
|
55
59
|
end
|
56
60
|
|
57
61
|
should 'always record coverage, set trace func, and add_files when sampling' do
|
58
62
|
request = Rack::MockRequest.env_for("/anything.json")
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
+
Coverband::Base.instance.reset_instance
|
64
|
+
middleware = Coverband::Middleware.new(fake_app)
|
65
|
+
assert_equal false, Coverband::Base.instance.instance_variable_get("@enabled")
|
66
|
+
Coverband::Base.instance.instance_variable_set("@sample_percentage", 100.0)
|
67
|
+
Coverband::Base.instance.expects(:add_file).at_least_once
|
63
68
|
results = middleware.call(request)
|
64
|
-
assert_equal true,
|
69
|
+
assert_equal true, Coverband::Base.instance.instance_variable_get("@enabled")
|
65
70
|
end
|
66
71
|
|
67
72
|
should 'always report coverage when sampling' do
|
68
73
|
request = Rack::MockRequest.env_for("/anything.json")
|
69
|
-
|
70
|
-
|
71
|
-
middleware.
|
74
|
+
Coverband::Base.instance.reset_instance
|
75
|
+
|
76
|
+
file_with_path = File.expand_path('../../lib/coverband/middleware.rb', File.dirname(__FILE__))
|
77
|
+
|
78
|
+
middleware = Coverband::Middleware.new(fake_app)
|
79
|
+
assert_equal false, Coverband::Base.instance.instance_variable_get("@enabled")
|
80
|
+
Coverband::Base.instance.instance_variable_set("@sample_percentage", 100.0)
|
72
81
|
fake_redis = Redis.new
|
73
|
-
|
82
|
+
Coverband::Base.instance.instance_variable_set("@reporter", Coverband::RedisStore.new(fake_redis))
|
74
83
|
fake_redis.stubs(:info).returns({'redis_version' => 3.0})
|
75
84
|
fake_redis.expects(:sadd).at_least_once
|
76
|
-
fake_redis.expects(:sadd).at_least_once.with("coverband.#{
|
85
|
+
fake_redis.expects(:sadd).at_least_once.with("coverband.#{file_with_path}", [11, 11, 11, 12])
|
77
86
|
results = middleware.call(request)
|
78
|
-
assert_equal true,
|
87
|
+
assert_equal true, Coverband::Base.instance.instance_variable_get("@enabled")
|
79
88
|
end
|
80
89
|
|
81
90
|
private
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coverband
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.1.0.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Mayer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -167,9 +167,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
167
167
|
version: '0'
|
168
168
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
169
|
requirements:
|
170
|
-
- - "
|
170
|
+
- - ">"
|
171
171
|
- !ruby/object:Gem::Version
|
172
|
-
version:
|
172
|
+
version: 1.3.1
|
173
173
|
requirements: []
|
174
174
|
rubyforge_project:
|
175
175
|
rubygems_version: 2.2.2
|