coverband 1.3.1 → 1.5.0

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +39 -6
  4. data/changes.md +23 -0
  5. data/coverband.gemspec +2 -1
  6. data/lib/coverband.rb +12 -11
  7. data/lib/coverband/adapters/file_store.rb +59 -0
  8. data/lib/coverband/adapters/memory_cache_store.rb +46 -0
  9. data/lib/coverband/adapters/redis_store.rb +101 -0
  10. data/lib/coverband/base.rb +4 -7
  11. data/lib/coverband/baseline.rb +35 -0
  12. data/lib/coverband/configuration.rb +20 -5
  13. data/lib/coverband/reporters/base.rb +150 -0
  14. data/lib/coverband/reporters/console_report.rb +17 -0
  15. data/lib/coverband/reporters/simple_cov_report.rb +46 -0
  16. data/lib/coverband/s3_report_writer.rb +6 -1
  17. data/lib/coverband/tasks.rb +26 -16
  18. data/lib/coverband/version.rb +1 -1
  19. data/test/benchmarks/benchmark.rake +57 -7
  20. data/test/test_helper.rb +20 -0
  21. data/test/unit/adapters_file_store_test.rb +41 -0
  22. data/test/unit/{memory_cache_store_test.rb → adapters_memory_cache_store_test.rb} +12 -12
  23. data/test/unit/adapters_redis_store_test.rb +164 -0
  24. data/test/unit/base_test.rb +8 -7
  25. data/test/unit/baseline_test.rb +50 -0
  26. data/test/unit/middleware_test.rb +8 -8
  27. data/test/unit/reports_base_test.rb +140 -0
  28. data/test/unit/reports_console_test.rb +37 -0
  29. data/test/unit/reports_simple_cov_test.rb +68 -0
  30. data/test/unit/s3_report_writer_test.rb +1 -0
  31. metadata +37 -24
  32. data/lib/coverband/memory_cache_store.rb +0 -42
  33. data/lib/coverband/redis_store.rb +0 -57
  34. data/lib/coverband/reporter.rb +0 -223
  35. data/test/unit/redis_store_test.rb +0 -107
  36. data/test/unit/reporter_test.rb +0 -207
