coverband 4.0.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +6 -1
  3. data/.travis.yml +8 -2
  4. data/Gemfile +2 -0
  5. data/Gemfile.rails4 +2 -0
  6. data/README.md +135 -157
  7. data/Rakefile +1 -1
  8. data/changes.md +56 -5
  9. data/coverband.gemspec +5 -3
  10. data/lib/coverband/adapters/base.rb +44 -11
  11. data/lib/coverband/adapters/file_store.rb +8 -0
  12. data/lib/coverband/adapters/redis_store.rb +65 -8
  13. data/lib/coverband/at_exit.rb +37 -0
  14. data/lib/coverband/collectors/coverage.rb +43 -72
  15. data/lib/coverband/collectors/delta.rb +67 -0
  16. data/lib/coverband/configuration.rb +71 -2
  17. data/lib/coverband/integrations/background.rb +9 -10
  18. data/lib/coverband/integrations/middleware.rb +3 -2
  19. data/lib/coverband/integrations/rack_server_check.rb +4 -1
  20. data/lib/coverband/integrations/resque.rb +27 -0
  21. data/lib/coverband/reporters/base.rb +24 -36
  22. data/lib/coverband/reporters/html_report.rb +40 -15
  23. data/lib/coverband/reporters/web.rb +52 -10
  24. data/lib/coverband/utils/file_groups.rb +53 -0
  25. data/lib/coverband/utils/file_path_helper.rb +57 -0
  26. data/lib/coverband/utils/gem_list.rb +31 -0
  27. data/lib/coverband/utils/html_formatter.rb +50 -8
  28. data/lib/coverband/utils/railtie.rb +9 -1
  29. data/lib/coverband/utils/result.rb +16 -19
  30. data/lib/coverband/utils/results.rb +63 -0
  31. data/lib/coverband/utils/source_file.rb +23 -1
  32. data/lib/coverband/utils/tasks.rb +17 -17
  33. data/lib/coverband/version.rb +1 -1
  34. data/lib/coverband.rb +32 -2
  35. data/public/application.css +5 -0
  36. data/public/application.js +117 -1640
  37. data/public/dependencies.js +1581 -0
  38. data/public/favicon.png +0 -0
  39. data/test/benchmarks/benchmark.rake +36 -1
  40. data/test/benchmarks/coverage_fork.sh +37 -0
  41. data/test/{unit/adapters_base_test.rb → coverband/adapters/base_test.rb} +1 -1
  42. data/test/{unit/adapters_file_store_test.rb → coverband/adapters/file_store_test.rb} +1 -1
  43. data/test/{unit/adapters_redis_store_test.rb → coverband/adapters/redis_store_test.rb} +28 -11
  44. data/test/coverband/at_exit_test.rb +13 -0
  45. data/test/coverband/collectors/coverage_test.rb +73 -0
  46. data/test/coverband/collectors/delta_test.rb +52 -0
  47. data/test/coverband/configuration_test.rb +67 -0
  48. data/test/{unit → coverband}/coverband_test.rb +7 -0
  49. data/test/coverband/integrations/background_test.rb +31 -0
  50. data/test/{unit → coverband/integrations}/middleware_test.rb +3 -2
  51. data/test/{unit/rack_server_checkout_test.rb → coverband/integrations/rack_server_check_test.rb} +1 -1
  52. data/test/coverband/integrations/resque_worker_test.rb +42 -0
  53. data/test/coverband/integrations/test_resque_job.rb +7 -0
  54. data/test/coverband/reporters/base_test.rb +168 -0
  55. data/test/{unit/reports_console_test.rb → coverband/reporters/console_test.rb} +8 -7
  56. data/test/coverband/reporters/html_test.rb +45 -0
  57. data/test/coverband/reporters/web_test.rb +52 -0
  58. data/test/coverband/utils/file_groups_test.rb +55 -0
  59. data/test/{unit → coverband}/utils/file_list_test.rb +1 -1
  60. data/test/coverband/utils/gem_list_test.rb +48 -0
  61. data/test/{unit → coverband}/utils/source_file_test.rb +1 -1
  62. data/test/{unit → integration}/full_stack_test.rb +25 -12
  63. data/test/{unit → integration}/rails_full_stack_test.rb +27 -7
  64. data/test/rails4_dummy/config/application.rb +1 -1
  65. data/test/rails4_dummy/config/coverband.rb +4 -1
  66. data/test/rails4_dummy/tmp/.keep +0 -0
  67. data/test/rails5_dummy/config/application.rb +1 -1
  68. data/test/rails5_dummy/config/coverband.rb +3 -1
  69. data/test/rails5_dummy/tmp/.keep +0 -0
  70. data/test/rails_test_helper.rb +13 -8
  71. data/test/test_helper.rb +12 -17
  72. data/test/unique_files.rb +23 -0
  73. data/views/file_list.erb +33 -20
  74. data/views/gem_list.erb +54 -0
  75. data/views/layout.erb +13 -4
  76. data/views/settings.erb +35 -0
  77. data/views/source_file.erb +16 -4
  78. data/views/source_file_loader.erb +7 -0
  79. metadata +108 -62
  80. data/test/unit/background_test.rb +0 -29
  81. data/test/unit/collectors_coverage_test.rb +0 -48
  82. data/test/unit/configuration_test.rb +0 -23
  83. data/test/unit/reports_base_test.rb +0 -109
  84. data/test/unit/reports_html_test.rb +0 -29
  85. data/test/unit/reports_web_test.rb +0 -39
  86. /data/test/{unit → coverband}/utils/lines_classifier_test.rb +0 -0
  87. /data/test/{unit → coverband}/utils/result_test.rb +0 -0
  88. /data/test/{unit → coverband}/utils/s3_report_test.rb +0 -0
  89. /data/test/{unit → coverband}/utils/source_file_line_test.rb +0 -0
  90. /data/test/{unit/dog.rb → dog.rb} +0 -0
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ Resque.after_fork do |job|
4
+ Coverband.start
5
+ Coverband.runtime_coverage!
6
+ # no reason to miss coverage on a first resque job
7
+ Coverband::Collectors::Delta.set_default_results
8
+ end
9
+
10
+ Resque.before_first_fork do
11
+ Coverband.eager_loading_coverage!
12
+ Coverband.configuration.background_reporting_enabled = false
13
+ Coverband::Background.stop
14
+ Coverband::Collectors::Coverage.instance.report_coverage(true)
15
+ end
16
+
17
+ module Coverband
18
+ module ResqueWorker
19
+ def perform
20
+ super
21
+ ensure
22
+ Coverband.report_coverage(true)
23
+ end
24
+ end
25
+ end
26
+
27
+ Resque::Job.prepend(Coverband::ResqueWorker)
@@ -8,41 +8,38 @@ module Coverband
8
8
  ###
