coverband 4.2.0 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +29 -9
  3. data/.travis.yml +10 -1
  4. data/Gemfile +3 -1
  5. data/Gemfile.rails4 +1 -0
  6. data/README.md +40 -15
  7. data/Rakefile +14 -3
  8. data/changes.md +47 -12
  9. data/coverband.gemspec +3 -1
  10. data/lib/coverband/adapters/base.rb +60 -36
  11. data/lib/coverband/adapters/file_store.rb +8 -8
  12. data/lib/coverband/adapters/redis_store.rb +18 -10
  13. data/lib/coverband/at_exit.rb +2 -11
  14. data/lib/coverband/collectors/coverage.rb +32 -36
  15. data/lib/coverband/collectors/delta.rb +34 -28
  16. data/lib/coverband/configuration.rb +76 -32
  17. data/lib/coverband/integrations/background.rb +8 -4
  18. data/lib/coverband/integrations/{middleware.rb → background_middleware.rb} +2 -6
  19. data/lib/coverband/integrations/bundler.rb +8 -0
  20. data/lib/coverband/integrations/report_middleware.rb +15 -0
  21. data/lib/coverband/integrations/resque.rb +3 -5
  22. data/lib/coverband/reporters/base.rb +39 -14
  23. data/lib/coverband/reporters/html_report.rb +21 -27
  24. data/lib/coverband/reporters/web.rb +13 -21
  25. data/lib/coverband/utils/file_list.rb +16 -5
  26. data/lib/coverband/utils/file_path_helper.rb +14 -3
  27. data/lib/coverband/utils/html_formatter.rb +25 -8
  28. data/lib/coverband/utils/lines_classifier.rb +5 -0
  29. data/lib/coverband/utils/railtie.rb +11 -12
  30. data/lib/coverband/utils/result.rb +1 -37
  31. data/lib/coverband/utils/results.rb +51 -0
  32. data/lib/coverband/utils/source_file.rb +31 -6
  33. data/lib/coverband/utils/tasks.rb +9 -10
  34. data/lib/coverband/version.rb +1 -1
  35. data/lib/coverband.rb +32 -21
  36. data/public/application.js +27 -0
  37. data/test/benchmarks/benchmark.rake +144 -26
  38. data/test/coverband/adapters/base_test.rb +73 -42
  39. data/test/coverband/adapters/file_store_test.rb +48 -37
  40. data/test/coverband/adapters/redis_store_test.rb +41 -10
  41. data/test/coverband/at_exit_test.rb +0 -2
  42. data/test/coverband/collectors/coverage_test.rb +57 -9
  43. data/test/coverband/collectors/delta_test.rb +36 -6
  44. data/test/coverband/configuration_test.rb +47 -7
  45. data/test/coverband/coverband_test.rb +14 -2
  46. data/test/coverband/integrations/background_middleware_test.rb +44 -0
  47. data/test/coverband/integrations/background_test.rb +1 -3
  48. data/test/coverband/integrations/report_middleware_test.rb +44 -0
  49. data/test/coverband/integrations/resque_worker_test.rb +4 -3
  50. data/test/coverband/integrations/test_resque_job.rb +3 -1
  51. data/test/coverband/reporters/base_test.rb +4 -4
  52. data/test/coverband/reporters/console_test.rb +1 -2
  53. data/test/coverband/reporters/html_test.rb +58 -19
  54. data/test/coverband/reporters/web_test.rb +0 -10
  55. data/test/coverband/utils/file_groups_test.rb +11 -5
  56. data/test/coverband/utils/file_list_test.rb +5 -5
  57. data/test/coverband/utils/html_formatter_test.rb +43 -0
  58. data/test/coverband/utils/result_test.rb +6 -47
  59. data/test/coverband/utils/results_test.rb +54 -0
  60. data/test/coverband/utils/s3_report_test.rb +2 -0
  61. data/test/coverband/utils/source_file_test.rb +50 -0
  62. data/test/dog.rb.erb +12 -0
  63. data/test/forked/rails_full_stack_test.rb +106 -0
  64. data/test/forked/rails_rake_full_stack_test.rb +40 -0
  65. data/test/integration/full_stack_test.rb +17 -15
  66. data/test/rails4_dummy/Rakefile +6 -0
  67. data/test/rails4_dummy/config/application.rb +8 -9
  68. data/test/rails4_dummy/config/coverband.rb +5 -3
  69. data/test/rails5_dummy/Rakefile +6 -0
  70. data/test/rails5_dummy/config/application.rb +6 -10
  71. data/test/rails5_dummy/config/coverband.rb +4 -2
  72. data/test/rails_test_helper.rb +22 -5
  73. data/test/test_helper.rb +42 -4
  74. data/test/unique_files.rb +17 -9
  75. data/views/file_list.erb +2 -2
  76. data/views/gem_list.erb +10 -1
  77. data/views/layout.erb +10 -3
  78. data/views/source_file.erb +13 -4
  79. data/views/source_file_loader.erb +1 -1
  80. metadata +52 -9
  81. data/test/coverband/integrations/middleware_test.rb +0 -96
  82. data/test/integration/rails_full_stack_test.rb +0 -95
