coverband 4.2.0 → 4.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +29 -10
  3. data/.travis.yml +16 -8
  4. data/CODE_OF_CONDUCT.md +76 -0
  5. data/Gemfile +3 -1
  6. data/Gemfile.rails4 +1 -0
  7. data/README.md +108 -59
  8. data/Rakefile +19 -3
  9. data/changes.md +85 -10
  10. data/coverband.gemspec +3 -5
  11. data/lib/coverband/adapters/base.rb +64 -45
  12. data/lib/coverband/adapters/file_store.rb +15 -9
  13. data/lib/coverband/adapters/hash_redis_store.rb +190 -0
  14. data/lib/coverband/adapters/redis_store.rb +35 -11
  15. data/lib/coverband/at_exit.rb +2 -11
  16. data/lib/coverband/collectors/coverage.rb +35 -41
  17. data/lib/coverband/collectors/delta.rb +34 -28
  18. data/lib/coverband/collectors/view_tracker.rb +157 -0
  19. data/lib/coverband/configuration.rb +115 -33
  20. data/lib/coverband/integrations/background.rb +12 -4
  21. data/lib/coverband/integrations/{middleware.rb → background_middleware.rb} +2 -6
  22. data/lib/coverband/integrations/bundler.rb +8 -0
  23. data/lib/coverband/integrations/report_middleware.rb +15 -0
  24. data/lib/coverband/integrations/resque.rb +3 -5
  25. data/lib/coverband/reporters/base.rb +40 -16
  26. data/lib/coverband/reporters/console_report.rb +1 -0
  27. data/lib/coverband/reporters/html_report.rb +31 -27
  28. data/lib/coverband/reporters/web.rb +63 -18
  29. data/lib/coverband/utils/absolute_file_converter.rb +47 -0
  30. data/lib/coverband/utils/file_hasher.rb +16 -0
  31. data/lib/coverband/utils/file_list.rb +16 -5
  32. data/lib/coverband/utils/html_formatter.rb +45 -8
  33. data/lib/coverband/utils/lines_classifier.rb +5 -0
  34. data/lib/coverband/utils/railtie.rb +28 -10
  35. data/lib/coverband/utils/relative_file_converter.rb +41 -0
  36. data/lib/coverband/utils/result.rb +6 -39
  37. data/lib/coverband/utils/results.rb +51 -0
  38. data/lib/coverband/utils/s3_report.rb +2 -3
  39. data/lib/coverband/utils/source_file.rb +34 -8
  40. data/lib/coverband/utils/tasks.rb +10 -10
  41. data/lib/coverband/version.rb +5 -1
  42. data/lib/coverband.rb +49 -27
  43. data/lua/install.sh +15 -0
  44. data/lua/lib/persist-coverage.lua +28 -0
  45. data/lua/test/bootstrap.lua +5 -0
  46. data/lua/test/harness.lua +19 -0
  47. data/lua/test/redis-call.lua +55 -0
  48. data/lua/test/test-persist-coverage.lua +132 -0
  49. data/public/application.css +14 -6
  50. data/public/application.js +27 -0
  51. data/test/benchmarks/benchmark.rake +167 -73
  52. data/test/benchmarks/init_rails.rake +10 -0
  53. data/test/coverband/adapters/base_test.rb +73 -42
  54. data/test/coverband/adapters/file_store_test.rb +48 -37
  55. data/test/coverband/adapters/hash_redis_store_test.rb +195 -0
  56. data/test/coverband/adapters/redis_store_test.rb +102 -59
  57. data/test/coverband/at_exit_test.rb +0 -2
  58. data/test/coverband/collectors/coverage_test.rb +71 -13
  59. data/test/coverband/collectors/delta_test.rb +36 -6
  60. data/test/coverband/collectors/view_tracker_test.rb +113 -0
  61. data/test/coverband/configuration_test.rb +47 -7
  62. data/test/coverband/coverband_test.rb +14 -2
  63. data/test/coverband/integrations/background_middleware_test.rb +44 -0
  64. data/test/coverband/integrations/background_test.rb +11 -3
  65. data/test/coverband/integrations/report_middleware_test.rb +44 -0
  66. data/test/coverband/integrations/resque_worker_test.rb +5 -6
  67. data/test/coverband/integrations/test_resque_job.rb +3 -1
  68. data/test/coverband/reporters/base_test.rb +5 -81
  69. data/test/coverband/reporters/console_test.rb +3 -10
  70. data/test/coverband/reporters/html_test.rb +71 -21
  71. data/test/coverband/reporters/web_test.rb +0 -10
  72. data/test/coverband/utils/absolute_file_converter_test.rb +56 -0
  73. data/test/coverband/utils/file_groups_test.rb +11 -5
  74. data/test/coverband/utils/file_hasher_test.rb +29 -0
  75. data/test/coverband/utils/file_list_test.rb +5 -5
  76. data/test/coverband/utils/html_formatter_test.rb +41 -0
  77. data/test/coverband/utils/relative_file_converter_test.rb +39 -0
  78. data/test/coverband/utils/result_test.rb +6 -47
  79. data/test/coverband/utils/results_test.rb +54 -0
  80. data/test/coverband/utils/s3_report_test.rb +2 -0
  81. data/test/coverband/utils/source_file_test.rb +54 -0
  82. data/test/dog.rb.erb +12 -0
  83. data/test/forked/rails_full_stack_test.rb +106 -0
  84. data/test/forked/rails_rake_full_stack_test.rb +58 -0
  85. data/test/integration/full_stack_test.rb +15 -16
  86. data/test/rails4_dummy/Rakefile +6 -0
  87. data/test/rails4_dummy/config/application.rb +8 -9
  88. data/test/rails4_dummy/config/coverband.rb +3 -11
  89. data/test/rails4_dummy/config/coverband_missing_redis.rb +3 -0
  90. data/test/rails5_dummy/Rakefile +6 -0
  91. data/test/rails5_dummy/config/application.rb +6 -10
  92. data/test/rails5_dummy/config/coverband.rb +7 -3
  93. data/test/rails5_dummy/config/coverband_missing_redis.rb +15 -0
  94. data/test/rails_test_helper.rb +22 -5
  95. data/test/test_helper.rb +52 -11
  96. data/test/unique_files.rb +17 -9
  97. data/views/data.erb +1 -0
  98. data/views/file_list.erb +13 -12
  99. data/views/gem_list.erb +10 -1
  100. data/views/layout.erb +6 -18
  101. data/views/nav.erb +36 -0
  102. data/views/source_file.erb +20 -4
  103. data/views/source_file_loader.erb +1 -1
  104. data/views/view_tracker.erb +52 -0
  105. metadata +71 -12
  106. data/lib/coverband/utils/file_path_helper.rb +0 -57
  107. data/test/coverband/integrations/middleware_test.rb +0 -96
  108. data/test/integration/rails_full_stack_test.rb +0 -95
