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.
- checksums.yaml +4 -4
- data/.rubocop.yml +29 -10
- data/.travis.yml +16 -8
- data/CODE_OF_CONDUCT.md +76 -0
- data/Gemfile +3 -1
- data/Gemfile.rails4 +1 -0
- data/README.md +108 -59
- data/Rakefile +19 -3
- data/changes.md +85 -10
- data/coverband.gemspec +3 -5
- data/lib/coverband/adapters/base.rb +64 -45
- data/lib/coverband/adapters/file_store.rb +15 -9
- data/lib/coverband/adapters/hash_redis_store.rb +190 -0
- data/lib/coverband/adapters/redis_store.rb +35 -11
- data/lib/coverband/at_exit.rb +2 -11
- data/lib/coverband/collectors/coverage.rb +35 -41
- data/lib/coverband/collectors/delta.rb +34 -28
- data/lib/coverband/collectors/view_tracker.rb +157 -0
- data/lib/coverband/configuration.rb +115 -33
- data/lib/coverband/integrations/background.rb +12 -4
- data/lib/coverband/integrations/{middleware.rb → background_middleware.rb} +2 -6
- data/lib/coverband/integrations/bundler.rb +8 -0
- data/lib/coverband/integrations/report_middleware.rb +15 -0
- data/lib/coverband/integrations/resque.rb +3 -5
- data/lib/coverband/reporters/base.rb +40 -16
- data/lib/coverband/reporters/console_report.rb +1 -0
- data/lib/coverband/reporters/html_report.rb +31 -27
- data/lib/coverband/reporters/web.rb +63 -18
- data/lib/coverband/utils/absolute_file_converter.rb +47 -0
- data/lib/coverband/utils/file_hasher.rb +16 -0
- data/lib/coverband/utils/file_list.rb +16 -5
- data/lib/coverband/utils/html_formatter.rb +45 -8
- data/lib/coverband/utils/lines_classifier.rb +5 -0
- data/lib/coverband/utils/railtie.rb +28 -10
- data/lib/coverband/utils/relative_file_converter.rb +41 -0
- data/lib/coverband/utils/result.rb +6 -39
- data/lib/coverband/utils/results.rb +51 -0
- data/lib/coverband/utils/s3_report.rb +2 -3
- data/lib/coverband/utils/source_file.rb +34 -8
- data/lib/coverband/utils/tasks.rb +10 -10
- data/lib/coverband/version.rb +5 -1
- data/lib/coverband.rb +49 -27
- data/lua/install.sh +15 -0
- data/lua/lib/persist-coverage.lua +28 -0
- data/lua/test/bootstrap.lua +5 -0
- data/lua/test/harness.lua +19 -0
- data/lua/test/redis-call.lua +55 -0
- data/lua/test/test-persist-coverage.lua +132 -0
- data/public/application.css +14 -6
- data/public/application.js +27 -0
- data/test/benchmarks/benchmark.rake +167 -73
- data/test/benchmarks/init_rails.rake +10 -0
- data/test/coverband/adapters/base_test.rb +73 -42
- data/test/coverband/adapters/file_store_test.rb +48 -37
- data/test/coverband/adapters/hash_redis_store_test.rb +195 -0
- data/test/coverband/adapters/redis_store_test.rb +102 -59
- data/test/coverband/at_exit_test.rb +0 -2
- data/test/coverband/collectors/coverage_test.rb +71 -13
- data/test/coverband/collectors/delta_test.rb +36 -6
- data/test/coverband/collectors/view_tracker_test.rb +113 -0
- data/test/coverband/configuration_test.rb +47 -7
- data/test/coverband/coverband_test.rb +14 -2
- data/test/coverband/integrations/background_middleware_test.rb +44 -0
- data/test/coverband/integrations/background_test.rb +11 -3
- data/test/coverband/integrations/report_middleware_test.rb +44 -0
- data/test/coverband/integrations/resque_worker_test.rb +5 -6
- data/test/coverband/integrations/test_resque_job.rb +3 -1
- data/test/coverband/reporters/base_test.rb +5 -81
- data/test/coverband/reporters/console_test.rb +3 -10
- data/test/coverband/reporters/html_test.rb +71 -21
- data/test/coverband/reporters/web_test.rb +0 -10
- data/test/coverband/utils/absolute_file_converter_test.rb +56 -0
- data/test/coverband/utils/file_groups_test.rb +11 -5
- data/test/coverband/utils/file_hasher_test.rb +29 -0
- data/test/coverband/utils/file_list_test.rb +5 -5
- data/test/coverband/utils/html_formatter_test.rb +41 -0
- data/test/coverband/utils/relative_file_converter_test.rb +39 -0
- data/test/coverband/utils/result_test.rb +6 -47
- data/test/coverband/utils/results_test.rb +54 -0
- data/test/coverband/utils/s3_report_test.rb +2 -0
- data/test/coverband/utils/source_file_test.rb +54 -0
- data/test/dog.rb.erb +12 -0
- data/test/forked/rails_full_stack_test.rb +106 -0
- data/test/forked/rails_rake_full_stack_test.rb +58 -0
- data/test/integration/full_stack_test.rb +15 -16
- data/test/rails4_dummy/Rakefile +6 -0
- data/test/rails4_dummy/config/application.rb +8 -9
- data/test/rails4_dummy/config/coverband.rb +3 -11
- data/test/rails4_dummy/config/coverband_missing_redis.rb +3 -0
- data/test/rails5_dummy/Rakefile +6 -0
- data/test/rails5_dummy/config/application.rb +6 -10
- data/test/rails5_dummy/config/coverband.rb +7 -3
- data/test/rails5_dummy/config/coverband_missing_redis.rb +15 -0
- data/test/rails_test_helper.rb +22 -5
- data/test/test_helper.rb +52 -11
- data/test/unique_files.rb +17 -9
- data/views/data.erb +1 -0
- data/views/file_list.erb +13 -12
- data/views/gem_list.erb +10 -1
- data/views/layout.erb +6 -18
- data/views/nav.erb +36 -0
- data/views/source_file.erb +20 -4
- data/views/source_file_loader.erb +1 -1
- data/views/view_tracker.erb +52 -0
- metadata +71 -12
- data/lib/coverband/utils/file_path_helper.rb +0 -57
- data/test/coverband/integrations/middleware_test.rb +0 -96
- 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
|
-
|
|
8
|
-
|
|
14
|
+
begin
|
|
15
|
+
app.middleware.use Coverband::BackgroundMiddleware
|
|
9
16
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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.
|
|
17
|
-
Coverband.
|
|
18
|
-
|
|
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, :
|
|
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
|
-
|
|
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 = '
|
|
30
|
-
Coverband.configuration.logger
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
#
|
|
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
|
data/lib/coverband/version.rb
CHANGED
|
@@ -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.
|
|
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/
|
|
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 =
|
|
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
|
|
54
|
+
configuration.logger.debug('using default configuration')
|
|
53
55
|
end
|
|
54
|
-
|
|
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
|
|
58
|
-
|
|
64
|
+
def self.report_coverage
|
|
65
|
+
coverage_instance.report_coverage
|
|
59
66
|
end
|
|
60
67
|
|
|
61
68
|
def self.configuration
|
|
62
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
95
|
+
coverage_instance.runtime!
|
|
79
96
|
end
|
|
80
97
|
|
|
81
|
-
def self.
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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,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;
|