coverband 6.0.1 → 6.0.3.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +10 -0
- data/.github/workflows/main.yml +2 -3
- data/.standard.yml +1 -1
- data/Gemfile +1 -1
- data/LICENSE +1 -1
- data/README.md +24 -10
- data/Rakefile +1 -1
- data/changes.md +12 -1
- data/coverband.gemspec +3 -3
- data/lib/coverband/adapters/base.rb +10 -10
- data/lib/coverband/adapters/file_store.rb +1 -1
- data/lib/coverband/adapters/hash_redis_store.rb +160 -10
- data/lib/coverband/adapters/null_store.rb +1 -1
- data/lib/coverband/adapters/stdout_store.rb +1 -1
- data/lib/coverband/adapters/web_service_store.rb +5 -7
- data/lib/coverband/collectors/coverage.rb +1 -1
- data/lib/coverband/collectors/route_tracker.rb +1 -1
- data/lib/coverband/collectors/translation_tracker.rb +2 -2
- data/lib/coverband/collectors/view_tracker.rb +1 -1
- data/lib/coverband/configuration.rb +12 -6
- data/lib/coverband/integrations/rack_server_check.rb +1 -3
- data/lib/coverband/reporters/base.rb +7 -7
- data/lib/coverband/reporters/html_report.rb +12 -4
- data/lib/coverband/reporters/json_report.rb +42 -4
- data/lib/coverband/reporters/web.rb +16 -2
- data/lib/coverband/reporters/web_pager.rb +28 -0
- data/lib/coverband/utils/absolute_file_converter.rb +19 -19
- data/lib/coverband/utils/file_hasher.rb +3 -3
- data/lib/coverband/utils/html_formatter.rb +4 -4
- data/lib/coverband/utils/method_definition_scanner.rb +1 -1
- data/lib/coverband/utils/railtie.rb +4 -6
- data/lib/coverband/utils/relative_file_converter.rb +7 -7
- data/lib/coverband/utils/results.rb +14 -14
- data/lib/coverband/utils/source_file.rb +2 -2
- data/lib/coverband/utils/tasks.rb +1 -1
- data/lib/coverband/version.rb +1 -1
- data/lib/coverband.rb +19 -3
- data/public/application.js +35 -0
- data/public/dependencies.js +1 -1
- data/roadmap.md +1 -1
- data/test/benchmarks/benchmark.rake +5 -5
- data/test/coverband/adapters/file_store_test.rb +1 -1
- data/test/coverband/adapters/hash_redis_store_test.rb +48 -0
- data/test/coverband/collectors/coverage_test.rb +1 -1
- data/test/coverband/reporters/json_test.rb +1 -1
- data/test/coverband/utils/source_file_test.rb +11 -11
- data/test/fixtures/casting_invitor.rb +1 -1
- data/test/fixtures/sample.rb +2 -2
- data/test/fixtures/skipped_and_executed.rb +1 -1
- data/test/forked/rails_rake_full_stack_test.rb +1 -1
- data/test/integration/full_stack_send_deferred_eager_test.rb +52 -0
- data/test/test_helper.rb +3 -5
- data/test/unique_files.rb +1 -1
- data/views/file_list.erb +38 -32
- data/views/layout.erb +3 -3
- metadata +17 -139
- data/LICENSE.txt +0 -22
@@ -16,7 +16,8 @@ module Coverband
|
|
16
16
|
attr_writer :logger, :s3_region, :s3_bucket, :s3_access_key_id,
|
17
17
|
:s3_secret_access_key, :password, :api_key, :service_url, :coverband_timeout, :service_dev_mode,
|
18
18
|
:service_test_mode, :process_type, :track_views, :redis_url,
|
19
|
-
:background_reporting_sleep_seconds, :reporting_wiggle
|
19
|
+
:background_reporting_sleep_seconds, :reporting_wiggle,
|
20
|
+
:send_deferred_eager_loading_data
|
20
21
|
|
21
22
|
attr_reader :track_gems, :ignore, :use_oneshot_lines_coverage
|
22
23
|
|
@@ -67,6 +68,7 @@ module Coverband
|
|
67
68
|
@background_reporting_enabled = true
|
68
69
|
@background_reporting_sleep_seconds = nil
|
69
70
|
@defer_eager_loading_data = false
|
71
|
+
@send_deferred_eager_loading_data = true
|
70
72
|
@test_env = nil
|
71
73
|
@web_enable_clear = false
|
72
74
|
@track_views = true
|
@@ -91,7 +93,7 @@ module Coverband
|
|
91
93
|
@coverband_timeout = nil
|
92
94
|
@service_dev_mode = nil
|
93
95
|
@service_test_mode = nil
|
94
|
-
@
|
96
|
+
@process_type = nil
|
95
97
|
|
96
98
|
@redis_url = nil
|
97
99
|
@redis_namespace = nil
|
@@ -152,7 +154,7 @@ module Coverband
|
|
152
154
|
def background_reporting_sleep_seconds
|
153
155
|
@background_reporting_sleep_seconds ||= if service?
|
154
156
|
# default to 10m for service
|
155
|
-
Coverband.configuration.coverband_env == "production" ? 600 : 60
|
157
|
+
(Coverband.configuration.coverband_env == "production") ? 600 : 60
|
156
158
|
elsif store.is_a?(Coverband::Adapters::HashRedisStore)
|
157
159
|
# Default to 5 minutes if using the hash redis store
|
158
160
|
300
|
@@ -178,7 +180,7 @@ module Coverband
|
|
178
180
|
def store=(store)
|
179
181
|
raise "Pass in an instance of Coverband::Adapters" unless store.is_a?(Coverband::Adapters::Base)
|
180
182
|
raise "invalid configuration: only coverband service expects an API Key" if api_key && store.class.to_s != "Coverband::Adapters::WebServiceStore"
|
181
|
-
raise "invalid configuration: coverband service shouldn't have redis url set" if ENV["COVERBAND_REDIS_URL"] && store.
|
183
|
+
raise "invalid configuration: coverband service shouldn't have redis url set" if ENV["COVERBAND_REDIS_URL"] && store.instance_of?(::Coverband::Adapters::WebServiceStore)
|
182
184
|
|
183
185
|
@store = store
|
184
186
|
end
|
@@ -260,11 +262,11 @@ module Coverband
|
|
260
262
|
end
|
261
263
|
|
262
264
|
def coverband_env
|
263
|
-
ENV["RACK_ENV"] || ENV["RAILS_ENV"] || (defined?(Rails) && Rails.respond_to?(:env) ? Rails.env : "unknown")
|
265
|
+
ENV["RACK_ENV"] || ENV["RAILS_ENV"] || ((defined?(Rails) && Rails.respond_to?(:env)) ? Rails.env : "unknown")
|
264
266
|
end
|
265
267
|
|
266
268
|
def coverband_timeout
|
267
|
-
@coverband_timeout ||= coverband_env == "development" ? 5 : 2
|
269
|
+
@coverband_timeout ||= (coverband_env == "development") ? 5 : 2
|
268
270
|
end
|
269
271
|
|
270
272
|
def service_dev_mode
|
@@ -287,6 +289,10 @@ module Coverband
|
|
287
289
|
@defer_eager_loading_data
|
288
290
|
end
|
289
291
|
|
292
|
+
def send_deferred_eager_loading_data?
|
293
|
+
@send_deferred_eager_loading_data
|
294
|
+
end
|
295
|
+
|
290
296
|
def service_disabled_dev_test_env?
|
291
297
|
return false unless service?
|
292
298
|
|
@@ -20,10 +20,8 @@ module Coverband
|
|
20
20
|
|
21
21
|
def rails_server?
|
22
22
|
@stack.any? do |location|
|
23
|
-
(
|
24
|
-
location.path.include?("rails/commands/commands_tasks.rb") && location.label == "server" ||
|
23
|
+
location.path.include?("rails/commands/commands_tasks.rb") && location.label == "server" ||
|
25
24
|
location.path.include?("rails/commands/server/server_command.rb") && location.label == "perform"
|
26
|
-
)
|
27
25
|
end
|
28
26
|
end
|
29
27
|
end
|
@@ -4,15 +4,15 @@ module Coverband
|
|
4
4
|
module Reporters
|
5
5
|
###
|
6
6
|
# This is the base clase for report generation
|
7
|
-
# it helps with filtering, normalization, etc for final
|
7
|
+
# it helps with filtering, normalization, etc for final report generation
|
8
8
|
###
|
9
9
|
class Base
|
10
10
|
class << self
|
11
11
|
DATA_KEY = "data"
|
12
12
|
|
13
|
-
def report(store,
|
13
|
+
def report(store, options = {})
|
14
14
|
all_roots = Coverband.configuration.all_root_paths
|
15
|
-
|
15
|
+
get_current_scov_data_imp(store, all_roots, options)
|
16
16
|
|
17
17
|
# These are extremelhy verbose but useful during coverband development, not generally for users
|
18
18
|
# Only available by uncommenting this mode is never released
|
@@ -20,7 +20,6 @@ module Coverband
|
|
20
20
|
# # msg = "report:\n #{scov_style_report.inspect}"
|
21
21
|
# # Coverband.configuration.logger.debug msg
|
22
22
|
# end
|
23
|
-
scov_style_report
|
24
23
|
end
|
25
24
|
|
26
25
|
###
|
@@ -71,7 +70,7 @@ module Coverband
|
|
71
70
|
# > [nil,0,0,1,0,1]
|
72
71
|
def merge_arrays(first, second)
|
73
72
|
merged = []
|
74
|
-
longest = first.length > second.length ? first : second
|
73
|
+
longest = (first.length > second.length) ? first : second
|
75
74
|
|
76
75
|
longest.each_with_index do |_line, index|
|
77
76
|
merged[index] = if first[index] || second[index]
|
@@ -86,12 +85,13 @@ module Coverband
|
|
86
85
|
# why do we need to merge covered files data?
|
87
86
|
# basically because paths on machines or deployed hosts could be different, so
|
88
87
|
# two different keys could point to the same filename or `line_key`
|
88
|
+
# this happens when deployment has a dynmaic path or the path change during deployment (hot code reload)
|
89
89
|
# TODO: think we are filtering based on ignore while sending to the store
|
90
90
|
# and as we also pull it out here
|
91
91
|
###
|
92
|
-
def get_current_scov_data_imp(store, roots)
|
92
|
+
def get_current_scov_data_imp(store, roots, options = {})
|
93
93
|
scov_style_report = {}
|
94
|
-
store.get_coverage_report.each_pair do |name, data|
|
94
|
+
store.get_coverage_report(options).each_pair do |name, data|
|
95
95
|
data.each_pair do |key, line_data|
|
96
96
|
next if Coverband.configuration.ignore.any? { |i| key.match(i) }
|
97
97
|
next unless line_data
|
@@ -4,17 +4,23 @@ module Coverband
|
|
4
4
|
module Reporters
|
5
5
|
class HTMLReport < Base
|
6
6
|
attr_accessor :filtered_report_files, :open_report, :notice,
|
7
|
-
:base_path, :filename
|
7
|
+
:base_path, :filename, :page
|
8
8
|
|
9
9
|
def initialize(store, options = {})
|
10
|
-
|
10
|
+
self.page = options.fetch(:page) { nil }
|
11
11
|
self.open_report = options.fetch(:open_report) { true }
|
12
12
|
# TODO: refactor notice out to top level of web only
|
13
13
|
self.notice = options.fetch(:notice) { nil }
|
14
14
|
self.base_path = options.fetch(:base_path) { "./" }
|
15
15
|
self.filename = options.fetch(:filename) { nil }
|
16
16
|
|
17
|
-
|
17
|
+
coverband_reports = Coverband::Reporters::Base.report(store, options)
|
18
|
+
# NOTE: at the moment the optimization around paging and filenames only works for hash redis store
|
19
|
+
self.filtered_report_files = if (page || filename) && store.is_a?(Coverband::Adapters::HashRedisStore)
|
20
|
+
coverband_reports
|
21
|
+
else
|
22
|
+
self.class.fix_reports(coverband_reports)
|
23
|
+
end
|
18
24
|
end
|
19
25
|
|
20
26
|
def file_details
|
@@ -36,12 +42,14 @@ module Coverband
|
|
36
42
|
def report_dynamic_html
|
37
43
|
Coverband::Utils::HTMLFormatter.new(filtered_report_files,
|
38
44
|
base_path: base_path,
|
39
|
-
notice: notice
|
45
|
+
notice: notice,
|
46
|
+
page: page).format_dynamic_html!
|
40
47
|
end
|
41
48
|
|
42
49
|
def report_dynamic_data
|
43
50
|
Coverband::Utils::HTMLFormatter.new(filtered_report_files,
|
44
51
|
base_path: base_path,
|
52
|
+
page: page,
|
45
53
|
notice: notice).format_dynamic_data!
|
46
54
|
end
|
47
55
|
end
|
@@ -5,11 +5,22 @@
|
|
5
5
|
module Coverband
|
6
6
|
module Reporters
|
7
7
|
class JSONReport < Base
|
8
|
-
attr_accessor :filtered_report_files
|
8
|
+
attr_accessor :filtered_report_files, :options, :page, :as_report, :store, :filename
|
9
9
|
|
10
10
|
def initialize(store, options = {})
|
11
|
+
self.options = options
|
12
|
+
self.page = options.fetch(:page) { nil }
|
13
|
+
self.filename = options.fetch(:filename) { nil }
|
14
|
+
self.as_report = options.fetch(:as_report) { false }
|
15
|
+
self.store = store
|
16
|
+
|
11
17
|
coverband_reports = Coverband::Reporters::Base.report(store, options)
|
12
|
-
|
18
|
+
# NOTE: paged reports can't find and add in files that has never been loaded
|
19
|
+
self.filtered_report_files = if page || filename
|
20
|
+
coverband_reports
|
21
|
+
else
|
22
|
+
self.class.fix_reports(coverband_reports)
|
23
|
+
end
|
13
24
|
end
|
14
25
|
|
15
26
|
def report
|
@@ -21,10 +32,35 @@ module Coverband
|
|
21
32
|
def report_as_json
|
22
33
|
result = Coverband::Utils::Results.new(filtered_report_files)
|
23
34
|
source_files = result.source_files
|
24
|
-
|
35
|
+
|
36
|
+
data = {
|
25
37
|
**coverage_totals(source_files),
|
26
38
|
files: coverage_files(result, source_files)
|
27
|
-
}
|
39
|
+
}
|
40
|
+
|
41
|
+
if as_report
|
42
|
+
row_data = []
|
43
|
+
data[:files].each_pair do |key, data|
|
44
|
+
row_data << [
|
45
|
+
key,
|
46
|
+
data[:covered_percent].to_s,
|
47
|
+
data[:runtime_percentage].to_s,
|
48
|
+
data[:lines_of_code].to_s,
|
49
|
+
(data[:lines_covered] + data[:lines_missed]).to_s,
|
50
|
+
data[:lines_covered].to_s,
|
51
|
+
data[:lines_runtime].to_s,
|
52
|
+
data[:lines_missed].to_s,
|
53
|
+
data[:covered_strength].to_s
|
54
|
+
]
|
55
|
+
end
|
56
|
+
filesreported = store.file_count(:runtime)
|
57
|
+
data["iTotalRecords"] = filesreported
|
58
|
+
data["iTotalDisplayRecords"] = filesreported
|
59
|
+
data["aaData"] = row_data
|
60
|
+
data.delete(:files)
|
61
|
+
data = data.as_json
|
62
|
+
end
|
63
|
+
data.to_json
|
28
64
|
end
|
29
65
|
|
30
66
|
def coverage_totals(source_files)
|
@@ -41,11 +77,13 @@ module Coverband
|
|
41
77
|
# Using a hash indexed by file name for quick lookups
|
42
78
|
def coverage_files(result, source_files)
|
43
79
|
source_files.each_with_object({}) do |source_file, hash|
|
80
|
+
runtime_coverage = result.file_with_type(source_file, Coverband::RUNTIME_TYPE)&.covered_lines_count || 0
|
44
81
|
hash[source_file.short_name] = {
|
45
82
|
never_loaded: source_file.never_loaded,
|
46
83
|
runtime_percentage: result.runtime_relevant_coverage(source_file),
|
47
84
|
lines_of_code: source_file.lines.count,
|
48
85
|
lines_covered: source_file.covered_lines.count,
|
86
|
+
lines_runtime: runtime_coverage,
|
49
87
|
lines_missed: source_file.missed_lines.count,
|
50
88
|
covered_percent: source_file.covered_percent,
|
51
89
|
covered_strength: source_file.covered_strength
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "base64"
|
3
4
|
require "coverband"
|
4
5
|
|
5
6
|
begin
|
@@ -56,7 +57,7 @@ module Coverband
|
|
56
57
|
|
57
58
|
return [401, {"www-authenticate" => 'Basic realm=""'}, [""]] unless check_auth
|
58
59
|
|
59
|
-
request_path_info = request.path_info == "" ? "/" : request.path_info
|
60
|
+
request_path_info = (request.path_info == "") ? "/" : request.path_info
|
60
61
|
tracker_route = false
|
61
62
|
Coverband.configuration.trackers.each do |tracker|
|
62
63
|
if request_path_info.match(tracker.class::REPORT_ROUTE)
|
@@ -97,6 +98,8 @@ module Coverband
|
|
97
98
|
[200, coverband_headers(content_type: "text/json"), [load_file_details]]
|
98
99
|
when %r{\/json}
|
99
100
|
[200, coverband_headers(content_type: "text/json"), [json]]
|
101
|
+
when %r{\/report_json}
|
102
|
+
[200, coverband_headers(content_type: "text/json"), [report_json]]
|
100
103
|
when %r{\/$}
|
101
104
|
[200, coverband_headers, [index]]
|
102
105
|
else
|
@@ -120,6 +123,17 @@ module Coverband
|
|
120
123
|
Coverband::Reporters::JSONReport.new(Coverband.configuration.store).report
|
121
124
|
end
|
122
125
|
|
126
|
+
def report_json
|
127
|
+
report_options = {
|
128
|
+
as_report: true
|
129
|
+
}
|
130
|
+
report_options[:page] = (request.params["page"] || 1).to_i if request.params["page"]
|
131
|
+
Coverband::Reporters::JSONReport.new(
|
132
|
+
Coverband.configuration.store,
|
133
|
+
report_options
|
134
|
+
).report
|
135
|
+
end
|
136
|
+
|
123
137
|
def settings
|
124
138
|
Coverband::Utils::HTMLFormatter.new(nil, base_path: base_path).format_settings!
|
125
139
|
end
|
@@ -218,7 +232,7 @@ module Coverband
|
|
218
232
|
# %r{\/.*\/}.match?(request.path) ? request.path.match("\/.*\/")[0] : "/"
|
219
233
|
# ^^ the above is NOT valid Ruby 2.3/2.4 even though rubocop / standard think it is
|
220
234
|
def base_path
|
221
|
-
request.path =~ %r{\/.*\/} ? request.path.match("/.*/")[0] : "/"
|
235
|
+
(request.path =~ %r{\/.*\/}) ? request.path.match("/.*/")[0] : "/"
|
222
236
|
end
|
223
237
|
end
|
224
238
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
require "coverband"
|
5
|
+
|
6
|
+
begin
|
7
|
+
require "rack"
|
8
|
+
rescue LoadError
|
9
|
+
puts "error loading Coverband web reporter as Rack is not available"
|
10
|
+
end
|
11
|
+
|
12
|
+
module Coverband
|
13
|
+
module Reporters
|
14
|
+
class WebPager < Web
|
15
|
+
def index
|
16
|
+
notice = "<strong>Notice:</strong> #{Rack::Utils.escape_html(request.params["notice"])}<br/>"
|
17
|
+
notice = request.params["notice"] ? notice : ""
|
18
|
+
# TODO: remove the call to the store render empty table
|
19
|
+
Coverband::Reporters::HTMLReport.new(Coverband.configuration.store,
|
20
|
+
page: (request.params["page"] || 1).to_i,
|
21
|
+
static: false,
|
22
|
+
base_path: base_path,
|
23
|
+
notice: notice,
|
24
|
+
open_report: false).report
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -22,25 +22,25 @@ module Coverband
|
|
22
22
|
|
23
23
|
def convert(relative_path)
|
24
24
|
@cache[relative_path] ||= begin
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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 { |root|
|
40
|
+
File.exist?(relative_filename.gsub("./", root))
|
41
|
+
}
|
42
|
+
local_root ? relative_filename.gsub("./", local_root) : local_filename
|
43
|
+
end
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -7,9 +7,9 @@ module Coverband
|
|
7
7
|
|
8
8
|
def self.hash_file(file, path_converter: AbsoluteFileConverter.instance)
|
9
9
|
@cache[file] ||= begin
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
file = path_converter.convert(file)
|
11
|
+
Digest::MD5.file(file).hexdigest if File.exist?(file)
|
12
|
+
end
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -13,12 +13,13 @@ require "time"
|
|
13
13
|
module Coverband
|
14
14
|
module Utils
|
15
15
|
class HTMLFormatter
|
16
|
-
attr_reader :notice, :base_path, :tracker
|
16
|
+
attr_reader :notice, :base_path, :tracker, :page
|
17
17
|
|
18
18
|
def initialize(report, options = {})
|
19
19
|
@notice = options.fetch(:notice, nil)
|
20
20
|
@base_path = options.fetch(:base_path, "./")
|
21
21
|
@tracker = options.fetch(:tracker, nil)
|
22
|
+
@page = options.fetch(:page, nil)
|
22
23
|
@coverage_result = Coverband::Utils::Results.new(report) if report
|
23
24
|
end
|
24
25
|
|
@@ -126,11 +127,10 @@ module Coverband
|
|
126
127
|
# Returns a table containing the given source files
|
127
128
|
def formatted_file_list(title, result, source_files, options = {})
|
128
129
|
title_id = title.gsub(/^[^a-zA-Z]+/, "").gsub(/[^a-zA-Z0-9\-\_]/, "")
|
129
|
-
# Silence a warning by using the following variable to assign to
|
130
|
+
# Silence a warning by using the following variable to assign to `_`:
|
130
131
|
# "warning: possibly useless use of a variable in void context"
|
131
132
|
# The variable is used by ERB via binding.
|
132
|
-
|
133
|
-
options = options
|
133
|
+
_ = title_id, options
|
134
134
|
|
135
135
|
template("file_list").result(binding)
|
136
136
|
end
|
@@ -71,7 +71,7 @@ if defined?(RubyVM::AbstractSyntaxTree)
|
|
71
71
|
def scan_node(node, class_name)
|
72
72
|
definitions = []
|
73
73
|
return definitions unless node.is_a?(RubyVM::AbstractSyntaxTree::Node)
|
74
|
-
current_class = node.type == :CLASS ? node.children.first.children.last : class_name
|
74
|
+
current_class = (node.type == :CLASS) ? node.children.first.children.last : class_name
|
75
75
|
if node.type == :DEFN
|
76
76
|
definitions <<
|
77
77
|
MethodDefinition.new(
|
@@ -11,12 +11,10 @@ module Coverband
|
|
11
11
|
|
12
12
|
class Railtie < Rails::Railtie
|
13
13
|
initializer "coverband.configure" do |app|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
Coverband.configuration.logger.info "If this is a setup task like assets:precompile feel free to ignore"
|
19
|
-
end
|
14
|
+
app.middleware.use Coverband::BackgroundMiddleware
|
15
|
+
rescue Redis::CannotConnectError => error
|
16
|
+
Coverband.configuration.logger.info "Redis is not available (#{error}), Coverband not configured"
|
17
|
+
Coverband.configuration.logger.info "If this is a setup task like assets:precompile feel free to ignore"
|
20
18
|
end
|
21
19
|
|
22
20
|
config.after_initialize do
|
@@ -22,13 +22,13 @@ module Coverband
|
|
22
22
|
|
23
23
|
def convert(file)
|
24
24
|
@cache[file] ||= begin
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
32
|
end
|
33
33
|
|
34
34
|
private
|
@@ -17,7 +17,11 @@ module Coverband
|
|
17
17
|
def file_with_type(source_file, results_type)
|
18
18
|
return unless get_results(results_type)
|
19
19
|
|
20
|
-
|
20
|
+
@files_with_type ||= {}
|
21
|
+
@files_with_type[results_type] ||= get_results(results_type).source_files.map do |source_file|
|
22
|
+
[source_file.filename, source_file]
|
23
|
+
end.to_h
|
24
|
+
@files_with_type[results_type][source_file.filename]
|
21
25
|
end
|
22
26
|
|
23
27
|
def runtime_relevant_coverage(source_file)
|
@@ -48,7 +52,11 @@ module Coverband
|
|
48
52
|
def file_from_path_with_type(full_path, results_type = :merged)
|
49
53
|
return unless get_results(results_type)
|
50
54
|
|
51
|
-
|
55
|
+
@files_from_path_with_type ||= {}
|
56
|
+
@files_from_path_with_type[results_type] ||= get_results(results_type).source_files.map do |source_file|
|
57
|
+
[source_file.filename, source_file]
|
58
|
+
end.to_h
|
59
|
+
@files_from_path_with_type[results_type][full_path]
|
52
60
|
end
|
53
61
|
|
54
62
|
def method_missing(method, *args)
|
@@ -60,21 +68,17 @@ module Coverband
|
|
60
68
|
end
|
61
69
|
|
62
70
|
def respond_to_missing?(method)
|
63
|
-
|
64
|
-
true
|
65
|
-
else
|
66
|
-
false
|
67
|
-
end
|
71
|
+
get_results(type).respond_to?(method)
|
68
72
|
end
|
69
73
|
|
70
74
|
private
|
71
75
|
|
72
76
|
def get_eager_file(source_file)
|
73
|
-
|
77
|
+
file_with_type(source_file, Coverband::EAGER_TYPE)
|
74
78
|
end
|
75
79
|
|
76
80
|
def get_runtime_file(source_file)
|
77
|
-
|
81
|
+
file_with_type(source_file, Coverband::RUNTIME_TYPE)
|
78
82
|
end
|
79
83
|
|
80
84
|
def eager_loading_coverage
|
@@ -93,11 +97,7 @@ module Coverband
|
|
93
97
|
def get_results(type)
|
94
98
|
return nil unless Coverband::ALL_TYPES.include?(type)
|
95
99
|
|
96
|
-
|
97
|
-
@results[type]
|
98
|
-
else
|
99
|
-
@results[type] = Coverband::Utils::Result.new(report[type])
|
100
|
-
end
|
100
|
+
@results[type] ||= Coverband::Utils::Result.new(report[type])
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
@@ -74,7 +74,7 @@ module Coverband
|
|
74
74
|
return "skipped" if skipped?
|
75
75
|
return "never" if never?
|
76
76
|
return "missed" if missed?
|
77
|
-
|
77
|
+
"covered" if covered?
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
@@ -162,7 +162,7 @@ module Coverband
|
|
162
162
|
return 0.0 if relevant_lines.zero?
|
163
163
|
|
164
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
|
165
|
+
[Float(covered_lines.size * 100.0 / relevant_lines.to_f), 100.0].min&.round(2)
|
166
166
|
end
|
167
167
|
|
168
168
|
def formatted_covered_percent
|
@@ -35,7 +35,7 @@ namespace :coverband do
|
|
35
35
|
Coverband.configuration.store.merge_mode = true
|
36
36
|
end
|
37
37
|
Rack::Server.start app: Coverband::Reporters::Web.new,
|
38
|
-
|
38
|
+
Port: ENV.fetch("COVERBAND_COVERAGE_PORT", 9022).to_i
|
39
39
|
end
|
40
40
|
|
41
41
|
###
|
data/lib/coverband/version.rb
CHANGED
data/lib/coverband.rb
CHANGED
@@ -82,9 +82,9 @@ module Coverband
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def self.tasks_to_ignore?
|
85
|
-
|
86
|
-
|
87
|
-
|
85
|
+
defined?(Rake) &&
|
86
|
+
Rake.respond_to?(:application) &&
|
87
|
+
(Rake&.application&.top_level_tasks || []).any? { |task| Coverband::Configuration::IGNORE_TASKS.include?(task) }
|
88
88
|
end
|
89
89
|
|
90
90
|
def self.eager_loading_coverage!
|
@@ -130,6 +130,7 @@ module Coverband
|
|
130
130
|
###
|
131
131
|
def initialize
|
132
132
|
require "coverband/reporters/web"
|
133
|
+
require "coverband/reporters/web_pager"
|
133
134
|
require "coverband/utils/html_formatter"
|
134
135
|
require "coverband/utils/result"
|
135
136
|
require "coverband/utils/file_list"
|
@@ -147,4 +148,19 @@ module Coverband
|
|
147
148
|
end
|
148
149
|
end
|
149
150
|
end
|
151
|
+
|
152
|
+
module Reporters
|
153
|
+
class WebPager < Web
|
154
|
+
def initialize
|
155
|
+
require "coverband/reporters/web"
|
156
|
+
require "coverband/reporters/web_pager"
|
157
|
+
super
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.call(env)
|
161
|
+
@app ||= new
|
162
|
+
@app.call(env)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
150
166
|
end
|
data/public/application.js
CHANGED
@@ -33,6 +33,41 @@ $(document).ready(function() {
|
|
33
33
|
]
|
34
34
|
});
|
35
35
|
|
36
|
+
// TODO: add support for searching...
|
37
|
+
// hmm should I just use manual paging? or load more...
|
38
|
+
if ($(".file_list.unsorted").length == 1) {
|
39
|
+
var current_rows = 0;
|
40
|
+
var total_rows = 0;
|
41
|
+
var page = 1;
|
42
|
+
|
43
|
+
// write a function to get a page of data and add it to the table
|
44
|
+
function get_page(page) {
|
45
|
+
$.ajax({
|
46
|
+
url: `/coverage/report_json?page=${page}`,
|
47
|
+
type: 'GET',
|
48
|
+
dataType: 'json',
|
49
|
+
success: function(data) {
|
50
|
+
console.log(data);
|
51
|
+
total_rows = data["iTotalRecords"];
|
52
|
+
// NOTE: we request 250 at a time, but we seem to have some files that we have as a list but 0 coverage,
|
53
|
+
// so we don't get back 250 per page... to ensure we we need to account for filtered out and empty files
|
54
|
+
// this 250 at the moment is synced to the 250 in the hash redis store
|
55
|
+
current_rows += 250; //data["aaData"].length;
|
56
|
+
console.log(current_rows);
|
57
|
+
console.log(total_rows);
|
58
|
+
$(".file_list.unsorted").dataTable().fnAddData(data["aaData"]);
|
59
|
+
page += 1;
|
60
|
+
// the page less than 100 is to stop infinite loop in case of folks never clearing out old coverage reports
|
61
|
+
if (page < 100 && current_rows < total_rows) {
|
62
|
+
get_page(page);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
});
|
66
|
+
}
|
67
|
+
get_page(page);
|
68
|
+
}
|
69
|
+
|
70
|
+
|
36
71
|
// Syntax highlight all files up front - deactivated
|
37
72
|
// $('.source_table pre code').each(function(i, e) {hljs.highlightBlock(e, ' ')});
|
38
73
|
|