coverband 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md 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