@@ -0,0 +1,37 @@
1
+ require File.expand_path('../test_helper', File.dirname(__FILE__))
2
+
3
+ class SimpleCovReportTest < Test::Unit::TestCase
4
+ BASE_KEY = Coverband::Adapters::RedisStore::BASE_KEY
5
+
6
+ def setup
7
+ @fake_redis = fake_redis
8
+ @store = Coverband::Adapters::RedisStore.new(@fake_redis, array: true)
9
+ end
10
+
11
+ test "report data" do
12
+ Coverband.configure do |config|
13
+ config.redis = @fake_redis
14
+ config.reporter = 'std_out'
15
+ end
16
+
17
+ Coverband::Reporters::ConsoleReport.expects(:current_root).returns('/tmp/root_dir')
18
+ @fake_redis.expects(:smembers).with(BASE_KEY).returns(fake_coverband_members)
19
+
20
+ fake_coverband_members.each do |key|
21
+ File.expects(:exists?).with(key).returns(true)
22
+ File.expects(:foreach).with(key).returns(Array.new(4){'LOC'})
23
+ @fake_redis.expects(:smembers).with("#{BASE_KEY}.#{key}").returns(["1", "3"])
24
+ end
25
+
26
+ Coverband.configuration.logger.stubs('info')
27
+
28
+ report = Coverband::Reporters::ConsoleReport.report(@store)
29
+ expected = {"/Users/danmayer/projects/hearno/app/controllers/application_controller.rb"=>
30
+ [1, nil, 1, nil],
31
+ "/Users/danmayer/projects/hearno/app/models/account.rb"=>[1, nil, 1, nil],
32
+ "/Users/danmayer/projects/hearno/script/tester.rb"=>[1, nil, 1, nil]}
33
+
34
+ assert_equal(expected, report)
35
+ end
36
+
37
+ end
@@ -0,0 +1,68 @@
1
+ require File.expand_path('../test_helper', File.dirname(__FILE__))
2
+
3
+ class ReportsSimpleCovTest < Test::Unit::TestCase
4
+ BASE_KEY = Coverband::Adapters::RedisStore::BASE_KEY
5
+
6
+ def setup
7
+ @fake_redis = fake_redis
8
+ @store = Coverband::Adapters::RedisStore.new(@fake_redis, array: true)
9
+ end
10
+
11
+ test "generate scov report" do
12
+ Coverband.configure do |config|
13
+ config.redis = @fake_redis
14
+ config.reporter = 'scov'
15
+ config.s3_bucket = nil
16
+ config.ignore = ["notsomething.rb"]
17
+ end
18
+
19
+ Coverband::Reporters::SimpleCovReport.expects(:current_root).at_least_once.returns('/tmp/root_dir')
20
+ @fake_redis.expects(:smembers).with(BASE_KEY).returns(fake_coverband_members)
21
+
22
+ SimpleCov.expects(:track_files)
23
+ SimpleCov.expects(:add_not_loaded_files).returns({})
24
+ SimpleCov::Result.any_instance.expects(:format!)
25
+ SimpleCov.stubs(:root)
26
+
27
+ fake_coverband_members.each do |key|
28
+ File.expects(:exists?).with(key).returns(true)
29
+ File.expects(:foreach).with(key).returns(Array.new(60){'LOC'})
30
+ @fake_redis.expects(:smembers).with("#{BASE_KEY}.#{key}").returns(["54", "55"])
31
+ end
32
+
33
+ Coverband.configuration.logger.stubs('info')
34
+
35
+ Coverband::Reporters::SimpleCovReport.report(@store, open_report: false)
36
+ end
37
+
38
+ test "generate scov report with additional data" do
39
+ Coverband.configure do |config|
40
+ config.redis = @fake_redis
41
+ config.reporter = 'scov'
42
+ config.s3_bucket = nil
43
+ config.ignore = ["notsomething.rb"]
44
+ end
45
+
46
+ Coverband::Reporters::SimpleCovReport.expects(:current_root).at_least_once.returns('/tmp/root_dir')
47
+ @fake_redis.expects(:smembers).with(BASE_KEY).returns(fake_coverband_members)
48
+
49
+ SimpleCov.expects(:track_files)
50
+ SimpleCov.expects(:add_not_loaded_files).returns({"fake_file.rb" => [1]})
51
+ SimpleCov::Result.any_instance.expects(:format!)
52
+ SimpleCov.stubs(:root)
53
+
54
+ fake_coverband_members.each do |key|
55
+ File.expects(:exists?).with(key).returns(true)
56
+ File.expects(:foreach).with(key).returns(['a','b','c'])
57
+ @fake_redis.expects(:smembers).with("#{BASE_KEY}.#{key}").returns(["54", "55"])
58
+ end
59
+
60
+ Coverband.configuration.logger.stubs('info')
61
+ additional_data = [
62
+ fake_coverage_report
63
+ ]
64
+
65
+ Coverband::Reporters::SimpleCovReport.report(@store, open_report: false, additional_scov_data: additional_data)
66
+ end
67
+
68
+ end
@@ -1,4 +1,5 @@
1
1
  require File.expand_path('../test_helper', File.dirname(__FILE__))
2
+ require 'aws-sdk'
2
3
 
3
4
  module Coverband
4
5
 
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.3.1
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Mayer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-27 00:00:00.000000000 Z
11
+ date: 2017-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -123,21 +123,21 @@ dependencies:
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  - !ruby/object:Gem::Dependency
126
- name: simplecov
126
+ name: aws-sdk
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - ">="
129
+ - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :runtime
131
+ version: '2'
132
+ type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - ">="
136
+ - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '0'
138
+ version: '2'
139
139
  - !ruby/object:Gem::Dependency