@@ -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
@@ -16,19 +16,10 @@ module Coverband
16
16
  at_exit do
17
17
  ::Coverband::Background.stop
18
18
 
19
- #####
20
- # TODO: This is is brittle and not a great solution to avoid deploy time
21
- # actions polluting the 'runtime' metrics
22
- #
23
- # * should we skip /bin/rails webpacker:compile ?
24
- # * Perhaps detect heroku deployment ENV var opposed to tasks?
25
- #####
26
- default_heroku_tasks = ['assets:clean', 'assets:precompile']
27
- if defined?(Rake) && Rake.respond_to?(:application) && (Rake&.application&.top_level_tasks || []).any? { |task| default_heroku_tasks.include?(task) }
19
+ if !Coverband.configuration.report_on_exit
28
20
  # skip reporting
29
21
  else
30
- Coverband.report_coverage(true)
31
- #Coverband.configuration.logger&.debug('Coverband: Reported coverage before exit')
22
+ Coverband.report_coverage
32
23
  end
33
24
  end
34
25
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'singleton'
3
4
  require_relative 'delta'
4
5
 
@@ -13,10 +14,13 @@ module Coverband
13
14
  class Coverage
14
15
  include Singleton
15
16
 
17
+ def self.ruby_version_greater_than_or_equal_to?(version)
18
+ Gem::Version.new(RUBY_VERSION) >= Gem::Version.new(version)
19
+ end
20
+
16
21
  def reset_instance
17
22
  @project_directory = File.expand_path(Coverband.configuration.root)
18
- @ignore_patterns = Coverband.configuration.ignore + ['internal:prelude', 'schema.rb']
19
- @reporting_frequency = Coverband.configuration.reporting_frequency
23
+ @ignore_patterns = Coverband.configuration.ignore
20
24
  @store = Coverband.configuration.store
21
25
  @verbose = Coverband.configuration.verbose
22
26
  @logger = Coverband.configuration.logger
@@ -27,36 +31,36 @@ module Coverband
27
31
  end
28
32
 
29
33
  def runtime!
30
- @store.type = nil
34
+ @store.type = Coverband::RUNTIME_TYPE
31
35
  end
32
36
 
33
37
  def eager_loading!
34
38
  @store.type = Coverband::EAGER_TYPE
35
39
  end
36
40
 
37
- def report_coverage(force_report = false)
38
- return if !ready_to_report? && !force_report
39
- raise 'no Coverband store set' unless @store
41
+ def eager_loading
42
+ old_coverage_type = @store.type
43
+ eager_loading!
44
+ yield
45
+ ensure
46
+ report_coverage
47
+ @store.type = old_coverage_type
48
+ end
40
49
 
41
- original_previous_set = Delta.previous_results
42
- files_with_line_usage = filtered_files(Delta.results)
50
+ def report_coverage
51
+ @semaphore.synchronize do
52
+ raise 'no Coverband store set' unless @store
43
53
 
44
- ###
45
- # Hack to prevent processes and threads from reporting first Coverage hit
46
- # when we are in runtime collection mode, which do not have a cache of previous
47
- # coverage to remove the initial stdlib Coverage loading data
48
- ###
49
- if ((original_previous_set.nil? && @store.type == Coverband::EAGER_TYPE) ||
50
- (original_previous_set && @store.type != Coverband::EAGER_TYPE))
54
+ files_with_line_usage = filtered_files(Delta.results)
51
55
  @store.save_report(files_with_line_usage)
