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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 32fd77ef72fd2521addf3e66074af44d1963f16b
4
- data.tar.gz: 6f978b929e31a9d2ad67e1964ea431b8823eed2e
3
+ metadata.gz: cc5c300ba7c487716b6a46f455757351caaaafea
4
+ data.tar.gz: 6a8509f0b16f807930bcb15a409f273bbdcdd241
5
5
  SHA512:
6
- metadata.gz: 5f88e79d2e75dfeb687438f27d251e3eda0693622cbd9dd8697b45bdff95d6ba6c750d2a3aae048829a2cacac87090747320d3e9a5dfe2f7fb17378a765bc2ea
7
- data.tar.gz: 6802dcf7c4d5efdd7f757f4ff7bf4ae5b4eafac379c9742580f28a84dc5396c8b2ca0f2a08c72c77e6724507d8650c260e0f3d174fbd1f9c28ab9b778ea94202
6
+ metadata.gz: fb3eeb08c1b3e00de3e9c19ceed3e3d3275c9e43b8e9b38bce8725788b00a951923bec225349eb8df1178810de31ccd914fe5663821f095f7fdcbc2c2692edc7
7
+ data.tar.gz: b7794879639fb64bab184ceca7763c716db6384cb36d814db091b6a2e2d77375fb51f47f2df6321b68f27c4f513318c509a5fd974a0d2ac54a2a5f06b8031cd2
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.0"
4
+ - "2.1"
5
+ - "2.2"
6
+ services:
7
+ - redis-server
8
+ before_install:
9
+ - gem install bundler
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in coverband.gemspec
4
4
  gemspec
5
+ gem 'classifier-reborn'
6
+
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
- 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 stopping 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.
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
- At the moment the performance impact of standard Ruby runtime coverage can be pretty large. Once getting things working. I highly recommend adding [coverband_ext](https://github.com/danmayer/coverband_ext) to the project which should shave the performance overhead down to something very reasonable. The two ways to deal with performance right now are lowering the sample rate and using the C extension. Often for smaller projects using the C extension makes 100% coverage possible.
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
- ## Usage
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
- #### Configure Coverband Options
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
- #### Configuring Rake
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
- #### Configure rack middleware
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
- #### Configure Manually (for example for background jobs)
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
- After a deploy where code has changed.
211
- The line numbers previously recorded in Redis may no longer match the current state of the files.
212
- If being slightly out of sync isn't as important as gathering data over a long period,
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
- #### Known issues
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
- ### TODO
304
+ ## TODO
266
305
 
267
- * Fix network performance by logging to files that purge later (like NR) (far more time lost in set_trace_func than sending files, hence not a high priority, but would be cool)
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
@@ -1,5 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
+ import 'test/benchmarks/benchmark.rake'
4
+
3
5
  task :default => :test
4
6
  require 'rake/testtask'
5
7
  Rake::TestTask.new(:test) do |test|
@@ -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"
@@ -2,25 +2,28 @@ require 'singleton'
2
2
 
3
3
  module Coverband
4
4
  class Base
5
- include Singleton
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 @reporter.class.name.match(/redis/i)
91
- before_time = Time.now
92
- @stats.count "coverband.files.recorded_files", @files.length if @stats
93
- @reporter.store_report(@files.dup)
94
- time_spent = Time.now - before_time
95
- @stats.timing "coverband.files.recorded_time", time_spent if @stats
96
- @files = {}
97
- @file_usage = Hash.new(0)
98
- @file_line_usage = {}
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
- set_trace_func proc { |event, file, line, id, binding, classname|
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
- if @tracer_set
124
- set_trace_func(nil)
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
@@ -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
- results = @app.call(env)
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
@@ -1,3 +1,3 @@
1
1
  module Coverband
2
- VERSION = "1.0.3"
2
+ VERSION = "1.1"
3
3
  end
@@ -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" ]
@@ -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 { |files|
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
- 1 + 1
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
@@ -0,0 +1,5 @@
1
+ class Dog
2
+ def bark
3
+ "woof"
4
+ end
5
+ 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
- middleware = Coverband::Middleware.new(fake_app)
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.#{file_with_path}", [11, 11, 11, 12])
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.0.3
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: 2015-12-08 00:00:00.000000000 Z
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.4.5
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