140
- name: json
140
+ name: simplecov
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
143
  - - ">="
@@ -151,7 +151,7 @@ dependencies:
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
153
  - !ruby/object:Gem::Dependency
154
- name: redis
154
+ name: json
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - ">="
@@ -165,19 +165,19 @@ dependencies:
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
167
  - !ruby/object:Gem::Dependency
168
- name: aws-sdk
168
+ name: redis
169
169
  requirement: !ruby/object:Gem::Requirement
170
170
  requirements:
171
- - - "~>"
171
+ - - ">="
172
172
  - !ruby/object:Gem::Version
173
- version: '2'
173
+ version: '0'
174
174
  type: :runtime
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
- - - "~>"
178
+ - - ">="
179
179
  - !ruby/object:Gem::Version
180
- version: '2'
180
+ version: '0'
181
181
  description: Rack middleware to help measure production code usage (LOC runtime usage)
182
182
  email:
183
183
  - dan@mayerdan.com
@@ -192,16 +192,21 @@ files:
192
192
  - LICENSE.txt
193
193
  - README.md
194
194
  - Rakefile
195
+ - changes.md
195
196
  - coverband.gemspec
196
197
  - docs/coverband_details.png
197
198
  - docs/coverband_index.png
198
199
  - lib/coverband.rb
200
+ - lib/coverband/adapters/file_store.rb
201
+ - lib/coverband/adapters/memory_cache_store.rb
202
+ - lib/coverband/adapters/redis_store.rb
199
203
  - lib/coverband/base.rb
204
+ - lib/coverband/baseline.rb
200
205
  - lib/coverband/configuration.rb
201
- - lib/coverband/memory_cache_store.rb
202
206
  - lib/coverband/middleware.rb
203
- - lib/coverband/redis_store.rb
204
- - lib/coverband/reporter.rb
207
+ - lib/coverband/reporters/base.rb
208
+ - lib/coverband/reporters/console_report.rb
209
+ - lib/coverband/reporters/simple_cov_report.rb
205
210
  - lib/coverband/s3_report_writer.rb
206
211
  - lib/coverband/s3_web.rb
207
212
  - lib/coverband/tasks.rb
@@ -211,13 +216,17 @@ files:
211
216
  - test/benchmarks/dog.rb
212
217
  - test/fake_app/basic_rack.rb
213
218
  - test/test_helper.rb
219
+ - test/unit/adapters_file_store_test.rb
220
+ - test/unit/adapters_memory_cache_store_test.rb
221
+ - test/unit/adapters_redis_store_test.rb
214
222
  - test/unit/base_test.rb
223
+ - test/unit/baseline_test.rb
215
224
  - test/unit/configuration_test.rb
216
225
  - test/unit/dog.rb
217
- - test/unit/memory_cache_store_test.rb
218
226
  - test/unit/middleware_test.rb
219
- - test/unit/redis_store_test.rb
220
- - test/unit/reporter_test.rb
227
+ - test/unit/reports_base_test.rb
228
+ - test/unit/reports_console_test.rb
229
+ - test/unit/reports_simple_cov_test.rb
221
230
  - test/unit/s3_report_writer_test.rb
222
231
  - test/unit/s3_web_test.rb
223
232
  homepage: https://github.com/danmayer/coverband
@@ -250,12 +259,16 @@ test_files:
250
259
  - test/benchmarks/dog.rb
251
260
  - test/fake_app/basic_rack.rb
252
261
  - test/test_helper.rb
262
+ - test/unit/adapters_file_store_test.rb
263
+ - test/unit/adapters_memory_cache_store_test.rb
264
+ - test/unit/adapters_redis_store_test.rb
253
265
  - test/unit/base_test.rb
266
+ - test/unit/baseline_test.rb
254
267
  - test/unit/configuration_test.rb
255
268
  - test/unit/dog.rb
256
- - test/unit/memory_cache_store_test.rb
257
269
  - test/unit/middleware_test.rb
