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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -0
  3. data/Gemfile +1 -0
  4. data/README.md +6 -4
  5. data/Rakefile +5 -2
  6. data/changes.md +32 -13
  7. data/lib/coverband.rb +3 -4
  8. data/lib/coverband/adapters/base.rb +59 -30
  9. data/lib/coverband/adapters/file_store.rb +8 -8
  10. data/lib/coverband/adapters/redis_store.rb +18 -10
  11. data/lib/coverband/collectors/coverage.rb +3 -4
  12. data/lib/coverband/collectors/delta.rb +18 -8
  13. data/lib/coverband/configuration.rb +25 -9
  14. data/lib/coverband/integrations/background.rb +3 -1
  15. data/lib/coverband/reporters/base.rb +6 -3
  16. data/lib/coverband/reporters/web.rb +11 -0
  17. data/lib/coverband/utils/file_path_helper.rb +12 -3
  18. data/lib/coverband/utils/source_file.rb +2 -1
  19. data/lib/coverband/utils/tasks.rb +1 -11
  20. data/lib/coverband/version.rb +1 -1
  21. data/test/benchmarks/benchmark.rake +135 -10
  22. data/test/coverband/adapters/base_test.rb +73 -42
  23. data/test/coverband/adapters/file_store_test.rb +48 -37
  24. data/test/coverband/adapters/redis_store_test.rb +35 -5
  25. data/test/coverband/collectors/coverage_test.rb +1 -1
  26. data/test/coverband/collectors/delta_test.rb +10 -1
  27. data/test/coverband/coverband_test.rb +14 -1
  28. data/test/coverband/utils/html_formatter_test.rb +0 -2
  29. data/test/dog.rb.erb +12 -0
  30. data/test/forked/rails_full_stack_test.rb +35 -25
  31. data/test/forked/rails_rake_full_stack_test.rb +12 -4
  32. data/test/rails4_dummy/config/coverband.rb +2 -0
  33. data/test/rails5_dummy/config/coverband.rb +2 -0
  34. data/test/rails_test_helper.rb +2 -1
  35. data/test/test_helper.rb +19 -6
  36. data/test/unique_files.rb +12 -5
  37. metadata +5 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5cb0c3fd4256ad4d254c22cd98528bc34528e968ca65cfebc3a4720844e2a07d
4
- data.tar.gz: 83cb0d9d97861c92e688274dc6885025ff2282d6a2625e473b148a3f68be0c05
3
+ metadata.gz: d0848a662aca5276be0eca2ac39bf30cd55a45e50ef85416d217a316e7f317fc
4
+ data.tar.gz: 22e16b8e9096dbb86e117e9ff7956fd11bfed781ab9807dc8e82af36ca966ccb
5
5
  SHA512:
6
- metadata.gz: 2d1206b71ffe2fddb8145a5f168a317a5e54373dd2b8b708ae6c86da800d015215c7d60b9906c791b725c8127add8bfb59343cad812f4f70ddc69b00d7a179a3
7
- data.tar.gz: d2c1e07f8df62c8b4c4be64f009e2206b103b1685e2064664b1042d6ae25c9196247832007e707b6b3a7e1aa67aed454857bc64f13d44d594d86878f903d1a02
6
+ metadata.gz: 8d191007b78a0d53f4d9138a9ea13b7a9d5cd98636c1b87f0b53f71754a5cd6b3356ba5765857383b62b99b9f3afcf754d51244ea9296f61f9d58c6262e29338
7
+ data.tar.gz: 0fce0a7624708291b3ca61b8d494980bbfe2a96fe6460a3e6508922ab17ab5e077488edf2d9eeaa73cf1db1ad15e77bd61b0a7fb8f885a1283112913c9a69bd4
@@ -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
@@ -6,5 +6,6 @@ source 'https://rubygems.org'
6
6
  gemspec
7
7
  gem 'rails', '~>5'
8
8
  # these gems are used for testing gem tracking
9
+ gem 'irb', require: false
9
10
  gem 'pundit'
10
11
  gem 'rainbow', require: false
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
- __NOTE:__ Gem Tracking is still in testing stagings we have some performance issues on the reporting side and have heard reports of increased memory usage. We recommend deploying WITHOUT `track_gems` first and only enabling it after confirming that Coverband is working and performing well.
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 benchmarks]
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
- exec 'irb -I lib -r coverband'
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
- - further improvements on eager_loading detection
82
- - fix on ignore configuration options
83
- - fix broken static server
84
- - fix issue where clear and coverage trigger coverage reports
85
- - improved logging
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
- - resolve regression on coverband rake tasks recording coverage
92
- - improved runtime / eager loading tracking
93
- - updated runtime relavent lines and runtime percentages
94
- - fix on gem runtime code coverage support, thanks @kbaum
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
@@ -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 = nil
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 'abstract'
22
+ raise ABSTRACT_KEY
15
23
  end
16
24
 
17
- def clear_file!(_file)
18
- raise 'abstract'
25
+ def clear_file!(_file = nil)
26
+ raise ABSTRACT_KEY
19
27
  end
20
28
 
21
29
  def migrate!
22
- raise 'abstract'
30
+ raise ABSTRACT_KEY
23
31
  end
24
32
 
25
33
  def size
26
- raise 'abstract'
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, get_report)
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
- data.update(type => get_report(type))
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 merged_coverage(types)
65
- types.reduce({}) do |data, type|
66
- merge_reports(data, get_report(type), skip_expansion: true)
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 save_coverage
71
- raise 'abstract'
72
- end
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
- 'first_updated_at' => report_time,
89
- 'last_updated_at' => updated_time,
90
- 'file_hash' => file_hash(key),
91
- 'data' => line_data
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]['file_hash'] == old_report[file]['file_hash']
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
- 'first_updated_at' => old_expanded['first_updated_at'],
118
- 'last_updated_at' => new_expanded['last_updated_at'],
119
- 'file_hash' => new_expanded['file_hash'],
120
- 'data' => array_add(new_expanded['data'], old_expanded['data'])
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
- latest.map.with_index { |v, i| (v && original[i]) ? v + original[i] : nil }
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 = get_report(type)
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 = get_report
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(get_report, relative_path_report, skip_expansion: true))
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
- [@format_version, @redis_namespace, local_type].compact.join('.')
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