coverband 0.0.9 → 0.0.10

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