@@ -1,21 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Coverband.eager_loading_coverage!
4
3
  module Coverband
4
+ module RailsEagerLoad
5
+ def eager_load!
6
+ Coverband.eager_loading_coverage!
7
+ super
8
+ end
9
+ end
10
+ Rails::Engine.prepend(RailsEagerLoad)
11
+
5
12
  class Railtie < Rails::Railtie
6
13
  initializer 'coverband.configure' do |app|
7
- app.middleware.use Coverband::Middleware
8
- end
14
+ begin
15
+ app.middleware.use Coverband::BackgroundMiddleware
9
16
 
10
- config.after_initialize do
11
- Coverband.report_coverage(true)
12
- Coverband.configuration.logger&.debug('Coverband: reported after_initialize')
13
- Coverband.runtime_coverage!
17
+ if Coverband.configuration.track_views
18
+ CoverbandViewTracker = Coverband::Collectors::ViewTracker.new
19
+ Coverband.configuration.view_tracker = CoverbandViewTracker
20
+
21
+ ActiveSupport::Notifications.subscribe(/render_partial.action_view|render_template.action_view/) do |name, start, finish, id, payload|
22
+ CoverbandViewTracker.track_views(name, start, finish, id, payload) unless name.include?('!')
23
+ end
24
+ end
25
+ rescue Redis::CannotConnectError => error
26
+ Coverband.configuration.logger.info "Redis is not available (#{error}), Coverband not configured"
27
+ Coverband.configuration.logger.info 'If this is a setup task like assets:precompile feel free to ignore'
28
+ end
14
29
  end
