coverband 4.2.3 → 4.2.4
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -6
- data/.travis.yml +6 -0
- data/README.md +40 -20
- data/changes.md +16 -9
- data/coverband.gemspec +1 -0
- data/lib/coverband/adapters/base.rb +1 -1
- data/lib/coverband/adapters/hash_redis_store.rb +49 -42
- data/lib/coverband/adapters/redis_store.rb +4 -2
- data/lib/coverband/collectors/coverage.rb +1 -1
- data/lib/coverband/collectors/view_tracker.rb +14 -5
- data/lib/coverband/configuration.rb +18 -5
- data/lib/coverband/integrations/background.rb +3 -0
- data/lib/coverband/reporters/html_report.rb +10 -0
- data/lib/coverband/reporters/web.rb +16 -0
- data/lib/coverband/utils/html_formatter.rb +8 -0
- data/lib/coverband/utils/railtie.rb +16 -9
- data/lib/coverband/utils/s3_report.rb +0 -1
- data/lib/coverband/version.rb +1 -1
- data/lib/coverband.rb +12 -7
- data/lua/install.sh +15 -0
- data/lua/lib/persist-coverage.lua +28 -0
- data/lua/test/bootstrap.lua +5 -0
- data/lua/test/harness.lua +19 -0
- data/lua/test/redis-call.lua +55 -0
- data/lua/test/test-persist-coverage.lua +132 -0
- data/test/coverband/adapters/hash_redis_store_test.rb +4 -3
- data/test/coverband/adapters/redis_store_test.rb +10 -5
- data/test/coverband/collectors/coverage_test.rb +10 -4
- data/test/coverband/collectors/view_tracker_test.rb +38 -18
- data/test/coverband/configuration_test.rb +10 -10
- data/test/coverband/integrations/background_test.rb +10 -0
- data/test/coverband/integrations/resque_worker_test.rb +0 -1
- data/test/coverband/reporters/base_test.rb +6 -5
- data/test/coverband/reporters/console_test.rb +2 -5
- data/test/coverband/reporters/html_test.rb +1 -3
- data/test/coverband/utils/html_formatter_test.rb +1 -3
- data/test/forked/rails_rake_full_stack_test.rb +10 -0
- data/test/integration/full_stack_test.rb +1 -3
- data/test/rails4_dummy/config/coverband.rb +3 -13
- data/test/rails4_dummy/config/coverband_missing_redis.rb +3 -0
- data/test/rails5_dummy/config/coverband.rb +4 -2
- data/test/rails5_dummy/config/coverband_missing_redis.rb +15 -0
- data/test/test_helper.rb +8 -4
- data/views/data.erb +1 -0
- data/views/nav.erb +1 -0
- data/views/view_tracker.erb +5 -2
- metadata +27 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1c094b4a6ef2b76f771cb96aa1fbdc246dc8beea74033ffca4cb466ef96c0a08
|
|
4
|
+
data.tar.gz: 1162d9244c0e1a1b28c0fa051d7cd53d29b6a4fdfc50c1fadb7d6793229aaf8b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d9eba70b91d87992d1e450490f7fd4545cccb5092bf093c424c51b964cb4b31ad5c8a1d49d1cc774ce6470c6466b7d3b7a7a1df92ecbc4e574a203467f621b55
|
|
7
|
+
data.tar.gz: 815c462d38f7a27b26f5d209044c53b2e0058c4ea2534db3f6663a267ad0cc435bd6c895128c5ea8f85c0aec61e4e31d9f61c426526b71423e24c941852f74be
|
data/.rubocop.yml
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
AllCops:
|
|
2
2
|
TargetRubyVersion: 2.4
|
|
3
3
|
Exclude:
|
|
4
|
+
- vendor/bundle/**/*
|
|
4
5
|
- docs/**/*
|
|
5
6
|
- test/rails4_dummy/**/*
|
|
6
7
|
- test/rails5_dummy/**/*
|
|
7
8
|
- test/fixtures/**/*
|
|
8
9
|
# Get the code passing first then we will enable for tests
|
|
9
|
-
- test/**/*
|
|
10
|
+
- test/**/*
|
|
10
11
|
Documentation:
|
|
11
12
|
Enabled: false
|
|
12
13
|
Metrics/MethodLength:
|
|
13
14
|
Enabled: false
|
|
14
|
-
|
|
15
|
+
Layout/LineLength:
|
|
15
16
|
Max: 160
|
|
16
17
|
Metrics/BlockNesting:
|
|
17
18
|
Max: 5
|
|
@@ -31,6 +32,8 @@ Metrics/ParameterLists:
|
|
|
31
32
|
Max: 10
|
|
32
33
|
Naming/AccessorMethodName:
|
|
33
34
|
Enabled: false
|
|
35
|
+
Naming/RescuedExceptionsVariableName:
|
|
36
|
+
Enabled: false
|
|
34
37
|
Naming/PredicateName:
|
|
35
38
|
Enabled: true
|
|
36
39
|
Style/TernaryParentheses:
|
|
@@ -59,10 +62,6 @@ Style/SymbolProc:
|
|
|
59
62
|
Enabled: false
|
|
60
63
|
Style/RegexpLiteral:
|
|
61
64
|
Enabled: false
|
|
62
|
-
Performance/Casecmp:
|
|
63
|
-
Enabled: false
|
|
64
|
-
Performance/RegexpMatch:
|
|
65
|
-
Enabled: false
|
|
66
65
|
Layout/MultilineMethodCallIndentation:
|
|
67
66
|
Enabled: true
|
|
68
67
|
Layout/MultilineOperationIndentation:
|
data/.travis.yml
CHANGED
|
@@ -4,12 +4,18 @@ rvm:
|
|
|
4
4
|
- "2.4"
|
|
5
5
|
- "2.5"
|
|
6
6
|
- "2.6.1"
|
|
7
|
+
cache:
|
|
8
|
+
bundler: true
|
|
9
|
+
directories:
|
|
10
|
+
- $HOME/lua51
|
|
7
11
|
gemfile:
|
|
8
12
|
- Gemfile
|
|
9
13
|
- Gemfile.rails4
|
|
10
14
|
services:
|
|
11
15
|
- redis-server
|
|
12
16
|
script:
|
|
17
|
+
- sudo ./lua/install.sh
|
|
18
|
+
- $HOME/lua51/bin/busted lua/test/*
|
|
13
19
|
- bundle exec rake rubocop
|
|
14
20
|
- COVERBAND_HASH_REDIS_STORE=t bundle exec rake
|
|
15
21
|
- COVERBAND_HASH_REDIS_STORE=t bundle exec rake forked_tests
|
data/README.md
CHANGED
|
@@ -67,7 +67,7 @@ See [changelog](https://github.com/danmayer/coverband/blob/master/changes.md).
|
|
|
67
67
|
|
|
68
68
|
## Rails
|
|
69
69
|
|
|
70
|
-
The Railtie integration means you shouldn't need to do anything
|
|
70
|
+
The Railtie integration means you shouldn't need to do anything else other than ensure coverband is required after rails within your Gemfile. The only exception to this is gem tracking of `Bundle.require` which depends on requiring coverband within the application.rb. See [Collecting Gem / Library Usage](https://github.com/danmayer/coverband#collecting-gem--library-usage).
|
|
71
71
|
|
|
72
72
|
## Sinatra
|
|
73
73
|
|
|
@@ -114,7 +114,7 @@ The web endpoint is a barebones endpoint that you can either expose direct (afte
|
|
|
114
114
|
- **force coverage collection:** This triggers coverage collection on the current webserver process
|
|
115
115
|
- **clear coverage report:** This will clear the coverage data. This wipes out all collected data (**dangerous**)
|
|
116
116
|
- View individual file details
|
|
117
|
-
- **clear individual file coverage:** This will clear the details of the file you are looking at. This is helpful if you don't want to
|
|
117
|
+
- **clear individual file coverage:** This will clear the details of the file you are looking at. This is helpful if you don't want to lose all Coverage data but made a change that you expect would impact a particular file.
|
|
118
118
|
|
|
119
119
|
### Rake Tasks
|
|
120
120
|
|
|
@@ -169,9 +169,29 @@ Coverband.configure do |config|
|
|
|
169
169
|
# and is safe to use if one is investigating issues in production, but it will slightly
|
|
170
170
|
# hit perf.
|
|
171
171
|
config.verbose = false
|
|
172
|
+
|
|
173
|
+
# default false. button at the top of the web interface which clears all data
|
|
174
|
+
config.web_enable_clear = true
|
|
175
|
+
|
|
176
|
+
# default false. Experimental support for tracking view layer tracking.
|
|
177
|
+
# Does not track line-level usage, only indicates if an entire file
|
|
178
|
+
# is used or not.
|
|
179
|
+
config.track_views = true
|
|
172
180
|
end
|
|
173
181
|
```
|
|
174
182
|
|
|
183
|
+
### Working with environment variables
|
|
184
|
+
|
|
185
|
+
Do you use figaro, mc-settings, dotenv or something else to inject environment variables into your app? If so ensure you have that done BEFORE coverband is required.
|
|
186
|
+
|
|
187
|
+
For example if you use dotenv, you need to do this, see https://github.com/bkeepers/dotenv#note-on-load-order
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
gem 'dotenv-rails', require: 'dotenv/rails-now'
|
|
191
|
+
gem 'coverband'
|
|
192
|
+
gem 'other-gem-that-requires-env-variables'
|
|
193
|
+
```
|
|
194
|
+
|
|
175
195
|
### Ignoring Files
|
|
176
196
|
|
|
177
197
|
Sometimes you have files that are known to be valuable perhaps in other environments or something that is just run very infrequently. Opposed to having to mentally filter them out of the report, you can just have them ignored in the Coverband reporting by using `config.ignore` as shown below. Ignore takes a string but can also match with regex rules see how below ignores all rake tasks as an example.
|
|
@@ -185,6 +205,7 @@ config.ignore += ['config/application.rb',
|
|
|
185
205
|
'config/environments/*',
|
|
186
206
|
'lib/tasks/*']
|
|
187
207
|
```
|
|
208
|
+
|
|
188
209
|
### View Tracking
|
|
189
210
|
|
|
190
211
|
Coverband allows an optional feature to track all view files that are used by an application.
|
|
@@ -216,6 +237,14 @@ ENV['AWS_ACCESS_KEY_ID']
|
|
|
216
237
|
ENV['AWS_SECRET_ACCESS_KEY']
|
|
217
238
|
```
|
|
218
239
|
|
|
240
|
+
### Avoiding Cache Stampede
|
|
241
|
+
|
|
242
|
+
If you have many servers and they all hit Redis at the same time you can see spikes in your Redis CPU, and memory. This is do to a concept called [cache stampede](https://en.wikipedia.org/wiki/Cache_stampede). It is better to spread out the reporting across your servers. A simple way to do this is to add a random wiggle on your background reporting. This configuration option allows a wiggle. The right amount of wiggle depends on the numbers of servers you have and how willing you are to have delays in your coverage reporting. I would recommend at least 1 second per server.
|
|
243
|
+
|
|
244
|
+
Add a wiggle (in seconds) to the background thread to avoid all your servers reporting at the same time:
|
|
245
|
+
|
|
246
|
+
`config.reporting_wiggle = 30`
|
|
247
|
+
|
|
219
248
|
### Redis Hash Store
|
|
220
249
|
|
|
221
250
|
Coverband on very high volume sites with many server processes reporting can have a race condition. To resolve the race condition and reduce Ruby memory overhead we have introduced a new Redis storage option. This moves the some of the work from the Ruby processes to Redis. It is worth noting because of this, it has a larger demands on the Redis server. So adjust your Redis instance accordingly. To help reduce the extra redis load you can also change the background reporting time period.
|
|
@@ -258,22 +287,6 @@ rake coverband:clear # reset coverband coverage data
|
|
|
258
287
|
rake coverband:coverage # report runtime coverband code coverage
|
|
259
288
|
```
|
|
260
289
|
|
|
261
|
-
### Forcing Coverband to Track Coverage on files loaded during boot `safe_reload_files`
|
|
262
|
-
|
|
263
|
-
Coverband will report code usage for anything `required` or `loaded` after calling `Coverband.start` which happens automatically when coverband is required. This means some of the files loaded before coverband such as the Rails application.rb will be reported as having no coverage.
|
|
264
|
-
|
|
265
|
-
The `safe_reload_files` reload option in the configuration options can help to ensure you can track any files regardless of loading before Coverband. For example if I wanted to show the coverage of `config/coverband.rb`, which has to be loaded before calling `Coverband.start`, I could do that by adding that path to the `safe_reload_files` option.
|
|
266
|
-
|
|
267
|
-
```
|
|
268
|
-
Coverband.configure do |config|
|
|
269
|
-
# ... a bunch of other options
|
|
270
|
-
# using the new safe reload to enforce files loaded
|
|
271
|
-
config.safe_reload_files = ['config/coverband.rb']
|
|
272
|
-
end
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
By adding any files above you will get reporting on those files as part of your coverage runtime report. The files are reloaded when Coverband first starts, you can also trigger a reload via the web interface.
|
|
276
|
-
|
|
277
290
|
### Collecting Gem / Library Usage
|
|
278
291
|
|
|
279
292
|
__WARNING:__ Gem Tracking is still in experimental stages and not recommended for production. We have some performance issues when view reports on large applications. Gem tracing also during background thread data collection has HIGH memory requirements, during report merging (seemingly around 128mb of extra memory, which is crazy). We recommend deploying WITHOUT `track_gems` first and only enabling it after confirming that Coverband is working and performing well.
|
|
@@ -338,6 +351,12 @@ If you are trying to debug locally wondering what code is being run during a req
|
|
|
338
351
|
- Coverband 3.0.X+ requires Ruby 2.3+
|
|
339
352
|
- Coverband currently requires Redis for production usage
|
|
340
353
|
|
|
354
|
+
### Ruby and Rails Version Support
|
|
355
|
+
|
|
356
|
+
We will match Heroku & Ruby's support lifetime, supporting the last 3 major Ruby releases. For details see [supported runtimes](https://devcenter.heroku.com/articles/ruby-support#supported-runtimes).
|
|
357
|
+
|
|
358
|
+
For Rails, we will follow the policy of the [Rails team maintenance policy](https://guides.rubyonrails.org/maintenance_policy.html). We officially support the last two major release versions, while providing minimal support (major bugs / security fixes) for an additional version. This means at the moment we primaryly target Rails 6.x, 5.x, and will try to keep current functionality working for Rails 4.x but may release new features that do not work on that target.
|
|
359
|
+
|
|
341
360
|
# Contributing To Coverband
|
|
342
361
|
|
|
343
362
|
If you are working on adding features, PRs, or bugfixes to Coverband this section should help get you going.
|
|
@@ -363,9 +382,10 @@ If you submit a change please make sure the tests and benchmarks are passing.
|
|
|
363
382
|
|
|
364
383
|
### Known Issues
|
|
365
384
|
|
|
366
|
-
- **total fail** on front end code, because of the precompiled template step basically coverage doesn't work well for `erb`, `slim`, and the like.
|
|
385
|
+
- **total fail** on front end code, for line for line coverage, because of the precompiled template step basically coverage doesn't work well for `erb`, `slim`, and the like.
|
|
367
386
|
- related it will try to report something, but 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. The default configuration excludes these files
|
|
368
|
-
-
|
|
387
|
+
- **NOTE:** We now have file level coverage for view files, but don't support line level detail
|
|
388
|
+
- The view file detection doesn't workf or mailers at the moment only for web related views / JSON templates. This is due to how Rails active mailer notifications work.
|
|
369
389
|
|
|
370
390
|
### Debugging Redis Store
|
|
371
391
|
|
data/changes.md
CHANGED
|
@@ -76,19 +76,26 @@ Feature Ideas:
|
|
|
76
76
|
|
|
77
77
|
# Alpha / Beta / Release Candidates
|
|
78
78
|
|
|
79
|
-
### Coverband 4.2.
|
|
79
|
+
### Coverband 4.2.5
|
|
80
80
|
|
|
81
|
-
-
|
|
82
|
-
(hundreds of clients), thanks @kbaum
|
|
83
|
-
- view_tracker supports tracking view layer files like `.html.erb`
|
|
84
|
-
- documentation improvements, thanks @brossetti1, @jjb, @kbaum
|
|
85
|
-
- we now have discordapp for discussions, thanks @kbaum,
|
|
86
|
-
https://discordapp.com/channels/609509533999562753/609509533999562756
|
|
87
|
-
- perf fix on Rails initialization, thanks @skangg
|
|
88
|
-
- simplified logging
|
|
81
|
+
- ?
|
|
89
82
|
|
|
90
83
|
# Released
|
|
91
84
|
|
|
85
|
+
### Coverband 4.2.4
|
|
86
|
+
|
|
87
|
+
- fixes related to startup without Redis, skipping Coverband on common rake tasks (assets:precompile), etc
|
|
88
|
+
- avoid calls to redis if coverage not updated
|
|
89
|
+
- split out lua work and added testing around lua scripts use for the hash redis store
|
|
90
|
+
- improvements to hash redis store resulting in 25% faster perf
|
|
91
|
+
- avoid duplicate hash calculations
|
|
92
|
+
- debug data now includes md5_hashes
|
|
93
|
+
- added support to download coverage and view data in JSON format
|
|
94
|
+
- documentation about working with environment variables
|
|
95
|
+
- add cache wiggle to avoid Redis CPU spikes (cache stampede on Redis server)
|
|
96
|
+
- make the nocov consistant on the data download and html view
|
|
97
|
+
- small performance improvements
|
|
98
|
+
|
|
92
99
|
### Coverband 4.2.3
|
|
93
100
|
|
|
94
101
|
- readme fixes
|
data/coverband.gemspec
CHANGED
|
@@ -35,6 +35,7 @@ Gem::Specification.new do |spec|
|
|
|
35
35
|
spec.add_development_dependency 'rake'
|
|
36
36
|
spec.add_development_dependency 'resque'
|
|
37
37
|
spec.add_development_dependency 'rubocop'
|
|
38
|
+
spec.add_development_dependency 'rubocop-performance'
|
|
38
39
|
|
|
39
40
|
spec.add_development_dependency 'coveralls'
|
|
40
41
|
# add when debugging
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
3
5
|
module Coverband
|
|
4
6
|
module Adapters
|
|
5
7
|
class HashRedisStore < Base
|
|
6
8
|
FILE_KEY = 'file'
|
|
7
9
|
FILE_LENGTH_KEY = 'file_length'
|
|
8
|
-
META_DATA_KEYS = [DATA_KEY, FIRST_UPDATED_KEY, LAST_UPDATED_KEY].freeze
|
|
10
|
+
META_DATA_KEYS = [DATA_KEY, FIRST_UPDATED_KEY, LAST_UPDATED_KEY, FILE_HASH].freeze
|
|
9
11
|
###
|
|
10
12
|
# This key isn't related to the coverband version, but to the interal format
|
|
11
13
|
# used to store data to redis. It is changed only when breaking changes to our
|
|
@@ -13,16 +15,19 @@ module Coverband
|
|
|
13
15
|
###
|
|
14
16
|
REDIS_STORAGE_FORMAT_VERSION = 'coverband_hash_3_3'
|
|
15
17
|
|
|
18
|
+
JSON_PAYLOAD_EXPIRATION = 5 * 60
|
|
19
|
+
|
|
16
20
|
attr_reader :redis_namespace
|
|
17
21
|
|
|
18
22
|
def initialize(redis, opts = {})
|
|
19
23
|
super()
|
|
20
24
|
@redis_namespace = opts[:redis_namespace]
|
|
25
|
+
@save_report_batch_size = opts[:save_report_batch_size] || 100
|
|
21
26
|
@format_version = REDIS_STORAGE_FORMAT_VERSION
|
|
22
27
|
@redis = redis
|
|
23
28
|
raise 'HashRedisStore requires redis >= 2.6.0' unless supported?
|
|
24
29
|
|
|
25
|
-
@ttl = opts[:ttl]
|
|
30
|
+
@ttl = opts[:ttl]
|
|
26
31
|
@relative_file_converter = opts[:relative_file_converter] || Utils::RelativeFileConverter
|
|
27
32
|
end
|
|
28
33
|
|
|
@@ -53,13 +58,14 @@ module Coverband
|
|
|
53
58
|
def save_report(report)
|
|
54
59
|
report_time = Time.now.to_i
|
|
55
60
|
updated_time = type == Coverband::EAGER_TYPE ? nil : report_time
|
|
56
|
-
|
|
57
|
-
@
|
|
58
|
-
|
|
61
|
+
keys = []
|
|
62
|
+
report.each_slice(@save_report_batch_size) do |slice|
|
|
63
|
+
files_data = slice.map do |(file, data)|
|
|
59
64
|
relative_file = @relative_file_converter.convert(file)
|
|
60
65
|
file_hash = file_hash(relative_file)
|
|
61
66
|
key = key(relative_file, file_hash: file_hash)
|
|
62
|
-
|
|
67
|
+
keys << key
|
|
68
|
+
script_input(
|
|
63
69
|
key: key,
|
|
64
70
|
file: relative_file,
|
|
65
71
|
file_hash: file_hash,
|
|
@@ -67,16 +73,24 @@ module Coverband
|
|
|
67
73
|
report_time: report_time,
|
|
68
74
|
updated_time: updated_time
|
|
69
75
|
)
|
|
70
|
-
@redis.evalsha(script_id, script_input[:keys], script_input[:args])
|
|
71
|
-
key
|
|
72
76
|
end
|
|
73
|
-
|
|
77
|
+
next unless files_data.any?
|
|
78
|
+
|
|
79
|
+
arguments_key = [@redis_namespace, SecureRandom.uuid].compact.join('.')
|
|
80
|
+
@redis.set(arguments_key, { ttl: @ttl, files_data: files_data }.to_json, ex: JSON_PAYLOAD_EXPIRATION)
|
|
81
|
+
@redis.evalsha(hash_incr_script, [arguments_key])
|
|
74
82
|
end
|
|
83
|
+
@redis.sadd(files_key, keys) if keys.any?
|
|
75
84
|
end
|
|
76
85
|
|
|
77
86
|
def coverage(local_type = nil)
|
|
78
|
-
files_set(local_type)
|
|
79
|
-
|
|
87
|
+
files_set = files_set(local_type)
|
|
88
|
+
@redis.pipelined do
|
|
89
|
+
files_set.map do |key|
|
|
90
|
+
@redis.hgetall(key)
|
|
91
|
+
end
|
|
92
|
+
end.each_with_object({}) do |data_from_redis, hash|
|
|
93
|
+
add_coverage_for_file(data_from_redis, hash)
|
|
80
94
|
end
|
|
81
95
|
end
|
|
82
96
|
|
|
@@ -94,9 +108,7 @@ module Coverband
|
|
|
94
108
|
|
|
95
109
|
private
|
|
96
110
|
|
|
97
|
-
def add_coverage_for_file(
|
|
98
|
-
data_from_redis = @redis.hgetall(key)
|
|
99
|
-
|
|
111
|
+
def add_coverage_for_file(data_from_redis, hash)
|
|
100
112
|
return if data_from_redis.empty?
|
|
101
113
|
|
|
102
114
|
file = data_from_redis[FILE_KEY]
|
|
@@ -116,38 +128,33 @@ module Coverband
|
|
|
116
128
|
end
|
|
117
129
|
end
|
|
118
130
|
|
|
119
|
-
def
|
|
120
|
-
data.each_with_index
|
|
121
|
-
|
|
122
|
-
if coverage
|
|
123
|
-
hash[:keys] << index
|
|
124
|
-
hash[:args] << coverage
|
|
125
|
-
end
|
|
131
|
+
def script_input(key:, file:, file_hash:, data:, report_time:, updated_time:)
|
|
132
|
+
coverage_data = data.each_with_index.each_with_object({}) do |(coverage, index), hash|
|
|
133
|
+
hash[index] = coverage if coverage
|
|
126
134
|
end
|
|
135
|
+
meta = {
|
|
136
|
+
first_updated_at: report_time,
|
|
137
|
+
file: file,
|
|
138
|
+
file_hash: file_hash,
|
|
139
|
+
file_length: data.length,
|
|
140
|
+
hash_key: key
|
|
141
|
+
}
|
|
142
|
+
meta[:last_updated_at] = updated_time if updated_time
|
|
143
|
+
{
|
|
144
|
+
hash_key: key,
|
|
145
|
+
meta: meta,
|
|
146
|
+
coverage: coverage_data
|
|
147
|
+
}
|
|
127
148
|
end
|
|
128
149
|
|
|
129
150
|
def hash_incr_script
|
|
130
|
-
@hash_incr_script ||= @redis.script(:load,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
local hash_key = table.remove(KEYS, 1)
|
|
138
|
-
redis.call('HMSET', hash_key, '#{LAST_UPDATED_KEY}', last_updated_at, '#{FILE_KEY}', file, '#{FILE_HASH}', file_hash, '#{FILE_LENGTH_KEY}', file_length)
|
|
139
|
-
redis.call('HSETNX', hash_key, '#{FIRST_UPDATED_KEY}', first_updated_at)
|
|
140
|
-
for i, key in ipairs(KEYS) do
|
|
141
|
-
if ARGV[i] == '-1' then
|
|
142
|
-
redis.call("HSET", hash_key, key, ARGV[i])
|
|
143
|
-
else
|
|
144
|
-
redis.call("HINCRBY", hash_key, key, ARGV[i])
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
if ttl ~= '-1' then
|
|
148
|
-
redis.call("EXPIRE", hash_key, ttl)
|
|
149
|
-
end
|
|
150
|
-
LUA
|
|
151
|
+
@hash_incr_script ||= @redis.script(:load, lua_script_content)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def lua_script_content
|
|
155
|
+
File.read(File.join(
|
|
156
|
+
File.dirname(__FILE__), '../../../lua/lib/persist-coverage.lua'
|
|
157
|
+
))
|
|
151
158
|
end
|
|
152
159
|
|
|
153
160
|
def values_from_redis(local_type, files)
|
|
@@ -77,7 +77,9 @@ module Coverband
|
|
|
77
77
|
def coverage(local_type = nil, opts = {})
|
|
78
78
|
local_type ||= opts.key?(:override_type) ? opts[:override_type] : type
|
|
79
79
|
data = redis.get type_base_key(local_type)
|
|
80
|
-
data ? JSON.parse(data) : {}
|
|
80
|
+
data = data ? JSON.parse(data) : {}
|
|
81
|
+
data.delete_if { |file_path, file_data| file_hash(file_path) != file_data['file_hash'] } unless opts[:skip_hash_check]
|
|
82
|
+
data
|
|
81
83
|
end
|
|
82
84
|
|
|
83
85
|
# Note: This could lead to slight race on redis
|
|
@@ -86,7 +88,7 @@ module Coverband
|
|
|
86
88
|
# and the tradeoff has always been acceptable
|
|
87
89
|
def save_report(report)
|
|
88
90
|
data = report.dup
|
|
89
|
-
data = merge_reports(data, coverage)
|
|
91
|
+
data = merge_reports(data, coverage(nil, skip_hash_check: true))
|
|
90
92
|
save_coverage(data)
|
|
91
93
|
end
|
|
92
94
|
|
|
@@ -71,7 +71,7 @@ module Coverband
|
|
|
71
71
|
###
|
|
72
72
|
def track_file?(file)
|
|
73
73
|
@ignore_patterns.none? do |pattern|
|
|
74
|
-
file.
|
|
74
|
+
file.match(pattern)
|
|
75
75
|
end && (file.start_with?(@project_directory) ||
|
|
76
76
|
(@track_gems &&
|
|
77
77
|
Coverband.configuration.gem_paths.any? { |path| file.start_with?(path) }))
|
|
@@ -29,10 +29,10 @@ module Coverband
|
|
|
29
29
|
|
|
30
30
|
@roots = options.fetch(:roots) { Coverband.configuration.all_root_patterns }
|
|
31
31
|
@roots = @roots.split(',') if @roots.is_a?(String)
|
|
32
|
+
@one_time_timestamp = false
|
|
32
33
|
|
|
33
34
|
@logged_views = []
|
|
34
35
|
@views_to_record = []
|
|
35
|
-
redis_store.set(tracker_time_key, Time.now.to_i) unless redis_store.exists(tracker_time_key)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
###
|
|
@@ -79,6 +79,13 @@ module Coverband
|
|
|
79
79
|
all_views.reject { |view| view.match(/\/layouts\//) && recently_used_views.any? { |used_view| view.include?(used_view) } }
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
+
def as_json
|
|
83
|
+
{
|
|
84
|
+
unused_views: unused_views,
|
|
85
|
+
used_views: used_views
|
|
86
|
+
}.to_json
|
|
87
|
+
end
|
|
88
|
+
|
|
82
89
|
def tracking_since
|
|
83
90
|
if (tracking_time = redis_store.get(tracker_time_key))
|
|
84
91
|
Time.at(tracking_time.to_i).iso8601
|
|
@@ -100,11 +107,9 @@ module Coverband
|
|
|
100
107
|
logged_views.delete(filename)
|
|
101
108
|
end
|
|
102
109
|
|
|
103
|
-
def self.supported_version?
|
|
104
|
-
defined?(Rails) && defined?(Rails::VERSION) && Rails::VERSION::STRING.split('.').first.to_i >= 4
|
|
105
|
-
end
|
|
106
|
-
|
|
107
110
|
def report_views_tracked
|
|
111
|
+
redis_store.set(tracker_time_key, Time.now.to_i) unless @one_time_timestamp || redis_store.exists(tracker_time_key)
|
|
112
|
+
@one_time_timestamp = true
|
|
108
113
|
reported_time = Time.now.to_i
|
|
109
114
|
views_to_record.each do |file|
|
|
110
115
|
redis_store.hset(tracker_key, file, reported_time)
|
|
@@ -116,6 +121,10 @@ module Coverband
|
|
|
116
121
|
logger&.error "Coverband: view_tracker failed to store, error #{e.class.name}"
|
|
117
122
|
end
|
|
118
123
|
|
|
124
|
+
def self.supported_version?
|
|
125
|
+
defined?(Rails) && defined?(Rails::VERSION) && Rails::VERSION::STRING.split('.').first.to_i >= 4
|
|
126
|
+
end
|
|
127
|
+
|
|
119
128
|
protected
|
|
120
129
|
|
|
121
130
|
def newly_seen_file?(file)
|
|
@@ -8,9 +8,11 @@ module Coverband
|
|
|
8
8
|
:background_reporting_enabled,
|
|
9
9
|
:background_reporting_sleep_seconds, :test_env,
|
|
10
10
|
:web_enable_clear, :gem_details, :web_debug, :report_on_exit,
|
|
11
|
-
:simulate_oneshot_lines_coverage, :track_views, :view_tracker
|
|
11
|
+
:simulate_oneshot_lines_coverage, :track_views, :view_tracker,
|
|
12
|
+
:reporting_wiggle
|
|
12
13
|
|
|
13
|
-
attr_writer :logger, :s3_region, :s3_bucket, :s3_access_key_id,
|
|
14
|
+
attr_writer :logger, :s3_region, :s3_bucket, :s3_access_key_id,
|
|
15
|
+
:s3_secret_access_key, :password
|
|
14
16
|
attr_reader :track_gems, :ignore, :use_oneshot_lines_coverage
|
|
15
17
|
|
|
16
18
|
#####
|
|
@@ -23,12 +25,22 @@ module Coverband
|
|
|
23
25
|
IGNORE_TASKS = ['coverband:clear',
|
|
24
26
|
'coverband:coverage',
|
|
25
27
|
'coverband:coverage_server',
|
|
26
|
-
'coverband:migrate'
|
|
28
|
+
'coverband:migrate',
|
|
29
|
+
'assets:precompile',
|
|
30
|
+
'db:version',
|
|
31
|
+
'db:create',
|
|
32
|
+
'db:drop',
|
|
33
|
+
'db:seed',
|
|
34
|
+
'db:setup',
|
|
35
|
+
'db:test:prepare',
|
|
36
|
+
'db:structure:dump',
|
|
37
|
+
'db:structure:load',
|
|
38
|
+
'db:version']
|
|
27
39
|
|
|
28
40
|
# Heroku when building assets runs code from a dynamic directory
|
|
29
41
|
# /tmp was added to avoid coverage from /tmp/build directories during
|
|
30
42
|
# heroku asset compilation
|
|
31
|
-
IGNORE_DEFAULTS = %w[vendor .erb$ .slim$ /tmp internal:prelude schema.rb]
|
|
43
|
+
IGNORE_DEFAULTS = %w[vendor/ .erb$ .slim$ /tmp internal:prelude schema.rb]
|
|
32
44
|
|
|
33
45
|
# Add in missing files which were never loaded
|
|
34
46
|
# we need to know what all paths to check for unloaded files
|
|
@@ -72,6 +84,7 @@ module Coverband
|
|
|
72
84
|
@s3_secret_access_key = nil
|
|
73
85
|
@redis_namespace = nil
|
|
74
86
|
@redis_ttl = 2_592_000 # in seconds. Default is 30 days.
|
|
87
|
+
@reporting_wiggle = nil
|
|
75
88
|
end
|
|
76
89
|
|
|
77
90
|
def logger
|
|
@@ -144,7 +157,7 @@ module Coverband
|
|
|
144
157
|
|
|
145
158
|
# by default we ignore vendor where many deployments put gems
|
|
146
159
|
# we will remove this default if track_gems is set
|
|
147
|
-
@ignore.delete('vendor')
|
|
160
|
+
@ignore.delete('vendor/')
|
|
148
161
|
# while we want to allow vendored gems we don't want to track vendored ruby STDLIB
|
|
149
162
|
@ignore << 'vendor/ruby-*' unless @ignore.include?('vendor/ruby-*')
|
|
150
163
|
add_group('App', root)
|
|
@@ -33,6 +33,9 @@ module Coverband
|
|
|
33
33
|
loop do
|
|
34
34
|
Coverband.report_coverage
|
|
35
35
|
Coverband.configuration.view_tracker&.report_views_tracked
|
|
36
|
+
if Coverband.configuration.reporting_wiggle
|
|
37
|
+
sleep_seconds = Coverband.configuration.background_reporting_sleep_seconds + rand(Coverband.configuration.reporting_wiggle.to_i)
|
|
38
|
+
end
|
|
36
39
|
if Coverband.configuration.verbose
|
|
37
40
|
logger.debug("Coverband: background reporting coverage (#{Coverband.configuration.store.type}). Sleeping #{sleep_seconds}s")
|
|
38
41
|
end
|
|
@@ -32,6 +32,10 @@ module Coverband
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
def report_data
|
|
36
|
+
report_dynamic_data
|
|
37
|
+
end
|
|
38
|
+
|
|
35
39
|
private
|
|
36
40
|
|
|
37
41
|
def static?
|
|
@@ -54,6 +58,12 @@ module Coverband
|
|
|
54
58
|
base_path: base_path,
|
|
55
59
|
notice: notice).format_dynamic_html!
|
|
56
60
|
end
|
|
61
|
+
|
|
62
|
+
def report_dynamic_data
|
|
63
|
+
Coverband::Utils::HTMLFormatter.new(filtered_report_files,
|
|
64
|
+
base_path: base_path,
|
|
65
|
+
notice: notice).format_dynamic_data!
|
|
66
|
+
end
|
|
57
67
|
end
|
|
58
68
|
end
|
|
59
69
|
end
|
|
@@ -51,8 +51,12 @@ module Coverband
|
|
|
51
51
|
@static.call(env)
|
|
52
52
|
when %r{\/settings}
|
|
53
53
|
[200, { 'Content-Type' => 'text/html' }, [settings]]
|
|
54
|
+
when %r{\/view_tracker_data}
|
|
55
|
+
[200, { 'Content-Type' => 'text/json' }, [view_tracker_data]]
|
|
54
56
|
when %r{\/view_tracker}
|
|
55
57
|
[200, { 'Content-Type' => 'text/html' }, [view_tracker]]
|
|
58
|
+
when %r{\/enriched_debug_data}
|
|
59
|
+
[200, { 'Content-Type' => 'text/json' }, [enriched_debug_data]]
|
|
56
60
|
when %r{\/debug_data}
|
|
57
61
|
[200, { 'Content-Type' => 'text/json' }, [debug_data]]
|
|
58
62
|
when %r{\/load_file_details}
|
|
@@ -87,10 +91,22 @@ module Coverband
|
|
|
87
91
|
base_path: base_path).format_view_tracker!
|
|
88
92
|
end
|
|
89
93
|
|
|
94
|
+
def view_tracker_data
|
|
95
|
+
Coverband::Collectors::ViewTracker.new(store: Coverband.configuration.store).as_json
|
|
96
|
+
end
|
|
97
|
+
|
|
90
98
|
def debug_data
|
|
91
99
|
Coverband.configuration.store.get_coverage_report.to_json
|
|
92
100
|
end
|
|
93
101
|
|
|
102
|
+
def enriched_debug_data
|
|
103
|
+
Coverband::Reporters::HTMLReport.new(Coverband.configuration.store,
|
|
104
|
+
static: false,
|
|
105
|
+
base_path: base_path,
|
|
106
|
+
notice: '',
|
|
107
|
+
open_report: false).report_data
|
|
108
|
+
end
|
|
109
|
+
|
|
94
110
|
def load_file_details
|
|
95
111
|
filename = request.params['filename']
|
|
96
112
|
Coverband::Reporters::HTMLReport.new(Coverband.configuration.store,
|
|
@@ -29,6 +29,10 @@ module Coverband
|
|
|
29
29
|
format_html(@coverage_result)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
def format_dynamic_data!
|
|
33
|
+
format_data(@coverage_result)
|
|
34
|
+
end
|
|
35
|
+
|
|
32
36
|
def format_settings!
|
|
33
37
|
format_settings
|
|
34
38
|
end
|
|
@@ -71,6 +75,10 @@ module Coverband
|
|
|
71
75
|
template('layout').result(binding)
|
|
72
76
|
end
|
|
73
77
|
|
|
78
|
+
def format_data(result)
|
|
79
|
+
template('data').result(binding)
|
|
80
|
+
end
|
|
81
|
+
|
|
74
82
|
# Returns the an erb instance for the template of given name
|
|
75
83
|
def template(name)
|
|
76
84
|
ERB.new(File.read(File.join(File.dirname(__FILE__), '../../../views/', "#{name}.erb")))
|