52
56
  end
53
- rescue StandardError => err
57
+ rescue StandardError => e
54
58
  if @verbose
55
- @logger&.error 'coverage failed to store'
56
- @logger&.error "error: #{err.inspect} #{err.message}"
57
- @logger&.error err.backtrace
59
+ @logger.error 'coverage failed to store'
60
+ @logger.error "error: #{e.inspect} #{e.message}"
61
+ @logger.error e.backtrace
58
62
  end
59
- raise err if @test_env
63
+ raise e if @test_env
60
64
  end
61
65
 
62
66
  protected
@@ -78,30 +82,22 @@ module Coverband
78
82
  private
79
83
 
80
84
  def filtered_files(new_results)
81
- new_results.each_with_object({}) do |(file, line_counts), file_line_usage|
82
- file_line_usage[file] = line_counts if track_file?(file)
83
- end.select { |_file_name, coverage| coverage.any? { |value| value&.nonzero? } }
84
- end
85
-
86
- def ready_to_report?
87
- (rand * 100.0) >= (100.0 - @reporting_frequency)
85
+ new_results.select! { |file, coverage| track_file?(file) && coverage.any? { |value| value&.nonzero? } }
86
+ new_results
88
87
  end
89
88
 
90
89
  def initialize
91
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3.0')
92
- raise NotImplementedError, 'not supported until Ruby 2.3.0 and later'
93
- end
90
+ @semaphore = Mutex.new
91
+ raise NotImplementedError, 'Coverage needs Ruby > 2.3.0' if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3.0')
92
+
94
93
  require 'coverage'
95
- if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
94
+ if Coverage.ruby_version_greater_than_or_equal_to?('2.6.0')
95
+ ::Coverage.start(oneshot_lines: Coverband.configuration.use_oneshot_lines_coverage) unless ::Coverage.running?
96
+ elsif Coverage.ruby_version_greater_than_or_equal_to?('2.5.0')
96
97
  ::Coverage.start unless ::Coverage.running?
97
98
  else
98
99
  ::Coverage.start
99
100
  end
100
- if Coverband.configuration.safe_reload_files
101
- Coverband.configuration.safe_reload_files.each do |safe_file|
102
- load safe_file
103
- end
104
- end
105
101
  reset_instance
106
102
  end
107
103
  end
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Coverband
3
4
  module Collectors
4
5
  class Delta
5
- @semaphore = Mutex.new
6
- @@previous_coverage = nil
6
+ @@previous_coverage = {}
7
+ @@stubs = {}
8
+
7
9
  attr_reader :current_coverage
8
10
 
9
11
  def initialize(current_coverage)
@@ -12,54 +14,58 @@ module Coverband
12
14
 
13
15
  class RubyCoverage
14
16
  def self.results
15
- ::Coverage.peek_result.dup
17
+ if Coverband.configuration.use_oneshot_lines_coverage
18
+ ::Coverage.result(clear: true, stop: false)
19
+ else
20
+ ::Coverage.peek_result
21
+ end
16
22
  end
17
23
  end
18
24
 
19
25
  def self.results(process_coverage = RubyCoverage)
20
- @semaphore.synchronize do
21
- set_default_results
22
- new(process_coverage.results).results
23
- end
24
- end
25
-
26
- def self.previous_results
27
- @@previous_coverage
28
- end
29
-
30
- def self.set_default_results
31
- @@previous_coverage ||= {}
26
+ coverage_results = process_coverage.results
27
+ new(coverage_results).results
32
28
  end
33
29
 
34
30
  def results
35
- new_results = generate
36
- @@previous_coverage = current_coverage
37
- new_results
31
+ if Coverband.configuration.use_oneshot_lines_coverage
32
+ transform_oneshot_lines_results(current_coverage)
33
+ else
34
+ new_results = generate
35
+ @@previous_coverage = current_coverage unless Coverband.configuration.simulate_oneshot_lines_coverage
36
+ new_results
37
+ end
38
38
  end
39
39
 
40
40
  def self.reset
41
- @@previous_coverage = nil
41
+ @@previous_coverage = {}
42
42
  end
43
43
 
