coverband 6.0.1 → 6.0.3.rc.1
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/.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
|
|