9
9
  class Base
10
10
  class << self
11
+ include Coverband::Utils::FilePathHelper
11
12
  def report(store, _options = {})
12
- scov_style_report = get_current_scov_data_imp(store, root_paths)
13
+ all_roots = Coverband.configuration.all_root_paths
14
+ scov_style_report = get_current_scov_data_imp(store, all_roots)
13
15
 
14
16
  if Coverband.configuration.verbose
15
17
  msg = "report:\n #{scov_style_report.inspect}"
16
- Coverband.configuration.logger.debug msg
18
+ # Coverband.configuration.logger.debug msg
17
19
  end
18
20
  scov_style_report
19
21
  end
20
22
 
21
23
  protected
22
24
 
23
- def root_paths
24
- roots = Coverband.configuration.root_paths
25
- roots << "#{current_root}/"
26
- roots
27
- end
28
-
29
- def current_root
30
- File.expand_path(Coverband.configuration.root)
31
- end
32
-
33
25
  def fix_file_names(report_hash, roots)
34
26
  if Coverband.configuration.verbose
35
27
  Coverband.configuration.logger.info "fixing root: #{roots.join(', ')}"
36
28
  end
37
29
 
38
30
  # normalize names across servers
39
- report_hash.each_with_object({}) do |(key, vals), fixed_report|
40
- filename = filename_from_key(key, roots)
41
- fixed_report[filename] = if fixed_report.key?(filename)
42
- merge_arrays(fixed_report[filename], vals)
43
- else
44
- vals
45
- end
31
+ report_hash.each_with_object({}) do |(name, report), fixed_report|
32
+ fixed_report[name] = {}
33
+ report.each_pair do |key, vals|
34
+ filename = relative_path_to_full(key, roots)
35
+ fixed_report[name][filename] = if fixed_report[name].key?(filename) && fixed_report[name][filename]['data'] && vals['data']
36
+ merged_data = merge_arrays(fixed_report[name][filename]['data'], vals['data'])
37
+ vals['data'] = merged_data
38
+ vals
39
+ else
40
+ vals
41
+ end
42
+ end
46
43
  end