44
44
  private
45
45
 
46
46
  def generate
47
47
  current_coverage.each_with_object({}) do |(file, line_counts), new_results|
48
- if @@previous_coverage[file]
49
- new_results[file] = array_diff(line_counts, @@previous_coverage[file])
50
- else
51
- new_results[file] = line_counts
52
- end
48
+ new_results[file] = if @@previous_coverage && @@previous_coverage[file]
49
+ array_diff(line_counts, @@previous_coverage[file])
50
+ else
51
+ line_counts
52
+ end
53
53
  end
54
54
  end
55
55
 
56
56
  def array_diff(latest, original)
57
57
  latest.map.with_index do |v, i|
58
- if (v && original[i])
59
- [0, v - original[i]].max
60
- else
61
- nil
58
+ [0, v - original[i]].max if v && original[i]
59
+ end
60
+ end
61
+
62
+ def transform_oneshot_lines_results(results)
63
+ results.each_with_object({}) do |(file, coverage), new_results|
64
+ @@stubs[file] ||= ::Coverage.line_stub(file)
65
+ transformed_line_counts = coverage[:oneshot_lines].each_with_object(@@stubs[file].dup) do |line_number, line_counts|
66
+ line_counts[line_number - 1] = 1
62
67
  end
68
+ new_results[file] = transformed_line_counts
63
69
  end
64
70
  end
65
71
  end
@@ -3,15 +3,32 @@
3
3
  module Coverband
4
4
  class Configuration
5
5
  attr_accessor :root_paths, :root,
6
- :ignore, :additional_files, :verbose,
7
- :reporter, :reporting_frequency,
8
- :redis_namespace, :redis_ttl,
9
- :safe_reload_files, :background_reporting_enabled,
6
+ :additional_files, :verbose,
7
+ :reporter, :redis_namespace, :redis_ttl,
8
+ :background_reporting_enabled,
10
9
  :background_reporting_sleep_seconds, :test_env,
11
- :web_enable_clear, :gem_details, :web_debug
10
+ :web_enable_clear, :gem_details, :web_debug, :report_on_exit,
11
+ :simulate_oneshot_lines_coverage
12
12
 
13
- attr_writer :logger, :s3_region, :s3_bucket, :s3_access_key_id, :s3_secret_access_key
14
- attr_reader :track_gems
13
+ attr_writer :logger, :s3_region, :s3_bucket, :s3_access_key_id, :s3_secret_access_key, :password
14
+ attr_reader :track_gems, :ignore, :use_oneshot_lines_coverage
15
+
16
+ #####
17
+ # TODO: This is is brittle and not a great solution to avoid deploy time
18
+ # actions polluting the 'runtime' metrics
19
+ #
20
+ # * should we skip /bin/rails webpacker:compile ?
21
+ # * Perhaps detect heroku deployment ENV var opposed to tasks?
22
+ #####
23
+ IGNORE_TASKS = ['coverband:clear',
24
+ 'coverband:coverage',
25
+ 'coverband:coverage_server',
26
+ 'coverband:migrate']
27
+
28
+ # Heroku when building assets runs code from a dynamic directory
29
+ # /tmp was added to avoid coverage from /tmp/build directories during
30
+ # heroku asset compilation
31
+ IGNORE_DEFAULTS = %w[vendor .erb$ .slim$ /tmp internal:prelude schema.rb]
15
32
 
16
33
  def initialize
17
34
  reset
@@ -20,12 +37,8 @@ module Coverband
20
37
  def reset
21
38
  @root = Dir.pwd
22
39
  @root_paths = []
23
- # Heroku when building assets runs code from a dynamic directory
24
- # /tmp was added to avoid coverage from /tmp/build directories during
25
- # heroku asset compilation
26
- @ignore = %w[vendor .erb$ .slim$ /tmp]
40
+ @ignore = IGNORE_DEFAULTS.dup
27
41
  @additional_files = []
28
- @reporting_frequency = 0.0
29
42
  @verbose = false
30
43
  @reporter = 'scov'
31
44
  @logger = nil
@@ -38,6 +51,13 @@ module Coverband
38
51
  @gem_details = false
39
52
  @groups = {}
40
53
  @web_debug = false