258
- - test/unit/redis_store_test.rb
259
- - test/unit/reporter_test.rb
270
+ - test/unit/reports_base_test.rb
271
+ - test/unit/reports_console_test.rb
272
+ - test/unit/reports_simple_cov_test.rb
260
273
  - test/unit/s3_report_writer_test.rb
261
274
  - test/unit/s3_web_test.rb
@@ -1,42 +0,0 @@
1
- module Coverband
2
- class MemoryCacheStore
3
-
4
- attr_accessor :store
5
-
6
-
7
- def self.files_cache
8
- @files_cache ||= Hash.new
9
- end
10
-
11
- def self.reset!
12
- files_cache.clear
13
- end
14
-
15
- def initialize(store)
16
- @store = store
17
- end
18
-
19
- def store_report files
20
- filtered_files = filter(files)
21
- store.store_report(filtered_files) if filtered_files.any?
22
- end
23
-
24
- private
25
-
26
- def files_cache
27
- self.class.files_cache
28
- end
29
-
30
- def filter(files)
31
- files.each_with_object(Hash.new) do |(file, lines), filtered_file_hash|
32
- #first time we see a file, we pre-init the in memory cache to whatever is in store(redis)
33
- line_cache = files_cache[file] ||= Set.new(store.covered_lines_for_file(file))
34
- lines.reject! do |line|
35
- line_cache.include?(line) ? true : (line_cache << line and false)
36
- end
37
- filtered_file_hash[file] = lines if lines.any?
38
- end
39
- end
40
-
41
- end
42
- end
@@ -1,57 +0,0 @@
1
- module Coverband
2
- class RedisStore
3
- def initialize(redis)
4
- @redis = redis
5
- @_sadd_supports_array = recent_gem_version? && recent_server_version?
6
- end
7
-
8
- def store_report(report)
9
- redis.pipelined do
10
- store_array('coverband', report.keys)
11
-
12
- report.each do |file, lines|
13
- store_array("coverband.#{file}", lines.keys)
14
- end
15
- end
16
- end
17
-
18
-
19
- def covered_lines_for_file(file)
20
- @redis.smembers("coverband.#{file}").map(&:to_i)
21
- end
22
-
23
-
24
- def sadd_supports_array?
25
- @_sadd_supports_array
26
- end
27
-
28
- private
29
-
30
- attr_reader :redis
31
-
32
- def store_array(key, values)
33
- if sadd_supports_array?
34
- redis.sadd(key, values) if (values.length > 0)
35
- else
36
- values.each do |value|
37
- redis.sadd(key, value)
38
- end
39
- end
40
- values
41
- end
42
-
43
- def recent_server_version?
44
- info_data = redis.info
45
- if info_data.is_a?(Hash)
46
- Gem::Version.new(info_data['redis_version']) >= Gem::Version.new('2.4')
47
- else
48
- #guess supported
49
- true
50
- end
51
- end
52
-
53
- def recent_gem_version?
54
- Gem::Version.new(Redis::VERSION) >= Gem::Version.new('3.0')
55
- end
56
- end
57
- end
@@ -1,223 +0,0 @@
1
- module Coverband
2
- class Reporter
3
-
4
- def self.baseline
5
- require 'coverage'
6
- Coverage.start
7
- yield
8
- @project_directory = File.expand_path(Coverband.configuration.root)
9
- results = Coverage.result
10
- results = results.reject { |key, val| !key.match(@project_directory) || Coverband.configuration.ignore.any? { |pattern| key.match(/#{pattern}/) } }
11
-
12
- if Coverband.configuration.verbose
13
- Coverband.configuration.logger.info results.inspect
14
- end
15
-
16
- config_dir = File.dirname(Coverband.configuration.baseline_file)
17
- Dir.mkdir config_dir unless File.exists?(config_dir)
18
- File.open(Coverband.configuration.baseline_file, 'w') { |f| f.write(results.to_json) }
19
- end
20
-
21
- def self.report(options = {})
22
- begin
23
- require 'simplecov' if Coverband.configuration.reporter=='scov'
24
- rescue
25
- Coverband.configuration.logger.error "coverband requires simplecov in order to generate a report, when configured for the scov report style."
26
- return
27
- end
28
- redis = Coverband.configuration.redis
29
- roots = get_roots
30
- existing_coverage = Coverband.configuration.coverage_baseline
31
- open_report = options.fetch(:open_report) { true }
32
-
33
- if Coverband.configuration.verbose
34
- Coverband.configuration.logger.info "fixing root: #{roots.join(', ')}"
35
- end
36
-
37
- if Coverband.configuration.reporter == 'scov'
38
- additional_scov_data = options.fetch(:additional_scov_data) { [] }
39
- if Coverband.configuration.verbose
40
- print additional_scov_data
41
- end
42
- report_scov(redis, existing_coverage, additional_scov_data, roots, open_report)
43
- else
44
- lines = redis.smembers('coverband').map{ |key| report_line(redis, key) }
45
- Coverband.configuration.logger.info lines.join("\n")
46
- end
47
- end
48
-
49
- def self.clear_coverage(redis = nil)
50
- redis ||= Coverband.configuration.redis
51
- redis.smembers('coverband').each { |key| redis.del("coverband.#{key}") }
52
- redis.del("coverband")
53
- end
54
-
55
- def self.get_roots
56
- roots = Coverband.configuration.root_paths
57
- roots << "#{current_root}/"
58
- roots
59
- end
60
-
61
- def self.current_root
62
- File.expand_path(Coverband.configuration.root)
63
- end
64
-
65
- private
66
-
67
- def self.fix_file_names(report_hash, roots)
68
- fixed_report = {} #normalize names across servers
69
- report_hash.each_pair do |key, values|
70
- filename = filename_from_key(key, roots)
71
- fixed_report[filename] = values
72
- end
73
- fixed_report
74
- end
75
-
76
- # [0,0,1,0,1]
77
- # [nil,0,1,0,0]
78
- # merge to
79
- # [0,0,1,0,1]
80
- def self.merge_arrays(first, second)
81
- merged = []
82
- longest = first.length > second.length ? first : second
83
-
84
- longest.each_with_index do |line, index|
85
- if first[index] || second[index]
86
- merged[index] = (first[index].to_i + second[index].to_i >= 1 ? 1 : 0)
87
- else
88
- merged[index] = nil
89
- end
90
- end
91
-
92
- merged
93
- end
94
-
95
-
96
- def self.get_current_scov_data(options = {})
97
- additional_scov_data = options.fetch(:additional_scov_data) { [] }
98
-
99
- if (additional_scov_data)
100
- report_scov_with_additional_data(Coverband.configuration.redis, Coverband.configuration.coverage_baseline, additional_scov_data, get_roots)
101
- else
102
- get_current_scov_data_imp(Coverband.configuration.redis, get_roots)
103
- end
104
- end
105
-
106
- def self.get_current_scov_data_imp(redis, roots)
107
- scov_style_report = {}
108
-
109
- redis.smembers('coverband').each do |key|
110
- next if Coverband.configuration.ignore.any?{ |i| key.match(i) }
111
- line_data = line_hash(redis, key, roots)
112
-
113
- if line_data
114
- line_key = line_data.keys.first
115
- previous_line_hash = scov_style_report[line_key]
116
-
117
- if previous_line_hash
118
- line_data[line_key] = merge_arrays(line_data[line_key], previous_line_hash)
119
- end
120
-
121
- scov_style_report.merge!(line_data)
122
- end
123
- end
124
-
125
- scov_style_report = fix_file_names(scov_style_report, roots)
126
- scov_style_report
127
- end
128
-
129
- def self.report_scov_with_additional_data(redis, existing_coverage, additional_scov_data, roots)
130
- scov_style_report = get_current_scov_data_imp redis, roots
131
- existing_coverage = fix_file_names(existing_coverage, roots)
132
- scov_style_report = merge_existing_coverage(scov_style_report, existing_coverage)
133
-
134
- additional_scov_data.each do |data|
135
- scov_style_report = merge_existing_coverage(scov_style_report, data)
136
- end
137
-
138
- scov_style_report
139
- end
140
-
141
- def self.report_scov(redis, existing_coverage, additional_scov_data, roots, open_report)
142
- scov_style_report = report_scov_with_additional_data(redis, existing_coverage, additional_scov_data, roots)
143
-
144
- if Coverband.configuration.verbose
145
- Coverband.configuration.logger.info "report: "
146
- Coverband.configuration.logger.info scov_style_report.inspect
147
- end
148
-
149
- # add in files never hit in coverband
150
- SimpleCov.track_files "#{current_root}/{app,lib,config}/**/*.{rb,haml,erb,slim}"
151
- SimpleCov::Result.new(SimpleCov.add_not_loaded_files(scov_style_report)).format!
152
- if open_report
153
- `open #{SimpleCov.coverage_dir}/index.html`
154
- else
155
- Coverband.configuration.logger.info "report is ready and viewable: open #{SimpleCov.coverage_dir}/index.html"
156
- end
157
- S3ReportWriter.new(Coverband.configuration.s3_bucket).persist! if Coverband.configuration.s3_bucket
158
- end
159
-
160
-
161
- def self.merge_existing_coverage(scov_style_report, existing_coverage)
162
- existing_coverage.each_pair do |key, lines|
163
- if current_lines = scov_style_report[key]
164
- lines.each_with_index do |line, index|
165
- if line.nil? && current_lines[index].to_i == 0
166
- current_lines[index] = nil
167
- else
168
- current_lines[index] = current_lines[index] ? (current_lines[index].to_i + line.to_i) : nil
169
- end
170
- end
171
- scov_style_report[key] = current_lines
172
- else
173
- scov_style_report[key] = lines
174
- end
175
- end
176
- scov_style_report
177
- end
178
-
179
- # /Users/danmayer/projects/cover_band_server/views/index/erb: ["0", "2", "3", "6", "65532", "65533"]
180
- # /Users/danmayer/projects/cover_band_server/app/rb: ["54", "55"]
181
- # /Users/danmayer/projects/cover_band_server/views/layout/erb: ["0", "33", "36", "37", "38", "39", "40", "62", "63", "66", "65532", "65533"]
182
- def self.report_line(redis, key)
183
- "#{key}: #{line_members(redis, key)}"
184
- end
185
-
186
- def self.line_members(redis, key)
187
- redis.smembers("coverband.#{key}").inspect
188
- end
189
-
190
- def self.filename_from_key(key, roots)
191
- filename = key
192
- roots.each do |root|
193
- filename = filename.gsub(/^#{root}/, './')
194
- end
195
- # the filename for SimpleCov is expected to be a full path.
196
- # roots.last should be roots << current_root}/
197
- # a fully expanded path of config.root
198
- filename = filename.gsub('./', roots.last)
199
- filename
200
- end
201
-
202
- # >> puts Coverage.result.inspect
203
- # {"/Users/danmayer/projects/hearno/script/tester.rb"=>[1, nil, 1, 1, nil, nil, nil]}
204
- def self.line_hash(redis, key, roots)
205
- filename = filename_from_key(key, roots)
206
- if File.exists?(filename)
207
- lines_hit = redis.smembers("coverband.#{key}")
208
- count = File.foreach(filename).inject(0) { |c, line| c + 1 }
209
- if filename.match(/\.erb/)
210
- line_array = Array.new(count, nil)
211
- else
212
- line_array = Array.new(count, 0)
213
- end
214
- line_array.each_with_index{|line,index| line_array[index] = 1 if lines_hit.include?((index + 1).to_s) }
215
- {filename => line_array}
216
- else
217
- Coverband.configuration.logger.info "file #{filename} not found in project"
218
- nil
219
- end
220
- end
221
-
222
- end
223
- end