coverband 4.2.0 → 4.2.2
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 -9
- data/.travis.yml +10 -8
- data/CODE_OF_CONDUCT.md +76 -0
- data/Gemfile +3 -1
- data/Gemfile.rails4 +1 -0
- data/README.md +72 -43
- data/Rakefile +14 -3
- data/changes.md +66 -11
- data/coverband.gemspec +2 -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 +182 -0
- data/lib/coverband/adapters/redis_store.rb +33 -11
- data/lib/coverband/at_exit.rb +2 -11
- data/lib/coverband/collectors/coverage.rb +34 -40
- data/lib/coverband/collectors/delta.rb +34 -28
- data/lib/coverband/collectors/view_tracker.rb +124 -0
- data/lib/coverband/configuration.rb +96 -32
- data/lib/coverband/integrations/background.rb +9 -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 +21 -27
- data/lib/coverband/reporters/web.rb +19 -21
- 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 +37 -8
- data/lib/coverband/utils/lines_classifier.rb +5 -0
- data/lib/coverband/utils/railtie.rb +20 -9
- data/lib/coverband/utils/relative_file_converter.rb +41 -0
- data/lib/coverband/utils/result.rb +1 -37
- data/lib/coverband/utils/results.rb +51 -0
- data/lib/coverband/utils/s3_report.rb +2 -2
- data/lib/coverband/utils/source_file.rb +31 -8
- data/lib/coverband/utils/tasks.rb +10 -10
- data/lib/coverband/version.rb +5 -1
- data/lib/coverband.rb +39 -22
- data/public/application.css +6 -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 +190 -0
- data/test/coverband/adapters/redis_store_test.rb +92 -59
- data/test/coverband/at_exit_test.rb +0 -2
- data/test/coverband/collectors/coverage_test.rb +62 -10
- data/test/coverband/collectors/delta_test.rb +36 -6
- data/test/coverband/collectors/view_tracker_test.rb +77 -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 +1 -3
- data/test/coverband/integrations/report_middleware_test.rb +44 -0
- data/test/coverband/integrations/resque_worker_test.rb +6 -6
- data/test/coverband/integrations/test_resque_job.rb +3 -1
- data/test/coverband/reporters/base_test.rb +2 -79
- data/test/coverband/reporters/console_test.rb +2 -6
- data/test/coverband/reporters/html_test.rb +58 -19
- 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 +43 -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 +50 -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 +48 -0
- data/test/integration/full_stack_test.rb +17 -16
- data/test/rails4_dummy/Rakefile +6 -0
- data/test/rails4_dummy/config/application.rb +8 -9
- data/test/rails4_dummy/config/coverband.rb +5 -3
- data/test/rails5_dummy/Rakefile +6 -0
- data/test/rails5_dummy/config/application.rb +6 -10
- data/test/rails5_dummy/config/coverband.rb +4 -2
- data/test/rails_test_helper.rb +22 -5
- data/test/test_helper.rb +45 -8
- data/test/unique_files.rb +17 -9
- data/views/file_list.erb +2 -2
- data/views/gem_list.erb +10 -1
- data/views/layout.erb +6 -18
- data/views/nav.erb +35 -0
- data/views/source_file.erb +13 -4
- data/views/source_file_loader.erb +1 -1
- data/views/view_tracker.erb +37 -0
- metadata +59 -25
- 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
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
module Coverband
|
|
4
4
|
module Reporters
|
|
5
5
|
class HTMLReport < Base
|
|
6
|
-
attr_accessor :filtered_report_files, :open_report, :
|
|
6
|
+
attr_accessor :filtered_report_files, :open_report, :static, :notice,
|
|
7
7
|
:base_path, :filename
|
|
8
8
|
|
|
9
9
|
def initialize(store, options = {})
|
|
10
10
|
coverband_reports = Coverband::Reporters::Base.report(store, options)
|
|
11
11
|
self.open_report = options.fetch(:open_report) { true }
|
|
12
|
-
self.
|
|
12
|
+
self.static = options.fetch(:static) { true }
|
|
13
13
|
# TODO: refactor notice out to top level of web only
|
|
14
14
|
self.notice = options.fetch(:notice) { nil }
|
|
15
15
|
self.base_path = options.fetch(:base_path) { nil }
|
|
@@ -25,40 +25,34 @@ module Coverband
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def report
|
|
28
|
-
if
|
|
29
|
-
|
|
30
|
-
base_path: base_path,
|
|
31
|
-
notice: notice).format_html!
|
|
28
|
+
if static?
|
|
29
|
+
report_static_site
|
|
32
30
|
else
|
|
33
|
-
|
|
34
|
-
if open_report
|
|
35
|
-
`open #{Coverband.configuration.root}/coverage/index.html`
|
|
36
|
-
else
|
|
37
|
-
Coverband.configuration.logger.info 'report is ready and viewable: open coverage/index.html'
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
Coverband::Utils::S3Report.instance.persist! if Coverband.configuration.s3_bucket
|
|
31
|
+
report_dynamic_html
|
|
41
32
|
end
|
|
42
33
|
end
|
|
43
34
|
|
|
44
35
|
private
|
|
45
36
|
|
|
46
|
-
def
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
filtered_report_files = {}
|
|
37
|
+
def static?
|
|
38
|
+
static
|
|
39
|
+
end
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
41
|
+
def report_static_site
|
|
42
|
+
Coverband::Utils::HTMLFormatter.new(filtered_report_files).format_static_html!
|
|
43
|
+
if open_report
|
|
44
|
+
`open #{Coverband.configuration.root}/coverage/index.html`
|
|
45
|
+
else
|
|
46
|
+
Coverband.configuration.logger.info 'report is ready and viewable: open coverage/index.html'
|
|
47
|
+
end
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
next if Coverband.configuration.ignore.any? { |i| file.match(i) }
|
|
49
|
+
Coverband::Utils::S3Report.instance.persist! if Coverband.configuration.s3_bucket
|
|
50
|
+
end
|
|
58
51
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
def report_dynamic_html
|
|
53
|
+
Coverband::Utils::HTMLFormatter.new(filtered_report_files,
|
|
54
|
+
base_path: base_path,
|
|
55
|
+
notice: notice).format_dynamic_html!
|
|
62
56
|
end
|
|
63
57
|
end
|
|
64
58
|
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
begin
|
|
4
4
|
require 'rack'
|
|
5
5
|
rescue LoadError
|
|
6
|
+
puts 'error loading Coverband web reporter as Rack is not available'
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
module Coverband
|
|
@@ -17,19 +18,26 @@ module Coverband
|
|
|
17
18
|
urls: [/.*\.css/, /.*\.js/, /.*\.gif/, /.*\.png/])
|
|
18
19
|
end
|
|
19
20
|
|
|
21
|
+
def check_auth
|
|
22
|
+
return true unless Coverband.configuration.password
|
|
23
|
+
|
|
24
|
+
auth_header = request.get_header('HTTP_AUTHORIZATION')
|
|
25
|
+
return unless auth_header
|
|
26
|
+
|
|
27
|
+
Coverband.configuration.password == Base64.decode64(auth_header.split[1]).split(':')[1]
|
|
28
|
+
end
|
|
29
|
+
|
|
20
30
|
def call(env)
|
|
21
31
|
@request = Rack::Request.new(env)
|
|
22
32
|
|
|
33
|
+
return [401, { 'www-authenticate' => 'Basic realm=""' }, ['']] unless check_auth
|
|
34
|
+
|
|
23
35
|
if request.post?
|
|
24
36
|
case request.path_info
|
|
25
37
|
when %r{\/clear_file}
|
|
26
38
|
clear_file
|
|
27
39
|
when %r{\/clear}
|
|
28
40
|
clear
|
|
29
|
-
when %r{\/collect_coverage}
|
|
30
|
-
collect_coverage
|
|
31
|
-
when %r{\/reload_files}
|
|
32
|
-
reload_files
|
|
33
41
|
else
|
|
34
42
|
[404, { 'Content-Type' => 'text/html' }, ['404 error!']]
|
|
35
43
|
end
|
|
@@ -39,6 +47,8 @@ module Coverband
|
|
|
39
47
|
@static.call(env)
|
|
40
48
|
when %r{\/settings}
|
|
41
49
|
[200, { 'Content-Type' => 'text/html' }, [settings]]
|
|
50
|
+
when %r{\/view_tracker}
|
|
51
|
+
[200, { 'Content-Type' => 'text/html' }, [view_tracker]]
|
|
42
52
|
when %r{\/debug_data}
|
|
43
53
|
[200, { 'Content-Type' => 'text/json' }, [debug_data]]
|
|
44
54
|
when %r{\/load_file_details}
|
|
@@ -55,7 +65,7 @@ module Coverband
|
|
|
55
65
|
notice = "<strong>Notice:</strong> #{Rack::Utils.escape_html(request.params['notice'])}<br/>"
|
|
56
66
|
notice = request.params['notice'] ? notice : ''
|
|
57
67
|
Coverband::Reporters::HTMLReport.new(Coverband.configuration.store,
|
|
58
|
-
|
|
68
|
+
static: false,
|
|
59
69
|
base_path: base_path,
|
|
60
70
|
notice: notice,
|
|
61
71
|
open_report: false).report
|
|
@@ -65,6 +75,10 @@ module Coverband
|
|
|
65
75
|
Coverband::Utils::HTMLFormatter.new(nil, base_path: base_path).format_settings!
|
|
66
76
|
end
|
|
67
77
|
|
|
78
|
+
def view_tracker
|
|
79
|
+
Coverband::Utils::HTMLFormatter.new(nil, base_path: base_path).format_view_tracker!
|
|
80
|
+
end
|
|
81
|
+
|
|
68
82
|
def debug_data
|
|
69
83
|
Coverband.configuration.store.get_coverage_report.to_json
|
|
70
84
|
end
|
|
@@ -77,12 +91,6 @@ module Coverband
|
|
|
77
91
|
open_report: false).file_details
|
|
78
92
|
end
|
|
79
93
|
|
|
80
|
-
def collect_coverage
|
|
81
|
-
Coverband.report_coverage(true)
|
|
82
|
-
notice = 'coverband coverage collected'
|
|
83
|
-
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
84
|
-
end
|
|
85
|
-
|
|
86
94
|
def clear
|
|
87
95
|
if Coverband.configuration.web_enable_clear
|
|
88
96
|
Coverband.configuration.store.clear!
|
|
@@ -104,16 +112,6 @@ module Coverband
|
|
|
104
112
|
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
105
113
|
end
|
|
106
114
|
|
|
107
|
-
def reload_files
|
|
108
|
-
Coverband.configuration&.safe_reload_files&.each do |safe_file|
|
|
109
|
-
load safe_file
|
|
110
|
-
end
|
|
111
|
-
# force reload
|
|
112
|
-
Coverband.configure
|
|
113
|
-
notice = 'coverband files reloaded'
|
|
114
|
-
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
115
|
-
end
|
|
116
|
-
|
|
117
115
|
private
|
|
118
116
|
|
|
119
117
|
# This method should get the root mounted endpoint
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coverband
|
|
4
|
+
module Utils
|
|
5
|
+
class AbsoluteFileConverter
|
|
6
|
+
def initialize(roots)
|
|
7
|
+
@cache = {}
|
|
8
|
+
@roots = roots.map { |root| "#{File.expand_path(root)}/" }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.instance
|
|
12
|
+
@instance ||= new(Coverband.configuration.all_root_paths)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.reset
|
|
16
|
+
@instance = nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.convert(relative_path)
|
|
20
|
+
instance.convert(relative_path)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def convert(relative_path)
|
|
24
|
+
@cache[relative_path] ||= begin
|
|
25
|
+
relative_filename = relative_path
|
|
26
|
+
local_filename = relative_filename
|
|
27
|
+
@roots.each do |root|
|
|
28
|
+
relative_filename = relative_filename.sub(/^#{root}/, './')
|
|
29
|
+
# once we have a relative path break out of the loop
|
|
30
|
+
break if relative_filename.start_with? './'
|
|
31
|
+
end
|
|
32
|
+
# the filename for our reports is expected to be a full path.
|
|
33
|
+
# roots.last should be roots << current_root}/
|
|
34
|
+
# a fully expanded path of config.root
|
|
35
|
+
# filename = filename.gsub('./', roots.last)
|
|
36
|
+
# above only works for app files
|
|
37
|
+
# we need to rethink some of this logic
|
|
38
|
+
# gems aren't at project root and can have multiple locations
|
|
39
|
+
local_root = @roots.find do |root|
|
|
40
|
+
File.exist?(relative_filename.gsub('./', root))
|
|
41
|
+
end
|
|
42
|
+
local_root ? relative_filename.gsub('./', local_root) : local_filename
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coverband
|
|
4
|
+
module Utils
|
|
5
|
+
class FileHasher
|
|
6
|
+
@cache = {}
|
|
7
|
+
|
|
8
|
+
def self.hash(file, path_converter: AbsoluteFileConverter.instance)
|
|
9
|
+
@cache[file] ||= begin
|
|
10
|
+
file = path_converter.convert(file)
|
|
11
|
+
Digest::MD5.file(file).hexdigest if File.exist?(file)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -13,24 +13,28 @@ module Coverband
|
|
|
13
13
|
# Returns the count of lines that have coverage
|
|
14
14
|
def covered_lines
|
|
15
15
|
return 0.0 if empty?
|
|
16
|
+
|
|
16
17
|
map { |f| f.covered_lines.count }.inject(:+)
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
# Returns the count of lines that have been missed
|
|
20
21
|
def missed_lines
|
|
21
22
|
return 0.0 if empty?
|
|
23
|
+
|
|
22
24
|
map { |f| f.missed_lines.count }.inject(:+)
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
# Returns the count of lines that are not relevant for coverage
|
|
26
28
|
def never_lines
|
|
27
29
|
return 0.0 if empty?
|
|
30
|
+
|
|
28
31
|
map { |f| f.never_lines.count }.inject(:+)
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
# Returns the count of skipped lines
|
|
32
35
|
def skipped_lines
|
|
33
36
|
return 0.0 if empty?
|
|
37
|
+
|
|
34
38
|
map { |f| f.skipped_lines.count }.inject(:+)
|
|
35
39
|
end
|
|
36
40
|
|
|
@@ -40,11 +44,6 @@ module Coverband
|
|
|
40
44
|
map(&:covered_percent)
|
|
41
45
|
end
|
|
42
46
|
|
|
43
|
-
# Finds the least covered file and returns that file's name
|
|
44
|
-
def least_covered_file
|
|
45
|
-
sort_by(&:covered_percent).first.filename
|
|
46
|
-
end
|
|
47
|
-
|
|
48
47
|
# Returns the overall amount of relevant lines of code across all files in this list
|
|
49
48
|
def lines_of_code
|
|
50
49
|
covered_lines + missed_lines
|
|
@@ -54,15 +53,27 @@ module Coverband
|
|
|
54
53
|
# @return [Float]
|
|
55
54
|
def covered_percent
|
|
56
55
|
return 100.0 if empty? || lines_of_code.zero?
|
|
56
|
+
|
|
57
57
|
Float(covered_lines * 100.0 / lines_of_code)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
# Computes the coverage based upon lines covered and lines missed, formatted
|
|
61
|
+
# @return [Float]
|
|
62
|
+
def formatted_covered_percent
|
|
63
|
+
covered_percent.round(2)
|
|
64
|
+
end
|
|
65
|
+
|
|
60
66
|
# Computes the strength (hits / line) based upon lines covered and lines missed
|
|
61
67
|
# @return [Float]
|
|
62
68
|
def covered_strength
|
|
63
69
|
return 0.0 if empty? || lines_of_code.zero?
|
|
70
|
+
|
|
64
71
|
Float(map { |f| f.covered_strength * f.lines_of_code }.inject(:+) / lines_of_code)
|
|
65
72
|
end
|
|
73
|
+
|
|
74
|
+
def first_seen_at
|
|
75
|
+
map(&:first_updated_at).reject { |el| el.is_a?(String) }.min
|
|
76
|
+
end
|
|
66
77
|
end
|
|
67
78
|
end
|
|
68
79
|
end
|
|
@@ -21,11 +21,11 @@ module Coverband
|
|
|
21
21
|
@coverage_result = Coverband::Utils::Results.new(report) if report
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def
|
|
24
|
+
def format_static_html!
|
|
25
25
|
format(@coverage_result)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def
|
|
28
|
+
def format_dynamic_html!
|
|
29
29
|
format_html(@coverage_result)
|
|
30
30
|
end
|
|
31
31
|
|
|
@@ -33,10 +33,18 @@ module Coverband
|
|
|
33
33
|
format_settings
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
def format_view_tracker!
|
|
37
|
+
format_view_tracker
|
|
38
|
+
end
|
|
39
|
+
|
|
36
40
|
def format_source_file!(filename)
|
|
37
41
|
source_file = @coverage_result.file_from_path_with_type(filename)
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
|
|
43
|
+
if source_file
|
|
44
|
+
formatted_source_file(@coverage_result, source_file)
|
|
45
|
+
else
|
|
46
|
+
'File No Longer Available'
|
|
47
|
+
end
|
|
40
48
|
end
|
|
41
49
|
|
|
42
50
|
private
|
|
@@ -45,6 +53,10 @@ module Coverband
|
|
|
45
53
|
template('settings').result(binding)
|
|
46
54
|
end
|
|
47
55
|
|
|
56
|
+
def format_view_tracker
|
|
57
|
+
template('view_tracker').result(binding)
|
|
58
|
+
end
|
|
59
|
+
|
|
48
60
|
def format(result)
|
|
49
61
|
Dir[File.join(File.dirname(__FILE__), '../../../public/*')].each do |path|
|
|
50
62
|
FileUtils.cp_r(path, asset_output_path)
|
|
@@ -70,16 +82,25 @@ module Coverband
|
|
|
70
82
|
|
|
71
83
|
def asset_output_path
|
|
72
84
|
return @asset_output_path if defined?(@asset_output_path) && @asset_output_path
|
|
85
|
+
|
|
73
86
|
@asset_output_path = File.join(output_path)
|
|
74
87
|
FileUtils.mkdir_p(@asset_output_path)
|
|
75
88
|
@asset_output_path
|
|
76
89
|
end
|
|
77
90
|
|
|
91
|
+
def served_html?
|
|
92
|
+
!static_html?
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def static_html?
|
|
96
|
+
base_path.nil?
|
|
97
|
+
end
|
|
98
|
+
|
|
78
99
|
def assets_path(name)
|
|
79
|
-
if
|
|
80
|
-
File.join(base_path, name)
|
|
81
|
-
else
|
|
100
|
+
if static_html?
|
|
82
101
|
File.join(name)
|
|
102
|
+
else
|
|
103
|
+
File.join(base_path, name)
|
|
83
104
|
end
|
|
84
105
|
end
|
|
85
106
|
|
|
@@ -91,6 +112,10 @@ module Coverband
|
|
|
91
112
|
button + '</form>'
|
|
92
113
|
end
|
|
93
114
|
|
|
115
|
+
def display_nav(nav_options = {})
|
|
116
|
+
template('nav').result(binding)
|
|
117
|
+
end
|
|
118
|
+
|
|
94
119
|
# Returns the html for the given source_file
|
|
95
120
|
def formatted_source_file(result, source_file)
|
|
96
121
|
template('source_file').result(binding)
|
|
@@ -157,7 +182,11 @@ module Coverband
|
|
|
157
182
|
end
|
|
158
183
|
|
|
159
184
|
def timeago(time)
|
|
160
|
-
|
|
185
|
+
if time
|
|
186
|
+
"<abbr class=\"timeago\" title=\"#{time.iso8601}\">#{time.iso8601}</abbr>"
|
|
187
|
+
else
|
|
188
|
+
'Not Available'
|
|
189
|
+
end
|
|
161
190
|
end
|
|
162
191
|
|
|
163
192
|
def shortened_filename(source_file)
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
####
|
|
4
|
+
#
|
|
5
|
+
# NOTE: with Ruby 2.6.0 and beyond we can replace this classifier with
|
|
6
|
+
# ::Coverage.line_stub
|
|
7
|
+
# https://ruby-doc.org/stdlib-2.6.1/libdoc/coverage/rdoc/Coverage.html#method-c-line_stub
|
|
8
|
+
#
|
|
4
9
|
# Thanks for all the help SimpleCov https://github.com/colszowka/simplecov-html
|
|
5
10
|
# initial version pulled into Coverband from Simplecov 12/04/2018
|
|
6
11
|
#
|
|
@@ -1,21 +1,32 @@
|
|
|
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::
|
|
8
|
-
end
|
|
14
|
+
app.middleware.use Coverband::BackgroundMiddleware
|
|
9
15
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
if Coverband.configuration.track_views
|
|
17
|
+
CoverbandViewTracker = Coverband::Collectors::ViewTracker.new
|
|
18
|
+
Coverband.configuration.view_tracker = CoverbandViewTracker
|
|
19
|
+
|
|
20
|
+
ActiveSupport::Notifications.subscribe(/render_partial.action_view|render_template.action_view/) do |name, start, finish, id, payload|
|
|
21
|
+
CoverbandViewTracker.track_views(name, start, finish, id, payload) unless name.include?('!')
|
|
22
|
+
end
|
|
23
|
+
end
|
|
14
24
|
end
|
|
15
25
|
|
|
16
|
-
config.
|
|
17
|
-
Coverband.configuration.logger&.debug('Coverband: set to eager_loading')
|
|
26
|
+
config.after_initialize do
|
|
18
27
|
Coverband.eager_loading_coverage!
|
|
28
|
+
Coverband.report_coverage
|
|
29
|
+
Coverband.runtime_coverage!
|
|
19
30
|
end
|
|
20
31
|
|
|
21
32
|
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,26 +51,6 @@ 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)
|
|
@@ -89,18 +65,6 @@ module Coverband
|
|
|
89
65
|
|
|
90
66
|
result
|
|
91
67
|
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
68
|
end
|
|
105
69
|
end
|
|
106
70
|
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,8 +26,8 @@ module Coverband
|
|
|
26
26
|
begin
|
|
27
27
|
require 'aws-sdk-s3'
|
|
28
28
|
rescue StandardError
|
|
29
|
-
err_msg = '
|
|
30
|
-
Coverband.configuration.logger
|
|
29
|
+
err_msg = 'Coverband requires aws-sdk in order use S3Report.'
|
|
30
|
+
Coverband.configuration.logger&.error err_msg
|
|
31
31
|
return
|
|
32
32
|
end
|
|
33
33
|
end
|