54
+ @report_on_exit = true
55
+ @use_oneshot_lines_coverage = ENV['ONESHOT'] || false
56
+ @simulate_oneshot_lines_coverage = ENV['SIMULATE_ONESHOT'] || false
57
+ @current_root = nil
58
+ @all_root_paths = nil
59
+ @all_root_patterns = nil
60
+ @password = nil
41
61
 
42
62
  # TODO: should we push these to adapter configs
43
63
  @s3_region = nil
@@ -45,17 +65,21 @@ module Coverband
45
65
  @s3_access_key_id = nil
46
66
  @s3_secret_access_key = nil
47
67
  @redis_namespace = nil
48
- @redis_ttl = nil
68
+ @redis_ttl = 2_592_000 # in seconds. Default is 30 days.
49
69
  end
50
70
 
51
71
  def logger
52
- @logger ||= if defined?(Rails.logger)
72
+ @logger ||= if defined?(Rails.logger) && Rails.logger
53
73
  Rails.logger
54
74
  else
55
75
  Logger.new(STDOUT)
56
76
  end
57
77
  end
58
78
 
79
+ def password
80
+ @password || ENV['COVERBAND_PASSWORD']
81
+ end
82
+
59
83
  def s3_bucket
60
84
  @s3_bucket || ENV['AWS_BUCKET']
61
85
  end
@@ -77,29 +101,33 @@ module Coverband
77
101
  end
78
102
 
79
103
  def store=(store)
80
- if store.is_a?(Coverband::Adapters::Base)
81
- @store = store
82
- else
83
- raise 'please pass in an subclass of Coverband::Adapters for supported stores'
84
- end
104
+ raise 'Pass in an instance of Coverband::Adapters' unless store.is_a?(Coverband::Adapters::Base)
105
+
106
+ @store = store
107
+ end
108
+
109
+ ###
110
+ # Don't allow the ignore to override things like gem tracking
111
+ ###
112
+ def ignore=(ignored_array)
113
+ @ignore = (@ignore + ignored_array).uniq
85
114
  end
86
115
 
87
116
  def track_gems=(value)
88
117
  @track_gems = value
89
118
  return unless @track_gems
119
+
90
120
  # by default we ignore vendor where many deployments put gems
91
121
  # we will remove this default if track_gems is set
92
122
  @ignore.delete('vendor')
93
123
  # while we want to allow vendored gems we don't want to track vendored ruby STDLIB
94
- @ignore << 'vendor/ruby-*'
124
+ @ignore << 'vendor/ruby-*' unless @ignore.include?('vendor/ruby-*')
95
125
  add_group('App', root)
96
126
  # TODO: rework support for multiple gem paths
97
- # currently this supports GEM_HOME (which should be first path)
98
- # but various gem managers setup multiple gem paths
99
- # gem_paths.each_with_index do |path, index|
100
- # add_group("gems_#{index}", path)
101
- # end
102
- add_group('Gems', gem_paths.first)
127
+ # this works but seems hacky and error prone
128
+ # basically since it is converted to a regex we join all the paths
129
+ # with a regex 'OR' using '|'
130
+ add_group('Gems', gem_paths.join('|'))
103
131
  end
104
132
 
105
133
  #
@@ -125,17 +153,23 @@ module Coverband
125
153
  end
126
154
 
127
155
  def current_root
128
- File.expand_path(Coverband.configuration.root)
156
+ @current_root ||= File.expand_path(Coverband.configuration.root).freeze
129
157
  end
130
158
 
131
159
  def all_root_paths