47
44
  end
48
45
 
@@ -61,32 +58,23 @@ module Coverband
61
58
  merged
62
59
  end
63
60
 
64
- def filename_from_key(key, roots)
65
- filename = key
66
- roots.each do |root|
67
- filename = filename.gsub(/^#{root}/, './')
68
- end
69
- # the filename for SimpleCov is expected to be a full path.
70
- # roots.last should be roots << current_root}/
71
- # a fully expanded path of config.root
72
- filename = filename.gsub('./', roots.last)
73
- filename
74
- end
75
-
76
61
  ###
77
62
  # why do we need to merge covered files data?
78
63
  # basically because paths on machines or deployed hosts could be different, so
79
64
  # two different keys could point to the same filename or `line_key`
80
- # this logic should be pushed to base report
81
65
  # TODO: think we are filtering based on ignore while sending to the store
82
66
  # and as we also pull it out here
83
67
  ###
84
68
  def get_current_scov_data_imp(store, roots)
85
69
  scov_style_report = {}
86
- store.coverage.each_pair do |key, line_data|
87
- next if Coverband.configuration.ignore.any? { |i| key.match(i) }
88
- next unless line_data
89
- scov_style_report[key] = line_data
70
+ store.get_coverage_report.each_pair do |name, data|
71
+ data.each_pair do |key, line_data|
72
+ next if Coverband.configuration.ignore.any? { |i| key.match(i) }
73
+ next unless line_data
74
+
75
+ scov_style_report[name] = {} unless scov_style_report.key?(name)
76
+ scov_style_report[name][key] = line_data
77
+ end
90
78
  end
91
79
 
92
80
  scov_style_report = fix_file_names(scov_style_report, roots)
@@ -3,23 +3,28 @@
3
3
  module Coverband
4
4
  module Reporters
5
5
  class HTMLReport < Base
6
- def self.report(store, options = {})
7
- scov_style_report = super(store, options)
8
- open_report = options.fetch(:open_report) { true }
9
- html = options.fetch(:html) { false }
10
- notice = options.fetch(:notice) { nil }
11
- base_path = options.fetch(:base_path) { nil }
6
+ attr_accessor :filtered_report_files, :open_report, :html, :notice,
7
+ :base_path, :filename
12
8
 
13
- # list all files, even if not tracked by Coverband (0% coverage)
14
- tracked_glob = "#{current_root}/{app,lib,config}/**/*.{rb}"
15
- report_files = Coverband::Utils::Result.add_not_loaded_files(scov_style_report, tracked_glob)
16
- # apply coverband filters
17
- filtered_report_files = {}
18
- report_files.each_pair do |file, data|
19
- next if Coverband.configuration.ignore.any? { |i| file.match(i) }
20
- filtered_report_files[file] = data
21
- end
9
+ def initialize(store, options = {})
10
+ coverband_reports = Coverband::Reporters::Base.report(store, options)
11
+ self.open_report = options.fetch(:open_report) { true }
12
+ self.html = options.fetch(:html) { false }
13
+ # TODO: refactor notice out to top level of web only
14
+ self.notice = options.fetch(:notice) { nil }
15
+ self.base_path = options.fetch(:base_path) { nil }
16
+ self.filename = options.fetch(:filename) { nil }
17
+
18
+ self.filtered_report_files = self.class.fix_reports(coverband_reports)
19
+ end
22
20
 
21
+ def file_details
22
+ Coverband::Utils::HTMLFormatter.new(filtered_report_files,
23
+ base_path: base_path,
24
+ notice: notice).format_source_file!(filename)
25
+ end
26
+
27
+ def report
23
28
  if html
24
29
  Coverband::Utils::HTMLFormatter.new(filtered_report_files,
25
30
  base_path: base_path,
@@ -35,6 +40,26 @@ module Coverband
35
40
  Coverband::Utils::S3Report.instance.persist! if Coverband.configuration.s3_bucket
36
41
  end
37
42
  end
43
+
44
+ private
45
+
46
+ def self.fix_reports(reports)
47
+ # list all files, even if not tracked by Coverband (0% coverage)
48
+ tracked_glob = "#{Coverband.configuration.current_root}/{app,lib,config}/**/*.{rb}"
49
+ filtered_report_files = {}
50
+
51
+ reports.each_pair do |report_name, report_data|
52
+ filtered_report_files[report_name] = {}
53
+ report_files = Coverband::Utils::Result.add_not_loaded_files(report_data, tracked_glob)
54
+
55
+ # apply coverband filters
56
+ report_files.each_pair do |file, data|
57
+ next if Coverband.configuration.ignore.any? { |i| file.match(i) }
58
+
59
+ filtered_report_files[report_name][file] = data
60
+ end
61
+ end
62
+ end
38
63
  end
39
64
  end
40
65
  end
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rack'
3
+ begin
4
+ require 'rack'
5
+ rescue LoadError
6
+ end
4
7
 
5
8
  module Coverband
6
9
  module Reporters
@@ -19,6 +22,8 @@ module Coverband
19
22
 
20
23
  if request.post?
21
24
  case request.path_info
25
+ when %r{\/clear_file}
26
+ clear_file
22
27
  when %r{\/clear}
23
28
  clear
24
29
  when %r{\/collect_coverage}
@@ -32,7 +37,13 @@ module Coverband
32
37
  case request.path_info
33
38
  when /.*\.(css|js|gif|png)/
34
39
  @static.call(env)
35
- when %r{\/}
40
+ when %r{\/settings}
41
+ [200, { 'Content-Type' => 'text/html' }, [settings]]
42
+ when %r{\/debug_data}
43
+ [200, { 'Content-Type' => 'text/json' }, [debug_data]]
44
+ when %r{\/load_file_details}
45
+ [200, { 'Content-Type' => 'text/json' }, [load_file_details]]
46
+ when %r{\/$}
36
47
  [200, { 'Content-Type' => 'text/html' }, [index]]
37
48
  else
38
49
  [404, { 'Content-Type' => 'text/html' }, ['404 error!']]
@@ -43,22 +54,53 @@ module Coverband
43
54
  def index
44
55
  notice = "<strong>Notice:</strong> #{Rack::Utils.escape_html(request.params['notice'])}<br/>"
45
56
  notice = request.params['notice'] ? notice : ''
46
- Coverband::Reporters::HTMLReport.report(Coverband.configuration.store,
47
- html: true,
48
- base_path: base_path,
49
- notice: notice,
50
- open_report: false)
57
+ Coverband::Reporters::HTMLReport.new(Coverband.configuration.store,
58
+ html: true,
59
+ base_path: base_path,
60
+ notice: notice,
61
+ open_report: false).report
62
+ end
63
+
64
+ def settings
65
+ Coverband::Utils::HTMLFormatter.new(nil, base_path: base_path).format_settings!
66
+ end
67
+
68
+ def debug_data
69
+ Coverband.configuration.store.get_coverage_report.to_json
70
+ end
71
+
72
+ def load_file_details
73
+ filename = request.params['filename']
74
+ Coverband::Reporters::HTMLReport.new(Coverband.configuration.store,
75
+ filename: filename,
76
+ base_path: base_path,
77
+ open_report: false).file_details
51
78
  end
52
79
 
53
80
  def collect_coverage
54
- Coverband::Collectors::Coverage.instance.report_coverage(true)
81
+ Coverband.report_coverage(true)
55
82
  notice = 'coverband coverage collected'
56
83
  [301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
57
84
  end
58
85
 
59
86
  def clear
60
- Coverband.configuration.store.clear!
61
- notice = 'coverband coverage cleared'
87
+ if Coverband.configuration.web_enable_clear
88
+ Coverband.configuration.store.clear!
89
+ notice = 'coverband coverage cleared'
90
+ else
91
+ notice = 'web_enable_clear isnt enabled in your configuration'
92
+ end
93
+ [301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
94
+ end
95
+
96
+ def clear_file
97
+ if Coverband.configuration.web_enable_clear
98
+ filename = request.params['filename']
99
+ Coverband.configuration.store.clear_file!(filename)
100
+ notice = "coverage for file #{filename} cleared"
101
+ else
102
+ notice = 'web_enable_clear isnt enabled in your configuration'
103
+ end
62
104
  [301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
63
105
  end
64
106
 
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Applies the configured groups to the given array of Coverband::SourceFile items
5
+ #
6
+ module Coverband
7
+ module Utils
8
+ class FileGroups
9
+ def initialize(files)
10
+ @grouped = {}
11
+ @files = files
12
+ filter_to_groups
13
+ end
14
+
15
+ def grouped_results
16
+ @grouped
17
+ end
18
+
19
+ private
20
+
21
+ def filter_to_groups
22
+ grouped_files = []
23
+ Coverband.configuration.groups.each do |name, filter|
24
+ if name == 'Gems'
25
+ gem_lists = gem_files(name, filter)
26
+ grouped_files.concat(gem_lists.flatten) if gem_lists.flatten.any?
27
+ else
28
+ app_files(name, filter)
29
+ grouped_files += @grouped[name]
30
+ end
31
+ end
32
+ if !Coverband.configuration.groups.empty? && !(other_files = @files.reject do |source_file|
33
+ grouped_files.include?(source_file)
34
+ end).empty?
35
+ @grouped['Ungrouped'] = Coverband::Utils::FileList.new(other_files)
36
+ end
37
+ end
38
+
39
+ def gem_files(name, filter)
40
+ grouped_gems = @files.select { |source_file| source_file.filename =~ /#{filter}/ }.group_by(&:gem_name)
41
+ gem_lists = grouped_gems.values.map { |gem_files| Coverband::Utils::FileList.new(gem_files) }
42
+ @grouped[name] = Coverband::Utils::GemList.new(gem_lists) if gem_lists.flatten.any?
43
+ gem_lists
44
+ end
45
+
46
+ def app_files(name, filter)
47
+ @grouped[name] = Coverband::Utils::FileList.new(@files.select do |source_file|
48
+ source_file.filename =~ /#{filter}/ && source_file.filename !~ /#{Coverband.configuration.gem_paths.first}/
49
+ end)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ ####
4
+ # Helper functions for shared logic related to file path manipulation
5
+ ####
6
+ module Coverband
7
+ module Utils
8
+ module FilePathHelper
9
+ ###
10
+ # Takes a full path and converts to a relative path
11
+ ###
12
+ def full_path_to_relative(full_path)
13
+ relative_filename = full_path
14
+ Coverband.configuration.all_root_paths.each do |root|
15
+ relative_filename = relative_filename.gsub(/^#{root}/, './')
16
+ # once we have a relative path break out of the loop
17
+ break if relative_filename.start_with? './'
18
+ end
19
+ relative_filename
20
+ end
21
+
22
+ ###
23
+ # relative_path_to_full code takes:
24
+ # relative_path: which is a full path the same as reported by Coverage
25
+ # roots: if a collection of all possible full app paths
26
+ # EX: [Coverband.configuration.root_paths, "#{current_root}/"]
27
+ # The LAST item should be the current file system root
28
+ # it expands that expands and adds a '/' as that isn't there from Dir.pwd
29
+ #
30
+ # NOTEs on configuration.root_paths usage
31
+ # strings: matching is pretty simple for full string paths
32
+ # regex: to get regex to work for changing deploy directories
33
+ # the regex must be double escaped in double quotes
34
+ # (if using \d for example)
35
+ # or use single qoutes
36
+ # example: '/box/apps/app_name/releases/\d+/'
37
+ # example: '/var/local/company/company.d/[0-9]*/'
38
+ ###
39
+ def relative_path_to_full(relative_path, roots)
40
+ relative_filename = relative_path
41
+ local_filename = relative_filename
42
+ roots.each do |root|
43
+ relative_filename = relative_filename.gsub(/^#{root}/, './')
44
+ end
45
+ # the filename for our reports is expected to be a full path.
46
+ # roots.last should be roots << current_root}/
47
+ # a fully expanded path of config.root
48
+ # filename = filename.gsub('./', roots.last)
49
+ # above only works for app files
50
+ # we need to rethink some of this logic
51
+ # gems aren't at project root and can have multiple locations
52
+ local_root = roots.find { |root| File.exist?(relative_filename.gsub('./', root)) }
53
+ local_root ? relative_filename.gsub('./', local_root) : local_filename
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ ####
4
+ # An array of FileLists instances with helpers to roll up the stats
5
+ # methods for calculating coverage across them etc.
6
+ ####
7
+ module Coverband
8
+ module Utils
9
+ class GemList < FileList
10
+ # Returns the count of lines that have coverage
11
+ def covered_lines
12
+ to_a.map(&:covered_lines).inject(:+)
13
+ end
14
+
15
+ # Returns the count of lines that have been missed
16
+ def missed_lines
17
+ to_a.map(&:missed_lines).inject(:+)
18
+ end
19
+
20
+ # Returns the count of lines that are not relevant for coverage
21
+ def never_lines
22
+ to_a.map(&:never_lines).inject(:+)
23
+ end
24
+
25
+ # Returns the count of skipped lines
26
+ def skipped_lines
27
+ to_a.map(&:skipped_lines).inject(:+)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -18,7 +18,7 @@ module Coverband
18
18
  def initialize(report, options = {})
19
19
  @notice = options.fetch(:notice) { nil }
20
20
  @base_path = options.fetch(:base_path) { nil }
21
- @coverage_result = Coverband::Utils::Result.new(report)
21
+ @coverage_result = Coverband::Utils::Results.new(report) if report
22
22
  end
23
23
 
24
24
  def format!
@@ -29,8 +29,22 @@ module Coverband
29
29
  format_html(@coverage_result)
30
30
  end
31
31
 
32
+ def format_settings!
33
+ format_settings
34
+ end
35
+
36
+ def format_source_file!(filename)
37
+ source_file = @coverage_result.file_from_path_with_type(filename)
38
+
39
+ formatted_source_file(@coverage_result, source_file)
40
+ end
41
+
32
42
  private
33
43
 
44
+ def format_settings
45
+ template('settings').result(binding)
46
+ end
47
+
34
48
  def format(result)
35
49
  Dir[File.join(File.dirname(__FILE__), '../../../public/*')].each do |path|
36
50
  FileUtils.cp_r(path, asset_output_path)
@@ -56,7 +70,7 @@ module Coverband
56
70
 
57
71
  def asset_output_path
58
72
  return @asset_output_path if defined?(@asset_output_path) && @asset_output_path
59
- @asset_output_path = File.join(output_path, 'assets', Coverband::VERSION)
73
+ @asset_output_path = File.join(output_path)
60
74
  FileUtils.mkdir_p(@asset_output_path)
61
75
  @asset_output_path
62
76
  end
@@ -78,24 +92,47 @@ module Coverband
78
92
  end
79
93
 
80
94
  # Returns the html for the given source_file
81
- def formatted_source_file(source_file)
95
+ def formatted_source_file(result, source_file)
82
96
  template('source_file').result(binding)
83
97
  rescue Encoding::CompatibilityError => e
84
- puts "Encoding problems with file #{source_file.filename}. Coverband/ERB can't handle non ASCII characters in filenames. Error: #{e.message}."
98
+ puts "Encoding error file:#{source_file.filename} Coverband/ERB error #{e.message}."
99
+ end
100
+
101
+ # Returns the html to ajax load a given source_file
102
+ def formatted_source_file_loader(result, source_file)
103
+ template('source_file_loader').result(binding)
104
+ rescue Encoding::CompatibilityError => e
105
+ puts "Encoding error file:#{source_file.filename} Coverband/ERB error #{e.message}."
85
106
  end
86
107
 
87
108
  # Returns a table containing the given source files
88
- def formatted_file_list(title, source_files)
109
+ def formatted_file_list(title, result, source_files, options = {})
89
110
  title_id = title.gsub(/^[^a-zA-Z]+/, '').gsub(/[^a-zA-Z0-9\-\_]/, '')
90
111
  # Silence a warning by using the following variable to assign to itself:
91
112
  # "warning: possibly useless use of a variable in void context"
92
113
  # The variable is used by ERB via binding.
93
114
  title_id = title_id
94
- template('file_list').result(binding)
115
+ options = options
116
+
117
+ if title == 'Gems'
118
+ template('gem_list').result(binding)
119
+ else
120
+ template('file_list').result(binding)
121
+ end
122
+ end
123
+
124
+ def view_gems?
125
+ Coverband.configuration.track_gems
126
+ end
127
+
128
+ def gem_details?
129
+ Coverband.configuration.gem_details
95
130
  end
96
131
 
97
132
  def coverage_css_class(covered_percent)
98
- if covered_percent > 90
133
+ if covered_percent.nil?
134
+ ''
135
+ elsif covered_percent > 90
99
136
  'green'
100
137
  elsif covered_percent > 80
101
138
  'yellow'
@@ -124,7 +161,12 @@ module Coverband
124
161
  end
125
162
 
126
163
  def shortened_filename(source_file)
127
- source_file.filename.sub(Coverband.configuration.root, '.').gsub(/^\.\//, '')
164
+ source_file.short_name
165
+ end
166
+
167
+ def link_to_gem_list(gem_name)
168
+ gem_id = gem_name.gsub(/^[^a-zA-Z]+/, '').gsub(/[^a-zA-Z0-9\-\_]/, '')
169
+ %(<a href="##{gem_id}" class="gem-link" title="#{gem_name}">#{gem_name}</a>)
128
170
  end
129
171
 
130
172
  def link_to_source_file(source_file)
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ Coverband.eager_loading_coverage!
3
4
  module Coverband
4
5
  class Railtie < Rails::Railtie
5
6
  initializer 'coverband.configure' do |app|
@@ -7,7 +8,14 @@ module Coverband
7
8
  end
8
9
 
9
10
  config.after_initialize do
10
- Coverband::Collectors::Coverage.instance.report_coverage(true)
11
+ Coverband.report_coverage(true)
12
+ Coverband.configuration.logger&.debug('Coverband: reported after_initialize')
13
+ Coverband.runtime_coverage!
14
+ end
15
+
16
+ config.before_initialize do
17
+ Coverband.configuration.logger&.debug('Coverband: set to eager_loading')
18
+ Coverband.eager_loading_coverage!
11
19
  end
12
20
 
13
21
  rake_tasks do
@@ -14,26 +14,29 @@ module Coverband
14
14
  module Utils
15
15
  class Result
16
16
  extend Forwardable
17
- # Returns the original Coverage.result used for this instance of SimpleCov::Result
17
+ # Returns the original Coverage.result used for this instance of Coverband::Result
18
18
  attr_reader :original_result
19
- # Returns all files that are applicable to this result (sans filters!) as instances of SimpleCov::SourceFile. Aliased as :source_files
19
+ # Returns all files that are applicable to this result (sans filters!)
20
+ # as instances of Coverband::SourceFile. Aliased as :source_files
20
21
  attr_reader :files
21
22
  alias source_files files
22
23
  # Explicitly set the Time this result has been created
23
24
  attr_writer :created_at
24
- # Explicitly set the command name that was used for this coverage result. Defaults to SimpleCov.command_name
25
+ # Explicitly set the command name that was used for this coverage result.
26
+ # Defaults to Coverband.command_name
25
27
  attr_writer :command_name
26
28
 
27
29
  def_delegators :files, :covered_percent, :covered_percentages, :least_covered_file, :covered_strength, :covered_lines, :missed_lines
28
30
  def_delegator :files, :lines_of_code, :total_lines
29
31
 
30
- # Initialize a new SimpleCov::Result from given Coverage.result (a Hash of filenames each containing an array of
32
+ # Initialize a new Coverband::Result from given Coverage.result (a Hash of filenames each containing an array of
31
33
  # coverage data)
32
34
  def initialize(original_result)
33
- @original_result = original_result.freeze
34
- @files = Coverband::Utils::FileList.new(original_result.map do |filename, coverage|
35
+ @original_result = (original_result || {}).freeze
36
+
37
+ @files = Coverband::Utils::FileList.new(@original_result.map do |filename, coverage|
35
38
  Coverband::Utils::SourceFile.new(filename, coverage) if File.file?(filename)
36
- end.compact.sort_by(&:filename))
39
+ end.compact.sort_by(&:short_name))
37
40
  filter!
38
41
  end
39
42
 
@@ -42,15 +45,9 @@ module Coverband
42
45
  files.map(&:filename)
43
46
  end
44
47
 
45
- # Returns a Hash of groups for this result. Define groups using SimpleCov.add_group 'Models', 'app/models'
46
- # Coverband doesn't currently support groups
48
+ # Returns a Hash of groups for this result. Define groups using Coverband.add_group 'Models', 'app/models'
47
49
  def groups
48
- @groups ||= [] # SimpleCov.grouped(files)
49
- end
50
-
51
- # Applies the configured SimpleCov.formatter on this result
52
- def format!
53
- # SimpleCov.formatter.new.format(self)
50
+ @groups ||= FileGroups.new(files).grouped_results
54
51
  end
55
52
 
56
53
  # Defines when this result has been created. Defaults to Time.now
@@ -59,9 +56,9 @@ module Coverband
59
56
  end
60
57
 
61
58
  # The command name that launched this result.
62
- # Delegated to SimpleCov.command_name if not set manually
59
+ # Delegated to Coverband.command_name if not set manually
63
60
  def command_name
64
- @command_name ||= 'SimpleCov.command_name'
61
+ @command_name ||= 'Coverband'
65
62
  end
66
63
 
67
64
  # Returns a hash representation of this Result that can be used for marshalling it into JSON
@@ -69,7 +66,7 @@ module Coverband
69
66
  { command_name => { 'coverage' => coverage, 'timestamp' => created_at.to_i } }
70
67
  end
71
68
 
72
- # Loads a SimpleCov::Result#to_hash dump
69
+ # Loads a Coverband::Result#to_hash dump
73
70
  def self.from_hash(hash)
74
71
  command_name, data = hash.first
75
72
  result = new(data['coverage'])
@@ -100,7 +97,7 @@ module Coverband
100
97
  Hash[keys.zip(original_result.values_at(*keys))]
101
98
  end
102
99
 
103
- # Applies all configured SimpleCov filters on this result's source files
100
+ # Applies all configured Coverband filters on this result's source files
104
101
  def filter!
105
102
  @files = files
106
103
  end