coverband 4.1.0 → 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 +4 -2
- data/README.md +74 -51
- data/Rakefile +1 -1
- data/changes.md +45 -25
- data/coverband.gemspec +4 -0
- data/lib/coverband/adapters/base.rb +24 -1
- data/lib/coverband/adapters/redis_store.rb +29 -8
- data/lib/coverband/at_exit.rb +19 -2
- data/lib/coverband/collectors/coverage.rb +35 -73
- data/lib/coverband/collectors/delta.rb +67 -0
- data/lib/coverband/integrations/background.rb +4 -3
- data/lib/coverband/integrations/rack_server_check.rb +4 -1
- data/lib/coverband/integrations/resque.rb +4 -1
- data/lib/coverband/reporters/base.rb +21 -14
- data/lib/coverband/reporters/html_report.rb +40 -15
- data/lib/coverband/reporters/web.rb +29 -6
- data/lib/coverband/utils/html_formatter.rb +20 -4
- data/lib/coverband/utils/railtie.rb +8 -0
- data/lib/coverband/utils/result.rb +3 -2
- data/lib/coverband/utils/results.rb +63 -0
- data/lib/coverband/utils/source_file.rb +5 -0
- data/lib/coverband/version.rb +1 -1
- data/lib/coverband.rb +23 -3
- data/public/application.css +5 -0
- data/public/application.js +108 -1644
- data/public/dependencies.js +1581 -0
- data/public/favicon.png +0 -0
- data/test/benchmarks/benchmark.rake +31 -0
- 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} +27 -10
- data/test/coverband/collectors/coverage_test.rb +73 -0
- data/test/coverband/collectors/delta_test.rb +52 -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 +2 -1
- data/test/{unit/rack_server_checkout_test.rb → coverband/integrations/rack_server_check_test.rb} +1 -1
- data/test/{unit → coverband/integrations}/resque_worker_test.rb +9 -3
- data/test/{unit/reports_base_test.rb → coverband/reporters/base_test.rb} +5 -5
- data/test/{unit/reports_console_test.rb → coverband/reporters/console_test.rb} +2 -2
- data/test/{unit/reports_html_test.rb → coverband/reporters/html_test.rb} +7 -8
- data/test/{unit/reports_web_test.rb → coverband/reporters/web_test.rb} +2 -2
- data/test/{unit → integration}/full_stack_test.rb +10 -8
- data/test/{unit → integration}/rails_full_stack_test.rb +23 -4
- 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 +9 -13
- data/test/unique_files.rb +23 -0
- data/views/file_list.erb +9 -0
- data/views/gem_list.erb +1 -1
- data/views/layout.erb +4 -3
- data/views/source_file.erb +16 -4
- data/views/source_file_loader.erb +7 -0
- metadata +100 -59
- data/test/unit/background_test.rb +0 -28
- data/test/unit/collectors_coverage_test.rb +0 -53
- data/test/unit/rails_gems_full_stack_test.rb +0 -36
- /data/test/{unit → coverband}/at_exit_test.rb +0 -0
- /data/test/{unit → coverband}/configuration_test.rb +0 -0
- /data/test/{unit → coverband/integrations}/test_resque_job.rb +0 -0
- /data/test/{unit → coverband}/utils/file_groups_test.rb +0 -0
- /data/test/{unit → coverband}/utils/file_list_test.rb +0 -0
- /data/test/{unit → coverband}/utils/gem_list_test.rb +0 -0
- /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 → coverband}/utils/source_file_test.rb +0 -0
- /data/test/{unit/dog.rb → dog.rb} +0 -0
|
@@ -1,48 +1,60 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require 'singleton'
|
|
3
|
+
require_relative 'delta'
|
|
2
4
|
|
|
3
5
|
module Coverband
|
|
4
6
|
module Collectors
|
|
5
7
|
###
|
|
6
|
-
#
|
|
7
|
-
# StandardError seems line be better option
|
|
8
|
+
# StandardError seems like be better option
|
|
8
9
|
# coverband previously had RuntimeError here
|
|
9
10
|
# but runtime error can let a large number of error crash this method
|
|
10
11
|
# and this method is currently in a ensure block in middleware and threads
|
|
11
12
|
###
|
|
12
13
|
class Coverage
|
|
13
|
-
|
|
14
|
-
Thread.current[:coverband_instance] ||= Coverband::Collectors::Coverage.new
|
|
15
|
-
end
|
|
14
|
+
include Singleton
|
|
16
15
|
|
|
17
16
|
def reset_instance
|
|
18
17
|
@project_directory = File.expand_path(Coverband.configuration.root)
|
|
19
|
-
@file_line_usage = {}
|
|
20
18
|
@ignore_patterns = Coverband.configuration.ignore + ['internal:prelude', 'schema.rb']
|
|
21
19
|
@reporting_frequency = Coverband.configuration.reporting_frequency
|
|
22
20
|
@store = Coverband.configuration.store
|
|
23
21
|
@verbose = Coverband.configuration.verbose
|
|
24
22
|
@logger = Coverband.configuration.logger
|
|
25
|
-
@current_thread = Thread.current
|
|
26
23
|
@test_env = Coverband.configuration.test_env
|
|
27
24
|
@track_gems = Coverband.configuration.track_gems
|
|
28
|
-
|
|
29
|
-
Thread.current[:coverband_instance] = nil
|
|
25
|
+
Delta.reset
|
|
30
26
|
self
|
|
31
27
|
end
|
|
32
28
|
|
|
29
|
+
def runtime!
|
|
30
|
+
@store.type = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def eager_loading!
|
|
34
|
+
@store.type = Coverband::EAGER_TYPE
|
|
35
|
+
end
|
|
36
|
+
|
|
33
37
|
def report_coverage(force_report = false)
|
|
34
38
|
return if !ready_to_report? && !force_report
|
|
35
39
|
raise 'no Coverband store set' unless @store
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
original_previous_set = Delta.previous_results
|
|
42
|
+
files_with_line_usage = filtered_files(Delta.results)
|
|
43
|
+
|
|
44
|
+
###
|
|
45
|
+
# Hack to prevent processes and threads from reporting first Coverage hit
|
|
46
|
+
# when we are in runtime collection mode, which do not have a cache of previous
|
|
47
|
+
# coverage to remove the initial stdlib Coverage loading data
|
|
48
|
+
###
|
|
49
|
+
if ((original_previous_set.nil? && @store.type == Coverband::EAGER_TYPE) ||
|
|
50
|
+
(original_previous_set && @store.type != Coverband::EAGER_TYPE))
|
|
51
|
+
@store.save_report(files_with_line_usage)
|
|
52
|
+
end
|
|
41
53
|
rescue StandardError => err
|
|
42
54
|
if @verbose
|
|
43
|
-
@logger
|
|
44
|
-
@logger
|
|
45
|
-
@logger
|
|
55
|
+
@logger&.error 'coverage failed to store'
|
|
56
|
+
@logger&.error "error: #{err.inspect} #{err.message}"
|
|
57
|
+
@logger&.error err.backtrace
|
|
46
58
|
end
|
|
47
59
|
raise err if @test_env
|
|
48
60
|
end
|
|
@@ -59,75 +71,27 @@ module Coverband
|
|
|
59
71
|
@ignore_patterns.none? do |pattern|
|
|
60
72
|
file.include?(pattern)
|
|
61
73
|
end && (file.start_with?(@project_directory) ||
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
(@track_gems &&
|
|
75
|
+
Coverband.configuration.gem_paths.any? { |path| file.start_with?(path) }))
|
|
64
76
|
end
|
|
65
77
|
|
|
66
78
|
private
|
|
67
79
|
|
|
68
|
-
def
|
|
69
|
-
new_results.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
end
|
|
80
|
+
def filtered_files(new_results)
|
|
81
|
+
new_results.each_with_object({}) do |(file, line_counts), file_line_usage|
|
|
82
|
+
file_line_usage[file] = line_counts if track_file?(file)
|
|
83
|
+
end.select { |_file_name, coverage| coverage.any? { |value| value&.nonzero? } }
|
|
73
84
|
end
|
|
74
85
|
|
|
75
86
|
def ready_to_report?
|
|
76
87
|
(rand * 100.0) >= (100.0 - @reporting_frequency)
|
|
77
88
|
end
|
|
78
89
|
|
|
79
|
-
def get_new_coverage_results
|
|
80
|
-
@semaphore.synchronize { new_coverage(::Coverage.peek_result.dup) }
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def files_with_line_usage
|
|
84
|
-
@file_line_usage.select do |_file_name, coverage|
|
|
85
|
-
coverage.any? { |value| value&.nonzero? }
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def array_diff(latest, original)
|
|
90
|
-
latest.map.with_index { |v, i| (v && original[i]) ? v - original[i] : nil }
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def previous_results
|
|
94
|
-
@@previous_results
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def add_previous_results(val)
|
|
98
|
-
@@previous_results = val
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def new_coverage(current_coverage)
|
|
102
|
-
if previous_results
|
|
103
|
-
new_results = {}
|
|
104
|
-
current_coverage.each_pair do |file, line_counts|
|
|
105
|
-
if previous_results[file]
|
|
106
|
-
new_results[file] = array_diff(line_counts, previous_results[file])
|
|
107
|
-
else
|
|
108
|
-
new_results[file] = line_counts
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
else
|
|
112
|
-
new_results = current_coverage
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
add_previous_results(current_coverage)
|
|
116
|
-
new_results.dup
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def add_file(file, line_counts)
|
|
120
|
-
@file_line_usage[file] = line_counts
|
|
121
|
-
end
|
|
122
|
-
|
|
123
90
|
def initialize
|
|
124
91
|
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3.0')
|
|
125
92
|
raise NotImplementedError, 'not supported until Ruby 2.3.0 and later'
|
|
126
93
|
end
|
|
127
|
-
|
|
128
|
-
# puts 'loading coverage'
|
|
129
|
-
require 'coverage'
|
|
130
|
-
end
|
|
94
|
+
require 'coverage'
|
|
131
95
|
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
|
|
132
96
|
::Coverage.start unless ::Coverage.running?
|
|
133
97
|
else
|
|
@@ -138,8 +102,6 @@ module Coverband
|
|
|
138
102
|
load safe_file
|
|
139
103
|
end
|
|
140
104
|
end
|
|
141
|
-
@semaphore = Mutex.new
|
|
142
|
-
@@previous_results = nil
|
|
143
105
|
reset_instance
|
|
144
106
|
end
|
|
145
107
|
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Coverband
|
|
3
|
+
module Collectors
|
|
4
|
+
class Delta
|
|
5
|
+
@semaphore = Mutex.new
|
|
6
|
+
@@previous_coverage = nil
|
|
7
|
+
attr_reader :current_coverage
|
|
8
|
+
|
|
9
|
+
def initialize(current_coverage)
|
|
10
|
+
@current_coverage = current_coverage
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class RubyCoverage
|
|
14
|
+
def self.results
|
|
15
|
+
::Coverage.peek_result.dup
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.results(process_coverage = RubyCoverage)
|
|
20
|
+
@semaphore.synchronize do
|
|
21
|
+
set_default_results
|
|
22
|
+
new(process_coverage.results).results
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.previous_results
|
|
27
|
+
@@previous_coverage
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.set_default_results
|
|
31
|
+
@@previous_coverage ||= {}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def results
|
|
35
|
+
new_results = generate
|
|
36
|
+
@@previous_coverage = current_coverage
|
|
37
|
+
new_results
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.reset
|
|
41
|
+
@@previous_coverage = nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def generate
|
|
47
|
+
current_coverage.each_with_object({}) do |(file, line_counts), new_results|
|
|
48
|
+
if @@previous_coverage[file]
|
|
49
|
+
new_results[file] = array_diff(line_counts, @@previous_coverage[file])
|
|
50
|
+
else
|
|
51
|
+
new_results[file] = line_counts
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def array_diff(latest, original)
|
|
57
|
+
latest.map.with_index do |v, i|
|
|
58
|
+
if (v && original[i])
|
|
59
|
+
[0, v - original[i]].max
|
|
60
|
+
else
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module Coverband
|
|
4
4
|
class Background
|
|
5
5
|
@semaphore = Mutex.new
|
|
6
|
+
@thread = nil
|
|
6
7
|
|
|
7
8
|
def self.stop
|
|
8
9
|
return unless @thread
|
|
@@ -15,15 +16,15 @@ module Coverband
|
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def self.running?
|
|
18
|
-
|
|
19
|
+
@thread && @thread.alive?
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def self.start
|
|
22
|
-
return if
|
|
23
|
+
return if running?
|
|
23
24
|
|
|
24
25
|
logger = Coverband.configuration.logger
|
|
25
26
|
@semaphore.synchronize do
|
|
26
|
-
return if
|
|
27
|
+
return if running?
|
|
27
28
|
logger&.debug('Coverband: Starting background reporting')
|
|
28
29
|
sleep_seconds = Coverband.configuration.background_reporting_sleep_seconds
|
|
29
30
|
@thread = Thread.new do
|
|
@@ -20,7 +20,10 @@ module Coverband
|
|
|
20
20
|
|
|
21
21
|
def rails_server?
|
|
22
22
|
@stack.any? do |location|
|
|
23
|
-
|
|
23
|
+
(
|
|
24
|
+
location.path.include?('rails/commands/commands_tasks.rb') && location.label == 'server' ||
|
|
25
|
+
location.path.include?('rails/commands/server/server_command.rb') && location.label == 'perform'
|
|
26
|
+
)
|
|
24
27
|
end
|
|
25
28
|
end
|
|
26
29
|
end
|
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Resque.after_fork do |job|
|
|
4
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
|
|
5
8
|
end
|
|
6
9
|
|
|
7
10
|
Resque.before_first_fork do
|
|
11
|
+
Coverband.eager_loading_coverage!
|
|
8
12
|
Coverband.configuration.background_reporting_enabled = false
|
|
9
13
|
Coverband::Background.stop
|
|
10
14
|
Coverband::Collectors::Coverage.instance.report_coverage(true)
|
|
@@ -21,4 +25,3 @@ module Coverband
|
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
Resque::Job.prepend(Coverband::ResqueWorker)
|
|
24
|
-
|
|
@@ -15,7 +15,7 @@ module Coverband
|
|
|
15
15
|
|
|
16
16
|
if Coverband.configuration.verbose
|
|
17
17
|
msg = "report:\n #{scov_style_report.inspect}"
|
|
18
|
-
Coverband.configuration.logger.debug msg
|
|
18
|
+
# Coverband.configuration.logger.debug msg
|
|
19
19
|
end
|
|
20
20
|
scov_style_report
|
|
21
21
|
end
|
|
@@ -28,15 +28,18 @@ module Coverband
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# normalize names across servers
|
|
31
|
-
report_hash.each_with_object({}) do |(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
40
43
|
end
|
|
41
44
|
end
|
|
42
45
|
|
|
@@ -64,10 +67,14 @@ module Coverband
|
|
|
64
67
|
###
|
|
65
68
|
def get_current_scov_data_imp(store, roots)
|
|
66
69
|
scov_style_report = {}
|
|
67
|
-
store.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
|
71
78
|
end
|
|
72
79
|
|
|
73
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
|
|
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
|
|
22
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
|
|
@@ -22,6 +22,8 @@ module Coverband
|
|
|
22
22
|
|
|
23
23
|
if request.post?
|
|
24
24
|
case request.path_info
|
|
25
|
+
when %r{\/clear_file}
|
|
26
|
+
clear_file
|
|
25
27
|
when %r{\/clear}
|
|
26
28
|
clear
|
|
27
29
|
when %r{\/collect_coverage}
|
|
@@ -39,6 +41,8 @@ module Coverband
|
|
|
39
41
|
[200, { 'Content-Type' => 'text/html' }, [settings]]
|
|
40
42
|
when %r{\/debug_data}
|
|
41
43
|
[200, { 'Content-Type' => 'text/json' }, [debug_data]]
|
|
44
|
+
when %r{\/load_file_details}
|
|
45
|
+
[200, { 'Content-Type' => 'text/json' }, [load_file_details]]
|
|
42
46
|
when %r{\/$}
|
|
43
47
|
[200, { 'Content-Type' => 'text/html' }, [index]]
|
|
44
48
|
else
|
|
@@ -50,11 +54,11 @@ module Coverband
|
|
|
50
54
|
def index
|
|
51
55
|
notice = "<strong>Notice:</strong> #{Rack::Utils.escape_html(request.params['notice'])}<br/>"
|
|
52
56
|
notice = request.params['notice'] ? notice : ''
|
|
53
|
-
Coverband::Reporters::HTMLReport.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
Coverband::Reporters::HTMLReport.new(Coverband.configuration.store,
|
|
58
|
+
html: true,
|
|
59
|
+
base_path: base_path,
|
|
60
|
+
notice: notice,
|
|
61
|
+
open_report: false).report
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
def settings
|
|
@@ -62,7 +66,15 @@ module Coverband
|
|
|
62
66
|
end
|
|
63
67
|
|
|
64
68
|
def debug_data
|
|
65
|
-
Coverband.configuration.store.
|
|
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
|
|
66
78
|
end
|
|
67
79
|
|
|
68
80
|
def collect_coverage
|
|
@@ -81,6 +93,17 @@ module Coverband
|
|
|
81
93
|
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
82
94
|
end
|
|
83
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
|
|
104
|
+
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
105
|
+
end
|
|
106
|
+
|
|
84
107
|
def reload_files
|
|
85
108
|
Coverband.configuration&.safe_reload_files&.each do |safe_file|
|
|
86
109
|
load safe_file
|
|
@@ -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!
|
|
@@ -33,6 +33,12 @@ module Coverband
|
|
|
33
33
|
format_settings
|
|
34
34
|
end
|
|
35
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
|
+
|
|
36
42
|
private
|
|
37
43
|
|
|
38
44
|
def format_settings
|
|
@@ -86,20 +92,28 @@ module Coverband
|
|
|
86
92
|
end
|
|
87
93
|
|
|
88
94
|
# Returns the html for the given source_file
|
|
89
|
-
def formatted_source_file(source_file)
|
|
95
|
+
def formatted_source_file(result, source_file)
|
|
90
96
|
template('source_file').result(binding)
|
|
91
97
|
rescue Encoding::CompatibilityError => e
|
|
92
98
|
puts "Encoding error file:#{source_file.filename} Coverband/ERB error #{e.message}."
|
|
93
99
|
end
|
|
94
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}."
|
|
106
|
+
end
|
|
107
|
+
|
|
95
108
|
# Returns a table containing the given source files
|
|
96
|
-
def formatted_file_list(title, source_files, options = {})
|
|
109
|
+
def formatted_file_list(title, result, source_files, options = {})
|
|
97
110
|
title_id = title.gsub(/^[^a-zA-Z]+/, '').gsub(/[^a-zA-Z0-9\-\_]/, '')
|
|
98
111
|
# Silence a warning by using the following variable to assign to itself:
|
|
99
112
|
# "warning: possibly useless use of a variable in void context"
|
|
100
113
|
# The variable is used by ERB via binding.
|
|
101
114
|
title_id = title_id
|
|
102
115
|
options = options
|
|
116
|
+
|
|
103
117
|
if title == 'Gems'
|
|
104
118
|
template('gem_list').result(binding)
|
|
105
119
|
else
|
|
@@ -116,7 +130,9 @@ module Coverband
|
|
|
116
130
|
end
|
|
117
131
|
|
|
118
132
|
def coverage_css_class(covered_percent)
|
|
119
|
-
if covered_percent
|
|
133
|
+
if covered_percent.nil?
|
|
134
|
+
''
|
|
135
|
+
elsif covered_percent > 90
|
|
120
136
|
'green'
|
|
121
137
|
elsif covered_percent > 80
|
|
122
138
|
'yellow'
|
|
@@ -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|
|
|
@@ -8,6 +9,13 @@ module Coverband
|
|
|
8
9
|
|
|
9
10
|
config.after_initialize do
|
|
10
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
|
|
@@ -32,8 +32,9 @@ module Coverband
|
|
|
32
32
|
# Initialize a new Coverband::Result from given Coverage.result (a Hash of filenames each containing an array of
|
|
33
33
|
# coverage data)
|
|
34
34
|
def initialize(original_result)
|
|
35
|
-
@original_result = original_result.freeze
|
|
36
|
-
|
|
35
|
+
@original_result = (original_result || {}).freeze
|
|
36
|
+
|
|
37
|
+
@files = Coverband::Utils::FileList.new(@original_result.map do |filename, coverage|
|
|
37
38
|
Coverband::Utils::SourceFile.new(filename, coverage) if File.file?(filename)
|
|
38
39
|
end.compact.sort_by(&:short_name))
|
|
39
40
|
filter!
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
####
|
|
4
|
+
# A way to access the various coverage data breakdowns
|
|
5
|
+
####
|
|
6
|
+
module Coverband
|
|
7
|
+
module Utils
|
|
8
|
+
class Results
|
|
9
|
+
attr_accessor :type, :report
|
|
10
|
+
|
|
11
|
+
def initialize(report)
|
|
12
|
+
self.report = report
|
|
13
|
+
self.type = Coverband::MERGED_TYPE
|
|
14
|
+
@results = {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def file_with_type(source_file, results_type)
|
|
18
|
+
return unless get_results(results_type)
|
|
19
|
+
|
|
20
|
+
get_results(results_type).source_files.find { |file| file.filename == source_file.filename }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def file_from_path_with_type(full_path, results_type = :merged)
|
|
24
|
+
return unless get_results(results_type)
|
|
25
|
+
|
|
26
|
+
get_results(results_type).source_files.find { |file| file.filename == full_path }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def method_missing(method, *args)
|
|
30
|
+
if get_results(type).respond_to?(method)
|
|
31
|
+
get_results(type).send(method, *args)
|
|
32
|
+
else
|
|
33
|
+
super
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def respond_to_missing?(method)
|
|
38
|
+
if get_results(type).respond_to?(method)
|
|
39
|
+
true
|
|
40
|
+
else
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
###
|
|
48
|
+
# This is a first version of lazy loading the results
|
|
49
|
+
# for the full advantage we need to push lazy loading to the file level
|
|
50
|
+
# inside Coverband::Utils::Result
|
|
51
|
+
###
|
|
52
|
+
def get_results(type)
|
|
53
|
+
return nil unless Coverband::ALL_TYPES.include?(type)
|
|
54
|
+
|
|
55
|
+
if @results.key?(type)
|
|
56
|
+
@results[type]
|
|
57
|
+
else
|
|
58
|
+
@results[type] = Coverband::Utils::Result.new(report[type])
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
module Coverband
|
|
11
11
|
module Utils
|
|
12
12
|
class SourceFile
|
|
13
|
+
include Coverband::Utils::FilePathHelper
|
|
13
14
|
# Representation of a single line in a source file including
|
|
14
15
|
# this specific line's source code, line_number and code coverage,
|
|
15
16
|
# with the coverage being either nil (coverage not applicable, e.g. comment
|
|
@@ -219,6 +220,10 @@ module Coverband
|
|
|
219
220
|
.gsub(%r{^\.\/}, '')
|
|
220
221
|
end
|
|
221
222
|
|
|
223
|
+
def relative_path
|
|
224
|
+
full_path_to_relative(filename)
|
|
225
|
+
end
|
|
226
|
+
|
|
222
227
|
def gem?
|
|
223
228
|
filename =~ %r{^.*\/gems\/}
|
|
224
229
|
end
|
data/lib/coverband/version.rb
CHANGED