132
- roots = Coverband.configuration.root_paths.dup
133
- roots += Coverband.configuration.gem_paths.dup if Coverband.configuration.track_gems
134
- roots << "#{Coverband.configuration.current_root}/"
135
- roots
160
+ return @all_root_paths if @all_root_paths
161
+
162
+ @all_root_paths = Coverband.configuration.root_paths.dup
163
+ @all_root_paths += Coverband.configuration.gem_paths.dup if Coverband.configuration.track_gems
164
+ @all_root_paths << "#{Coverband.configuration.current_root}/"
165
+ @all_root_paths
166
+ end
167
+
168
+ def all_root_patterns
169
+ @all_root_patterns ||= all_root_paths.map { |path| /^#{path}/ }.freeze
136
170
  end
137
171
 
138
- SKIPPED_SETTINGS = %w(@s3_secret_access_key @store)
172
+ SKIPPED_SETTINGS = %w[@s3_secret_access_key @store]
139
173
  def to_h
140
174
  instance_variables
141
175
  .each_with_object('gem_paths': gem_paths) do |var, hash|
@@ -143,6 +177,16 @@ module Coverband
143
177
  end
144
178
  end
145
179
 
180
+ def use_oneshot_lines_coverage=(value)
181
+ raise(Exception, 'One shot line coverage is only available in ruby >= 2.6') unless one_shot_coverage_implemented_in_ruby_version? || !value
182
+
183
+ @use_oneshot_lines_coverage = value
184
+ end
185
+
186
+ def one_shot_coverage_implemented_in_ruby_version?
187
+ Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6.0')
188
+ end
189
+
146
190
  private
147
191
 
148
192
  def redis_url
@@ -7,6 +7,7 @@ module Coverband
7
7
 
8
8
  def self.stop
9
9
  return unless @thread
10
+
10
11
  @semaphore.synchronize do
11
12
  if @thread
12
13
  @thread.exit
@@ -16,7 +17,7 @@ module Coverband
16
17
  end
17
18
 
18
19
  def self.running?
19
- @thread && @thread.alive?
20
+ @thread&.alive?
20
21
  end
21
22
 
22
23
  def self.start
@@ -25,12 +26,15 @@ module Coverband
25
26
  logger = Coverband.configuration.logger
26
27
  @semaphore.synchronize do
27
28
  return if running?
28
- logger&.debug('Coverband: Starting background reporting')
29
+
30
+ logger.debug('Coverband: Starting background reporting')
29
31
  sleep_seconds = Coverband.configuration.background_reporting_sleep_seconds
30
32
  @thread = Thread.new do
31
33
  loop do
32
- Coverband.report_coverage(true)
33
- logger&.debug("Coverband: Reported coverage via thread. Sleeping #{sleep_seconds}s") if Coverband.configuration.verbose
34
+ Coverband.report_coverage
35
+ if Coverband.configuration.verbose
36
+ logger.debug("Coverband: background reporting coverage (#{Coverband.configuration.store.type}). Sleeping #{sleep_seconds}s")
37
+ end
34
38
  sleep(sleep_seconds)
35
39
  end
36
40
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Coverband
4
- class Middleware
4
+ class BackgroundMiddleware
5
5
  def initialize(app)
6
6
  @app = app
7
7
  end
@@ -10,11 +10,7 @@ module Coverband
10
10
  @app.call(env)
11
11
  ensure
12
12
  AtExit.register
13
- if Coverband.configuration.background_reporting_enabled
14
- Background.start
15
- else
16
- Collectors::Coverage.instance.report_coverage
17
- end
13
+ Background.start if Coverband.configuration.background_reporting_enabled
18
14
  end
19
15
  end
20
16
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BundlerEagerLoad
4
+ def require(*groups)
5
+ Coverband.eager_loading_coverage { super }
6
+ end
7
+ end
8
+ Bundler.singleton_class.prepend BundlerEagerLoad
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coverband
4
+ class ReportMiddleware
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ @app.call(env)
11
+ ensure
12
+ Collectors::Coverage.instance.report_coverage
13
+ end
14
+ end
15
+ end
@@ -1,17 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Resque.after_fork do |job|
3
+ Resque.after_fork do |_job|
4
4
  Coverband.start
5
5
  Coverband.runtime_coverage!
6
- # no reason to miss coverage on a first resque job
7
- Coverband::Collectors::Delta.set_default_results
8
6
  end
9
7
 
10
8
  Resque.before_first_fork do
11
9
  Coverband.eager_loading_coverage!
12
10
  Coverband.configuration.background_reporting_enabled = false
13
11
  Coverband::Background.stop
14
- Coverband::Collectors::Coverage.instance.report_coverage(true)
12
+ Coverband.report_coverage
15
13
  end
16
14
 
17
15
  module Coverband
@@ -19,7 +17,7 @@ module Coverband
19
17
  def perform
20
18
  super
21
19
  ensure
22
- Coverband.report_coverage(true)
20
+ Coverband.report_coverage
23
21
  end
24
22
  end
25
23
  end