coverband 5.1.0.rcmailer.1 → 5.2.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/diagram.yml +16 -0
  3. data/.github/workflows/main.yml +5 -5
  4. data/Gemfile +2 -5
  5. data/README.md +33 -1
  6. data/changes.md +19 -66
  7. data/coverband.gemspec +16 -4
  8. data/diagram.svg +1 -0
  9. data/lib/coverband/adapters/base.rb +8 -7
  10. data/lib/coverband/adapters/file_store.rb +1 -1
  11. data/lib/coverband/adapters/hash_redis_store.rb +1 -1
  12. data/lib/coverband/adapters/null_store.rb +42 -0
  13. data/lib/coverband/adapters/redis_store.rb +1 -1
  14. data/lib/coverband/at_exit.rb +3 -0
  15. data/lib/coverband/collectors/coverage.rb +19 -1
  16. data/lib/coverband/collectors/view_tracker.rb +27 -20
  17. data/lib/coverband/collectors/view_tracker_service.rb +3 -3
  18. data/lib/coverband/configuration.rb +9 -2
  19. data/lib/coverband/integrations/background.rb +6 -3
  20. data/lib/coverband/reporters/base.rb +1 -1
  21. data/lib/coverband/reporters/web.rb +4 -4
  22. data/lib/coverband/utils/dead_methods.rb +63 -0
  23. data/lib/coverband/utils/method_definition_scanner.rb +96 -0
  24. data/lib/coverband/utils/railtie.rb +3 -2
  25. data/lib/coverband/utils/result.rb +1 -1
  26. data/lib/coverband/utils/source_file.rb +2 -2
  27. data/lib/coverband/utils/tasks.rb +17 -3
  28. data/lib/coverband/version.rb +2 -2
  29. data/lib/coverband.rb +1 -0
  30. data/public/dependencies.js +4 -4
  31. data/roadmap.md +56 -0
  32. data/test/coverband/adapters/null_store_test.rb +39 -0
  33. data/test/coverband/configuration_test.rb +5 -5
  34. data/test/coverband/utils/dead_methods_test.rb +53 -0
  35. data/test/coverband/utils/method_definition_scanner_test.rb +85 -0
  36. data/test/fixtures/casting_invitor.rb +60 -0
  37. data/test/forked/rails_full_stack_test.rb +1 -1
  38. data/test/integration/full_stack_deferred_eager_test.rb +51 -0
  39. data/test/test_helper.rb +1 -0
  40. data/test/unique_files.rb +3 -3
  41. data/views/file_list.erb +2 -2
  42. metadata +25 -4
@@ -8,8 +8,8 @@ module Coverband
8
8
  class ViewTrackerService < ViewTracker
9
9
  def report_views_tracked
10
10
  reported_time = Time.now.to_i
11
- if views_to_record.any?
12
- relative_views = views_to_record.map! do |view|
11
+ if @views_to_record.any?
12
+ relative_views = @views_to_record.map! do |view|
13
13
  roots.each do |root|
