coverband 1.0.3 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +9 -0
- data/Gemfile +2 -0
- data/README.md +87 -48
- data/Rakefile +2 -0
- data/coverband.gemspec +1 -0
- data/lib/coverband/base.rb +51 -50
- data/lib/coverband/configuration.rb +4 -2
- data/lib/coverband/middleware.rb +4 -4
- data/lib/coverband/version.rb +1 -1
- data/test/benchmarks/.gitignore +1 -0
- data/test/benchmarks/benchmark.rake +89 -0
- data/test/unit/base_test.rb +8 -8
- data/test/unit/configuration_test.rb +21 -0
- data/test/unit/dog.rb +5 -0
- data/test/unit/middleware_test.rb +37 -5
- metadata +26 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc5c300ba7c487716b6a46f455757351caaaafea
|
4
|
+
data.tar.gz: 6a8509f0b16f807930bcb15a409f273bbdcdd241
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb3eeb08c1b3e00de3e9c19ceed3e3d3275c9e43b8e9b38bce8725788b00a951923bec225349eb8df1178810de31ccd914fe5663821f095f7fdcbc2c2692edc7
|
7
|
+
data.tar.gz: b7794879639fb64bab184ceca7763c716db6384cb36d814db091b6a2e2d77375fb51f47f2df6321b68f27c4f513318c509a5fd974a0d2ac54a2a5f06b8031cd2
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,19 +1,22 @@
|
|
1
1
|
# Coverband
|
2
2
|
|
3
|
+
[![Build Status](https://travis-ci.org/danmayer/coverband.svg?branch=master)](https://travis-ci.org/danmayer/coverband)
|
4
|
+
|
3
5
|
A gem to measure production code coverage. Coverband allows easy configuration to collect and report on production code coverage. It can be used as Rack middleware, wrapping a block with sampling, or manually configured to meet any need (like coverage on background jobs).
|
4
6
|
|
5
7
|
* Allow sampling to avoid the performance overhead on every request.
|
6
8
|
* Ignore directories to avoid overhead data collection on vendor, lib, etc.
|
7
9
|
* Take a baseline to get initial app loading coverage.
|
8
10
|
|
9
|
-
|
11
|
+
coverband 1.1+ supports ruby 2.0+. For ruby 1.9 use coverband 1.0.X.
|
12
|
+
|
10
13
|
|
11
14
|
###### Success:
|
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.
|
15
|
+
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.
|
13
16
|
|
14
17
|
###### Performance Impact
|
15
18
|
|
16
|
-
|
19
|
+
Because coverband 2.0+ uses TracePoint which is much speedier than the ruby 1.9 set_trace_func, the performance impact is reasonable and there is no need for the c-ext [coverband_ext](https://github.com/danmayer/coverband_ext). If you are stuck on ruby 1.9 and coverband 1.0.X, the [coverband_ext](https://github.com/danmayer/coverband_ext) is a must.
|
17
20
|
|
18
21
|
## Installation
|
19
22
|
|
@@ -37,7 +40,7 @@ $ gem install coverband
|
|
37
40
|
|
38
41
|
## Example Output
|
39
42
|
|
40
|
-
Since Coverband is [Simplecov](https://github.com/colszowka/simplecov) output compatible it should work with any of the `SimpleCov::Formatter`'s available. The output below is produced using the default Simplecov HTML formatter.
|
43
|
+
Since Coverband is [Simplecov](https://github.com/colszowka/simplecov) output compatible it should work with any of the `SimpleCov::Formatter`'s available. The output below is produced using the default Simplecov HTML formatter.
|
41
44
|
|
42
45
|
Index Page
|
43
46
|
![image](https://raw.github.com/danmayer/coverband/master/docs/coverband_index.png)
|
@@ -55,22 +58,9 @@ Details on a example Sinatra app
|
|
55
58
|
* Using Redis 2.x gem, while supported, is slow and not recommended. It will have a larger impact on overhead performance. Although the Ruby collection dwarfs the redis time, so it likely doesn't matter much.
|
56
59
|
* Make sure to ignore any folders like `vendor` and possibly `lib` as it can help reduce performance overhead. Or ignore specific frequently hit in app files for better perf.
|
57
60
|
|
58
|
-
##
|
59
|
-
|
60
|
-
After installing the gem. There are a few steps to gather data, view reports, and for cleaning up the data.
|
61
|
-
|
62
|
-
See an [example Sinatra app](https://github.com/danmayer/churn-site) and example [non rack ruby app](https://github.com/danmayer/coverband_examples) configured with coverband.
|
63
|
-
|
64
|
-
1. First configure cover band options using the config file, See the section below
|
65
|
-
2. Then configure Rake, with helpful tasks. Make sure things are working by recording your Coverband baseline. See the section below
|
66
|
-
3. Setup the rack middleware, the middleware is what makes Coverband gather metrics when your app runs. See below for details
|
67
|
-
* I setup Coverband in my rackup `config.ru` you can also set it up in rails middleware, but it may miss recording some code coverage early in the rails process. It does improve performance to have it later in the middleware stack. So there is a tradeoff there.
|
68
|
-
* To debug in development mode, I recommend turning verbose logging on `config.verbose = true` and passing in the Rails.logger `config.logger = Rails.logger` to the Coverband config. This makes it easy to follow in development mode. Be careful to not leave these on in production as they will effect performance.
|
69
|
-
4. Start your server with `rackup config.ru` If you use `rails s` make sure it is using your `config.ru` or Coverband won't be recording any data.
|
70
|
-
5. Hit your development server exercising the endpoints you want to verify Coverband is recording.
|
71
|
-
6. Now to view changes in live coverage run `rake coverband:coverage` again, previously it should have only shown the baseline data of your app initializing. After using it in development it should show increased coverage from the actions you have exercised.
|
61
|
+
## Configuration
|
72
62
|
|
73
|
-
|
63
|
+
### 1. Create Coverband config file
|
74
64
|
|
75
65
|
You need to configure cover band you can either do that passing in all configuration options to `Coverband.configure` in block format, or a simpler style is to call `Coverband.configure` with nothing while will load `config/coverband.rb` expecting it to configure the app correctly. Below is an example config file for a Sinatra app:
|
76
66
|
|
@@ -88,15 +78,15 @@ Coverband.configure do |config|
|
|
88
78
|
config.coverage_baseline = baseline
|
89
79
|
config.root_paths = ['/app/'] # /app/ is needed for heroku deployments
|
90
80
|
# regex paths can help if you are seeing files duplicated for each capistrano deployment release
|
91
|
-
#config.root_paths = ['/server/apps/my_app/releases/\d+/']
|
81
|
+
#config.root_paths = ['/server/apps/my_app/releases/\d+/']
|
92
82
|
config.ignore = ['vendor','lib/scrazy_i18n_patch_thats_hit_all_the_time.rb']
|
93
83
|
# Since rails and other frameworks lazy load code. I have found it is bad to allow
|
94
84
|
# initial requests to record with coverband. This ignores first 15 requests
|
95
85
|
config.startup_delay = Rails.env.production? ? 15 : 2
|
96
86
|
config.percentage = Rails.env.production? ? 30.0 : 100.0
|
97
|
-
|
87
|
+
|
98
88
|
config.logger = Rails.logger
|
99
|
-
|
89
|
+
|
100
90
|
#stats help you collect how often you are sampling requests and other info
|
101
91
|
if defined? Statsd
|
102
92
|
config.stats = Statsd.new('statsd.host.com', 8125)
|
@@ -109,7 +99,7 @@ Coverband.configure do |config|
|
|
109
99
|
end
|
110
100
|
```
|
111
101
|
|
112
|
-
|
102
|
+
### 2. Configuring Rake
|
113
103
|
|
114
104
|
Either add the below to your `Rakefile` or to a file included in your Rakefile such as `lib/tasks/coverband` if you want to break it up that way.
|
115
105
|
|
@@ -143,22 +133,72 @@ end
|
|
143
133
|
|
144
134
|
To verify that rake is working run `rake coverband:baseline`
|
145
135
|
then run `rake coverband:coverage` to view what your baseline coverage looks like before any runtime traffic has been recorded.
|
146
|
-
|
147
|
-
|
136
|
+
|
137
|
+
### 3. Configure Rack to use the Coverband middleware
|
138
|
+
|
139
|
+
The middleware is what makes Coverband gather metrics when your app runs.
|
140
|
+
I setup Coverband in my rackup `config.ru` you can also set it up in rails middleware, but it may miss recording some code coverage early in the rails process. It does improve performance to have it later in the middleware stack. So there is a tradeoff there.
|
141
|
+
|
142
|
+
#### For Sinatra apps
|
148
143
|
|
149
144
|
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, though you may end up missing some boot up coverage.
|
150
145
|
|
151
146
|
```ruby
|
152
147
|
require File.dirname(__FILE__) + '/config/environment'
|
153
|
-
|
148
|
+
|
154
149
|
require 'coverband'
|
155
150
|
Coverband.configure
|
156
151
|
|
157
152
|
use Coverband::Middleware
|
158
153
|
run ActionController::Dispatcher.new
|
159
154
|
```
|
160
|
-
|
161
|
-
####
|
155
|
+
|
156
|
+
#### For Rails apps
|
157
|
+
|
158
|
+
Create an initializer file
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
# config/initializes/coverband_middleware.rb
|
162
|
+
|
163
|
+
# Configure the Coverband Middleware
|
164
|
+
require 'coverband'
|
165
|
+
Coverband.configure
|
166
|
+
```
|
167
|
+
|
168
|
+
Then add the middleware to your Rails rake middle ware stack:
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
# config/application.rb
|
172
|
+
[...]
|
173
|
+
|
174
|
+
module MyApplication
|
175
|
+
class Application < Rails::Application
|
176
|
+
[...]
|
177
|
+
|
178
|
+
# Coverband use Middleware
|
179
|
+
config.middleware.use Coverband::Middleware
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
Note: To debug in development mode, I recommend turning verbose logging on `config.verbose = true` and passing in the Rails.logger `config.logger = Rails.logger` to the Coverband config. This makes it easy to follow in development mode. Be careful to not leave these on in production as they will effect performance.
|
186
|
+
|
187
|
+
## Usage
|
188
|
+
|
189
|
+
1. Start your server with `rails s` or `rackup config.ru`.
|
190
|
+
2. Hit your development server exercising the endpoints you want to verify Coverband is recording (you should see debug outputs in your server console)
|
191
|
+
3. Run `rake coverband:coverage` again, previously it should have only shown the baseline data of your app initializing. After using it in development it should show increased coverage from the actions you have exercised.
|
192
|
+
|
193
|
+
Note: if you use `rails s` and data aren't reccorded, make sure it is using your `config.ru`.
|
194
|
+
|
195
|
+
## Example apps
|
196
|
+
|
197
|
+
- [Rails app](https://github.com/arnlen/rails-coverband-example-app)
|
198
|
+
- [Sinatra app](https://github.com/danmayer/churn-site)
|
199
|
+
- [Non rack ruby app](https://github.com/danmayer/coverband_examples)
|
200
|
+
|
201
|
+
### Manual configuration (for example for background jobs)
|
162
202
|
|
163
203
|
It is easy to use Coverband outside of a Rack environment. Make sure you configure Coverband in whatever environment you are using (such as `config/initializers/*.rb`). Then you can hook into before and after events to add coverage around background jobs, or for any non Rack code.
|
164
204
|
|
@@ -176,7 +216,7 @@ def before_perform(*args)
|
|
176
216
|
@recording_samples = false
|
177
217
|
end
|
178
218
|
end
|
179
|
-
|
219
|
+
|
180
220
|
def after_perform(*args)
|
181
221
|
if @recording_samples
|
182
222
|
Coverband::Base.instance.stop
|
@@ -191,25 +231,25 @@ In general you can run Coverband anywhere by using the lines below. This can be
|
|
191
231
|
```ruby
|
192
232
|
require "coverband"
|
193
233
|
Coverband.configure
|
194
|
-
|
234
|
+
|
195
235
|
coverband = Coverband::Base.instance
|
196
|
-
|
236
|
+
|
197
237
|
#manual
|
198
238
|
coverband.start
|
199
239
|
coverband.stop
|
200
240
|
coverband.save
|
201
|
-
|
241
|
+
|
202
242
|
#sampling
|
203
243
|
coverband.sample {
|
204
244
|
#code to sample coverband
|
205
245
|
}
|
206
246
|
```
|
207
|
-
|
208
|
-
#### Clearing Line Coverage Data
|
209
247
|
|
210
|
-
|
211
|
-
|
212
|
-
|
248
|
+
### Clearing Line Coverage Data
|
249
|
+
|
250
|
+
After a deploy where code has changed.
|
251
|
+
The line numbers previously recorded in Redis may no longer match the current state of the files.
|
252
|
+
If being slightly out of sync isn't as important as gathering data over a long period,
|
213
253
|
you can live with minor inconsistency for some files.
|
214
254
|
|
215
255
|
As often as you like or as part of a deploy hook you can clear the recorded Coverband data with the following command.
|
@@ -225,10 +265,10 @@ You can also do this with the included rake tasks.
|
|
225
265
|
|
226
266
|
### Verbose debug mode for development
|
227
267
|
|
228
|
-
If you are trying to debug locally wondering what code is being run during a request. The verbose modes `config.verbose = true` and `config.verbose = 'debug'` can be useful. With true set it will output the number of lines executed per file, to the passed in log. The files are sorted from least used file to most active file. I have even run that mode in production without much of a problem. The debug verbose mode outputs both file usage and provides the number of calls per line of code. For example if you see something like below which indicates that the `application_helper` has 43150 lines executed. That might seem odd. Then looking at the breakdown of `application_helper` we can see that line `516` was executed 38,577 times. That seems bad, and is likely worth investigating perhaps memoizing or cacheing is required.
|
268
|
+
If you are trying to debug locally wondering what code is being run during a request. The verbose modes `config.verbose = true` and `config.verbose = 'debug'` can be useful. With true set it will output the number of lines executed per file, to the passed in log. The files are sorted from least used file to most active file. I have even run that mode in production without much of a problem. The debug verbose mode outputs both file usage and provides the number of calls per line of code. For example if you see something like below which indicates that the `application_helper` has 43150 lines executed. That might seem odd. Then looking at the breakdown of `application_helper` we can see that line `516` was executed 38,577 times. That seems bad, and is likely worth investigating perhaps memoizing or cacheing is required.
|
229
269
|
|
230
270
|
config.verbose = 'debug'
|
231
|
-
|
271
|
+
|
232
272
|
coverband file usage:
|
233
273
|
[["/Users/danmayer/projects/app_name/lib/facebook.rb", 6],
|
234
274
|
["/Users/danmayer/projects/app_name/app/models/some_modules.rb", 9],
|
@@ -236,35 +276,34 @@ If you are trying to debug locally wondering what code is being run during a req
|
|
236
276
|
["/Users/danmayer/projects/app_name/app/models/user.rb", 2606],
|
237
277
|
["/Users/danmayer/projects/app_name/app/helpers/application_helper.rb",
|
238
278
|
43150]]
|
239
|
-
|
279
|
+
|
240
280
|
file:
|
241
281
|
/Users/danmayer/projects/app_name/app/helpers/application_helper.rb =>
|
242
282
|
[[448, 1], [202, 1],
|
243
283
|
...
|
244
284
|
[517, 1617], [516, 38577]]
|
245
|
-
|
246
|
-
### Merge coverage data over time
|
285
|
+
|
286
|
+
### Merge coverage data over time
|
247
287
|
|
248
288
|
If you are clearing data on every deploy. You might want to write the data out to a file first. Then you could merge the data into the final results later.
|
249
289
|
|
250
290
|
```ruby
|
251
291
|
data = JSON.generate Coverband::Reporter.get_current_scov_data
|
252
|
-
File.write("blah.json", data)
|
292
|
+
File.write("blah.json", data)
|
253
293
|
# Then later on, pass it in to the html reporter:
|
254
294
|
data = JSON.parse(File.read("blah.json"))
|
255
|
-
Coverband::Reporter.report :additional_scov_data => [data]
|
295
|
+
Coverband::Reporter.report :additional_scov_data => [data]
|
256
296
|
```
|
257
297
|
|
258
|
-
|
298
|
+
### Known issues
|
259
299
|
|
260
|
-
* `set_trace_func` isn't perfect in sending each line of code executed and can be a bit wonky in a few places. Such as missing the `end` lines in code blocks. If you notice examples of this send them to me.
|
261
300
|
* If you don't have a baseline recorded your coverage can look odd like you are missing a bunch of data. It would be good if coverband gave a more actionable warning in this situation.
|
262
301
|
* If you have simplecov filters, you need to clear them prior to generating your coverage report. As the filters will be applied to coverband as well and can often filter out everything we are recording.
|
263
302
|
* the line numbers reported for `ERB` files are often off and aren't considered useful. I recommend filtering out .erb using the `config.ignore` option.
|
264
303
|
|
265
|
-
|
304
|
+
## TODO
|
266
305
|
|
267
|
-
* Fix network performance by logging to files that purge later (like NR) (far more time lost in
|
306
|
+
* Fix network performance by logging to files that purge later (like NR) (far more time lost in TracePoint than sending files, hence not a high priority, but would be cool)
|
268
307
|
* Add support for [zadd](http://redis.io/topics/data-types-intro) so one could determine single call versus multiple calls on a line, letting us determine the most executed code in production.
|
269
308
|
* Possibly add ability to record code run for a given route
|
270
309
|
* Improve client code api, around manual usage of sampling (like event usage)
|
data/Rakefile
CHANGED
data/coverband.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
spec.add_development_dependency "mocha", "~> 0.14.0"
|
24
24
|
spec.add_development_dependency "rack"
|
25
|
+
spec.add_development_dependency "test-unit"
|
25
26
|
spec.add_runtime_dependency "simplecov"
|
26
27
|
spec.add_runtime_dependency "json"
|
27
28
|
spec.add_runtime_dependency "redis"
|
data/lib/coverband/base.rb
CHANGED
@@ -2,25 +2,28 @@ require 'singleton'
|
|
2
2
|
|
3
3
|
module Coverband
|
4
4
|
class Base
|
5
|
-
|
5
|
+
|
6
|
+
def self.instance
|
7
|
+
Thread.current[:coverband_instance] ||= Coverband::Base.new
|
8
|
+
end
|
6
9
|
|
7
10
|
def start
|
8
11
|
@enabled = true
|
9
12
|
record_coverage
|
10
13
|
end
|
11
|
-
|
14
|
+
|
12
15
|
def stop
|
13
16
|
@enabled = false
|
14
17
|
unset_tracer
|
15
18
|
end
|
16
|
-
|
19
|
+
|
17
20
|
def sample
|
18
21
|
configure_sampling
|
19
22
|
record_coverage
|
20
23
|
yield
|
21
24
|
report_coverage
|
22
25
|
end
|
23
|
-
|
26
|
+
|
24
27
|
def save
|
25
28
|
@enabled = true
|
26
29
|
report_coverage
|
@@ -39,12 +42,15 @@ module Coverband
|
|
39
42
|
@file_usage = Hash.new(0)
|
40
43
|
@file_line_usage = {}
|
41
44
|
@startup_delay = Coverband.configuration.startup_delay
|
42
|
-
@ignore_patterns = Coverband.configuration.ignore
|
45
|
+
@ignore_patterns = Coverband.configuration.ignore + ["internal:prelude"]
|
46
|
+
@ignore_patterns += ['gems'] unless Coverband.configuration.include_gems
|
43
47
|
@sample_percentage = Coverband.configuration.percentage
|
44
48
|
@reporter = Coverband::RedisStore.new(Coverband.configuration.redis) if Coverband.configuration.redis
|
45
49
|
@stats = Coverband.configuration.stats
|
46
50
|
@verbose = Coverband.configuration.verbose
|
47
51
|
@logger = Coverband.configuration.logger
|
52
|
+
@current_thread = Thread.current
|
53
|
+
@trace = create_trace_point
|
48
54
|
self
|
49
55
|
end
|
50
56
|
|
@@ -79,7 +85,10 @@ module Coverband
|
|
79
85
|
|
80
86
|
unset_tracer
|
81
87
|
|
88
|
+
@files.reject!{|file, lines| !track_file?(file) }
|
89
|
+
|
82
90
|
if @verbose
|
91
|
+
@file_usage.reject!{|file, line_count| !track_file?(file) }
|
83
92
|
@logger.info "coverband file usage: #{@file_usage.sort_by {|_key, value| value}.inspect}"
|
84
93
|
if @verbose=="debug"
|
85
94
|
output_file_line_usage
|
@@ -87,15 +96,19 @@ module Coverband
|
|
87
96
|
end
|
88
97
|
|
89
98
|
if @reporter
|
90
|
-
if @
|
91
|
-
before_time = Time.now
|
92
|
-
@stats.count "coverband.files.recorded_files", @files.length
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
@
|
97
|
-
@
|
98
|
-
|
99
|
+
if @stats
|
100
|
+
@before_time = Time.now
|
101
|
+
@stats.count "coverband.files.recorded_files", @files.length
|
102
|
+
end
|
103
|
+
@reporter.store_report(@files)
|
104
|
+
if @stats
|
105
|
+
@time_spent = Time.now - @before_time
|
106
|
+
@stats.timing "coverband.files.recorded_time", @time_spent
|
107
|
+
end
|
108
|
+
@files.clear
|
109
|
+
if @verbose
|
110
|
+
@file_usage.clear
|
111
|
+
@file_line_usage.clear
|
99
112
|
end
|
100
113
|
elsif @verbose
|
101
114
|
@logger.info "coverage report: "
|
@@ -110,49 +123,21 @@ module Coverband
|
|
110
123
|
|
111
124
|
protected
|
112
125
|
|
126
|
+
def track_file? file
|
127
|
+
!@ignore_patterns.any?{ |pattern| file.include?(pattern) } && file.start_with?(@project_directory)
|
128
|
+
end
|
129
|
+
|
130
|
+
|
113
131
|
def set_tracer
|
114
132
|
unless @tracer_set
|
115
|
-
|
116
|
-
add_file(file, line)
|
117
|
-
}
|
133
|
+
@trace.enable
|
118
134
|
@tracer_set = true
|
119
135
|
end
|
120
136
|
end
|
121
137
|
|
122
138
|
def unset_tracer
|
123
|
-
|
124
|
-
|
125
|
-
@tracer_set = false
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def add_file(file, line)
|
130
|
-
if !file.match(/(\/gems\/|internal\:prelude)/) && file.match(@project_directory) && !@ignore_patterns.any?{|pattern| file.match(/#{pattern}/) }
|
131
|
-
add_file_without_checks(file, line)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
# file from ruby coverband at this method call is a full path
|
136
|
-
# file from native coverband is also a full path
|
137
|
-
#
|
138
|
-
# at the moment the full paths don't merge so the 'last' one wins and each deploy
|
139
|
-
# with a normal capistrano setup resets coverage.
|
140
|
-
#
|
141
|
-
# should we make it relative in this method (slows down collection)
|
142
|
-
# -- OR --
|
143
|
-
# we could have the reporter MERGE the results after normalizing the filenames
|
144
|
-
# (went with this route see report_scov previous_line_hash)
|
145
|
-
def add_file_without_checks(file, line)
|
146
|
-
if @verbose
|
147
|
-
@file_usage[file] += 1
|
148
|
-
@file_line_usage[file] = Hash.new(0) unless @file_line_usage.include?(file)
|
149
|
-
@file_line_usage[file][line] += 1
|
150
|
-
end
|
151
|
-
if @files.include?(file)
|
152
|
-
@files[file] << line unless @files.include?(line)
|
153
|
-
else
|
154
|
-
@files[file] = [line]
|
155
|
-
end
|
139
|
+
@trace.disable
|
140
|
+
@tracer_set = false
|
156
141
|
end
|
157
142
|
|
158
143
|
def output_file_line_usage
|
@@ -166,6 +151,22 @@ module Coverband
|
|
166
151
|
|
167
152
|
private
|
168
153
|
|
154
|
+
def create_trace_point
|
155
|
+
TracePoint.new(*Coverband.configuration.trace_point_events) do |tp|
|
156
|
+
if Thread.current == @current_thread
|
157
|
+
file = tp.path
|
158
|
+
line = tp.lineno
|
159
|
+
if @verbose
|
160
|
+
@file_usage[file] += 1
|
161
|
+
@file_line_usage[file] = Hash.new(0) unless @file_line_usage.include?(file)
|
162
|
+
@file_line_usage[file][line] += 1
|
163
|
+
end
|
164
|
+
file_lines = (@files[file] ||= [])
|
165
|
+
file_lines << line
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
169
170
|
def initialize
|
170
171
|
reset_instance
|
171
172
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Coverband
|
2
2
|
class Configuration
|
3
|
-
attr_accessor :redis, :coverage_baseline, :root_paths, :root, :ignore, :percentage, :verbose, :reporter, :stats, :logger, :startup_delay, :baseline_file
|
4
|
-
|
3
|
+
attr_accessor :redis, :coverage_baseline, :root_paths, :root, :ignore, :percentage, :verbose, :reporter, :stats, :logger, :startup_delay, :baseline_file, :trace_point_events, :include_gems
|
4
|
+
|
5
5
|
def initialize
|
6
6
|
@root = Dir.pwd
|
7
7
|
@redis = nil
|
@@ -10,11 +10,13 @@ module Coverband
|
|
10
10
|
@baseline_file = './tmp/coverband_baseline.json'
|
11
11
|
@root_paths = []
|
12
12
|
@ignore = []
|
13
|
+
@include_gems = false
|
13
14
|
@percentage = 0.0
|
14
15
|
@verbose = false
|
15
16
|
@reporter = 'scov'
|
16
17
|
@logger = Logger.new(STDOUT)
|
17
18
|
@startup_delay = 0
|
19
|
+
@trace_point_events = [:line]
|
18
20
|
end
|
19
21
|
|
20
22
|
def logger
|
data/lib/coverband/middleware.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Coverband
|
2
2
|
class Middleware
|
3
|
-
|
3
|
+
|
4
4
|
def initialize(app)
|
5
5
|
@app = app
|
6
6
|
end
|
@@ -8,10 +8,10 @@ module Coverband
|
|
8
8
|
def call(env)
|
9
9
|
Coverband::Base.instance.configure_sampling
|
10
10
|
Coverband::Base.instance.record_coverage
|
11
|
-
|
11
|
+
@app.call(env)
|
12
|
+
ensure
|
12
13
|
Coverband::Base.instance.report_coverage
|
13
|
-
results
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
end
|
17
17
|
end
|
data/lib/coverband/version.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
classifier-reborn
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'coverband'
|
2
|
+
require 'redis'
|
3
|
+
|
4
|
+
namespace :benchmarks do
|
5
|
+
|
6
|
+
def classifier_dir
|
7
|
+
classifier_dir = File.join(File.dirname(__FILE__), 'classifier-reborn')
|
8
|
+
end
|
9
|
+
|
10
|
+
def clone_classifier
|
11
|
+
unless Dir.exist? classifier_dir
|
12
|
+
system "git clone git@github.com:jekyll/classifier-reborn.git #{classifier_dir}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'set up coverband'
|
17
|
+
task :setup do
|
18
|
+
clone_classifier
|
19
|
+
$LOAD_PATH.unshift(File.join(classifier_dir, 'lib'))
|
20
|
+
require 'benchmark'
|
21
|
+
require 'classifier-reborn'
|
22
|
+
|
23
|
+
Coverband.configure do |config|
|
24
|
+
config.redis = Redis.new
|
25
|
+
config.root = Dir.pwd
|
26
|
+
config.startup_delay = 0
|
27
|
+
config.percentage = 100.0
|
28
|
+
config.logger = $stdout
|
29
|
+
config.verbose = false
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def bayes_classification
|
36
|
+
b = ClassifierReborn::Bayes.new 'Interesting', 'Uninteresting'
|
37
|
+
b.train_interesting "here are some good words. I hope you love them"
|
38
|
+
b.train_uninteresting "here are some bad words, I hate you"
|
39
|
+
b.classify "I hate bad words and you" # returns 'Uninteresting'
|
40
|
+
end
|
41
|
+
|
42
|
+
def lsi_classification
|
43
|
+
lsi = ClassifierReborn::LSI.new
|
44
|
+
strings = [ ["This text deals with dogs. Dogs.", :dog],
|
45
|
+
["This text involves dogs too. Dogs! ", :dog],
|
46
|
+
["This text revolves around cats. Cats.", :cat],
|
47
|
+
["This text also involves cats. Cats!", :cat],
|
48
|
+
["This text involves birds. Birds.",:bird ]]
|
49
|
+
strings.each {|x| lsi.add_item x.first, x.last}
|
50
|
+
lsi.search("dog", 3)
|
51
|
+
lsi.find_related(strings[2], 2)
|
52
|
+
lsi.classify "This text is also about dogs!"
|
53
|
+
end
|
54
|
+
|
55
|
+
def work
|
56
|
+
5.times do
|
57
|
+
bayes_classification
|
58
|
+
lsi_classification
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
desc 'runs benchmarks'
|
65
|
+
task :run => :setup do
|
66
|
+
SAMPLINGS = 3
|
67
|
+
bm = Benchmark.bm(15) do |x|
|
68
|
+
|
69
|
+
x.report 'coverband' do
|
70
|
+
SAMPLINGS.times do
|
71
|
+
Coverband::Base.instance.sample do
|
72
|
+
work
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
x.report "no coverband" do
|
78
|
+
SAMPLINGS.times do
|
79
|
+
work
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "runs benchmarks"
|
89
|
+
task benchmarks: [ "benchmarks:run" ]
|
data/test/unit/base_test.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require File.expand_path('../test_helper', File.dirname(__FILE__))
|
2
|
+
require File.expand_path('./dog', File.dirname(__FILE__))
|
2
3
|
|
3
4
|
class BaseTest < Test::Unit::TestCase
|
4
5
|
|
@@ -14,7 +15,7 @@ class BaseTest < Test::Unit::TestCase
|
|
14
15
|
coverband = Coverband::Base.instance.reset_instance
|
15
16
|
assert_equal false, coverband.extended?
|
16
17
|
end
|
17
|
-
|
18
|
+
|
18
19
|
test "stop should disable coverage" do
|
19
20
|
coverband = Coverband::Base.instance.reset_instance
|
20
21
|
assert_equal false, coverband.instance_variable_get("@enabled")
|
@@ -25,7 +26,7 @@ class BaseTest < Test::Unit::TestCase
|
|
25
26
|
assert_equal false, coverband.instance_variable_get("@enabled")
|
26
27
|
assert_equal false, coverband.instance_variable_get("@tracer_set")
|
27
28
|
end
|
28
|
-
|
29
|
+
|
29
30
|
test "allow for sampling with a block and enable when 100 percent sample" do
|
30
31
|
logger = Logger.new(STDOUT)
|
31
32
|
coverband = Coverband::Base.instance.reset_instance
|
@@ -38,7 +39,7 @@ class BaseTest < Test::Unit::TestCase
|
|
38
39
|
coverband.sample { 1 + 1 }
|
39
40
|
assert_equal true, coverband.instance_variable_get("@enabled")
|
40
41
|
end
|
41
|
-
|
42
|
+
|
42
43
|
test "allow reporting with start stop save" do
|
43
44
|
logger = Logger.new(STDOUT)
|
44
45
|
coverband = Coverband::Base.instance.reset_instance
|
@@ -53,19 +54,18 @@ class BaseTest < Test::Unit::TestCase
|
|
53
54
|
coverband.stop
|
54
55
|
coverband.save
|
55
56
|
end
|
56
|
-
|
57
|
+
|
57
58
|
test "allow reporting to redis start stop save" do
|
59
|
+
dog_file = File.expand_path('./dog.rb', File.dirname(__FILE__))
|
58
60
|
coverband = Coverband::Base.instance.reset_instance
|
59
61
|
coverband.instance_variable_set("@sample_percentage", 100.0)
|
60
62
|
coverband.instance_variable_set("@verbose", true)
|
61
63
|
store = Coverband::RedisStore.new(Redis.new)
|
62
64
|
coverband.instance_variable_set("@reporter", store)
|
63
|
-
store.expects(:store_report).once.with
|
64
|
-
files.keys.include?("#{File.expand_path('../../../', __FILE__)}/lib/coverband/base.rb")
|
65
|
-
}
|
65
|
+
store.expects(:store_report).once.with(has_entries(dog_file => [3]) )
|
66
66
|
assert_equal false, coverband.instance_variable_get("@enabled")
|
67
67
|
coverband.start
|
68
|
-
|
68
|
+
Dog.new.bark
|
69
69
|
coverband.stop
|
70
70
|
coverband.save
|
71
71
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path('../test_helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class BaseTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
test "defaults to line trace point event" do
|
6
|
+
assert_equal Coverband.configuration.trace_point_events, [:line]
|
7
|
+
end
|
8
|
+
|
9
|
+
test "defaults to ignore gems" do
|
10
|
+
assert_equal Coverband.configuration.include_gems, false
|
11
|
+
coverband = Coverband::Base.instance.reset_instance
|
12
|
+
assert_equal ['vendor', 'internal:prelude', 'gems'], coverband.instance_variable_get("@ignore_patterns")
|
13
|
+
end
|
14
|
+
|
15
|
+
test "doesn't ignore gems if include_gems = true" do
|
16
|
+
Coverband.configuration.include_gems = true
|
17
|
+
coverband = Coverband::Base.instance.reset_instance
|
18
|
+
assert_equal ['vendor', 'internal:prelude'], coverband.instance_variable_get("@ignore_patterns")
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require File.expand_path('../test_helper', File.dirname(__FILE__))
|
2
|
+
require File.expand_path('../fake_app/basic_rack', File.dirname(__FILE__))
|
2
3
|
require 'rack'
|
3
4
|
|
4
5
|
class MiddlewareTest < Test::Unit::TestCase
|
5
|
-
|
6
6
|
test "call app" do
|
7
7
|
request = Rack::MockRequest.env_for("/anything.json")
|
8
8
|
Coverband::Base.instance.reset_instance
|
@@ -64,37 +64,69 @@ class MiddlewareTest < Test::Unit::TestCase
|
|
64
64
|
middleware = Coverband::Middleware.new(fake_app)
|
65
65
|
assert_equal false, Coverband::Base.instance.instance_variable_get("@enabled")
|
66
66
|
Coverband::Base.instance.instance_variable_set("@sample_percentage", 100.0)
|
67
|
-
Coverband::Base.instance.expects(:add_file).at_least_once
|
68
67
|
results = middleware.call(request)
|
69
68
|
assert_equal true, Coverband::Base.instance.instance_variable_get("@enabled")
|
70
69
|
end
|
71
70
|
|
71
|
+
test 'reports coverage when an error is raised' do
|
72
|
+
request = Rack::MockRequest.env_for("/anything.json")
|
73
|
+
Coverband::Base.instance.reset_instance
|
74
|
+
Coverband::Base.instance.expects(:report_coverage).once
|
75
|
+
middleware = Coverband::Middleware.new(fake_app_raise_error)
|
76
|
+
middleware.call(request) rescue nil
|
77
|
+
end
|
78
|
+
|
79
|
+
|
72
80
|
test 'always report coverage when sampling' do
|
73
81
|
request = Rack::MockRequest.env_for("/anything.json")
|
74
82
|
Coverband::Base.instance.reset_instance
|
83
|
+
middleware = Coverband::Middleware.new(fake_app_with_lines)
|
84
|
+
assert_equal false, Coverband::Base.instance.instance_variable_get("@enabled")
|
85
|
+
Coverband::Base.instance.instance_variable_set("@sample_percentage", 100.0)
|
86
|
+
fake_redis = Redis.new
|
87
|
+
Coverband::Base.instance.instance_variable_set("@reporter", Coverband::RedisStore.new(fake_redis))
|
88
|
+
fake_redis.stubs(:info).returns({'redis_version' => 3.0})
|
89
|
+
fake_redis.expects(:sadd).at_least_once
|
90
|
+
fake_redis.expects(:sadd).at_least_once.with("coverband.#{basic_rack_ruby_file}", [5])
|
91
|
+
results = middleware.call(request)
|
92
|
+
assert_equal true, Coverband::Base.instance.instance_variable_get("@enabled")
|
93
|
+
end
|
75
94
|
|
76
|
-
file_with_path = File.expand_path('../../lib/coverband/middleware.rb', File.dirname(__FILE__))
|
77
95
|
|
78
|
-
|
96
|
+
test 'report only on calls when configured' do
|
97
|
+
request = Rack::MockRequest.env_for("/anything.json")
|
98
|
+
Coverband.configuration.trace_point_events = [:call]
|
99
|
+
Coverband::Base.instance.reset_instance
|
100
|
+
middleware = Coverband::Middleware.new(fake_app_with_lines)
|
79
101
|
assert_equal false, Coverband::Base.instance.instance_variable_get("@enabled")
|
80
102
|
Coverband::Base.instance.instance_variable_set("@sample_percentage", 100.0)
|
81
103
|
fake_redis = Redis.new
|
82
104
|
Coverband::Base.instance.instance_variable_set("@reporter", Coverband::RedisStore.new(fake_redis))
|
83
105
|
fake_redis.stubs(:info).returns({'redis_version' => 3.0})
|
84
106
|
fake_redis.expects(:sadd).at_least_once
|
85
|
-
fake_redis.expects(:sadd).at_least_once.with("coverband.#{
|
107
|
+
fake_redis.expects(:sadd).at_least_once.with("coverband.#{basic_rack_ruby_file}", [4])
|
86
108
|
results = middleware.call(request)
|
87
109
|
assert_equal true, Coverband::Base.instance.instance_variable_get("@enabled")
|
88
110
|
end
|
89
111
|
|
112
|
+
|
113
|
+
|
90
114
|
private
|
91
115
|
|
92
116
|
def fake_app
|
93
117
|
@app ||= lambda { |env| [200, {'Content-Type' => 'text/plain'}, env['PATH_INFO']] }
|
94
118
|
end
|
95
119
|
|
120
|
+
def fake_app_raise_error
|
121
|
+
@fake_app_raise_error ||= lambda { raise "sh** happens" }
|
122
|
+
end
|
123
|
+
|
96
124
|
def fake_app_with_lines
|
97
125
|
@lines_app ||= ::HelloWorld.new
|
98
126
|
end
|
99
127
|
|
128
|
+
def basic_rack_ruby_file
|
129
|
+
File.expand_path('../fake_app/basic_rack.rb', File.dirname(__FILE__))
|
130
|
+
end
|
131
|
+
|
100
132
|
end
|
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: 1.
|
4
|
+
version: '1.1'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Mayer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: test-unit
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: simplecov
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,6 +130,7 @@ extensions: []
|
|
116
130
|
extra_rdoc_files: []
|
117
131
|
files:
|
118
132
|
- ".gitignore"
|
133
|
+
- ".travis.yml"
|
119
134
|
- Gemfile
|
120
135
|
- LICENSE
|
121
136
|
- LICENSE.txt
|
@@ -132,9 +147,13 @@ files:
|
|
132
147
|
- lib/coverband/reporter.rb
|
133
148
|
- lib/coverband/tasks.rb
|
134
149
|
- lib/coverband/version.rb
|
150
|
+
- test/benchmarks/.gitignore
|
151
|
+
- test/benchmarks/benchmark.rake
|
135
152
|
- test/fake_app/basic_rack.rb
|
136
153
|
- test/test_helper.rb
|
137
154
|
- test/unit/base_test.rb
|
155
|
+
- test/unit/configuration_test.rb
|
156
|
+
- test/unit/dog.rb
|
138
157
|
- test/unit/middleware_test.rb
|
139
158
|
- test/unit/redis_store_test.rb
|
140
159
|
- test/unit/reporter_test.rb
|
@@ -158,14 +177,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
158
177
|
version: '0'
|
159
178
|
requirements: []
|
160
179
|
rubyforge_project:
|
161
|
-
rubygems_version: 2.
|
180
|
+
rubygems_version: 2.2.5
|
162
181
|
signing_key:
|
163
182
|
specification_version: 4
|
164
183
|
summary: Rack middleware to help measure production code coverage
|
165
184
|
test_files:
|
185
|
+
- test/benchmarks/.gitignore
|
186
|
+
- test/benchmarks/benchmark.rake
|
166
187
|
- test/fake_app/basic_rack.rb
|
167
188
|
- test/test_helper.rb
|
168
189
|
- test/unit/base_test.rb
|
190
|
+
- test/unit/configuration_test.rb
|
191
|
+
- test/unit/dog.rb
|
169
192
|
- test/unit/middleware_test.rb
|
170
193
|
- test/unit/redis_store_test.rb
|
171
194
|
- test/unit/reporter_test.rb
|