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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +10 -0
  3. data/.github/workflows/main.yml +2 -3
  4. data/.standard.yml +1 -1
  5. data/Gemfile +1 -1
  6. data/LICENSE +1 -1
  7. data/README.md +24 -10
  8. data/Rakefile +1 -1
  9. data/changes.md +12 -1
  10. data/coverband.gemspec +3 -3
  11. data/lib/coverband/adapters/base.rb +10 -10
  12. data/lib/coverband/adapters/file_store.rb +1 -1
  13. data/lib/coverband/adapters/hash_redis_store.rb +160 -10
  14. data/lib/coverband/adapters/null_store.rb +1 -1
  15. data/lib/coverband/adapters/stdout_store.rb +1 -1
  16. data/lib/coverband/adapters/web_service_store.rb +5 -7
  17. data/lib/coverband/collectors/coverage.rb +1 -1
  18. data/lib/coverband/collectors/route_tracker.rb +1 -1
  19. data/lib/coverband/collectors/translation_tracker.rb +2 -2
  20. data/lib/coverband/collectors/view_tracker.rb +1 -1
  21. data/lib/coverband/configuration.rb +12 -6
  22. data/lib/coverband/integrations/rack_server_check.rb +1 -3
  23. data/lib/coverband/reporters/base.rb +7 -7
  24. data/lib/coverband/reporters/html_report.rb +12 -4
  25. data/lib/coverband/reporters/json_report.rb +42 -4
  26. data/lib/coverband/reporters/web.rb +16 -2
  27. data/lib/coverband/reporters/web_pager.rb +28 -0
  28. data/lib/coverband/utils/absolute_file_converter.rb +19 -19
  29. data/lib/coverband/utils/file_hasher.rb +3 -3
  30. data/lib/coverband/utils/html_formatter.rb +4 -4
  31. data/lib/coverband/utils/method_definition_scanner.rb +1 -1
  32. data/lib/coverband/utils/railtie.rb +4 -6
  33. data/lib/coverband/utils/relative_file_converter.rb +7 -7
  34. data/lib/coverband/utils/results.rb +14 -14
  35. data/lib/coverband/utils/source_file.rb +2 -2
  36. data/lib/coverband/utils/tasks.rb +1 -1
  37. data/lib/coverband/version.rb +1 -1
  38. data/lib/coverband.rb +19 -3
  39. data/public/application.js +35 -0
  40. data/public/dependencies.js +1 -1
  41. data/roadmap.md +1 -1
  42. data/test/benchmarks/benchmark.rake +5 -5
  43. data/test/coverband/adapters/file_store_test.rb +1 -1
  44. data/test/coverband/adapters/hash_redis_store_test.rb +48 -0
  45. data/test/coverband/collectors/coverage_test.rb +1 -1
  46. data/test/coverband/reporters/json_test.rb +1 -1
  47. data/test/coverband/utils/source_file_test.rb +11 -11
  48. data/test/fixtures/casting_invitor.rb +1 -1
  49. data/test/fixtures/sample.rb +2 -2
  50. data/test/fixtures/skipped_and_executed.rb +1 -1
  51. data/test/forked/rails_rake_full_stack_test.rb +1 -1
  52. data/test/integration/full_stack_send_deferred_eager_test.rb +52 -0
  53. data/test/test_helper.rb +3 -5
  54. data/test/unique_files.rb +1 -1
  55. data/views/file_list.erb +38 -32
  56. data/views/layout.erb +3 -3
  57. metadata +17 -139
  58. 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
- @proces_type = nil
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.class.to_s == "Coverband::Adapters::WebServiceStore"
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 reprort generation
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, _options = {})
13
+ def report(store, options = {})
14
14
  all_roots = Coverband.configuration.all_root_paths
15
- scov_style_report = get_current_scov_data_imp(store, all_roots)
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
- coverband_reports = Coverband::Reporters::Base.report(store, options)
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
- self.filtered_report_files = self.class.fix_reports(coverband_reports)
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).format_dynamic_html!
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
- self.filtered_report_files = self.class.fix_reports(coverband_reports)
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
- }.to_json
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
- 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
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
- file = path_converter.convert(file)
11
- Digest::MD5.file(file).hexdigest if File.exist?(file)
12
- end
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 itself:
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
- title_id = title_id
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
- begin
15
- app.middleware.use Coverband::BackgroundMiddleware
16
- rescue Redis::CannotConnectError => error
17
- Coverband.configuration.logger.info "Redis is not available (#{error}), Coverband not configured"
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
- 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
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
- get_results(results_type).source_files.find { |file| file.filename == source_file.filename }
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
- get_results(results_type).source_files.find { |file| file.filename == full_path }
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
- if get_results(type).respond_to?(method)
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
- eager_loading_coverage.source_files.find { |file| file.filename == source_file.filename }
77
+ file_with_type(source_file, Coverband::EAGER_TYPE)
74
78
  end
75
79
 
76
80
  def get_runtime_file(source_file)
77
- runtime_coverage.source_files.find { |file| file.filename == source_file.filename }
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
- if @results.key?(type)
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
- return "covered" if covered?
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
- Port: ENV.fetch("COVERBAND_COVERAGE_PORT", 9022).to_i
38
+ Port: ENV.fetch("COVERBAND_COVERAGE_PORT", 9022).to_i
39
39
  end
40
40
 
41
41
  ###
@@ -5,5 +5,5 @@
5
5
  # use format "4.2.1.rc.1" ~> 4.2.1.rc to prerelease versions like v4.2.1.rc.2 and v4.2.1.rc.3
6
6
  ###
7
7
  module Coverband
8
- VERSION = "6.0.1"
8
+ VERSION = "6.0.3.rc.1"
9
9
  end
data/lib/coverband.rb CHANGED
@@ -82,9 +82,9 @@ module Coverband
82
82
  end
83
83
 
84
84
  def self.tasks_to_ignore?
85
- (defined?(Rake) &&
86
- Rake.respond_to?(:application) &&
87
- (Rake&.application&.top_level_tasks || []).any? { |task| Coverband::Configuration::IGNORE_TASKS.include?(task) })
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
@@ -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