14
14
  view = view.gsub(/#{root}/, "")
15
15
  end
@@ -17,7 +17,7 @@ module Coverband
17
17
  end
18
18
  save_tracked_views(views: relative_views, reported_time: reported_time)
19
19
  end
20
- self.views_to_record = []
20
+ @views_to_record = []
21
21
  rescue => e
22
22
  # we don't want to raise errors if Coverband can't reach the service
23
23
  logger&.error "Coverband: view_tracker failed to store, error #{e.class.name}" if Coverband.configuration.verbose || Coverband.configuration.service_dev_mode
@@ -8,7 +8,7 @@ module Coverband
8
8
  :background_reporting_enabled,
9
9
  :test_env, :web_enable_clear, :gem_details, :web_debug, :report_on_exit,
10
10
  :simulate_oneshot_lines_coverage,
11
- :view_tracker
11
+ :view_tracker, :defer_eager_loading_data
12
12
  attr_writer :logger, :s3_region, :s3_bucket, :s3_access_key_id,
13
13
  :s3_secret_access_key, :password, :api_key, :service_url, :coverband_timeout, :service_dev_mode,
14
14
  :service_test_mode, :process_type, :track_views, :redis_url,
@@ -62,6 +62,7 @@ module Coverband
62
62
  @store = nil
63
63
  @background_reporting_enabled = true
64
64
  @background_reporting_sleep_seconds = nil
65
+ @defer_eager_loading_data = false
65
66
  @test_env = nil
66
67
  @web_enable_clear = false
67
68
  @track_views = true
@@ -147,7 +148,9 @@ module Coverband
147
148
  end
148
149
 
149
150
  def track_views
150
- @track_views ||= service_disabled_dev_test_env? ? false : true
151
+ return false if service_disabled_dev_test_env?
152
+
153
+ @track_views
151
154
  end
152
155
 
153
156
  ###
@@ -241,6 +244,10 @@ module Coverband
241
244
  Coverband.coverband_service? || !api_key.nil?
242
245
  end
243
246
 
247
+ def defer_eager_loading_data?
248
+ @defer_eager_loading_data
249
+ end
250
+
244
251
  def service_disabled_dev_test_env?
245
252
  (coverband_env == "test" && !Coverband.configuration.service_test_mode) ||
246
253
  (coverband_env == "development" && !Coverband.configuration.service_dev_mode)
@@ -31,15 +31,18 @@ module Coverband
31
31
  sleep_seconds = Coverband.configuration.background_reporting_sleep_seconds.to_i
32
32
  @thread = Thread.new {
33
33
  loop do
34
- Coverband.report_coverage
35
- Coverband.configuration.view_tracker&.report_views_tracked
36
34
  if Coverband.configuration.reporting_wiggle
37
35
  sleep_seconds = Coverband.configuration.background_reporting_sleep_seconds.to_i + rand(Coverband.configuration.reporting_wiggle.to_i)
38
36
  end
37
+ # NOTE: Normally as processes first start we immediately report, this causes a redis spike on deploys
38
+ # if deferred is set also sleep frst to spread load
39
+ sleep(sleep_seconds.to_i) if Coverband.configuration.defer_eager_loading_data?
40
+ Coverband.report_coverage
41
+ Coverband.configuration.view_tracker&.report_views_tracked
39
42
  if Coverband.configuration.verbose
40
43
  logger.debug("Coverband: background reporting coverage (#{Coverband.configuration.store.type}). Sleeping #{sleep_seconds}s")
41
44
  end
42
- sleep(sleep_seconds.to_i)
45
+ sleep(sleep_seconds.to_i) unless Coverband.configuration.defer_eager_loading_data?
43
46
  end
44
47
  }
45
48
  end
@@ -15,7 +15,7 @@ module Coverband
15
15
  scov_style_report = get_current_scov_data_imp(store, all_roots)
16
16
 
17
17
  # These are extremelhy verbose but useful during coverband development, not generally for users
18
- # Only avaiable by uncommenting this mode is never released
18
+ # Only available by uncommenting this mode is never released
19
19
  # if Coverband.configuration.verbose
20
20
  # # msg = "report:\n #{scov_style_report.inspect}"
21
21
  # # Coverband.configuration.logger.debug msg
@@ -124,7 +124,7 @@ module Coverband
124
124
  Coverband.configuration.store.clear!
125
125
  notice = "coverband coverage cleared"
126
126
  else
127
- notice = "web_enable_clear isnt enabled in your configuration"
127
+ notice = "web_enable_clear isn't enabled in your configuration"
128
128
  end
129
129
  [301, {"Location" => "#{base_path}?notice=#{notice}"}, []]
130
130
  end
@@ -135,7 +135,7 @@ module Coverband
135
135
  Coverband.configuration.store.clear_file!(filename)
136
136
  notice = "coverage for file #{filename} cleared"
137
137
  else
138
- notice = "web_enable_clear isnt enabled in your configuration"
138
+ notice = "web_enable_clear isn't enabled in your configuration"
139
139
  end
140
140
  [301, {"Location" => "#{base_path}?notice=#{notice}"}, []]
141
141
  end
@@ -146,7 +146,7 @@ module Coverband
146
146
  tracker.reset_recordings
147
147
  notice = "view tracking reset"
148
148
  else
149
- notice = "web_enable_clear isnt enabled in your configuration"
149
+ notice = "web_enable_clear isn't enabled in your configuration"
150
150
  end
151
151
  [301, {"Location" => "#{base_path}/view_tracker?notice=#{notice}"}, []]
152
152
  end
@@ -158,7 +158,7 @@ module Coverband
158
158
  tracker.clear_file!(filename)
159
159
  notice = "coverage for file #{filename} cleared"
160
160
  else
161
- notice = "web_enable_clear isnt enabled in your configuration"
161
+ notice = "web_enable_clear isn't enabled in your configuration"
162
162
  end
163
163
  [301, {"Location" => "#{base_path}/view_tracker?notice=#{notice}"}, []]
164
164
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: ntrue
2
+
3
+ require "coverband/utils/method_definition_scanner"
4
+
5
+ module Coverband
6
+ module Utils
7
+ module ArrayToTableInConsole
8
+ refine Array do
9
+ def to_table
10
+ column_sizes =
11
+ reduce([]) { |lengths, row|
12
+ row.each_with_index.map do |iterand, index|
13
+ [lengths[index] || 0, iterand.to_s.length].max
14
+ end
15
+ }
16
+ puts head =
17
+ "-" * (column_sizes.inject(&:+) + (3 * column_sizes.count) + 1)
18
+ each do |row|
19
+ row = row.fill(nil, row.size..(column_sizes.size - 1))
20
+ row =
21
+ row.each_with_index.map { |v, i|
22
+ v.to_s + " " * (column_sizes[i] - v.to_s.length)
23
+ }
24
+ puts "| " + row.join(" | ") + " |"
25
+ end
26
+ puts head
27
+ end
28
+ end
29
+ end
30
+
31
+ class DeadMethods
32
+ using ArrayToTableInConsole
33
+ def self.scan(file_path:, coverage:)
34
+ MethodDefinitionScanner.scan(file_path).reject do |method_definition|
35
+ method_definition.body.coverage?(coverage)
36
+ end
37
+ end
38
+
39
+ def self.scan_all
40
+ coverage = Coverband.configuration.store.coverage
41
+ coverage.flat_map do |file_path, coverage|
42
+ scan(file_path: file_path, coverage: coverage["data"])
43
+ end
44
+ end
45
+
46
+ def self.output_all
47
+ rows =
48
+ scan_all.each_with_object(
49
+ [%w[file class method line_number]]
50
+ ) { |dead_method, rows|
51
+ rows <<
52
+ [
53
+ dead_method.file_path,
54
+ dead_method.class_name,
55
+ dead_method.name,
56
+ dead_method.first_line_number
57
+ ]
58
+ }
59
+ rows.to_table
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(RubyVM::AbstractSyntaxTree)
4
+ module Coverband
5
+ module Utils
6
+ class MethodDefinitionScanner
7
+ attr_reader :path
8
+
9
+ def initialize(path)
10
+ @path = path
11
+ end
12
+
13
+ def scan
14
+ scan_node(RubyVM::AbstractSyntaxTree.parse_file(path), nil)
15
+ end
16
+
17
+ def self.scan(path)
18
+ new(path).scan
19
+ end
20
+
21
+ class MethodBody
22
+ def initialize(method_definition)
23
+ @method_definition = method_definition
24
+ end
25
+
26
+ def coverage?(file_coverage)
27
+ body_coverage =
28
+ file_coverage[(first_line_number - 1)..(last_line_number - 1)]
29
+ body_coverage.map(&:to_i).any?(&:positive?)
30
+ end
31
+
32
+ private
33
+
34
+ def first_line_number
35
+ @method_definition.first_line_number + 1
36
+ end
37
+
38
+ def last_line_number
39
+ @method_definition.last_line_number - 1
40
+ end
41
+ end
42
+
43
+ class MethodDefinition
44
+ attr_reader :last_line_number,
45
+ :first_line_number,
46
+ :name,
47
+ :class_name,
48
+ :file_path
49
+
50
+ def initialize(
51
+ first_line_number:,
52
+ last_line_number:,
53
+ name:,
54
+ class_name:,
55
+ file_path:
56
+ )
57
+ @first_line_number = first_line_number
58
+ @last_line_number = last_line_number
59
+ @name = name
60
+ @class_name = class_name
61
+ @file_path = file_path
62
+ end
63
+
64
+ def body
65
+ MethodBody.new(self)
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def scan_node(node, class_name)
72
+ definitions = []
73
+ return definitions unless node.is_a?(RubyVM::AbstractSyntaxTree::Node)
74
+ current_class = node.type == :CLASS ? node.children.first.children.last : class_name
75
+ if node.type == :DEFN
76
+ definitions <<
77
+ MethodDefinition.new(
78
+ first_line_number: node.first_lineno,
79
+ last_line_number: node.last_lineno,
80
+ name: node.children.first,
81
+ class_name: current_class,
82
+ file_path: path
83
+ )
84
+ end
85
+ definitions + scan_children(node, current_class)
86
+ end
87
+
88
+ def scan_children(node, current_class)
89
+ node.children.flatten.compact.map { |child|
90
+ scan_node(child, current_class)
91
+ }.flatten
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -21,7 +21,7 @@ module Coverband
21
21
 
22
22
  config.after_initialize do
23
23
  unless Coverband.tasks_to_ignore?
24
- Coverband.configure
24
+ Coverband.configure unless Coverband.configured?
25
25
  Coverband.eager_loading_coverage!
26
26
  Coverband.report_coverage
27
27
  Coverband.runtime_coverage!
@@ -37,7 +37,7 @@ module Coverband
37
37
 
38
38
  Coverband.configuration.view_tracker = COVERBAND_VIEW_TRACKER
39
39
 
40
- ActiveSupport::Notifications.subscribe(/render_partial.action_view|render_template.action_view/) do |name, start, finish, id, payload|
40
+ ActiveSupport::Notifications.subscribe(/render_(template|partial|collection).action_view/) do |name, start, finish, id, payload|
41
41
  COVERBAND_VIEW_TRACKER.track_views(name, start, finish, id, payload) unless name.include?("!")
42
42
  end
43
43
  end
@@ -50,6 +50,7 @@ module Coverband
50
50
  config.before_configuration do
51
51
  unless ENV["COVERBAND_DISABLE_AUTO_START"]
52
52
  begin
53
+ Coverband.configure unless Coverband.configured?
53
54
  Coverband.start
54
55
  rescue Redis::CannotConnectError => error
55
56
  Coverband.configuration.logger.info "Redis is not available (#{error}), Coverband not configured"
@@ -55,7 +55,7 @@ module Coverband
55
55
  Dir[tracked_files].each do |file|
56
56
  absolute = File.expand_path(file)
57
57
  result[absolute] ||= {
58
- "data" => Array.new(File.foreach(absolute).count) { 0 },
58
+ "data" => [],
59
59
  "never_loaded" => true
60
60
  }
61
61
  end
@@ -101,7 +101,7 @@ module Coverband
101
101
  @last_updated_at = Time.at(file_data["last_updated_at"]) if file_data["last_updated_at"]
102
102
  @never_loaded = file_data["never_loaded"] || false
103
103
  else
104
- # TODO: Deprecate this code path this was backwards compatability from 3-4
104
+ # TODO: Deprecate this code path this was backwards compatibility from 3-4
105
105
  @coverage = file_data
106
106
  @first_updated_at = NOT_AVAILABLE
107
107
  @last_updated_at = NOT_AVAILABLE
@@ -139,7 +139,7 @@ module Coverband
139
139
  coverage_exceeding_source_warn if coverage.size > src.size
140
140
 
141
141
  lines = src.map.with_index(1) { |src, i|
142
- Coverband::Utils::SourceFile::Line.new(src, i, coverage[i - 1])
142
+ Coverband::Utils::SourceFile::Line.new(src, i, never_loaded ? 0 : coverage[i - 1])
143
143
  }
144
144
 
145
145
  process_skipped_lines(lines)
@@ -9,11 +9,25 @@ namespace :coverband do
9
9
  Coverband::Reporters::ConsoleReport.report(Coverband.configuration.store)
10
10
  end
11
11
 
12
+ if defined?(RubyVM::AbstractSyntaxTree)
13
+ require "coverband/utils/dead_methods"
14
+
15
+ desc "Output all dead methods"
16
+ task :dead_methods do
17
+ Coverband::Utils::DeadMethods.output_all
18
+ end
19
+ end
20
+
12
21
  desc "report runtime Coverband code coverage"
13
22
  task :coverage_server do
14
- Rake.application["environment"].invoke if Rake::Task.task_defined?("environment")
15
- Coverband.configuration.store.merge_mode = true if Coverband.configuration.store.is_a?(Coverband::Adapters::FileStore)
16
- Rack::Server.start app: Coverband::Reporters::Web.new, Port: ENV.fetch("COVERBAND_COVERAGE_PORT", 9022).to_i
23
+ if Rake::Task.task_defined?("environment")
24
+ Rake.application["environment"].invoke
25
+ end
26
+ if Coverband.configuration.store.is_a?(Coverband::Adapters::FileStore)
27
+ Coverband.configuration.store.merge_mode = true
28
+ end
29
+ Rack::Server.start app: Coverband::Reporters::Web.new,
30
+ Port: ENV.fetch("COVERBAND_COVERAGE_PORT", 9022).to_i
17
31
  end
18
32
 
19
33
  ###
@@ -2,8 +2,8 @@
2
2
 
3
3
  ###
4
4
  # ensure we properly do release candidate versioning; https://github.com/danmayer/coverband/issues/288
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
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 = "5.1.0.rcmailer.1"
8
+ VERSION = '5.2.0.rc.1'
9
9
  end
data/lib/coverband.rb CHANGED
@@ -13,6 +13,7 @@ require "coverband/adapters/redis_store"
13
13
  require "coverband/adapters/hash_redis_store"
14
14
  require "coverband/adapters/file_store"
15
15
  require "coverband/adapters/stdout_store"
16
+ require "coverband/adapters/null_store"
16
17
  require "coverband/utils/file_hasher"
17
18
  require "coverband/collectors/coverage"
18
19
  require "coverband/collectors/view_tracker"
@@ -103,7 +103,7 @@ var hljs=new function(){function l(o){return o.replace(/&/gm,"&amp;").replace(/<
103
103
  return this.rel;
104
104
  },
105
105
  href: function() {
106
- // using this.href would give the absolute url, when the href may have been inteded as a selector (e.g. '#container')
106
+ // using this.href would give the absolute url, when the href may have been intedned as a selector (e.g. '#container')
107
107
  return $(this).attr('href');
108
108
  },
109
109
  title: function() {
@@ -397,7 +397,7 @@ var hljs=new function(){function l(o){return o.replace(/&/gm,"&amp;").replace(/<
397
397
  loadedHeight = $loaded.outerHeight(true);
398
398
  loadedWidth = $loaded.outerWidth(true);
399
399
 
400
- // Opens inital empty Colorbox prior to content being loaded.
400
+ // Opens initial empty Colorbox prior to content being loaded.
401
401
  var initialWidth = setSize(settings.get('initialWidth'), 'x');
402
402
  var initialHeight = setSize(settings.get('initialHeight'), 'y');
403
403
  var maxWidth = settings.get('maxWidth');
@@ -983,7 +983,7 @@ var hljs=new function(){function l(o){return o.replace(/&/gm,"&amp;").replace(/<
983
983
  return;
984
984
  }
985
985
 
986
- // A small pause because some browsers will occassionaly report a
986
+ // A small pause because some browsers will occasionally report a
987
987
  // img.width and img.height of zero immediately after the img.onload fires
988
988
  setTimeout(function(){
989
989
  var percent;
@@ -1236,7 +1236,7 @@ u;this.oApi._fnAddColumn=y;this.oApi._fnColumnOptions=C;this.oApi._fnAddData=w;t
1236
1236
  Q;this.oApi._fnFilterCreateSearch=fa;this.oApi._fnDataToSearch=ga;this.oApi._fnSort=O;this.oApi._fnSortAttachListener=$;this.oApi._fnSortingClasses=W;this.oApi._fnFeatureHtmlPaginate=wa;this.oApi._fnPageChange=Ba;this.oApi._fnFeatureHtmlInfo=va;this.oApi._fnUpdateInfo=Ca;this.oApi._fnFeatureHtmlLength=ra;this.oApi._fnFeatureHtmlProcessing=ta;this.oApi._fnProcessingDisplay=K;this.oApi._fnVisibleToColumnIndex=da;this.oApi._fnColumnIndexToVisible=N;this.oApi._fnNodeToDataIndex=R;this.oApi._fnVisbleColumns=
1237
1237
  T;this.oApi._fnCalculateEnd=F;this.oApi._fnConvertToWidth=Da;this.oApi._fnCalculateColumnWidths=ea;this.oApi._fnScrollingWidthAdjust=ia;this.oApi._fnGetWidestNode=Ea;this.oApi._fnGetMaxLenString=Fa;this.oApi._fnStringToCss=v;this.oApi._fnArrayCmp=La;this.oApi._fnDetectType=Z;this.oApi._fnSettingsFromNode=B;this.oApi._fnGetDataMaster=V;this.oApi._fnGetTrNodes=S;this.oApi._fnGetTdNodes=X;this.oApi._fnEscapeRegex=ha;this.oApi._fnDeleteIndex=ja;this.oApi._fnReOrderIndex=qa;this.oApi._fnColumnOrdering=
1238
1238
  aa;this.oApi._fnLog=J;this.oApi._fnClearTable=ba;this.oApi._fnSaveState=Ga;this.oApi._fnLoadState=Ia;this.oApi._fnCreateCookie=Ha;this.oApi._fnReadCookie=ka;this.oApi._fnGetUniqueThs=ca;this.oApi._fnScrollBarWidth=Ja;this.oApi._fnApplyToChildren=M;this.oApi._fnMap=n;var Ka=this;return this.each(function(){var a=0,b,c,d,f;a=0;for(b=E.length;a<b;a++){if(E[a].nTable==this)if(typeof g=="undefined"||typeof g.bRetrieve!="undefined"&&g.bRetrieve===true)return E[a].oInstance;else if(typeof g.bDestroy!="undefined"&&
1239
- g.bDestroy===true){E[a].oInstance.fnDestroy();break}else{J(E[a],0,"Cannot reinitialise DataTable.\n\nTo retrieve the DataTables object for this table, please pass either no arguments to the dataTable() function, or set bRetrieve to true. Alternatively, to destory the old table and create a new one, set bDestroy to true (note that a lot of changes to the configuration can be made through the API which is usually much faster).");return}if(E[a].sTableId!==""&&E[a].sTableId==this.getAttribute("id")){E.splice(a,
1239
+ g.bDestroy===true){E[a].oInstance.fnDestroy();break}else{J(E[a],0,"Cannot reinitialise DataTable.\n\nTo retrieve the DataTables object for this table, please pass either no arguments to the dataTable() function, or set bRetrieve to true. Alternatively, to destroy the old table and create a new one, set bDestroy to true (note that a lot of changes to the configuration can be made through the API which is usually much faster).");return}if(E[a].sTableId!==""&&E[a].sTableId==this.getAttribute("id")){E.splice(a,
1240
1240
  1);break}}var e=new l;E.push(e);var i=false,h=false;a=this.getAttribute("id");if(a!==null){e.sTableId=a;e.sInstance=a}else e.sInstance=m._oExternConfig.iNextUnique++;e.oInstance=Ka;e.nTable=this;e.oApi=Ka.oApi;if(typeof g!="undefined"&&g!==null){e.oInit=g;n(e.oFeatures,g,"bPaginate");n(e.oFeatures,g,"bLengthChange");n(e.oFeatures,g,"bFilter");n(e.oFeatures,g,"bSort");n(e.oFeatures,g,"bInfo");n(e.oFeatures,g,"bProcessing");n(e.oFeatures,g,"bAutoWidth");n(e.oFeatures,g,"bSortClasses");n(e.oFeatures,
1241
1241
  g,"bServerSide");n(e.oScroll,g,"sScrollX","sX");n(e.oScroll,g,"sScrollXInner","sXInner");n(e.oScroll,g,"sScrollY","sY");n(e.oScroll,g,"bScrollCollapse","bCollapse");n(e,g,"asStripClasses");n(e,g,"fnRowCallback");n(e,g,"fnHeaderCallback");n(e,g,"fnFooterCallback");n(e,g,"fnInitComplete");n(e,g,"fnServerData");n(e,g,"fnFormatNumber");n(e,g,"aaSorting");n(e,g,"aaSortingFixed");n(e,g,"aLengthMenu");n(e,g,"sPaginationType");n(e,g,"sAjaxSource");n(e,g,"iCookieDuration");n(e,g,"sCookiePrefix");n(e,g,"sDom");
1242
1242
  n(e,g,"oSearch","oPreviousSearch");n(e,g,"aoSearchCols","aoPreSearchCols");n(e,g,"iDisplayLength","_iDisplayLength");n(e,g,"bJQueryUI","bJUI");typeof g.fnDrawCallback=="function"&&e.aoDrawCallback.push({fn:g.fnDrawCallback,sName:"user"});e.oFeatures.bServerSide&&e.oFeatures.bSort&&e.oFeatures.bSortClasses&&e.aoDrawCallback.push({fn:W,sName:"server_side_sort_classes"});if(typeof g.bJQueryUI!="undefined"&&g.bJQueryUI){e.oClasses=m.oJUIClasses;if(typeof g.sDom=="undefined")e.sDom='<"H"lfr>t<"F"ip>'}if(e.oScroll.sX!==
data/roadmap.md ADDED
@@ -0,0 +1,56 @@
1
+ # Future Roadmap
2
+
3
+ ### Research Alternative Redis formats
4
+
5
+ - Look at alternative storage formats for Redis
6
+ - [redis bitmaps](http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps/)
7
+ - [redis bitfield](https://stackoverflow.com/questions/47100606/optimal-way-to-store-array-of-integers-in-redis-database)
8
+ - Add support for [zadd](http://redis.io/topics/data-types-intro) so one could determine single call versus multiple calls on a line, letting us determine the most executed code in production.
9
+
10
+ ### Coverband Future...
11
+
12
+ Will be the fully modern release that drops maintenance legacy support in favor of increased performance, ease of use, and maintainability.
13
+
14
+ - Release will be aimed as significantly simplifying ease of use
15
+ - near zero config setup for Rails apps
16
+ - add built-in support for easy loading via Railties
17
+ - built in support for activejob, sidekiq, and other common frameworks
18
+ - reduced configuration options
19
+ - support oneshot
20
+ - drop middleware figure out a way to kick off background without middelware
21
+ - options on reporting
22
+ - background reporting
23
+ - or middleware reporting
24
+ - Support for file versions
25
+ - md5 or release tags
26
+ - add coverage timerange support
27
+ - Drop Simplecov dependency
28
+ - improved web reporting
29
+ - lists current config options
30
+ - eventually allow updating remote config
31
+ - full theming
32
+ - list redis data dump for debugging
33
+ - additional adapters: Memcache, S3, and ActiveRecord
34
+ - add articles / podcasts like prontos readme https://github.com/prontolabs/pronto
35
+ - Add detailed Gem usage report, if we collect and send gem usage we can give percentage of gem code used, which should help application developers know when to remove gem dependencies (0%) or perhaps inline single methods for little usage (using <= 5%) for example.
36
+ - add meta data information first seen last recorded to the coverage report views (probably need to drop simplecov for that).
37
+ - more details in this issue: https://github.com/danmayer/coverband/issues/118
38
+ - Make good video on setup, install, usage
39
+ - See if we can add support for views / templates
40
+ - using this technique https://github.com/ioquatix/covered
41
+ - Better default grouping (could use groups features for gems for rails controllers, models, lib, etc)
42
+ - Improved logging for easier debugging and development
43
+ - drop the verbose mode and better support standard logger levels
44
+ - Possibly setup a build assets system
45
+ - my JS rules expanded the compressed JS at the top of application.js, basically we want to stitch together JS
46
+ - I guess we could also load multiple JS files as most of the JS is just default compressed JS and a tiny amount of actual app JS.
47
+ - lazy load for Coverband results
48
+ - view layer file coverage
49
+ - move all code to work with relative paths leaving only stdlib Coverage working on full paths
50
+ - add gem_safe_lists to track only some gems
51
+ - add gem_details_safe list to report on details on some gems
52
+ - - display gems that are in loaded with 0 coverage, thanks @kbaum
53
+
54
+ # Alpha / Beta / Release Candidates
55
+
56
+ ### Coverband 5.?.?
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path("../../test_helper", File.dirname(__FILE__))
4
+
5
+ class AdaptersNullStoreTest < Minitest::Test
6
+ def test_covered_lines_when_no_file
7
+ @store = Coverband::Adapters::NullStore.new("")
8
+ expected = {}
9
+ assert_equal expected, @store.coverage
10
+ end
11
+
12
+ describe "Coverband::Adapters::NullStore" do
13
+ def setup
14
+ super
15
+ @store = Coverband::Adapters::NullStore.new(@test_file_path)
16
+ end
17
+
18
+ def test_coverage
19
+ assert_equal @store.coverage, {}
20
+ end
21
+
22
+ def test_covered_lines_when_null
23
+ assert_nil @store.coverage["none.rb"]
24
+ end
25
+
26
+ def test_covered_files
27
+ assert_equal @store.covered_files.include?("dog.rb"), false
28
+ end
29
+
30
+ def test_clear
31
+ assert_nil @store.clear!
32
+ end
33
+
34
+ def test_save_report
35
+ @store.send(:save_report, "cat.rb" => [0, 1])
36
+ assert_equal @store.coverage, {}
37
+ end
38
+ end
39
+ end
@@ -10,7 +10,7 @@ class BaseTest < Minitest::Test
10
10
  Coverband.configure do |config|
11
11
  config.root = Dir.pwd
12
12
  config.root_paths = ["/app_path/"]
13
- config.ignore = ["config/envionments"]
13
+ config.ignore = ["config/environments"]
14
14
  config.reporter = "std_out"
15
15
  config.store = Coverband::Adapters::RedisStore.new(Coverband::Test.redis, redis_namespace: "coverband_test")
16
16
  end
@@ -18,7 +18,7 @@ class BaseTest < Minitest::Test
18
18
 
19
19
  test "ignore works with equal" do
20
20
  Coverband::Collectors::Coverage.instance.reset_instance
21
- expected = ["vendor/", ".erb$", ".slim$", "/tmp", "internal:prelude", "db/schema.rb", "config/envionments"]
21
+ expected = ["vendor/", ".erb$", ".slim$", "/tmp", "internal:prelude", "db/schema.rb", "config/environments"]
22
22
  assert_equal expected, Coverband.configuration.ignore
23
23
  end
24
24
 
@@ -33,7 +33,7 @@ class BaseTest < Minitest::Test
33
33
  "/tmp",
34
34
  "internal:prelude",
35
35
  "db/schema.rb",
36
- "config/envionments",
36
+ "config/environments",
37
37
  "config/initializers"]
38
38
  assert_equal expected, Coverband.configuration.ignore
39
39
  end
@@ -47,7 +47,7 @@ class BaseTest < Minitest::Test
47
47
  Coverband::Collectors::Coverage.instance.reset_instance
48
48
  current_paths = Coverband.configuration.root_paths.dup
49
49
  # verify previous bug fix
50
- # it would extend the root_paths instance variable on each invokation
50
+ # it would extend the root_paths instance variable on each invocation
51
51
  Coverband.configuration.all_root_paths
52
52
  Coverband.configuration.all_root_paths
53
53
  assert_equal current_paths, Coverband.configuration.root_paths
@@ -103,7 +103,7 @@ class BaseTest < Minitest::Test
103
103
  end
104
104
  end
105
105
 
106
- test "store doesnt raises when api key and redis_url" do
106
+ test "store doesn't raises when api key and redis_url" do
107
107
  Coverband::Collectors::Coverage.instance.reset_instance
108
108
  Coverband.configuration.reset
109
109
  Coverband.configure do |config|
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path("../../test_helper", File.dirname(__FILE__))
4
+
5
+ if defined?(RubyVM::AbstractSyntaxTree)
6
+ require "coverband/utils/dead_methods"
7
+ module Coverband
8
+ module Utils
9
+ class DeadMethodsTest < Minitest::Test
10
+ attr_accessor :coverband
11
+
12
+ def setup
13
+ super
14
+ @coverband = Coverband::Collectors::Coverage.instance
15
+ end
16
+
17
+ def test_dog_dead_methods
18
+ file_path = require_unique_file
19
+ coverage = [nil, nil, 1, 1, 0, nil, nil]
20
+ dead_methods =
21
+ DeadMethods.scan(file_path: file_path, coverage: coverage)
22
+ assert_equal(1, dead_methods.length)
23
+ dead_method = dead_methods.first
24
+ assert_equal(4, dead_method.first_line_number)
25
+ assert_equal(6, dead_method.last_line_number)
26
+ assert_equal(file_path, dead_method.file_path)
27
+ end
28
+
29
+ def test_all_dead_methods
30
+ require_unique_file
31
+ @coverband.report_coverage
32
+ dead_methods = DeadMethods.scan_all
33
+ dead_method = dead_methods.find { |method| method.class_name == :Dog }
34
+ assert(dead_method)
35
+ assert_equal(4, dead_method.first_line_number)
36
+ assert_equal(6, dead_method.last_line_number)
37
+ end
38
+
39
+ def test_output_all
40
+ require_unique_file
41
+ @coverband.report_coverage
42
+ DeadMethods.output_all
43
+ end
44
+
45
+ def test_dog_methods_not_dead
46
+ file = require_unique_file
47
+ coverage = [nil, nil, 1, 1, 1, nil, nil]
48
+ assert_empty(DeadMethods.scan(file_path: file, coverage: coverage))
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end