coverband 4.2.1 → 4.2.2
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/.travis.yml +2 -9
- data/CODE_OF_CONDUCT.md +76 -0
- data/README.md +32 -28
- data/changes.md +31 -11
- data/coverband.gemspec +0 -5
- data/lib/coverband/adapters/base.rb +8 -13
- data/lib/coverband/adapters/file_store.rb +10 -4
- data/lib/coverband/adapters/hash_redis_store.rb +182 -0
- data/lib/coverband/adapters/redis_store.rb +15 -1
- data/lib/coverband/collectors/coverage.rb +5 -7
- data/lib/coverband/collectors/view_tracker.rb +124 -0
- data/lib/coverband/configuration.rb +21 -1
- data/lib/coverband/integrations/background.rb +2 -1
- data/lib/coverband/reporters/base.rb +4 -5
- data/lib/coverband/reporters/console_report.rb +1 -0
- data/lib/coverband/reporters/web.rb +6 -0
- data/lib/coverband/utils/absolute_file_converter.rb +47 -0
- data/lib/coverband/utils/file_hasher.rb +16 -0
- data/lib/coverband/utils/html_formatter.rb +12 -0
- data/lib/coverband/utils/railtie.rb +15 -3
- data/lib/coverband/utils/relative_file_converter.rb +41 -0
- data/lib/coverband/utils/s3_report.rb +2 -2
- data/lib/coverband/utils/source_file.rb +1 -3
- data/lib/coverband/utils/tasks.rb +1 -0
- data/lib/coverband/version.rb +5 -1
- data/lib/coverband.rb +7 -1
- data/public/application.css +6 -6
- data/test/benchmarks/benchmark.rake +25 -49
- data/test/benchmarks/init_rails.rake +10 -0
- data/test/coverband/adapters/hash_redis_store_test.rb +190 -0
- data/test/coverband/adapters/redis_store_test.rb +90 -88
- data/test/coverband/collectors/coverage_test.rb +6 -2
- data/test/coverband/collectors/view_tracker_test.rb +77 -0
- data/test/coverband/integrations/resque_worker_test.rb +2 -3
- data/test/coverband/reporters/base_test.rb +1 -78
- data/test/coverband/reporters/console_test.rb +1 -4
- data/test/coverband/reporters/html_test.rb +9 -9
- data/test/coverband/utils/absolute_file_converter_test.rb +56 -0
- data/test/coverband/utils/file_hasher_test.rb +29 -0
- data/test/coverband/utils/relative_file_converter_test.rb +39 -0
- data/test/forked/rails_rake_full_stack_test.rb +9 -1
- data/test/integration/full_stack_test.rb +1 -2
- data/test/test_helper.rb +6 -7
- data/test/unique_files.rb +1 -1
- data/views/layout.erb +1 -20
- data/views/nav.erb +35 -0
- data/views/view_tracker.erb +37 -0
- metadata +22 -31
- data/lib/coverband/utils/file_path_helper.rb +0 -68
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 33ec68822b5c228e86048d62ded36cfeba06adf77022d07b39cdcb897c215f6f
|
|
4
|
+
data.tar.gz: 7494e163926def8f8406da850efe02f5cd79f6ba6377abaf14ccb09aedc6ca85
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fda6dd49e21244819834f20e857ba2f4c8fddec4b1096a2b7ecfad7acdf6352cb2d4a8783f1cb5efc1ea2dc1ef5d03e7712f709fb928e29461dd41fc2dff76d8
|
|
7
|
+
data.tar.gz: b318161bc9c3709d41a35bb8a4e108a5ab38bea2feb095b1fc8d900f6e8a9bcfcc37dbf90392408d2efa3d510a58ceff5c69d19a422e4923049c08ee69d20c9c
|
data/.travis.yml
CHANGED
|
@@ -11,18 +11,11 @@ services:
|
|
|
11
11
|
- redis-server
|
|
12
12
|
script:
|
|
13
13
|
- bundle exec rake rubocop
|
|
14
|
+
- COVERBAND_HASH_REDIS_STORE=t bundle exec rake
|
|
15
|
+
- COVERBAND_HASH_REDIS_STORE=t bundle exec rake forked_tests
|
|
14
16
|
- bundle exec rake
|
|
15
17
|
- bundle exec rake forked_tests
|
|
16
18
|
# remove this for now as it is flaky
|
|
17
19
|
# passes locally on 2.3.5 on travis passes sometimes on 2.4 and always on 2.6.1
|
|
18
20
|
#- bundle exec rake benchmarks:memory
|
|
19
21
|
- bundle exec rake benchmarks
|
|
20
|
-
before_install:
|
|
21
|
-
- echo 'this is a hack to clear default bundler and force bundler 1.17.3'
|
|
22
|
-
- ls /home/travis/.rvm/gems/
|
|
23
|
-
- rm /home/travis/.rvm/gems/ruby-2.3.7@global/specifications/bundler-2.0.1.gemspec || true
|
|
24
|
-
- rm /home/travis/.rvm/gems/ruby-2.4.6@global/specifications/bundler-2.0.1.gemspec || true
|
|
25
|
-
- rm /home/travis/.rvm/gems/ruby-2.5.5@global/specifications/bundler-2.0.1.gemspec || true
|
|
26
|
-
- gem uninstall bundler || true
|
|
27
|
-
- gem install bundler -v '1.17.3'
|
|
28
|
-
- bundler --version
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
|
8
|
+
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
|
9
|
+
level of experience, education, socio-economic status, nationality, personal
|
|
10
|
+
appearance, race, religion, or sexual identity and orientation.
|
|
11
|
+
|
|
12
|
+
## Our Standards
|
|
13
|
+
|
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
|
15
|
+
include:
|
|
16
|
+
|
|
17
|
+
* Using welcoming and inclusive language
|
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
|
19
|
+
* Gracefully accepting constructive criticism
|
|
20
|
+
* Focusing on what is best for the community
|
|
21
|
+
* Showing empathy towards other community members
|
|
22
|
+
|
|
23
|
+
Examples of unacceptable behavior by participants include:
|
|
24
|
+
|
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
26
|
+
advances
|
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
28
|
+
* Public or private harassment
|
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
|
30
|
+
address, without explicit permission
|
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
|
32
|
+
professional setting
|
|
33
|
+
|
|
34
|
+
## Our Responsibilities
|
|
35
|
+
|
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
|
38
|
+
response to any instances of unacceptable behavior.
|
|
39
|
+
|
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
|
44
|
+
threatening, offensive, or harmful.
|
|
45
|
+
|
|
46
|
+
## Scope
|
|
47
|
+
|
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
|
49
|
+
when an individual is representing the project or its community. Examples of
|
|
50
|
+
representing a project or community include using an official project e-mail
|
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
|
53
|
+
further defined and clarified by project maintainers.
|
|
54
|
+
|
|
55
|
+
## Enforcement
|
|
56
|
+
|
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
58
|
+
reported by contacting the project team at danmayer@gmail.com. All
|
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
|
63
|
+
|
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
|
66
|
+
members of the project's leadership.
|
|
67
|
+
|
|
68
|
+
## Attribution
|
|
69
|
+
|
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
71
|
+
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
|
72
|
+
|
|
73
|
+
[homepage]: https://www.contributor-covenant.org
|
|
74
|
+
|
|
75
|
+
For answers to common questions about this code of conduct, see
|
|
76
|
+
https://www.contributor-covenant.org/faq
|
data/README.md
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
[](https://travis-ci.org/danmayer/coverband)
|
|
6
6
|
[](https://coveralls.io/github/danmayer/coverband?branch=master)
|
|
7
7
|
[](https://codeclimate.com/github/danmayer/coverband/maintainability)
|
|
8
|
+
[](https://discord.gg/KAH38EV)
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
<p align="center">
|
|
10
12
|
<a href="#key-features">Key Features</a> •
|
|
@@ -12,7 +14,8 @@
|
|
|
12
14
|
<a href="#coverage-report">Coverage Report</a> •
|
|
13
15
|
<a href="#advanced-config">Advanced Config</a> •
|
|
14
16
|
<a href="#license">License</a> •
|
|
15
|
-
<a href="/changes.md">Change Log / Roadmap</a>
|
|
17
|
+
<a href="/changes.md">Change Log / Roadmap</a> •
|
|
18
|
+
<a href="/CODE_OF_CONDUCT.md">Code of Conduct</a>
|
|
16
19
|
</p>
|
|
17
20
|
|
|
18
21
|
A gem to measure production code usage, showing a counter for the number of times each line of code that is executed. Coverband allows easy configuration to collect and report on production code usage. It reports in the background via a thread or can be used as Rack middleware, or manually configured to meet any need.
|
|
@@ -161,10 +164,10 @@ Coverband.configure do |config|
|
|
|
161
164
|
config.s3_access_key_id = ENV['AWS_ACCESS_KEY_ID']
|
|
162
165
|
config.s3_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
|
|
163
166
|
|
|
164
|
-
# config options false, true
|
|
167
|
+
# config options false, true. (defaults to false)
|
|
165
168
|
# true and debug can give helpful and interesting code usage information
|
|
166
|
-
#
|
|
167
|
-
#
|
|
169
|
+
# and is safe to use if one is investigating issues in production, but it will slightly
|
|
170
|
+
# hit perf.
|
|
168
171
|
config.verbose = false
|
|
169
172
|
end
|
|
170
173
|
```
|
|
@@ -178,10 +181,19 @@ config.ignore += ['config/application.rb',
|
|
|
178
181
|
'config/boot.rb',
|
|
179
182
|
'config/puma.rb',
|
|
180
183
|
'config/schedule.rb',
|
|
181
|
-
'bin/*'
|
|
184
|
+
'bin/*',
|
|
182
185
|
'config/environments/*',
|
|
183
186
|
'lib/tasks/*']
|
|
184
187
|
```
|
|
188
|
+
### View Tracking
|
|
189
|
+
|
|
190
|
+
Coverband allows an optional feature to track all view files that are used by an application.
|
|
191
|
+
|
|
192
|
+
To opt-in to this feature... enable the feature in your Coverband config.
|
|
193
|
+
|
|
194
|
+
`config.track_views = true`
|
|
195
|
+
|
|
196
|
+

|
|
185
197
|
|
|
186
198
|
### Writing Coverband Results to S3
|
|
187
199
|
|
|
@@ -204,13 +216,13 @@ ENV['AWS_ACCESS_KEY_ID']
|
|
|
204
216
|
ENV['AWS_SECRET_ACCESS_KEY']
|
|
205
217
|
```
|
|
206
218
|
|
|
207
|
-
###
|
|
219
|
+
### Redis Hash Store
|
|
208
220
|
|
|
209
|
-
|
|
221
|
+
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.
|
|
210
222
|
|
|
211
|
-
`
|
|
212
|
-
|
|
213
|
-
|
|
223
|
+
* set the new Redis store: `config.store = Coverband::Adapters::HashRedisStore.new(Redis.new(url: redis_url))`
|
|
224
|
+
* adjust from default 30s reporting `config.background_reporting_sleep_seconds = 120`
|
|
225
|
+
* reminder it is recommended to have a unique Redis per workload (background jobs, caching, Coverband), for this store, it may be more important to have a dedicated Redis.
|
|
214
226
|
|
|
215
227
|
### Clear Coverage
|
|
216
228
|
|
|
@@ -218,6 +230,14 @@ Now that Coverband uses MD5 hashes there should be no reason to manually clear c
|
|
|
218
230
|
|
|
219
231
|
`rake coverband:clear`
|
|
220
232
|
|
|
233
|
+
### Coverage Data Migration
|
|
234
|
+
|
|
235
|
+
Between the release of 4.0 and 4.1 our data format changed. This resets all your coverage data. If you want to restore your previous coverage data, feel free to migrate.
|
|
236
|
+
|
|
237
|
+
`rake coverband:migrate`
|
|
238
|
+
|
|
239
|
+
- We will be working to support migrations going forward, when possible
|
|
240
|
+
|
|
221
241
|
### Adding Rake Tasks outside of Rails
|
|
222
242
|
|
|
223
243
|
Rails apps should automaticallly include the tasks via the Railtie.
|
|
@@ -307,27 +327,11 @@ Coverband.start
|
|
|
307
327
|
|
|
308
328
|
### Verbose Debug / Development Mode
|
|
309
329
|
|
|
310
|
-
Note: To debug issues getting Coverband working. I recommend running in development mode, by 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 affect performance.
|
|
330
|
+
Note: To debug issues getting Coverband working. I recommend running in development mode, by turning verbose logging on `config.verbose = true` and passing in the Rails.logger `config.logger = Rails.logger` to the Coverband config. We respect the log level, and I would recommend log level info generally, but if you are investigating a prolbem Coverband logs additional data at the `debug` level. This makes it easy to follow in development mode. Be careful to not leave these on in production as they will affect performance.
|
|
311
331
|
|
|
312
332
|
---
|
|
313
333
|
|
|
314
|
-
If you are trying to debug locally wondering what code is being run during a request. The verbose modes `config.verbose = true`
|
|
315
|
-
|
|
316
|
-
config.verbose = 'debug'
|
|
317
|
-
|
|
318
|
-
coverband file usage:
|
|
319
|
-
[["/Users/danmayer/projects/app_name/lib/facebook.rb", 6],
|
|
320
|
-
["/Users/danmayer/projects/app_name/app/models/some_modules.rb", 9],
|
|
321
|
-
...
|
|
322
|
-
["/Users/danmayer/projects/app_name/app/models/user.rb", 2606],
|
|
323
|
-
["/Users/danmayer/projects/app_name/app/helpers/application_helper.rb",
|
|
324
|
-
43150]]
|
|
325
|
-
|
|
326
|
-
file:
|
|
327
|
-
/Users/danmayer/projects/app_name/app/helpers/application_helper.rb =>
|
|
328
|
-
[[448, 1], [202, 1],
|
|
329
|
-
...
|
|
330
|
-
[517, 1617], [516, 38577]]
|
|
334
|
+
If you are trying to debug locally wondering what code is being run during a request. The verbose modes `config.verbose = true` && `Rails.logger.level = :debug`. With true set it will output the number of lines executed per file, to the passed in log.
|
|
331
335
|
|
|
332
336
|
# Prerequisites
|
|
333
337
|
|
data/changes.md
CHANGED
|
@@ -49,7 +49,7 @@ Will be the fully modern release that drops maintenance legacy support in favor
|
|
|
49
49
|
- move all code to work with relative paths leaving only stdlib Coverage working on full paths
|
|
50
50
|
- add gem_safe_lists to track only some gems
|
|
51
51
|
- add gem_details_safe list to report on details on some gems
|
|
52
|
-
- - display gems that are in loaded with 0 coverage, thanks @kbaum
|
|
52
|
+
- - display gems that are in loaded with 0 coverage, thanks @kbaum
|
|
53
53
|
|
|
54
54
|
### Coverband_jam_session
|
|
55
55
|
|
|
@@ -76,6 +76,30 @@ Feature Ideas:
|
|
|
76
76
|
|
|
77
77
|
# Alpha / Beta / Release Candidates
|
|
78
78
|
|
|
79
|
+
### Coverband 4.2.2
|
|
80
|
+
|
|
81
|
+
- new experimental hash redis store for high volume collection
|
|
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
|
|
89
|
+
|
|
90
|
+
# Released
|
|
91
|
+
|
|
92
|
+
### Coverband 4.2.2
|
|
93
|
+
|
|
94
|
+
- new experimental hash redis store for high volume collection
|
|
95
|
+
(hundreds of clients), thanks @kbaum
|
|
96
|
+
- view_tracker supports tracking view layer files like `.html.erb`
|
|
97
|
+
- documentation improvements, thanks @brossetti1, @jjb, @kbaum
|
|
98
|
+
- we now have discordapp for discussions, thanks @kbaum,
|
|
99
|
+
https://discordapp.com/channels/609509533999562753/609509533999562756
|
|
100
|
+
- perf fix on Rails initialization, thanks @skangg
|
|
101
|
+
- simplified logging
|
|
102
|
+
|
|
79
103
|
### Coverband 4.2.1
|
|
80
104
|
|
|
81
105
|
- larger changes
|
|
@@ -83,14 +107,13 @@ Feature Ideas:
|
|
|
83
107
|
- fix issue where reports didn't include files with 0 activity
|
|
84
108
|
- updated runtime relavent lines and runtime percentages
|
|
85
109
|
- add Oneshot coverage support for Ruby 2.6.0 thanks @zwalker
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
110
|
+
- I would consider this our test oneshot release, please report any issues
|
|
111
|
+
- improved control over memory vs functionality
|
|
112
|
+
- oneshot support is the best memory and speed if you are on Ruby 2.6.\*
|
|
113
|
+
- simulated_oneshot works for Ruby prior to 2.6.\*, keeps lower runtime memory, at a trade of only being able to detect eager_load or runtime hits not both
|
|
114
|
+
- full hit tracking (The previous and current default), this uses more memory than other options, but provides full robust data
|
|
92
115
|
|
|
93
|
-
|
|
116
|
+
* small changes fixes
|
|
94
117
|
- further improvements on eager_loading detection, thanks @kbaum
|
|
95
118
|
- fix on ignore configuration options
|
|
96
119
|
- fix broken static server
|
|
@@ -112,9 +135,6 @@ Feature Ideas:
|
|
|
112
135
|
- additional benchmarks to ensure proper memory handling, thanks @kbaum
|
|
113
136
|
- default redis TTL, thanks @jjb
|
|
114
137
|
|
|
115
|
-
|
|
116
|
-
# Released
|
|
117
|
-
|
|
118
138
|
### Coverband 4.2.0
|
|
119
139
|
|
|
120
140
|
**NOTE:** This release introduces load time and runtime Coverage. To get the full benifit of this feature you need to reset you coverage data (`rake coverband:clear` or clear via the web UI). Until you clear the combined result is still correct, but your runtime data will be mixed with previous data. This is due to an additive change in how we store coverage. We didn't reset by default as the coverage history for some folks may be more important than runtime breakdown.
|
data/coverband.gemspec
CHANGED
|
@@ -35,12 +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
|
-
# temporarily needed to run tests for classifier-reborn as part of benchmarks
|
|
39
|
-
spec.add_development_dependency 'minitest-reporters'
|
|
40
38
|
|
|
41
|
-
# TODO: used for benchmarking and tests I think we have other better benchmarking
|
|
42
|
-
# perhaps time to drop this and refactor.
|
|
43
|
-
spec.add_development_dependency 'classifier-reborn'
|
|
44
39
|
spec.add_development_dependency 'coveralls'
|
|
45
40
|
# add when debugging
|
|
46
41
|
# require 'byebug'; byebug
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
module Coverband
|
|
4
4
|
module Adapters
|
|
5
5
|
class Base
|
|
6
|
-
include Coverband::Utils::FilePathHelper
|
|
7
|
-
|
|
8
6
|
DATA_KEY = 'data'
|
|
9
7
|
FIRST_UPDATED_KEY = 'first_updated_at'
|
|
10
8
|
LAST_UPDATED_KEY = 'last_updated_at'
|
|
@@ -14,7 +12,6 @@ module Coverband
|
|
|
14
12
|
attr_accessor :type
|
|
15
13
|
|
|
16
14
|
def initialize
|
|
17
|
-
@file_hash_cache = {}
|
|
18
15
|
@type = Coverband::RUNTIME_TYPE
|
|
19
16
|
end
|
|
20
17
|
|
|
@@ -46,14 +43,8 @@ module Coverband
|
|
|
46
43
|
format('%.2f', (size.to_f / 2**20))
|
|
47
44
|
end
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
# the Coverband 2 had the same issue,
|
|
52
|
-
# and the tradeoff has always been acceptable
|
|
53
|
-
def save_report(report)
|
|
54
|
-
data = report.dup
|
|
55
|
-
data = merge_reports(data, coverage)
|
|
56
|
-
save_coverage(data)
|
|
46
|
+
def save_report(_report)
|
|
47
|
+
raise 'abstract'
|
|
57
48
|
end
|
|
58
49
|
|
|
59
50
|
def get_coverage_report
|
|
@@ -65,6 +56,10 @@ module Coverband
|
|
|
65
56
|
coverage.keys || []
|
|
66
57
|
end
|
|
67
58
|
|
|
59
|
+
def raw_store
|
|
60
|
+
raise ABSTRACT_KEY
|
|
61
|
+
end
|
|
62
|
+
|
|
68
63
|
protected
|
|
69
64
|
|
|
70
65
|
def split_coverage(types)
|
|
@@ -93,7 +88,7 @@ module Coverband
|
|
|
93
88
|
end
|
|
94
89
|
|
|
95
90
|
def file_hash(file)
|
|
96
|
-
|
|
91
|
+
Coverband::Utils::FileHasher.hash(file)
|
|
97
92
|
end
|
|
98
93
|
|
|
99
94
|
# TODO: modify to extend report inline?
|
|
@@ -108,7 +103,7 @@ module Coverband
|
|
|
108
103
|
FILE_HASH => file_hash(key),
|
|
109
104
|
DATA_KEY => line_data
|
|
110
105
|
}
|
|
111
|
-
expanded[
|
|
106
|
+
expanded[Utils::RelativeFileConverter.convert(key)] = extended_data
|
|
112
107
|
end
|
|
113
108
|
expanded
|
|
114
109
|
end
|
|
@@ -36,13 +36,19 @@ module Coverband
|
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
def save_report(report)
|
|
40
|
+
data = report.dup
|
|
41
|
+
data = merge_reports(data, coverage)
|
|
42
|
+
File.open(path, 'w') { |f| f.write(data.to_json) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def raw_store
|
|
46
|
+
raise NotImplementedError, "FileStore doesn't support raw_store"
|
|
47
|
+
end
|
|
48
|
+
|
|
39
49
|
private
|
|
40
50
|
|
|
41
51
|
attr_accessor :path
|
|
42
|
-
|
|
43
|
-
def save_coverage(report)
|
|
44
|
-
File.open(path, 'w') { |f| f.write(report.to_json) }
|
|
45
|
-
end
|
|
46
52
|
end
|
|
47
53
|
end
|
|
48
54
|
end
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coverband
|
|
4
|
+
module Adapters
|
|
5
|
+
class HashRedisStore < Base
|
|
6
|
+
FILE_KEY = 'file'
|
|
7
|
+
FILE_LENGTH_KEY = 'file_length'
|
|
8
|
+
META_DATA_KEYS = [DATA_KEY, FIRST_UPDATED_KEY, LAST_UPDATED_KEY].freeze
|
|
9
|
+
###
|
|
10
|
+
# This key isn't related to the coverband version, but to the interal format
|
|
11
|
+
# used to store data to redis. It is changed only when breaking changes to our
|
|
12
|
+
# redis format are required.
|
|
13
|
+
###
|
|
14
|
+
REDIS_STORAGE_FORMAT_VERSION = 'coverband_hash_3_3'
|
|
15
|
+
|
|
16
|
+
attr_reader :redis_namespace
|
|
17
|
+
|
|
18
|
+
def initialize(redis, opts = {})
|
|
19
|
+
super()
|
|
20
|
+
@redis_namespace = opts[:redis_namespace]
|
|
21
|
+
@format_version = REDIS_STORAGE_FORMAT_VERSION
|
|
22
|
+
@redis = redis
|
|
23
|
+
raise 'HashRedisStore requires redis >= 2.6.0' unless supported?
|
|
24
|
+
|
|
25
|
+
@ttl = opts[:ttl] || -1
|
|
26
|
+
@relative_file_converter = opts[:relative_file_converter] || Utils::RelativeFileConverter
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def supported?
|
|
30
|
+
Gem::Version.new(@redis.info['redis_version']) >= Gem::Version.new('2.6.0')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def clear!
|
|
34
|
+
old_type = type
|
|
35
|
+
Coverband::TYPES.each do |type|
|
|
36
|
+
self.type = type
|
|
37
|
+
file_keys = files_set
|
|
38
|
+
@redis.del(*file_keys) if file_keys.any?
|
|
39
|
+
@redis.del(files_key)
|
|
40
|
+
end
|
|
41
|
+
self.type = old_type
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def clear_file!(file)
|
|
45
|
+
file_hash = file_hash(file)
|
|
46
|
+
relative_path_file = @relative_file_converter.convert(file)
|
|
47
|
+
Coverband::TYPES.each do |type|
|
|
48
|
+
@redis.del(key(relative_path_file, type, file_hash: file_hash))
|
|
49
|
+
end
|
|
50
|
+
@redis.srem(files_key, relative_path_file)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def save_report(report)
|
|
54
|
+
report_time = Time.now.to_i
|
|
55
|
+
updated_time = type == Coverband::EAGER_TYPE ? nil : report_time
|
|
56
|
+
script_id = hash_incr_script
|
|
57
|
+
@redis.pipelined do
|
|
58
|
+
keys = report.map do |file, data|
|
|
59
|
+
relative_file = @relative_file_converter.convert(file)
|
|
60
|
+
file_hash = file_hash(relative_file)
|
|
61
|
+
key = key(relative_file, file_hash: file_hash)
|
|
62
|
+
script_input = save_report_script_input(
|
|
63
|
+
key: key,
|
|
64
|
+
file: relative_file,
|
|
65
|
+
file_hash: file_hash,
|
|
66
|
+
data: data,
|
|
67
|
+
report_time: report_time,
|
|
68
|
+
updated_time: updated_time
|
|
69
|
+
)
|
|
70
|
+
@redis.evalsha(script_id, script_input[:keys], script_input[:args])
|
|
71
|
+
key
|
|
72
|
+
end
|
|
73
|
+
@redis.sadd(files_key, keys) if keys.any?
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def coverage(local_type = nil)
|
|
78
|
+
files_set(local_type).each_with_object({}) do |key, hash|
|
|
79
|
+
add_coverage_for_file(key, hash)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def raw_store
|
|
84
|
+
@redis
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def size
|
|
88
|
+
'not available'
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def size_in_mib
|
|
92
|
+
'not available'
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def add_coverage_for_file(key, hash)
|
|
98
|
+
data_from_redis = @redis.hgetall(key)
|
|
99
|
+
|
|
100
|
+
return if data_from_redis.empty?
|
|
101
|
+
|
|
102
|
+
file = data_from_redis[FILE_KEY]
|
|
103
|
+
return unless file_hash(file) == data_from_redis[FILE_HASH]
|
|
104
|
+
|
|
105
|
+
data = coverage_data_from_redis(data_from_redis)
|
|
106
|
+
hash[file] = data_from_redis.select { |meta_data_key, _value| META_DATA_KEYS.include?(meta_data_key) }.merge!('data' => data)
|
|
107
|
+
hash[file].merge!(LAST_UPDATED_KEY => hash[file][LAST_UPDATED_KEY].to_i, FIRST_UPDATED_KEY => hash[file][FIRST_UPDATED_KEY].to_i)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def coverage_data_from_redis(data_from_redis)
|
|
111
|
+
max = data_from_redis[FILE_LENGTH_KEY].to_i - 1
|
|
112
|
+
Array.new(max + 1) do |index|
|
|
113
|
+
line_coverage = data_from_redis[index.to_s]
|
|
114
|
+
line_coverage.nil? ? nil : line_coverage.to_i
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def save_report_script_input(key:, file:, file_hash:, data:, report_time:, updated_time:)
|
|
119
|
+
data.each_with_index
|
|
120
|
+
.each_with_object(keys: [key], args: [report_time, updated_time, file, file_hash, @ttl, data.length]) do |(coverage, index), hash|
|
|
121
|
+
if coverage
|
|
122
|
+
hash[:keys] << index
|
|
123
|
+
hash[:args] << coverage
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def hash_incr_script
|
|
129
|
+
@hash_incr_script ||= @redis.script(:load, <<~LUA)
|
|
130
|
+
local first_updated_at = table.remove(ARGV, 1)
|
|
131
|
+
local last_updated_at = table.remove(ARGV, 1)
|
|
132
|
+
local file = table.remove(ARGV, 1)
|
|
133
|
+
local file_hash = table.remove(ARGV, 1)
|
|
134
|
+
local ttl = table.remove(ARGV, 1)
|
|
135
|
+
local file_length = table.remove(ARGV, 1)
|
|
136
|
+
local hash_key = table.remove(KEYS, 1)
|
|
137
|
+
redis.call('HMSET', hash_key, '#{LAST_UPDATED_KEY}', last_updated_at, '#{FILE_KEY}', file, '#{FILE_HASH}', file_hash, '#{FILE_LENGTH_KEY}', file_length)
|
|
138
|
+
redis.call('HSETNX', hash_key, '#{FIRST_UPDATED_KEY}', first_updated_at)
|
|
139
|
+
for i, key in ipairs(KEYS) do
|
|
140
|
+
if ARGV[i] == '-1' then
|
|
141
|
+
redis.call("HSET", hash_key, key, ARGV[i])
|
|
142
|
+
else
|
|
143
|
+
redis.call("HINCRBY", hash_key, key, ARGV[i])
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
if ttl ~= '-1' then
|
|
147
|
+
redis.call("EXPIRE", hash_key, ttl)
|
|
148
|
+
end
|
|
149
|
+
LUA
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def values_from_redis(local_type, files)
|
|
153
|
+
return files if files.empty?
|
|
154
|
+
|
|
155
|
+
@redis.mget(*files.map { |file| key(file, local_type) }).map do |value|
|
|
156
|
+
value.nil? ? {} : JSON.parse(value)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def relative_paths(files)
|
|
161
|
+
files&.map! { |file| full_path_to_relative(file) }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def files_set(local_type = nil)
|
|
165
|
+
@redis.smembers(files_key(local_type))
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def files_key(local_type = nil)
|
|
169
|
+
"#{key_prefix(local_type)}.files"
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def key(file, local_type = nil, file_hash:)
|
|
173
|
+
[key_prefix(local_type), file, file_hash].join('.')
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def key_prefix(local_type = nil)
|
|
177
|
+
local_type ||= type
|
|
178
|
+
[@format_version, @redis_namespace, local_type].compact.join('.')
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
@@ -61,7 +61,7 @@ module Coverband
|
|
|
61
61
|
exit 0
|
|
62
62
|
end
|
|
63
63
|
relative_path_report = previous_data.each_with_object({}) do |(key, vals), fixed_report|
|
|
64
|
-
fixed_report[
|
|
64
|
+
fixed_report[Utils::RelativeFileConverter.convert(key)] = vals
|
|
65
65
|
end
|
|
66
66
|
clear!
|
|
67
67
|
reset_base_key
|
|
@@ -80,6 +80,20 @@ module Coverband
|
|
|
80
80
|
data ? JSON.parse(data) : {}
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
# Note: This could lead to slight race on redis
|
|
84
|
+
# where multiple processes pull the old coverage and add to it then push
|
|
85
|
+
# the Coverband 2 had the same issue,
|
|
86
|
+
# and the tradeoff has always been acceptable
|
|
87
|
+
def save_report(report)
|
|
88
|
+
data = report.dup
|
|
89
|
+
data = merge_reports(data, coverage)
|
|
90
|
+
save_coverage(data)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def raw_store
|
|
94
|
+
@redis
|
|
95
|
+
end
|
|
96
|
+
|
|
83
97
|
private
|
|
84
98
|
|
|
85
99
|
attr_reader :redis
|
|
@@ -22,8 +22,8 @@ module Coverband
|
|
|
22
22
|
@project_directory = File.expand_path(Coverband.configuration.root)
|
|
23
23
|
@ignore_patterns = Coverband.configuration.ignore
|
|
24
24
|
@store = Coverband.configuration.store
|
|
25
|
-
@verbose
|
|
26
|
-
@logger
|
|
25
|
+
@verbose = Coverband.configuration.verbose
|
|
26
|
+
@logger = Coverband.configuration.logger
|
|
27
27
|
@test_env = Coverband.configuration.test_env
|
|
28
28
|
@track_gems = Coverband.configuration.track_gems
|
|
29
29
|
Delta.reset
|
|
@@ -55,11 +55,9 @@ module Coverband
|
|
|
55
55
|
@store.save_report(files_with_line_usage)
|
|
56
56
|
end
|
|
57
57
|
rescue StandardError => e
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
@logger.error e.backtrace
|
|
62
|
-
end
|
|
58
|
+
@logger&.error 'coverage failed to store'
|
|
59
|
+
@logger&.error "Coverband Error: #{e.inspect} #{e.message}"
|
|
60
|
+
e.backtrace.each { |line| @logger&.error line } if @verbose
|
|
63
61
|
raise e if @test_env
|
|
64
62
|
end
|
|
65
63
|
|