coverband 4.2.1.rc3 → 4.2.1.rc4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -0
- data/Gemfile +1 -0
- data/README.md +6 -4
- data/Rakefile +5 -2
- data/changes.md +32 -13
- data/lib/coverband.rb +3 -4
- data/lib/coverband/adapters/base.rb +59 -30
- data/lib/coverband/adapters/file_store.rb +8 -8
- data/lib/coverband/adapters/redis_store.rb +18 -10
- data/lib/coverband/collectors/coverage.rb +3 -4
- data/lib/coverband/collectors/delta.rb +18 -8
- data/lib/coverband/configuration.rb +25 -9
- data/lib/coverband/integrations/background.rb +3 -1
- data/lib/coverband/reporters/base.rb +6 -3
- data/lib/coverband/reporters/web.rb +11 -0
- data/lib/coverband/utils/file_path_helper.rb +12 -3
- data/lib/coverband/utils/source_file.rb +2 -1
- data/lib/coverband/utils/tasks.rb +1 -11
- data/lib/coverband/version.rb +1 -1
- data/test/benchmarks/benchmark.rake +135 -10
- data/test/coverband/adapters/base_test.rb +73 -42
- data/test/coverband/adapters/file_store_test.rb +48 -37
- data/test/coverband/adapters/redis_store_test.rb +35 -5
- data/test/coverband/collectors/coverage_test.rb +1 -1
- data/test/coverband/collectors/delta_test.rb +10 -1
- data/test/coverband/coverband_test.rb +14 -1
- data/test/coverband/utils/html_formatter_test.rb +0 -2
- data/test/dog.rb.erb +12 -0
- data/test/forked/rails_full_stack_test.rb +35 -25
- data/test/forked/rails_rake_full_stack_test.rb +12 -4
- data/test/rails4_dummy/config/coverband.rb +2 -0
- data/test/rails5_dummy/config/coverband.rb +2 -0
- data/test/rails_test_helper.rb +2 -1
- data/test/test_helper.rb +19 -6
- data/test/unique_files.rb +12 -5
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0848a662aca5276be0eca2ac39bf30cd55a45e50ef85416d217a316e7f317fc
|
4
|
+
data.tar.gz: 22e16b8e9096dbb86e117e9ff7956fd11bfed781ab9807dc8e82af36ca966ccb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d191007b78a0d53f4d9138a9ea13b7a9d5cd98636c1b87f0b53f71754a5cd6b3356ba5765857383b62b99b9f3afcf754d51244ea9296f61f9d58c6262e29338
|
7
|
+
data.tar.gz: 0fce0a7624708291b3ca61b8d494980bbfe2a96fe6460a3e6508922ab17ab5e077488edf2d9eeaa73cf1db1ad15e77bd61b0a7fb8f885a1283112913c9a69bd4
|
data/.travis.yml
CHANGED
@@ -13,6 +13,10 @@ script:
|
|
13
13
|
- bundle exec rake rubocop
|
14
14
|
- bundle exec rake
|
15
15
|
- bundle exec rake forked_tests
|
16
|
+
# remove this for now as it is flaky
|
17
|
+
# passes locally on 2.3.5 on travis passes sometimes on 2.4 and always on 2.6.1
|
18
|
+
#- bundle exec rake benchmarks:memory
|
19
|
+
- bundle exec rake benchmarks
|
16
20
|
before_install:
|
17
21
|
- echo 'this is a hack to clear default bundler and force bundler 1.17.3'
|
18
22
|
- ls /home/travis/.rvm/gems/
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -10,7 +10,6 @@
|
|
10
10
|
<a href="#key-features">Key Features</a> •
|
11
11
|
<a href="#installation">Installation</a> •
|
12
12
|
<a href="#coverage-report">Coverage Report</a> •
|
13
|
-
<a href="#verify-correct-installation">Verify Correct Installation</a> •
|
14
13
|
<a href="#advanced-config">Advanced Config</a> •
|
15
14
|
<a href="#license">License</a> •
|
16
15
|
<a href="/changes.md">Change Log / Roadmap</a>
|
@@ -29,7 +28,7 @@ The primary goal of Coverband is giving deep insight into your production runtim
|
|
29
28
|
- Out of the box support for all standard code execution paths (web, cron, background jobs, rake tasks, etc)
|
30
29
|
- Splits load time (Rails eager load) and Run time metrics
|
31
30
|
- Easy to understand actionable insights from the report
|
32
|
-
- Tracks Gem usage
|
31
|
+
- Tracks Gem usage (still in experimental stages and not recommended for production)
|
33
32
|
- Development mode, offers deep insight of code usage details (number of LOC execution during single request, etc) during development.
|
34
33
|
- Mountable web interface to easily share reports
|
35
34
|
|
@@ -99,6 +98,8 @@ Rails.application.routes.draw do
|
|
99
98
|
end
|
100
99
|
```
|
101
100
|
|
101
|
+
or you can enable basic auth by setting `ENV['COVERBAND_PASSWORD']` or via your configuration `config.password = 'my_pass'`
|
102
|
+
|
102
103
|
### Coverband Web Endpoint
|
103
104
|
|
104
105
|
The web endpoint is a barebones endpoint that you can either expose direct (after authentication) or you can just link to the actions you wish to expose. The index is intended as a example to showcase all the features.
|
@@ -108,8 +109,9 @@ The web endpoint is a barebones endpoint that you can either expose direct (afte
|
|
108
109
|
> The web index as available on the Coverband Demo site
|
109
110
|
|
110
111
|
- **force coverage collection:** This triggers coverage collection on the current webserver process
|
111
|
-
- **reload Coverband files:** This has Coverband reload files as configured (force reload of some files that might not capture Coverage on boot). This can be used to reload files on demand.
|
112
112
|
- **clear coverage report:** This will clear the coverage data. This wipes out all collected data (**dangerous**)
|
113
|
+
- View individual file details
|
114
|
+
- **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 loose all Coverage data but made a change that you expect would impact a particular file.
|
113
115
|
|
114
116
|
### Rake Tasks
|
115
117
|
|
@@ -254,7 +256,7 @@ By adding any files above you will get reporting on those files as part of your
|
|
254
256
|
|
255
257
|
### Collecting Gem / Library Usage
|
256
258
|
|
257
|
-
|
259
|
+
__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.
|
258
260
|
|
259
261
|
Gem usage can be tracked by enabling the `track_gems` config.
|
260
262
|
|
data/Rakefile
CHANGED
@@ -6,7 +6,9 @@ require 'rubocop/rake_task'
|
|
6
6
|
|
7
7
|
RuboCop::RakeTask.new
|
8
8
|
|
9
|
-
task default: %i[test
|
9
|
+
task default: %i[test]
|
10
|
+
|
11
|
+
task 'test:all': %i[rubocop test forked_tests benchmarks:memory benchmarks]
|
10
12
|
|
11
13
|
task :test
|
12
14
|
require 'rake/testtask'
|
@@ -27,7 +29,8 @@ end
|
|
27
29
|
|
28
30
|
desc 'load irb with this gem'
|
29
31
|
task :console do
|
30
|
-
|
32
|
+
puts 'running console'
|
33
|
+
exec 'bundle exec console'
|
31
34
|
end
|
32
35
|
|
33
36
|
# This is really just for testing and development because without configuration
|
data/changes.md
CHANGED
@@ -78,20 +78,39 @@ Feature Ideas:
|
|
78
78
|
|
79
79
|
### Coverband 4.2.1
|
80
80
|
|
81
|
-
-
|
82
|
-
-
|
83
|
-
- fix
|
84
|
-
-
|
85
|
-
-
|
86
|
-
- fix sorting of runtime coverage
|
87
|
-
- add runtime coverage to gem summary pages
|
88
|
-
- fix issue where reports didn't include files with 0 activity
|
89
|
-
- add Oneshot coverage support for Ruby 2.6.0 thanks @zwalker
|
81
|
+
- larger changes
|
82
|
+
- reduce memory usage
|
83
|
+
- fix issue where reports didn't include files with 0 activity
|
84
|
+
- updated runtime relavent lines and runtime percentages
|
85
|
+
- add Oneshot coverage support for Ruby 2.6.0 thanks @zwalker
|
90
86
|
- I would consider this our test oneshot release, please report any issues
|
91
|
-
-
|
92
|
-
-
|
93
|
-
-
|
94
|
-
-
|
87
|
+
- improved control over memory vs functionality
|
88
|
+
- oneshot support is the best memory and speed if you are on Ruby 2.6.*
|
89
|
+
- 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
|
90
|
+
- full hit tracking (The previous and current default), this uses more memory than other options, but provides full robust data
|
91
|
+
|
92
|
+
|
93
|
+
- small changes fixes
|
94
|
+
- further improvements on eager_loading detection, thanks @kbaum
|
95
|
+
- fix on ignore configuration options
|
96
|
+
- fix broken static server
|
97
|
+
- fix issue where clear and coverage trigger coverage reports
|
98
|
+
- improved logging
|
99
|
+
- fix on gem runtime code coverage support, thanks @kbaum
|
100
|
+
- fix sorting of runtime coverage
|
101
|
+
- add runtime coverage to gem summary pages
|
102
|
+
- documented redis TTL, thanks @jjb
|
103
|
+
- resolve regression on coverband rake tasks recording coverage
|
104
|
+
- improved runtime / eager loading tracking
|
105
|
+
- fix on small memory leaks
|
106
|
+
- added warnings that gem tracking isn't production ready
|
107
|
+
- built in support for basic auth on the web rack app, thanks @jjb
|
108
|
+
- don't clobber rake environment method definition, thanks @shioyama
|
109
|
+
- readme improvements, thanks @yuriyalekseyev
|
110
|
+
- fix duplicate requires thanks @yuriyalekseyev
|
111
|
+
- various cleanups and improvements, thanks @kbaum
|
112
|
+
- additional benchmarks to ensure proper memory handling, thanks @kbaum
|
113
|
+
- default redis TTL, thanks @jjb
|
95
114
|
|
96
115
|
|
97
116
|
# Released
|
data/lib/coverband.rb
CHANGED
@@ -23,16 +23,15 @@ require 'coverband/collectors/coverage'
|
|
23
23
|
require 'coverband/reporters/base'
|
24
24
|
require 'coverband/reporters/html_report'
|
25
25
|
require 'coverband/reporters/console_report'
|
26
|
-
require 'coverband/integrations/background'
|
27
|
-
require 'coverband/integrations/rack_server_check'
|
28
26
|
require 'coverband/reporters/web'
|
29
|
-
require 'coverband/integrations/background_middleware'
|
30
27
|
require 'coverband/integrations/background'
|
28
|
+
require 'coverband/integrations/background_middleware'
|
29
|
+
require 'coverband/integrations/rack_server_check'
|
31
30
|
|
32
31
|
module Coverband
|
33
32
|
@@configured = false
|
34
33
|
CONFIG_FILE = './config/coverband.rb'
|
35
|
-
RUNTIME_TYPE =
|
34
|
+
RUNTIME_TYPE = :runtime
|
36
35
|
EAGER_TYPE = :eager_loading
|
37
36
|
MERGED_TYPE = :merged
|
38
37
|
TYPES = [RUNTIME_TYPE, EAGER_TYPE]
|
@@ -4,26 +4,42 @@ module Coverband
|
|
4
4
|
module Adapters
|
5
5
|
class Base
|
6
6
|
include Coverband::Utils::FilePathHelper
|
7
|
+
|
8
|
+
DATA_KEY = 'data'
|
9
|
+
FIRST_UPDATED_KEY = 'first_updated_at'
|
10
|
+
LAST_UPDATED_KEY = 'last_updated_at'
|
11
|
+
FILE_HASH = 'file_hash'
|
12
|
+
ABSTRACT_KEY = 'abstract'
|
13
|
+
|
7
14
|
attr_accessor :type
|
8
15
|
|
9
16
|
def initialize
|
10
17
|
@file_hash_cache = {}
|
18
|
+
@type = Coverband::RUNTIME_TYPE
|
11
19
|
end
|
12
20
|
|
13
21
|
def clear!
|
14
|
-
raise
|
22
|
+
raise ABSTRACT_KEY
|
15
23
|
end
|
16
24
|
|
17
|
-
def clear_file!(_file)
|
18
|
-
raise
|
25
|
+
def clear_file!(_file = nil)
|
26
|
+
raise ABSTRACT_KEY
|
19
27
|
end
|
20
28
|
|
21
29
|
def migrate!
|
22
|
-
raise
|
30
|
+
raise ABSTRACT_KEY
|
23
31
|
end
|
24
32
|
|
25
33
|
def size
|
26
|
-
raise
|
34
|
+
raise ABSTRACT_KEY
|
35
|
+
end
|
36
|
+
|
37
|
+
def save_coverage
|
38
|
+
raise ABSTRACT_KEY
|
39
|
+
end
|
40
|
+
|
41
|
+
def coverage(_local_type = nil)
|
42
|
+
raise ABSTRACT_KEY
|
27
43
|
end
|
28
44
|
|
29
45
|
def size_in_mib
|
@@ -36,14 +52,10 @@ module Coverband
|
|
36
52
|
# and the tradeoff has always been acceptable
|
37
53
|
def save_report(report)
|
38
54
|
data = report.dup
|
39
|
-
data = merge_reports(data,
|
55
|
+
data = merge_reports(data, coverage)
|
40
56
|
save_coverage(data)
|
41
57
|
end
|
42
58
|
|
43
|
-
def coverage
|
44
|
-
get_report
|
45
|
-
end
|
46
|
-
|
47
59
|
def get_coverage_report
|
48
60
|
data = Coverband.configuration.store.split_coverage(Coverband::TYPES)
|
49
61
|
data.merge(Coverband::MERGED_TYPE => Coverband.configuration.store.merged_coverage(Coverband::TYPES))
|
@@ -57,38 +69,44 @@ module Coverband
|
|
57
69
|
|
58
70
|
def split_coverage(types)
|
59
71
|
types.reduce({}) do |data, type|
|
60
|
-
|
72
|
+
if type == Coverband::RUNTIME_TYPE && Coverband.configuration.simulate_oneshot_lines_coverage
|
73
|
+
data.update(type => simulated_runtime_coverage)
|
74
|
+
else
|
75
|
+
data.update(type => coverage(type))
|
76
|
+
end
|
61
77
|
end
|
62
78
|
end
|
63
79
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
80
|
+
def simulated_runtime_coverage
|
81
|
+
runtime_data = coverage(Coverband::RUNTIME_TYPE)
|
82
|
+
eager_data = coverage(Coverband::EAGER_TYPE)
|
83
|
+
eager_data.values do |vals|
|
84
|
+
vals['data'].map! { |line_coverage| line_coverage ? (0 - line_coverage) : line_coverage }
|
67
85
|
end
|
86
|
+
merge_reports(runtime_data, eager_data, skip_expansion: true)
|
68
87
|
end
|
69
88
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
def get_report(_type = nil)
|
75
|
-
raise 'abstract'
|
89
|
+
def merged_coverage(types)
|
90
|
+
types.reduce({}) do |data, type|
|
91
|
+
merge_reports(data, coverage(type), skip_expansion: true)
|
92
|
+
end
|
76
93
|
end
|
77
94
|
|
78
95
|
def file_hash(file)
|
79
96
|
@file_hash_cache[file] ||= Digest::MD5.file(file).hexdigest
|
80
97
|
end
|
81
98
|
|
99
|
+
# TODO: modify to extend report inline?
|
82
100
|
def expand_report(report)
|
83
101
|
expanded = {}
|
84
102
|
report_time = Time.now.to_i
|
85
103
|
updated_time = type == Coverband::EAGER_TYPE ? nil : report_time
|
86
104
|
report.each_pair do |key, line_data|
|
87
105
|
extended_data = {
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
106
|
+
FIRST_UPDATED_KEY => report_time,
|
107
|
+
LAST_UPDATED_KEY => updated_time,
|
108
|
+
FILE_HASH => file_hash(key),
|
109
|
+
DATA_KEY => line_data
|
92
110
|
}
|
93
111
|
expanded[full_path_to_relative(key)] = extended_data
|
94
112
|
end
|
@@ -96,12 +114,16 @@ module Coverband
|
|
96
114
|
end
|
97
115
|
|
98
116
|
def merge_reports(new_report, old_report, options = {})
|
117
|
+
# transparently update from RUNTIME_TYPE = nil to RUNTIME_TYPE = :runtime
|
118
|
+
# transparent update for format coveband_3_2
|
119
|
+
old_report = coverage(nil, override_type: nil) if old_report.nil? && type == Coverband::RUNTIME_TYPE
|
120
|
+
|
99
121
|
new_report = expand_report(new_report) unless options[:skip_expansion]
|
100
122
|
keys = (new_report.keys + old_report.keys).uniq
|
101
123
|
keys.each do |file|
|
102
124
|
new_report[file] = if new_report[file] &&
|
103
125
|
old_report[file] &&
|
104
|
-
new_report[file][
|
126
|
+
new_report[file][FILE_HASH] == old_report[file][FILE_HASH]
|
105
127
|
merge_expanded_data(new_report[file], old_report[file])
|
106
128
|
elsif new_report[file]
|
107
129
|
new_report[file]
|
@@ -114,15 +136,22 @@ module Coverband
|
|
114
136
|
|
115
137
|
def merge_expanded_data(new_expanded, old_expanded)
|
116
138
|
{
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
139
|
+
FIRST_UPDATED_KEY => old_expanded[FIRST_UPDATED_KEY],
|
140
|
+
LAST_UPDATED_KEY => new_expanded[LAST_UPDATED_KEY],
|
141
|
+
FILE_HASH => new_expanded[FILE_HASH],
|
142
|
+
DATA_KEY => array_add(new_expanded[DATA_KEY], old_expanded[DATA_KEY])
|
121
143
|
}
|
122
144
|
end
|
123
145
|
|
146
|
+
# TODO: This should only be 2 cases get our dup / not dups aligned
|
124
147
|
def array_add(latest, original)
|
125
|
-
|
148
|
+
if Coverband.configuration.use_oneshot_lines_coverage
|
149
|
+
latest.map!.with_index { |v, i| (v + original[i] >= 1 ? 1 : 0) if v && original[i] }
|
150
|
+
elsif Coverband.configuration.simulate_oneshot_lines_coverage
|
151
|
+
latest.map.with_index { |v, i| (v + original[i] >= 1 ? 1 : 0) if v && original[i] }
|
152
|
+
else
|
153
|
+
latest.map.with_index { |v, i| (v && original[i]) ? v + original[i] : nil }
|
154
|
+
end
|
126
155
|
end
|
127
156
|
end
|
128
157
|
end
|
@@ -28,6 +28,14 @@ module Coverband
|
|
28
28
|
raise NotImplementedError, "FileStore doesn't support migrations"
|
29
29
|
end
|
30
30
|
|
31
|
+
def coverage(_local_type = nil)
|
32
|
+
if File.exist?(path)
|
33
|
+
JSON.parse(File.read(path))
|
34
|
+
else
|
35
|
+
{}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
31
39
|
private
|
32
40
|
|
33
41
|
attr_accessor :path
|
@@ -35,14 +43,6 @@ module Coverband
|
|
35
43
|
def save_coverage(report)
|
36
44
|
File.open(path, 'w') { |f| f.write(report.to_json) }
|
37
45
|
end
|
38
|
-
|
39
|
-
def get_report(_local_type = nil)
|
40
|
-
if File.exist?(path)
|
41
|
-
JSON.parse(File.read(path))
|
42
|
-
else
|
43
|
-
{}
|
44
|
-
end
|
45
|
-
end
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
@@ -13,23 +13,31 @@ module Coverband
|
|
13
13
|
###
|
14
14
|
REDIS_STORAGE_FORMAT_VERSION = 'coverband_3_2'
|
15
15
|
|
16
|
+
attr_reader :redis_namespace
|
17
|
+
|
16
18
|
def initialize(redis, opts = {})
|
17
19
|
super()
|
18
20
|
@redis = redis
|
19
21
|
@ttl = opts[:ttl]
|
20
22
|
@redis_namespace = opts[:redis_namespace]
|
21
23
|
@format_version = REDIS_STORAGE_FORMAT_VERSION
|
24
|
+
@keys = {}
|
25
|
+
Coverband::TYPES.each do |type|
|
26
|
+
@keys[type] = [@format_version, @redis_namespace, type].compact.join('.')
|
27
|
+
end
|
22
28
|
end
|
23
29
|
|
24
30
|
def clear!
|
25
31
|
Coverband::TYPES.each do |type|
|
26
32
|
@redis.del(type_base_key(type))
|
27
33
|
end
|
34
|
+
# temporarily clear the old namespace of coverband_3_2
|
35
|
+
@redis.del(type_base_key(nil))
|
28
36
|
end
|
29
37
|
|
30
38
|
def clear_file!(filename)
|
31
39
|
Coverband::TYPES.each do |type|
|
32
|
-
data =
|
40
|
+
data = coverage(type)
|
33
41
|
data.delete(filename)
|
34
42
|
save_coverage(data, type)
|
35
43
|
end
|
@@ -47,7 +55,7 @@ module Coverband
|
|
47
55
|
def migrate!
|
48
56
|
reset_base_key
|
49
57
|
@format_version = 'coverband3_1'
|
50
|
-
previous_data =
|
58
|
+
previous_data = coverage
|
51
59
|
if previous_data.empty?
|
52
60
|
puts 'no previous data to migrate found'
|
53
61
|
exit 0
|
@@ -58,7 +66,7 @@ module Coverband
|
|
58
66
|
clear!
|
59
67
|
reset_base_key
|
60
68
|
@format_version = REDIS_STORAGE_FORMAT_VERSION
|
61
|
-
save_coverage(merge_reports(
|
69
|
+
save_coverage(merge_reports(coverage, relative_path_report, skip_expansion: true))
|
62
70
|
end
|
63
71
|
|
64
72
|
def type=(type)
|
@@ -66,6 +74,12 @@ module Coverband
|
|
66
74
|
reset_base_key
|
67
75
|
end
|
68
76
|
|
77
|
+
def coverage(local_type = nil, opts = {})
|
78
|
+
local_type ||= opts.key?(:override_type) ? opts[:override_type] : type
|
79
|
+
data = redis.get type_base_key(local_type)
|
80
|
+
data ? JSON.parse(data) : {}
|
81
|
+
end
|
82
|
+
|
69
83
|
private
|
70
84
|
|
71
85
|
attr_reader :redis
|
@@ -79,7 +93,7 @@ module Coverband
|
|
79
93
|
end
|
80
94
|
|
81
95
|
def type_base_key(local_type)
|
82
|
-
[
|
96
|
+
@keys[local_type]
|
83
97
|
end
|
84
98
|
|
85
99
|
def save_coverage(data, local_type = nil)
|
@@ -87,12 +101,6 @@ module Coverband
|
|
87
101
|
redis.set type_base_key(local_type), data.to_json
|
88
102
|
redis.expire(type_base_key(local_type), @ttl) if @ttl
|
89
103
|
end
|
90
|
-
|
91
|
-
def get_report(local_type = nil)
|
92
|
-
local_type ||= type
|
93
|
-
data = redis.get type_base_key(local_type)
|
94
|
-
data ? JSON.parse(data) : {}
|
95
|
-
end
|
96
104
|
end
|
97
105
|
end
|
98
106
|
end
|