coverband 4.0.1 → 4.2.0
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 +5 -5
- data/.gitignore +6 -1
- data/.travis.yml +8 -2
- data/Gemfile +2 -0
- data/Gemfile.rails4 +2 -0
- data/README.md +135 -157
- data/Rakefile +1 -1
- data/changes.md +56 -5
- data/coverband.gemspec +5 -3
- data/lib/coverband/adapters/base.rb +44 -11
- data/lib/coverband/adapters/file_store.rb +8 -0
- data/lib/coverband/adapters/redis_store.rb +65 -8
- data/lib/coverband/at_exit.rb +37 -0
- data/lib/coverband/collectors/coverage.rb +43 -72
- data/lib/coverband/collectors/delta.rb +67 -0
- data/lib/coverband/configuration.rb +71 -2
- data/lib/coverband/integrations/background.rb +9 -10
- data/lib/coverband/integrations/middleware.rb +3 -2
- data/lib/coverband/integrations/rack_server_check.rb +4 -1
- data/lib/coverband/integrations/resque.rb +27 -0
- data/lib/coverband/reporters/base.rb +24 -36
- data/lib/coverband/reporters/html_report.rb +40 -15
- data/lib/coverband/reporters/web.rb +52 -10
- data/lib/coverband/utils/file_groups.rb +53 -0
- data/lib/coverband/utils/file_path_helper.rb +57 -0
- data/lib/coverband/utils/gem_list.rb +31 -0
- data/lib/coverband/utils/html_formatter.rb +50 -8
- data/lib/coverband/utils/railtie.rb +9 -1
- data/lib/coverband/utils/result.rb +16 -19
- data/lib/coverband/utils/results.rb +63 -0
- data/lib/coverband/utils/source_file.rb +23 -1
- data/lib/coverband/utils/tasks.rb +17 -17
- data/lib/coverband/version.rb +1 -1
- data/lib/coverband.rb +32 -2
- data/public/application.css +5 -0
- data/public/application.js +117 -1640
- data/public/dependencies.js +1581 -0
- data/public/favicon.png +0 -0
- data/test/benchmarks/benchmark.rake +36 -1
- data/test/benchmarks/coverage_fork.sh +37 -0
- data/test/{unit/adapters_base_test.rb → coverband/adapters/base_test.rb} +1 -1
- data/test/{unit/adapters_file_store_test.rb → coverband/adapters/file_store_test.rb} +1 -1
- data/test/{unit/adapters_redis_store_test.rb → coverband/adapters/redis_store_test.rb} +28 -11
- data/test/coverband/at_exit_test.rb +13 -0
- data/test/coverband/collectors/coverage_test.rb +73 -0
- data/test/coverband/collectors/delta_test.rb +52 -0
- data/test/coverband/configuration_test.rb +67 -0
- data/test/{unit → coverband}/coverband_test.rb +7 -0
- data/test/coverband/integrations/background_test.rb +31 -0
- data/test/{unit → coverband/integrations}/middleware_test.rb +3 -2
- data/test/{unit/rack_server_checkout_test.rb → coverband/integrations/rack_server_check_test.rb} +1 -1
- data/test/coverband/integrations/resque_worker_test.rb +42 -0
- data/test/coverband/integrations/test_resque_job.rb +7 -0
- data/test/coverband/reporters/base_test.rb +168 -0
- data/test/{unit/reports_console_test.rb → coverband/reporters/console_test.rb} +8 -7
- data/test/coverband/reporters/html_test.rb +45 -0
- data/test/coverband/reporters/web_test.rb +52 -0
- data/test/coverband/utils/file_groups_test.rb +55 -0
- data/test/{unit → coverband}/utils/file_list_test.rb +1 -1
- data/test/coverband/utils/gem_list_test.rb +48 -0
- data/test/{unit → coverband}/utils/source_file_test.rb +1 -1
- data/test/{unit → integration}/full_stack_test.rb +25 -12
- data/test/{unit → integration}/rails_full_stack_test.rb +27 -7
- data/test/rails4_dummy/config/application.rb +1 -1
- data/test/rails4_dummy/config/coverband.rb +4 -1
- data/test/rails4_dummy/tmp/.keep +0 -0
- data/test/rails5_dummy/config/application.rb +1 -1
- data/test/rails5_dummy/config/coverband.rb +3 -1
- data/test/rails5_dummy/tmp/.keep +0 -0
- data/test/rails_test_helper.rb +13 -8
- data/test/test_helper.rb +12 -17
- data/test/unique_files.rb +23 -0
- data/views/file_list.erb +33 -20
- data/views/gem_list.erb +54 -0
- data/views/layout.erb +13 -4
- data/views/settings.erb +35 -0
- data/views/source_file.erb +16 -4
- data/views/source_file_loader.erb +7 -0
- metadata +108 -62
- data/test/unit/background_test.rb +0 -29
- data/test/unit/collectors_coverage_test.rb +0 -48
- data/test/unit/configuration_test.rb +0 -23
- data/test/unit/reports_base_test.rb +0 -109
- data/test/unit/reports_html_test.rb +0 -29
- data/test/unit/reports_web_test.rb +0 -39
- /data/test/{unit → coverband}/utils/lines_classifier_test.rb +0 -0
- /data/test/{unit → coverband}/utils/result_test.rb +0 -0
- /data/test/{unit → coverband}/utils/s3_report_test.rb +0 -0
- /data/test/{unit → coverband}/utils/source_file_line_test.rb +0 -0
- /data/test/{unit/dog.rb → dog.rb} +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Resque.after_fork do |job|
|
|
4
|
+
Coverband.start
|
|
5
|
+
Coverband.runtime_coverage!
|
|
6
|
+
# no reason to miss coverage on a first resque job
|
|
7
|
+
Coverband::Collectors::Delta.set_default_results
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
Resque.before_first_fork do
|
|
11
|
+
Coverband.eager_loading_coverage!
|
|
12
|
+
Coverband.configuration.background_reporting_enabled = false
|
|
13
|
+
Coverband::Background.stop
|
|
14
|
+
Coverband::Collectors::Coverage.instance.report_coverage(true)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module Coverband
|
|
18
|
+
module ResqueWorker
|
|
19
|
+
def perform
|
|
20
|
+
super
|
|
21
|
+
ensure
|
|
22
|
+
Coverband.report_coverage(true)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
Resque::Job.prepend(Coverband::ResqueWorker)
|
|
@@ -8,41 +8,38 @@ module Coverband
|
|
|
8
8
|
###
|
|
9
9
|
class Base
|
|
10
10
|
class << self
|
|
11
|
+
include Coverband::Utils::FilePathHelper
|
|
11
12
|
def report(store, _options = {})
|
|
12
|
-
|
|
13
|
+
all_roots = Coverband.configuration.all_root_paths
|
|
14
|
+
scov_style_report = get_current_scov_data_imp(store, all_roots)
|
|
13
15
|
|
|
14
16
|
if Coverband.configuration.verbose
|
|
15
17
|
msg = "report:\n #{scov_style_report.inspect}"
|
|
16
|
-
Coverband.configuration.logger.debug msg
|
|
18
|
+
# Coverband.configuration.logger.debug msg
|
|
17
19
|
end
|
|
18
20
|
scov_style_report
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
protected
|
|
22
24
|
|
|
23
|
-
def root_paths
|
|
24
|
-
roots = Coverband.configuration.root_paths
|
|
25
|
-
roots << "#{current_root}/"
|
|
26
|
-
roots
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def current_root
|
|
30
|
-
File.expand_path(Coverband.configuration.root)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
25
|
def fix_file_names(report_hash, roots)
|
|
34
26
|
if Coverband.configuration.verbose
|
|
35
27
|
Coverband.configuration.logger.info "fixing root: #{roots.join(', ')}"
|
|
36
28
|
end
|
|
37
29
|
|
|
38
30
|
# normalize names across servers
|
|
39
|
-
report_hash.each_with_object({}) do |(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
31
|
+
report_hash.each_with_object({}) do |(name, report), fixed_report|
|
|
32
|
+
fixed_report[name] = {}
|
|
33
|
+
report.each_pair do |key, vals|
|
|
34
|
+
filename = relative_path_to_full(key, roots)
|
|
35
|
+
fixed_report[name][filename] = if fixed_report[name].key?(filename) && fixed_report[name][filename]['data'] && vals['data']
|
|
36
|
+
merged_data = merge_arrays(fixed_report[name][filename]['data'], vals['data'])
|
|
37
|
+
vals['data'] = merged_data
|
|
38
|
+
vals
|
|
39
|
+
else
|
|
40
|
+
vals
|
|
41
|
+
end
|
|
42
|
+
end
|
|
46
43
|
end
|
|
47
44
|
end
|
|
48
45
|
|
|
@@ -61,32 +58,23 @@ module Coverband
|
|
|
61
58
|
merged
|
|
62
59
|
end
|
|
63
60
|
|
|
64
|
-
def filename_from_key(key, roots)
|
|
65
|
-
filename = key
|
|
66
|
-
roots.each do |root|
|
|
67
|
-
filename = filename.gsub(/^#{root}/, './')
|
|
68
|
-
end
|
|
69
|
-
# the filename for SimpleCov is expected to be a full path.
|
|
70
|
-
# roots.last should be roots << current_root}/
|
|
71
|
-
# a fully expanded path of config.root
|
|
72
|
-
filename = filename.gsub('./', roots.last)
|
|
73
|
-
filename
|
|
74
|
-
end
|
|
75
|
-
|
|
76
61
|
###
|
|
77
62
|
# why do we need to merge covered files data?
|
|
78
63
|
# basically because paths on machines or deployed hosts could be different, so
|
|
79
64
|
# two different keys could point to the same filename or `line_key`
|
|
80
|
-
# this logic should be pushed to base report
|
|
81
65
|
# TODO: think we are filtering based on ignore while sending to the store
|
|
82
66
|
# and as we also pull it out here
|
|
83
67
|
###
|
|
84
68
|
def get_current_scov_data_imp(store, roots)
|
|
85
69
|
scov_style_report = {}
|
|
86
|
-
store.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
70
|
+
store.get_coverage_report.each_pair do |name, data|
|
|
71
|
+
data.each_pair do |key, line_data|
|
|
72
|
+
next if Coverband.configuration.ignore.any? { |i| key.match(i) }
|
|
73
|
+
next unless line_data
|
|
74
|
+
|
|
75
|
+
scov_style_report[name] = {} unless scov_style_report.key?(name)
|
|
76
|
+
scov_style_report[name][key] = line_data
|
|
77
|
+
end
|
|
90
78
|
end
|
|
91
79
|
|
|
92
80
|
scov_style_report = fix_file_names(scov_style_report, roots)
|
|
@@ -3,23 +3,28 @@
|
|
|
3
3
|
module Coverband
|
|
4
4
|
module Reporters
|
|
5
5
|
class HTMLReport < Base
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
open_report = options.fetch(:open_report) { true }
|
|
9
|
-
html = options.fetch(:html) { false }
|
|
10
|
-
notice = options.fetch(:notice) { nil }
|
|
11
|
-
base_path = options.fetch(:base_path) { nil }
|
|
6
|
+
attr_accessor :filtered_report_files, :open_report, :html, :notice,
|
|
7
|
+
:base_path, :filename
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
9
|
+
def initialize(store, options = {})
|
|
10
|
+
coverband_reports = Coverband::Reporters::Base.report(store, options)
|
|
11
|
+
self.open_report = options.fetch(:open_report) { true }
|
|
12
|
+
self.html = options.fetch(:html) { false }
|
|
13
|
+
# TODO: refactor notice out to top level of web only
|
|
14
|
+
self.notice = options.fetch(:notice) { nil }
|
|
15
|
+
self.base_path = options.fetch(:base_path) { nil }
|
|
16
|
+
self.filename = options.fetch(:filename) { nil }
|
|
17
|
+
|
|
18
|
+
self.filtered_report_files = self.class.fix_reports(coverband_reports)
|
|
19
|
+
end
|
|
22
20
|
|
|
21
|
+
def file_details
|
|
22
|
+
Coverband::Utils::HTMLFormatter.new(filtered_report_files,
|
|
23
|
+
base_path: base_path,
|
|
24
|
+
notice: notice).format_source_file!(filename)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def report
|
|
23
28
|
if html
|
|
24
29
|
Coverband::Utils::HTMLFormatter.new(filtered_report_files,
|
|
25
30
|
base_path: base_path,
|
|
@@ -35,6 +40,26 @@ module Coverband
|
|
|
35
40
|
Coverband::Utils::S3Report.instance.persist! if Coverband.configuration.s3_bucket
|
|
36
41
|
end
|
|
37
42
|
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def self.fix_reports(reports)
|
|
47
|
+
# list all files, even if not tracked by Coverband (0% coverage)
|
|
48
|
+
tracked_glob = "#{Coverband.configuration.current_root}/{app,lib,config}/**/*.{rb}"
|
|
49
|
+
filtered_report_files = {}
|
|
50
|
+
|
|
51
|
+
reports.each_pair do |report_name, report_data|
|
|
52
|
+
filtered_report_files[report_name] = {}
|
|
53
|
+
report_files = Coverband::Utils::Result.add_not_loaded_files(report_data, tracked_glob)
|
|
54
|
+
|
|
55
|
+
# apply coverband filters
|
|
56
|
+
report_files.each_pair do |file, data|
|
|
57
|
+
next if Coverband.configuration.ignore.any? { |i| file.match(i) }
|
|
58
|
+
|
|
59
|
+
filtered_report_files[report_name][file] = data
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
38
63
|
end
|
|
39
64
|
end
|
|
40
65
|
end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
begin
|
|
4
|
+
require 'rack'
|
|
5
|
+
rescue LoadError
|
|
6
|
+
end
|
|
4
7
|
|
|
5
8
|
module Coverband
|
|
6
9
|
module Reporters
|
|
@@ -19,6 +22,8 @@ module Coverband
|
|
|
19
22
|
|
|
20
23
|
if request.post?
|
|
21
24
|
case request.path_info
|
|
25
|
+
when %r{\/clear_file}
|
|
26
|
+
clear_file
|
|
22
27
|
when %r{\/clear}
|
|
23
28
|
clear
|
|
24
29
|
when %r{\/collect_coverage}
|
|
@@ -32,7 +37,13 @@ module Coverband
|
|
|
32
37
|
case request.path_info
|
|
33
38
|
when /.*\.(css|js|gif|png)/
|
|
34
39
|
@static.call(env)
|
|
35
|
-
when %r{\/}
|
|
40
|
+
when %r{\/settings}
|
|
41
|
+
[200, { 'Content-Type' => 'text/html' }, [settings]]
|
|
42
|
+
when %r{\/debug_data}
|
|
43
|
+
[200, { 'Content-Type' => 'text/json' }, [debug_data]]
|
|
44
|
+
when %r{\/load_file_details}
|
|
45
|
+
[200, { 'Content-Type' => 'text/json' }, [load_file_details]]
|
|
46
|
+
when %r{\/$}
|
|
36
47
|
[200, { 'Content-Type' => 'text/html' }, [index]]
|
|
37
48
|
else
|
|
38
49
|
[404, { 'Content-Type' => 'text/html' }, ['404 error!']]
|
|
@@ -43,22 +54,53 @@ module Coverband
|
|
|
43
54
|
def index
|
|
44
55
|
notice = "<strong>Notice:</strong> #{Rack::Utils.escape_html(request.params['notice'])}<br/>"
|
|
45
56
|
notice = request.params['notice'] ? notice : ''
|
|
46
|
-
Coverband::Reporters::HTMLReport.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
Coverband::Reporters::HTMLReport.new(Coverband.configuration.store,
|
|
58
|
+
html: true,
|
|
59
|
+
base_path: base_path,
|
|
60
|
+
notice: notice,
|
|
61
|
+
open_report: false).report
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def settings
|
|
65
|
+
Coverband::Utils::HTMLFormatter.new(nil, base_path: base_path).format_settings!
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def debug_data
|
|
69
|
+
Coverband.configuration.store.get_coverage_report.to_json
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def load_file_details
|
|
73
|
+
filename = request.params['filename']
|
|
74
|
+
Coverband::Reporters::HTMLReport.new(Coverband.configuration.store,
|
|
75
|
+
filename: filename,
|
|
76
|
+
base_path: base_path,
|
|
77
|
+
open_report: false).file_details
|
|
51
78
|
end
|
|
52
79
|
|
|
53
80
|
def collect_coverage
|
|
54
|
-
Coverband
|
|
81
|
+
Coverband.report_coverage(true)
|
|
55
82
|
notice = 'coverband coverage collected'
|
|
56
83
|
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
57
84
|
end
|
|
58
85
|
|
|
59
86
|
def clear
|
|
60
|
-
Coverband.configuration.
|
|
61
|
-
|
|
87
|
+
if Coverband.configuration.web_enable_clear
|
|
88
|
+
Coverband.configuration.store.clear!
|
|
89
|
+
notice = 'coverband coverage cleared'
|
|
90
|
+
else
|
|
91
|
+
notice = 'web_enable_clear isnt enabled in your configuration'
|
|
92
|
+
end
|
|
93
|
+
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def clear_file
|
|
97
|
+
if Coverband.configuration.web_enable_clear
|
|
98
|
+
filename = request.params['filename']
|
|
99
|
+
Coverband.configuration.store.clear_file!(filename)
|
|
100
|
+
notice = "coverage for file #{filename} cleared"
|
|
101
|
+
else
|
|
102
|
+
notice = 'web_enable_clear isnt enabled in your configuration'
|
|
103
|
+
end
|
|
62
104
|
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
63
105
|
end
|
|
64
106
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Applies the configured groups to the given array of Coverband::SourceFile items
|
|
5
|
+
#
|
|
6
|
+
module Coverband
|
|
7
|
+
module Utils
|
|
8
|
+
class FileGroups
|
|
9
|
+
def initialize(files)
|
|
10
|
+
@grouped = {}
|
|
11
|
+
@files = files
|
|
12
|
+
filter_to_groups
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def grouped_results
|
|
16
|
+
@grouped
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def filter_to_groups
|
|
22
|
+
grouped_files = []
|
|
23
|
+
Coverband.configuration.groups.each do |name, filter|
|
|
24
|
+
if name == 'Gems'
|
|
25
|
+
gem_lists = gem_files(name, filter)
|
|
26
|
+
grouped_files.concat(gem_lists.flatten) if gem_lists.flatten.any?
|
|
27
|
+
else
|
|
28
|
+
app_files(name, filter)
|
|
29
|
+
grouped_files += @grouped[name]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
if !Coverband.configuration.groups.empty? && !(other_files = @files.reject do |source_file|
|
|
33
|
+
grouped_files.include?(source_file)
|
|
34
|
+
end).empty?
|
|
35
|
+
@grouped['Ungrouped'] = Coverband::Utils::FileList.new(other_files)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def gem_files(name, filter)
|
|
40
|
+
grouped_gems = @files.select { |source_file| source_file.filename =~ /#{filter}/ }.group_by(&:gem_name)
|
|
41
|
+
gem_lists = grouped_gems.values.map { |gem_files| Coverband::Utils::FileList.new(gem_files) }
|
|
42
|
+
@grouped[name] = Coverband::Utils::GemList.new(gem_lists) if gem_lists.flatten.any?
|
|
43
|
+
gem_lists
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def app_files(name, filter)
|
|
47
|
+
@grouped[name] = Coverband::Utils::FileList.new(@files.select do |source_file|
|
|
48
|
+
source_file.filename =~ /#{filter}/ && source_file.filename !~ /#{Coverband.configuration.gem_paths.first}/
|
|
49
|
+
end)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
####
|
|
4
|
+
# Helper functions for shared logic related to file path manipulation
|
|
5
|
+
####
|
|
6
|
+
module Coverband
|
|
7
|
+
module Utils
|
|
8
|
+
module FilePathHelper
|
|
9
|
+
###
|
|
10
|
+
# Takes a full path and converts to a relative path
|
|
11
|
+
###
|
|
12
|
+
def full_path_to_relative(full_path)
|
|
13
|
+
relative_filename = full_path
|
|
14
|
+
Coverband.configuration.all_root_paths.each do |root|
|
|
15
|
+
relative_filename = relative_filename.gsub(/^#{root}/, './')
|
|
16
|
+
# once we have a relative path break out of the loop
|
|
17
|
+
break if relative_filename.start_with? './'
|
|
18
|
+
end
|
|
19
|
+
relative_filename
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
###
|
|
23
|
+
# relative_path_to_full code takes:
|
|
24
|
+
# relative_path: which is a full path the same as reported by Coverage
|
|
25
|
+
# roots: if a collection of all possible full app paths
|
|
26
|
+
# EX: [Coverband.configuration.root_paths, "#{current_root}/"]
|
|
27
|
+
# The LAST item should be the current file system root
|
|
28
|
+
# it expands that expands and adds a '/' as that isn't there from Dir.pwd
|
|
29
|
+
#
|
|
30
|
+
# NOTEs on configuration.root_paths usage
|
|
31
|
+
# strings: matching is pretty simple for full string paths
|
|
32
|
+
# regex: to get regex to work for changing deploy directories
|
|
33
|
+
# the regex must be double escaped in double quotes
|
|
34
|
+
# (if using \d for example)
|
|
35
|
+
# or use single qoutes
|
|
36
|
+
# example: '/box/apps/app_name/releases/\d+/'
|
|
37
|
+
# example: '/var/local/company/company.d/[0-9]*/'
|
|
38
|
+
###
|
|
39
|
+
def relative_path_to_full(relative_path, roots)
|
|
40
|
+
relative_filename = relative_path
|
|
41
|
+
local_filename = relative_filename
|
|
42
|
+
roots.each do |root|
|
|
43
|
+
relative_filename = relative_filename.gsub(/^#{root}/, './')
|
|
44
|
+
end
|
|
45
|
+
# the filename for our reports is expected to be a full path.
|
|
46
|
+
# roots.last should be roots << current_root}/
|
|
47
|
+
# a fully expanded path of config.root
|
|
48
|
+
# filename = filename.gsub('./', roots.last)
|
|
49
|
+
# above only works for app files
|
|
50
|
+
# we need to rethink some of this logic
|
|
51
|
+
# gems aren't at project root and can have multiple locations
|
|
52
|
+
local_root = roots.find { |root| File.exist?(relative_filename.gsub('./', root)) }
|
|
53
|
+
local_root ? relative_filename.gsub('./', local_root) : local_filename
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
####
|
|
4
|
+
# An array of FileLists instances with helpers to roll up the stats
|
|
5
|
+
# methods for calculating coverage across them etc.
|
|
6
|
+
####
|
|
7
|
+
module Coverband
|
|
8
|
+
module Utils
|
|
9
|
+
class GemList < FileList
|
|
10
|
+
# Returns the count of lines that have coverage
|
|
11
|
+
def covered_lines
|
|
12
|
+
to_a.map(&:covered_lines).inject(:+)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Returns the count of lines that have been missed
|
|
16
|
+
def missed_lines
|
|
17
|
+
to_a.map(&:missed_lines).inject(:+)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Returns the count of lines that are not relevant for coverage
|
|
21
|
+
def never_lines
|
|
22
|
+
to_a.map(&:never_lines).inject(:+)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Returns the count of skipped lines
|
|
26
|
+
def skipped_lines
|
|
27
|
+
to_a.map(&:skipped_lines).inject(:+)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -18,7 +18,7 @@ module Coverband
|
|
|
18
18
|
def initialize(report, options = {})
|
|
19
19
|
@notice = options.fetch(:notice) { nil }
|
|
20
20
|
@base_path = options.fetch(:base_path) { nil }
|
|
21
|
-
@coverage_result = Coverband::Utils::
|
|
21
|
+
@coverage_result = Coverband::Utils::Results.new(report) if report
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def format!
|
|
@@ -29,8 +29,22 @@ module Coverband
|
|
|
29
29
|
format_html(@coverage_result)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
def format_settings!
|
|
33
|
+
format_settings
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def format_source_file!(filename)
|
|
37
|
+
source_file = @coverage_result.file_from_path_with_type(filename)
|
|
38
|
+
|
|
39
|
+
formatted_source_file(@coverage_result, source_file)
|
|
40
|
+
end
|
|
41
|
+
|
|
32
42
|
private
|
|
33
43
|
|
|
44
|
+
def format_settings
|
|
45
|
+
template('settings').result(binding)
|
|
46
|
+
end
|
|
47
|
+
|
|
34
48
|
def format(result)
|
|
35
49
|
Dir[File.join(File.dirname(__FILE__), '../../../public/*')].each do |path|
|
|
36
50
|
FileUtils.cp_r(path, asset_output_path)
|
|
@@ -56,7 +70,7 @@ module Coverband
|
|
|
56
70
|
|
|
57
71
|
def asset_output_path
|
|
58
72
|
return @asset_output_path if defined?(@asset_output_path) && @asset_output_path
|
|
59
|
-
@asset_output_path = File.join(output_path
|
|
73
|
+
@asset_output_path = File.join(output_path)
|
|
60
74
|
FileUtils.mkdir_p(@asset_output_path)
|
|
61
75
|
@asset_output_path
|
|
62
76
|
end
|
|
@@ -78,24 +92,47 @@ module Coverband
|
|
|
78
92
|
end
|
|
79
93
|
|
|
80
94
|
# Returns the html for the given source_file
|
|
81
|
-
def formatted_source_file(source_file)
|
|
95
|
+
def formatted_source_file(result, source_file)
|
|
82
96
|
template('source_file').result(binding)
|
|
83
97
|
rescue Encoding::CompatibilityError => e
|
|
84
|
-
puts "Encoding
|
|
98
|
+
puts "Encoding error file:#{source_file.filename} Coverband/ERB error #{e.message}."
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Returns the html to ajax load a given source_file
|
|
102
|
+
def formatted_source_file_loader(result, source_file)
|
|
103
|
+
template('source_file_loader').result(binding)
|
|
104
|
+
rescue Encoding::CompatibilityError => e
|
|
105
|
+
puts "Encoding error file:#{source_file.filename} Coverband/ERB error #{e.message}."
|
|
85
106
|
end
|
|
86
107
|
|
|
87
108
|
# Returns a table containing the given source files
|
|
88
|
-
def formatted_file_list(title, source_files)
|
|
109
|
+
def formatted_file_list(title, result, source_files, options = {})
|
|
89
110
|
title_id = title.gsub(/^[^a-zA-Z]+/, '').gsub(/[^a-zA-Z0-9\-\_]/, '')
|
|
90
111
|
# Silence a warning by using the following variable to assign to itself:
|
|
91
112
|
# "warning: possibly useless use of a variable in void context"
|
|
92
113
|
# The variable is used by ERB via binding.
|
|
93
114
|
title_id = title_id
|
|
94
|
-
|
|
115
|
+
options = options
|
|
116
|
+
|
|
117
|
+
if title == 'Gems'
|
|
118
|
+
template('gem_list').result(binding)
|
|
119
|
+
else
|
|
120
|
+
template('file_list').result(binding)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def view_gems?
|
|
125
|
+
Coverband.configuration.track_gems
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def gem_details?
|
|
129
|
+
Coverband.configuration.gem_details
|
|
95
130
|
end
|
|
96
131
|
|
|
97
132
|
def coverage_css_class(covered_percent)
|
|
98
|
-
if covered_percent
|
|
133
|
+
if covered_percent.nil?
|
|
134
|
+
''
|
|
135
|
+
elsif covered_percent > 90
|
|
99
136
|
'green'
|
|
100
137
|
elsif covered_percent > 80
|
|
101
138
|
'yellow'
|
|
@@ -124,7 +161,12 @@ module Coverband
|
|
|
124
161
|
end
|
|
125
162
|
|
|
126
163
|
def shortened_filename(source_file)
|
|
127
|
-
source_file.
|
|
164
|
+
source_file.short_name
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def link_to_gem_list(gem_name)
|
|
168
|
+
gem_id = gem_name.gsub(/^[^a-zA-Z]+/, '').gsub(/[^a-zA-Z0-9\-\_]/, '')
|
|
169
|
+
%(<a href="##{gem_id}" class="gem-link" title="#{gem_name}">#{gem_name}</a>)
|
|
128
170
|
end
|
|
129
171
|
|
|
130
172
|
def link_to_source_file(source_file)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
Coverband.eager_loading_coverage!
|
|
3
4
|
module Coverband
|
|
4
5
|
class Railtie < Rails::Railtie
|
|
5
6
|
initializer 'coverband.configure' do |app|
|
|
@@ -7,7 +8,14 @@ module Coverband
|
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
config.after_initialize do
|
|
10
|
-
Coverband
|
|
11
|
+
Coverband.report_coverage(true)
|
|
12
|
+
Coverband.configuration.logger&.debug('Coverband: reported after_initialize')
|
|
13
|
+
Coverband.runtime_coverage!
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
config.before_initialize do
|
|
17
|
+
Coverband.configuration.logger&.debug('Coverband: set to eager_loading')
|
|
18
|
+
Coverband.eager_loading_coverage!
|
|
11
19
|
end
|
|
12
20
|
|
|
13
21
|
rake_tasks do
|
|
@@ -14,26 +14,29 @@ module Coverband
|
|
|
14
14
|
module Utils
|
|
15
15
|
class Result
|
|
16
16
|
extend Forwardable
|
|
17
|
-
# Returns the original Coverage.result used for this instance of
|
|
17
|
+
# Returns the original Coverage.result used for this instance of Coverband::Result
|
|
18
18
|
attr_reader :original_result
|
|
19
|
-
# Returns all files that are applicable to this result (sans filters!)
|
|
19
|
+
# Returns all files that are applicable to this result (sans filters!)
|
|
20
|
+
# as instances of Coverband::SourceFile. Aliased as :source_files
|
|
20
21
|
attr_reader :files
|
|
21
22
|
alias source_files files
|
|
22
23
|
# Explicitly set the Time this result has been created
|
|
23
24
|
attr_writer :created_at
|
|
24
|
-
# Explicitly set the command name that was used for this coverage result.
|
|
25
|
+
# Explicitly set the command name that was used for this coverage result.
|
|
26
|
+
# Defaults to Coverband.command_name
|
|
25
27
|
attr_writer :command_name
|
|
26
28
|
|
|
27
29
|
def_delegators :files, :covered_percent, :covered_percentages, :least_covered_file, :covered_strength, :covered_lines, :missed_lines
|
|
28
30
|
def_delegator :files, :lines_of_code, :total_lines
|
|
29
31
|
|
|
30
|
-
# Initialize a new
|
|
32
|
+
# Initialize a new Coverband::Result from given Coverage.result (a Hash of filenames each containing an array of
|
|
31
33
|
# coverage data)
|
|
32
34
|
def initialize(original_result)
|
|
33
|
-
@original_result = original_result.freeze
|
|
34
|
-
|
|
35
|
+
@original_result = (original_result || {}).freeze
|
|
36
|
+
|
|
37
|
+
@files = Coverband::Utils::FileList.new(@original_result.map do |filename, coverage|
|
|
35
38
|
Coverband::Utils::SourceFile.new(filename, coverage) if File.file?(filename)
|
|
36
|
-
end.compact.sort_by(&:
|
|
39
|
+
end.compact.sort_by(&:short_name))
|
|
37
40
|
filter!
|
|
38
41
|
end
|
|
39
42
|
|
|
@@ -42,15 +45,9 @@ module Coverband
|
|
|
42
45
|
files.map(&:filename)
|
|
43
46
|
end
|
|
44
47
|
|
|
45
|
-
# Returns a Hash of groups for this result. Define groups using
|
|
46
|
-
# Coverband doesn't currently support groups
|
|
48
|
+
# Returns a Hash of groups for this result. Define groups using Coverband.add_group 'Models', 'app/models'
|
|
47
49
|
def groups
|
|
48
|
-
@groups ||=
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Applies the configured SimpleCov.formatter on this result
|
|
52
|
-
def format!
|
|
53
|
-
# SimpleCov.formatter.new.format(self)
|
|
50
|
+
@groups ||= FileGroups.new(files).grouped_results
|
|
54
51
|
end
|
|
55
52
|
|
|
56
53
|
# Defines when this result has been created. Defaults to Time.now
|
|
@@ -59,9 +56,9 @@ module Coverband
|
|
|
59
56
|
end
|
|
60
57
|
|
|
61
58
|
# The command name that launched this result.
|
|
62
|
-
# Delegated to
|
|
59
|
+
# Delegated to Coverband.command_name if not set manually
|
|
63
60
|
def command_name
|
|
64
|
-
@command_name ||= '
|
|
61
|
+
@command_name ||= 'Coverband'
|
|
65
62
|
end
|
|
66
63
|
|
|
67
64
|
# Returns a hash representation of this Result that can be used for marshalling it into JSON
|
|
@@ -69,7 +66,7 @@ module Coverband
|
|
|
69
66
|
{ command_name => { 'coverage' => coverage, 'timestamp' => created_at.to_i } }
|
|
70
67
|
end
|
|
71
68
|
|
|
72
|
-
# Loads a
|
|
69
|
+
# Loads a Coverband::Result#to_hash dump
|
|
73
70
|
def self.from_hash(hash)
|
|
74
71
|
command_name, data = hash.first
|
|
75
72
|
result = new(data['coverage'])
|
|
@@ -100,7 +97,7 @@ module Coverband
|
|
|
100
97
|
Hash[keys.zip(original_result.values_at(*keys))]
|
|
101
98
|
end
|
|
102
99
|
|
|
103
|
-
# Applies all configured
|
|
100
|
+
# Applies all configured Coverband filters on this result's source files
|
|
104
101
|
def filter!
|
|
105
102
|
@files = files
|
|
106
103
|
end
|