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.
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