15
30
 
16
- config.before_initialize do
17
- Coverband.configuration.logger&.debug('Coverband: set to eager_loading')
18
- Coverband.eager_loading_coverage!
31
+ config.after_initialize do
32
+ unless Coverband.tasks_to_ignore?
33
+ Coverband.eager_loading_coverage!
34
+ Coverband.report_coverage
35
+ Coverband.runtime_coverage!
36
+ end
19
37
  end
20
38
 
21
39
  rake_tasks do
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coverband
4
+ module Utils
5
+ class RelativeFileConverter
6
+ def self.instance
7
+ @instance ||= new(Coverband.configuration.all_root_paths)
8
+ end
9
+
10
+ def self.reset
11
+ @instance = nil
12
+ end
13
+
14
+ def self.convert(file)
15
+ instance.convert(file)
16
+ end
17
+
18
+ def initialize(roots)
19
+ @cache = {}
20
+ @roots = normalize(roots)
21
+ end
22
+
23
+ def convert(file)
24
+ @cache[file] ||= begin
25
+ relative_file = file
26
+ @roots.each do |root|
27
+ relative_file = file.gsub(/^#{root}/, '.')
28
+ break relative_file if relative_file.start_with?('.')
29
+ end
30
+ relative_file
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def normalize(paths)
37
+ paths.map { |root| File.expand_path(root) }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -22,11 +22,8 @@ module Coverband
22
22
  alias source_files files
23
23
  # Explicitly set the Time this result has been created
24
24
  attr_writer :created_at
25
- # Explicitly set the command name that was used for this coverage result.
26
- # Defaults to Coverband.command_name
27
- attr_writer :command_name
28
25
 
29
- def_delegators :files, :covered_percent, :covered_percentages, :least_covered_file, :covered_strength, :covered_lines, :missed_lines
26
+ def_delegators :files, :covered_percent, :covered_percentages, :covered_strength, :covered_lines, :missed_lines
30
27
  def_delegator :files, :lines_of_code, :total_lines
31
28
 
32
29
  # Initialize a new Coverband::Result from given Coverage.result (a Hash of filenames each containing an array of
@@ -37,7 +34,6 @@ module Coverband
37
34
  @files = Coverband::Utils::FileList.new(@original_result.map do |filename, coverage|
38
35
  Coverband::Utils::SourceFile.new(filename, coverage) if File.file?(filename)
39
36
  end.compact.sort_by(&:short_name))
40
- filter!
41
37
  end
42
38
 
43
39
  # Returns all filenames for source files contained in this result
@@ -55,52 +51,23 @@ module Coverband
55
51
  @created_at ||= Time.now
56
52
  end
57
53
 
58
- # The command name that launched this result.
59
- # Delegated to Coverband.command_name if not set manually
60
- def command_name
61
- @command_name ||= 'Coverband'
62
- end
63
-
64
- # Returns a hash representation of this Result that can be used for marshalling it into JSON
65
- def to_hash
66
- { command_name => { 'coverage' => coverage, 'timestamp' => created_at.to_i } }
67
- end
68
-
69
- # Loads a Coverband::Result#to_hash dump
70
- def self.from_hash(hash)
71
- command_name, data = hash.first
72
- result = new(data['coverage'])
73
- result.command_name = command_name
74
- result.created_at = Time.at(data['timestamp'])
75
- result
76
- end
77
-
78
54
  # Finds files that were to be tracked but were not loaded and initializes
79
55
  # the line-by-line coverage to zero (if relevant) or nil (comments / whitespace etc).
80
56
  def self.add_not_loaded_files(result, tracked_files)
81
57
  if tracked_files
58
+ # TODO: Can we get rid of this dup it wastes memory
82
59
  result = result.dup
83
60
  Dir[tracked_files].each do |file|
84
61
  absolute = File.expand_path(file)
85
-
86
- result[absolute] ||= Coverband::Utils::LinesClassifier.new.classify(File.foreach(absolute))
62
+ result[absolute] ||= {
63
+ 'data' => Array.new(File.foreach(absolute).count) { 0 },
64
+ 'never_loaded' => true
65
+ }
87
66
  end
88
67
  end
89
68
 
90
69
  result
91
70
  end
92
-
93
- private
94
-
95
- def coverage
96
- keys = original_result.keys & filenames
97
- Hash[keys.zip(original_result.values_at(*keys))]
98
- end
99
-
100
- # Applies all configured Coverband filters on this result's source files
101
- def filter!
102
- @files = files
103
- end
104
71
  end
105
72
  end
106
73
  end
@@ -20,6 +20,41 @@ module Coverband
20
20
  get_results(results_type).source_files.find { |file| file.filename == source_file.filename }
21
21
  end
22
22
 
23
+ def runtime_relevant_coverage(source_file)
24
+ return unless eager_loading_coverage && runtime_coverage
25
+
26
+ eager_file = get_eager_file(source_file)
27
+ runtime_file = get_runtime_file(source_file)
28
+
29
+ return 0.0 unless runtime_file
30
+
31
+ return runtime_file.formatted_covered_percent unless eager_file
32
+
33
+ runtime_relavant_lines = eager_file.relevant_lines - eager_file.covered_lines_count
34
+ runtime_file.runtime_relavant_calculations(runtime_relavant_lines) { |file| file.formatted_covered_percent }
35
+ end
36
+
37
+ def runtime_relavent_lines(source_file)
38
+ return 0 unless runtime_coverage
39
+
40
+ eager_file = get_eager_file(source_file)
41
+ runtime_file = get_runtime_file(source_file)
42
+
43
+ return runtime_file.covered_lines_count unless eager_file
44
+
45
+ eager_file.relevant_lines - eager_file.covered_lines_count
46
+ end
47
+
48
+ ###
49
+ # TODO: Groups still have some issues, this should be generic for groups, but right now gem_name
50
+ # is specifically called out, need to revisit all gorups code.
51
+ ###
52
+ def group_file_list_with_type(group, file_list, results_type)
53
+ return unless get_results(results_type)
54
+
55
+ get_results(results_type).groups[group].find { |gem_files| gem_files.first.gem_name == file_list.first.gem_name }
56
+ end
57
+
23
58
  def file_from_path_with_type(full_path, results_type = :merged)
24
59
  return unless get_results(results_type)
25
60
 
@@ -44,6 +79,22 @@ module Coverband
44
79
 
45
80
  private
46
81
 
82
+ def get_eager_file(source_file)
83
+ eager_loading_coverage.source_files.find { |file| file.filename == source_file.filename }
84
+ end
85
+
86
+ def get_runtime_file(source_file)
87
+ runtime_coverage.source_files.find { |file| file.filename == source_file.filename }
88
+ end
89
+
90
+ def eager_loading_coverage
91
+ get_results(Coverband::EAGER_TYPE)
92
+ end
93
+
94
+ def runtime_coverage
95
+ get_results(Coverband::RUNTIME_TYPE)
96
+ end
97
+
47
98
  ###
48
99
  # This is a first version of lazy loading the results
49
100
  # for the full advantage we need to push lazy loading to the file level
@@ -26,9 +26,8 @@ module Coverband
26
26
  begin
27
27
  require 'aws-sdk-s3'
28
28
  rescue StandardError
29
- err_msg = 'coverband requires aws-sdk in order use S3Report.'
30
- Coverband.configuration.logger.error err_msg
31
- return
29
+ err_msg = 'Coverband requires aws-sdk in order use S3Report.'
30
+ Coverband.configuration.logger&.error err_msg
32
31
  end
33
32
  end
34
33
 
@@ -10,7 +10,7 @@
10
10
  module Coverband
11
11
  module Utils
12
12
  class SourceFile
13
- include Coverband::Utils::FilePathHelper
13
+ # TODO: Refactor Line into its own file
14
14
  # Representation of a single line in a source file including
15
15
  # this specific line's source code, line_number and code coverage,
16
16
  # with the coverage being either nil (coverage not applicable, e.g. comment
@@ -35,6 +35,7 @@ module Coverband
35
35
  raise ArgumentError, 'Only String accepted for source' unless src.is_a?(String)
36
36
  raise ArgumentError, 'Only Integer accepted for line_number' unless line_number.is_a?(Integer)
37
37
  raise ArgumentError, 'Only Integer and nil accepted for coverage' unless coverage.is_a?(Integer) || coverage.nil?
38
+
38
39
  @src = src
39
40
  @line_number = line_number
40
41
  @coverage = coverage
@@ -48,7 +49,7 @@ module Coverband
48
49
 
49
50
  # Returns true if this is a line that has been covered
50
51
  def covered?
51
- !never? && !skipped? && coverage > 0
52
+ !never? && !skipped? && coverage.positive?
52
53
  end
53
54
 
54
55
  # Returns true if this line is not relevant for coverage
@@ -64,7 +65,7 @@ module Coverband
64
65
  # Returns true if this line was skipped, false otherwise. Lines are skipped if they are wrapped with
65
66
  # # :nocov: comment lines.
66
67
  def skipped?
67
- !!skipped
68
+ skipped
68
69
  end
69
70
 
70
71
  # The status of this line - either covered, missed, skipped or never. Useful i.e. for direct use
@@ -86,22 +87,34 @@ module Coverband
86
87
  attr_reader :first_updated_at
87
88
  # the date this version of the file last saw any coverage activity
88
89
  attr_reader :last_updated_at
90
+ # meta data that the file was never loaded during boot or runtime
91
+ attr_reader :never_loaded
89
92
  NOT_AVAILABLE = 'not available'
90
93
 
91
94
  def initialize(filename, file_data)
92
95
  @filename = filename
96
+ @runtime_relavant_lines = nil
93
97
  if file_data.is_a?(Hash)
94
98
  @coverage = file_data['data']
95
99
  @first_updated_at = @last_updated_at = NOT_AVAILABLE
96
100
  @first_updated_at = Time.at(file_data['first_updated_at']) if file_data['first_updated_at']
97
101
  @last_updated_at = Time.at(file_data['last_updated_at']) if file_data['last_updated_at']
102
+ @never_loaded = file_data['never_loaded'] || false
98
103
  else
104
+ # TODO: Deprecate this code path this was backwards compatability from 3-4
99
105
  @coverage = file_data
100
106
  @first_updated_at = NOT_AVAILABLE
101
107
  @last_updated_at = NOT_AVAILABLE
102
108
  end
103
109
  end
104
110
 
111
+ def runtime_relavant_calculations(runtime_relavant_lines)
112
+ @runtime_relavant_lines = runtime_relavant_lines
113
+ yield self
114
+ ensure
115
+ @runtime_relavant_lines = nil
116
+ end
117
+
105
118
  # The path to this source file relative to the projects directory
106
119
  def project_filename
107
120
  @filename.sub(/^#{Coverband.configuration.root}/, '')
@@ -148,7 +161,12 @@ module Coverband
148
161
 
149
162
  return 0.0 if relevant_lines.zero?
150
163
 
151
- Float(covered_lines.size * 100.0 / relevant_lines.to_f)
164
+ # handle edge case where runtime in dev can go over 100%
165
+ [Float(covered_lines.size * 100.0 / relevant_lines.to_f), 100.0].min
166
+ end
167
+
168
+ def formatted_covered_percent
169
+ covered_percent&.round(2)
152
170
  end
153
171
 
154
172
  def covered_strength
@@ -166,7 +184,7 @@ module Coverband
166
184
  end
167
185
 
168
186
  def relevant_lines
169
- lines.size - never_lines.size - skipped_lines.size
187
+ @runtime_relavant_lines || (lines.size - never_lines.size - skipped_lines.size)
170
188
  end
171
189
 
172
190
  # Returns all covered lines as SimpleCov::SourceFile::Line
@@ -174,6 +192,14 @@ module Coverband
174
192
  @covered_lines ||= lines.select(&:covered?)
175
193
  end
176
194
 
195
+ def covered_lines_count
196
+ covered_lines&.count
197
+ end
198
+
199
+ def line_coverage(index)
200
+ lines[index]&.coverage
201
+ end
202
+
177
203
  # Returns all lines that should have been, but were not covered
178
204
  # as instances of SimpleCov::SourceFile::Line
179
205
  def missed_lines
@@ -216,12 +242,12 @@ module Coverband
216
242
  # I had previously patched this in my local Rails app
217
243
  def short_name
218
244
  filename.sub(/^#{Coverband.configuration.root}/, '.')
219
- .sub(%r{^.*\/gems}, '.')
245
+ .sub(%r{^.*\/gems\/}, '.')
220
246
  .gsub(%r{^\.\/}, '')
221
247
  end
222
248
 
223
249
  def relative_path
224
- full_path_to_relative(filename)
250
+ RelativeFileConverter.convert(filename)
225
251
  end
226
252
 
227
253
  def gem?
@@ -229,7 +255,7 @@ module Coverband
229
255
  end
230
256
 
231
257
  def gem_name
232
- gem? ? short_name.split('/').first : nil
258
+ gem? ? short_name.split('/').first.gsub(%r{^\.}, '') : nil
233
259
  end
234
260
 
235
261
  private
@@ -1,37 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  namespace :coverband do
4
- Coverband.configure
5
-
6
- def environment
7
- Rake.application['environment'].invoke if Rake::Task.task_defined?('environment')
8
- end
4
+ # handles configuring in require => false and COVERBAND_DISABLE_AUTO_START cases
5
+ Coverband.configure unless Coverband.configured?
9
6
 
10
7
  desc 'report runtime Coverband code coverage'
11
8
  task :coverage do
12
- environment
13
9
  if Coverband.configuration.reporter == 'scov'
14
- Coverband::Reporters::HTMLReport.report(Coverband.configuration.store)
10
+ Coverband::Reporters::HTMLReport.new(Coverband.configuration.store).report
15
11
  else
16
12
  Coverband::Reporters::ConsoleReport.report(Coverband.configuration.store)
17
13
  end
18
14
  end
19
15
 
16
+ desc 'report runtime Coverband code coverage'
17
+ task :coverage_server do
18
+ Rake.application['environment'].invoke if Rake::Task.task_defined?('environment')
19
+ Rack::Server.start app: Coverband::Reporters::Web.new, Port: ENV.fetch('COVERBAND_COVERAGE_PORT', 1022).to_i
20
+ end
21
+
20
22
  ###
21
23
  # clear data helpful for development or after configuration issues
22
24
  ###
23
25
  desc 'reset Coverband coverage data, helpful for development, debugging, etc'
24
26
  task :clear do
25
- environment
26
27
  Coverband.configuration.store.clear!
27
28
  end
28
29
 
29
30
  ###
30
- # clear data helpful for development or after configuration issues
31
+ # Updates the data in the coverband store from one format to another
31
32
  ###
32
33
  desc 'upgrade previous Coverband datastore to latest format'
33
34
  task :migrate do
34
- environment
35
35
  Coverband.configuration.store.migrate!
36
36
  end
37
37
  end
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ###
4
+ # ensure we properly do release candidate versioning; https://github.com/danmayer/coverband/issues/288
5
+ # use format '4.2.1.rc.1' ~> 4.2.1.rc to prerelease versions like v4.2.1.rc.2 and v4.2.1.rc.3
6
+ ###
3
7
  module Coverband
4
- VERSION = '4.2.0'
8
+ VERSION = '4.2.4'
5
9
  end
data/lib/coverband.rb CHANGED
@@ -6,10 +6,13 @@ require 'redis'
6
6
  require 'coverband/version'
7
7
  require 'coverband/at_exit'
8
8
  require 'coverband/configuration'
9
- require 'coverband/utils/file_path_helper'
9
+ require 'coverband/utils/relative_file_converter'
10
+ require 'coverband/utils/absolute_file_converter'
10
11
  require 'coverband/adapters/base'
11
12
  require 'coverband/adapters/redis_store'
13
+ require 'coverband/adapters/hash_redis_store'
12
14
  require 'coverband/adapters/file_store'
15
+ require 'coverband/utils/file_hasher'
13
16
  require 'coverband/utils/s3_report'
14
17
  require 'coverband/utils/html_formatter'
15
18
  require 'coverband/utils/result'
@@ -20,27 +23,26 @@ require 'coverband/utils/file_groups'
20
23
  require 'coverband/utils/lines_classifier'
21
24
  require 'coverband/utils/results'
22
25
  require 'coverband/collectors/coverage'
26
+ require 'coverband/collectors/view_tracker'
23
27
  require 'coverband/reporters/base'
24
28
  require 'coverband/reporters/html_report'
25
29
  require 'coverband/reporters/console_report'
26
- require 'coverband/integrations/background'
27
- require 'coverband/integrations/rack_server_check'
28
30
  require 'coverband/reporters/web'
29
- require 'coverband/integrations/middleware'
30
31
  require 'coverband/integrations/background'
32
+ require 'coverband/integrations/background_middleware'
33
+ require 'coverband/integrations/rack_server_check'
34
+
35
+ Coverband::Adapters::RedisStore = Coverband::Adapters::HashRedisStore if ENV['COVERBAND_HASH_REDIS_STORE']
31
36
 
32
37
  module Coverband
38
+ @@configured = false
33
39
  CONFIG_FILE = './config/coverband.rb'
34
- RUNTIME_TYPE = nil
40
+ RUNTIME_TYPE = :runtime
35
41
  EAGER_TYPE = :eager_loading
36
42
  MERGED_TYPE = :merged
37
43
  TYPES = [RUNTIME_TYPE, EAGER_TYPE]
38
44
  ALL_TYPES = TYPES + [:merged]
39
45
 
40
- class << self
41
- attr_accessor :configuration_data
42
- end
43
-
44
46
  def self.configure(file = nil)
45
47
  configuration_file = file || ENV.fetch('COVERBAND_CONFIG', CONFIG_FILE)
46
48
  configuration
@@ -49,45 +51,65 @@ module Coverband
49
51
  elsif File.exist?(configuration_file)
50
52
  load configuration_file
51
53
  else
52
- configuration.logger&.debug('using default configuration')
54
+ configuration.logger.debug('using default configuration')
53
55
  end
54
- coverage.reset_instance
56
+ @@configured = true
57
+ coverage_instance.reset_instance
58
+ end
59
+
60
+ def self.configured?
61
+ @@configured
55
62
  end
56
63
 
57
- def self.report_coverage(force_report = false)
58
- Coverband::Collectors::Coverage.instance.report_coverage(force_report)
64
+ def self.report_coverage
65
+ coverage_instance.report_coverage
59
66
  end
60
67
 
61
68
  def self.configuration
62
- self.configuration_data ||= Configuration.new
69
+ @configuration ||= Configuration.new
63
70
  end
64
71
 
65
72
  def self.start
66
73
  Coverband::Collectors::Coverage.instance
67
- # TODO Railtie sets up at_exit after forks, via middleware, perhaps this hsould be
74
+ # TODO: Railtie sets up at_exit after forks, via middleware, perhaps this should be
68
75
  # added if not rails or if rails but not rackserverrunning
69
- AtExit.register
70
- Background.start if configuration.background_reporting_enabled && !RackServerCheck.running?
76
+ AtExit.register unless tasks_to_ignore?
77
+ Background.start if configuration.background_reporting_enabled && !RackServerCheck.running? && !tasks_to_ignore?
78
+ end
79
+
80
+ def self.tasks_to_ignore?
81
+ (defined?(Rake) &&
82
+ Rake.respond_to?(:application) &&
83
+ (Rake&.application&.top_level_tasks || []).any? { |task| Coverband::Configuration::IGNORE_TASKS.include?(task) })
71
84
  end
72
85
 
73
86
  def self.eager_loading_coverage!
74
- coverage.eager_loading!
87
+ coverage_instance.eager_loading!
88
+ end
89
+
90
+ def self.eager_loading_coverage(&block)
91
+ coverage_instance.eager_loading(&block)
75
92
  end
76
93
 
77
94
  def self.runtime_coverage!
78
- coverage.runtime!
95
+ coverage_instance.runtime!
79
96
  end
80
97
 
81
- def self.coverage
98
+ private_class_method def self.coverage_instance
82
99
  Coverband::Collectors::Coverage.instance
83
100
  end
84
-
85
101
  unless ENV['COVERBAND_DISABLE_AUTO_START']
86
- # Coverband should be setup as early as possible
87
- # to capture usage of things loaded by initializers or other Rails engines
88
- configure
89
- start
90
- require 'coverband/utils/railtie' if defined? ::Rails::Railtie
91
- require 'coverband/integrations/resque' if defined? Resque
102
+ begin
103
+ # Coverband should be setup as early as possible
104
+ # to capture usage of things loaded by initializers or other Rails engines
105
+ configure
106
+ start
107
+ require 'coverband/utils/railtie' if defined? ::Rails::Railtie
108
+ require 'coverband/integrations/resque' if defined? ::Resque
109
+ require 'coverband/integrations/bundler' if defined? ::Bundler
110
+ rescue Redis::CannotConnectError => error
111
+ Coverband.configuration.logger.info "Redis is not available (#{error}), Coverband not configured"
112
+ Coverband.configuration.logger.info 'If this is a setup task like assets:precompile feel free to ignore'
113
+ end
92
114
  end
93
115
  end
data/lua/install.sh ADDED
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ LUA_DIR="$HOME/lua51"
4
+ LUA="$LUA_DIR/bin/lua"
5
+
6
+ if [ ! -f $LUA ]; then
7
+ echo "Installing lua"
8
+ pip install hererocks
9
+ hererocks $LUA_DIR -l5.1 -rlatest
10
+ fi
11
+ source $LUA_DIR/bin/activate
12
+ lua -v
13
+ for i in luacov busted redis-lua inspect lua-cjson; do
14
+ luarocks install $i;
15
+ done
@@ -0,0 +1,28 @@
1
+ local hmset = function (key, dict)
2
+ if next(dict) == nil then return nil end
3
+ local bulk = {}
4
+ for k, v in pairs(dict) do
5
+ table.insert(bulk, k)
6
+ table.insert(bulk, v)
7
+ end
8
+ return redis.call('HMSET', key, unpack(bulk))
9
+ end
10
+ local payload = cjson.decode(redis.call('get', (KEYS[1])))
11
+ local ttl = payload.ttl
12
+ local files_data = payload.files_data
13
+ redis.call('DEL', KEYS[1])
14
+ for _, file_data in ipairs(files_data) do
15
+
16
+ local hash_key = file_data.hash_key
17
+ local first_updated_at = file_data.meta.first_updated_at
18
+ file_data.meta.first_updated_at = nil
19
+
20
+ hmset(hash_key, file_data.meta)
21
+ redis.call('HSETNX', hash_key, 'first_updated_at', first_updated_at)
22
+ for line, coverage in pairs(file_data.coverage) do
23
+ redis.call("HINCRBY", hash_key, line, coverage)
24
+ end
25
+ if ttl and ttl ~= cjson.null then
26
+ redis.call("EXPIRE", hash_key, ttl)
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ inspect = require "inspect"
2
+ cjson = require 'cjson'
3
+ require './lua/test/redis-call'
4
+
5
+
@@ -0,0 +1,19 @@
1
+ require './lua/test/bootstrap'
2
+
3
+
4
+ KEYS = {}
5
+ ARGV = {}
6
+
7
+
8
+ function call_redis_script(script, keys, argv)
9
+ -- This may not be strictly necessary
10
+ for k,v in pairs(ARGV) do ARGV[k] = nil end
11
+ for k,v in pairs(KEYS) do KEYS[k] = nil end
12
+
13
+ for k,v in pairs(keys) do table.insert(KEYS, v) end
14
+ for k,v in pairs(argv) do table.insert(ARGV, v) end
15
+
16
+ return dofile('./lua/lib/' .. script)
17
+ end
18
+
19
+ return call_redis_script;