coverband 1.5.4 → 2.0.0.alpha

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +2 -1
  5. data/Rakefile +5 -3
  6. data/coverband.gemspec +27 -23
  7. data/lib/coverband.rb +10 -14
  8. data/lib/coverband/adapters/file_store.rb +6 -7
  9. data/lib/coverband/adapters/memory_cache_store.rb +8 -5
  10. data/lib/coverband/adapters/redis_store.rb +10 -50
  11. data/lib/coverband/baseline.rb +5 -5
  12. data/lib/coverband/collectors/base.rb +140 -0
  13. data/lib/coverband/collectors/coverage.rb +148 -0
  14. data/lib/coverband/collectors/trace.rb +100 -0
  15. data/lib/coverband/configuration.rb +8 -10
  16. data/lib/coverband/middleware.rb +5 -5
  17. data/lib/coverband/reporters/base.rb +26 -31
  18. data/lib/coverband/reporters/console_report.rb +2 -3
  19. data/lib/coverband/reporters/simple_cov_report.rb +5 -6
  20. data/lib/coverband/s3_report_writer.rb +7 -8
  21. data/lib/coverband/s3_web.rb +3 -5
  22. data/lib/coverband/tasks.rb +23 -26
  23. data/lib/coverband/version.rb +3 -1
  24. data/test/benchmarks/benchmark.rake +38 -32
  25. data/test/benchmarks/dog.rb +3 -3
  26. data/test/fake_app/basic_rack.rb +4 -2
  27. data/test/test_helper.rb +17 -11
  28. data/test/unit/adapters_file_store_test.rb +12 -11
  29. data/test/unit/adapters_memory_cache_store_test.rb +3 -4
  30. data/test/unit/adapters_redis_store_test.rb +42 -118
  31. data/test/unit/baseline_test.rb +17 -20
  32. data/test/unit/collectors_base_test.rb +96 -0
  33. data/test/unit/collectors_coverage_test.rb +137 -0
  34. data/test/unit/collectors_trace_test.rb +96 -0
  35. data/test/unit/configuration_test.rb +8 -8
  36. data/test/unit/dog.rb +3 -1
  37. data/test/unit/middleware_test.rb +70 -61
  38. data/test/unit/reports_base_test.rb +62 -62
  39. data/test/unit/reports_console_test.rb +18 -21
  40. data/test/unit/reports_simple_cov_test.rb +23 -26
  41. data/test/unit/s3_report_writer_test.rb +6 -8
  42. data/test/unit/s3_web_test.rb +2 -1
  43. metadata +45 -25
  44. data/lib/coverband/base.rb +0 -210
  45. data/test/unit/base_test.rb +0 -100
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coverband
4
+ module Collectors
5
+ class Coverage < Base
6
+ def record_coverage
7
+ # noop
8
+ end
9
+
10
+ def report_coverage
11
+ unless @enabled
12
+ @logger.info 'coverage disabled' if @verbose
13
+ return
14
+ end
15
+
16
+ if failed_recently?
17
+ @logger.info 'coverage reporting standing-by because of recent failure' if @verbose
18
+ return
19
+ end
20
+
21
+ new_results = nil
22
+ @semaphore.synchronize { new_results = new_coverage(::Coverage.peek_result.dup) }
23
+ new_results.each_pair do |file, line_counts|
24
+ next if @ignored_files.include?(file)
25
+ next unless track_file?(file)
26
+ add_file(file, line_counts)
27
+ end
28
+
29
+ if @verbose
30
+ @logger.info "coverband file usage: #{file_usage.inspect}"
31
+ output_file_line_usage if @verbose == 'debug'
32
+ end
33
+
34
+ if @store
35
+ if @stats
36
+ @before_time = Time.now
37
+ @stats.count 'coverband.files.recorded_files', @file_line_usage.length
38
+ end
39
+
40
+ @store.save_report(@file_line_usage)
41
+ if @stats
42
+ @time_spent = Time.now - @before_time
43
+ @stats.timing 'coverband.files.recorded_time', @time_spent
44
+ end
45
+ @file_line_usage.clear
46
+ elsif @verbose
47
+ @logger.info 'coverage report: '
48
+ @logger.info @file_line_usage.inspect
49
+ end
50
+ rescue RuntimeError => err
51
+ failed!
52
+ if @verbose
53
+ @logger.info 'coverage missing'
54
+ @logger.info "error: #{err.inspect} #{err.message}"
55
+ end
56
+ end
57
+
58
+ protected
59
+
60
+ def set_tracer
61
+ # no op
62
+ end
63
+
64
+ def unset_tracer
65
+ # no op
66
+ end
67
+
68
+ private
69
+
70
+ def array_diff(latest, original)
71
+ latest.map.with_index { |v, i| v ? v - original[i] : nil }
72
+ end
73
+
74
+ def previous_results
75
+ @@previous_results
76
+ end
77
+
78
+ def add_previous_results(val)
79
+ @@previous_results = val
80
+ end
81
+
82
+ def new_coverage(current_coverage)
83
+ if previous_results
84
+ new_results = {}
85
+ current_coverage.each_pair do |file, line_counts|
86
+ new_results[file] = array_diff(line_counts, previous_results[file])
87
+ end
88
+ else
89
+ new_results = current_coverage
90
+ end
91
+
92
+ # if current_coverage['/Users/danmayer/projects/coverage_rails_benchmark/app/controllers/posts_controller.rb']
93
+ # puts "total"
94
+ # puts current_coverage['/Users/danmayer/projects/coverage_rails_benchmark/app/controllers/posts_controller.rb'].inspect
95
+ # end
96
+ #
97
+ #
98
+ # if previous_results && previous_results['/Users/danmayer/projects/coverage_rails_benchmark/app/controllers/posts_controller.rb']
99
+ # puts "prev"
100
+ # puts previous_results['/Users/danmayer/projects/coverage_rails_benchmark/app/controllers/posts_controller.rb'].inspect
101
+ # end
102
+ #
103
+ # if new_results['/Users/danmayer/projects/coverage_rails_benchmark/app/controllers/posts_controller.rb']
104
+ # puts "new"
105
+ # puts new_results['/Users/danmayer/projects/coverage_rails_benchmark/app/controllers/posts_controller.rb'].inspect
106
+ # end
107
+
108
+ add_previous_results(current_coverage)
109
+ new_results.dup
110
+ end
111
+
112
+ # TODO this seems like a dumb conversion for the already good coverage format
113
+ # coverage is 0 based other implementation matches line number
114
+ def add_file(file, line_counts)
115
+ @file_line_usage[file] = Hash.new(0) unless @file_line_usage.include?(file)
116
+ line_counts.each_with_index do |line_count, index|
117
+ @file_line_usage[file][(index + 1)] = line_count if line_count
118
+ end
119
+ end
120
+
121
+ def file_usage
122
+ hash = {}
123
+ @file_line_usage.each do |file, lines|
124
+ hash[file] = lines.values.compact.inject(0, :+)
125
+ end
126
+ hash.sort_by { |_key, value| value }
127
+ end
128
+
129
+ def initialize
130
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3.0')
131
+ raise NotImplementedError, 'not supported until Ruby 2.3.0 and later'
132
+ end
133
+ unless defined?(::Coverage)
134
+ # puts 'loading coverage'
135
+ require 'coverage'
136
+ end
137
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
138
+ ::Coverage.start unless ::Coverage.running?
139
+ else
140
+ ::Coverage.start
141
+ end
142
+ @semaphore = Mutex.new
143
+ @@previous_results = nil
144
+ reset_instance
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coverband
4
+ module Collectors
5
+ class Trace < Base
6
+
7
+ def reset_instance
8
+ super
9
+ @tracer_set = false
10
+ @trace = create_trace_point
11
+ self
12
+ end
13
+
14
+ def report_coverage
15
+ unless @enabled
16
+ @logger.info 'coverage disabled' if @verbose
17
+ return
18
+ end
19
+
20
+ unset_tracer
21
+
22
+ if failed_recently?
23
+ @logger.info 'coverage reporting standing-by because of recent failure' if @verbose
24
+ return
25
+ end
26
+
27
+ if @verbose
28
+ @logger.info "coverband file usage: #{file_usage.inspect}"
29
+ output_file_line_usage if @verbose == 'debug'
30
+ end
31
+
32
+ if @store
33
+ if @stats
34
+ @before_time = Time.now
35
+ @stats.count 'coverband.files.recorded_files', @file_line_usage.length
36
+ end
37
+ @store.save_report(@file_line_usage)
38
+ if @stats
39
+ @time_spent = Time.now - @before_time
40
+ @stats.timing 'coverband.files.recorded_time', @time_spent
41
+ end
42
+ @file_line_usage.clear
43
+ elsif @verbose
44
+ @logger.info 'coverage report: '
45
+ @logger.info @file_line_usage.inspect
46
+ end
47
+ rescue RuntimeError => err
48
+ failed!
49
+ if @verbose
50
+ @logger.info 'coverage missing'
51
+ @logger.info "error: #{err.inspect} #{err.message}"
52
+ end
53
+ end
54
+
55
+ protected
56
+
57
+ def set_tracer
58
+ unless @tracer_set
59
+ @trace.enable
60
+ @tracer_set = true
61
+ end
62
+ end
63
+
64
+ def unset_tracer
65
+ @trace.disable
66
+ @tracer_set = false
67
+ end
68
+
69
+ private
70
+
71
+ def add_file(file, line)
72
+ @file_line_usage[file] = Hash.new(0) unless @file_line_usage.include?(file)
73
+ @file_line_usage[file][line] += 1
74
+ end
75
+
76
+ def file_usage
77
+ hash = {}
78
+ @file_line_usage.each do |file, lines|
79
+ hash[file] = lines.values.inject(0, :+)
80
+ end
81
+ hash.sort_by { |_key, value| value }
82
+ end
83
+
84
+ def create_trace_point
85
+ TracePoint.new(*Coverband.configuration.trace_point_events) do |tp|
86
+ if Thread.current == @current_thread
87
+ file = tp.path
88
+ unless @ignored_files.include?(file)
89
+ if track_file?(file)
90
+ add_file(file, tp.lineno)
91
+ else
92
+ @ignored_files << file
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -1,15 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coverband
2
4
  class Configuration
3
-
4
5
  attr_accessor :redis, :root_paths, :root,
5
6
  :ignore, :additional_files, :percentage, :verbose, :reporter,
6
- :stats, :logger, :startup_delay, :trace_point_events,
7
- :include_gems, :memory_caching, :s3_bucket, :coverage_file, :store,
8
- :disable_on_failure_for
9
-
10
- # deprecated, but leaving to allow old configs to 'just work'
11
- # remove for 2.0
12
- attr_accessor :coverage_baseline
7
+ :stats, :startup_delay, :trace_point_events,
8
+ :include_gems, :memory_caching, :s3_bucket, :coverage_file,
9
+ :collector, :disable_on_failure_for
10
+ attr_writer :logger, :store
13
11
 
14
12
  def initialize
15
13
  @root = Dir.pwd
@@ -22,6 +20,7 @@ module Coverband
22
20
  @percentage = 0.0
23
21
  @verbose = false
24
22
  @reporter = 'scov'
23
+ @collector = 'trace'
25
24
  @logger = Logger.new(STDOUT)
26
25
  @startup_delay = 0
27
26
  @trace_point_events = [:line]
@@ -35,7 +34,7 @@ module Coverband
35
34
  @logger ||= Logger.new(STDOUT)
36
35
  end
37
36
 
38
- #TODO considering removing @redis / @coveragefile and have user set store directly
37
+ # TODO: considering removing @redis / @coveragefile and have user set store directly
39
38
  def store
40
39
  return @store if @store
41
40
  if redis
@@ -45,6 +44,5 @@ module Coverband
45
44
  end
46
45
  @store
47
46
  end
48
-
49
47
  end
50
48
  end
@@ -1,17 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coverband
2
4
  class Middleware
3
-
4
5
  def initialize(app)
5
6
  @app = app
6
7
  end
7
8
 
8
9
  def call(env)
9
- Coverband::Base.instance.configure_sampling
10
- Coverband::Base.instance.record_coverage
10
+ Coverband::Collectors::Base.instance.configure_sampling
11
+ Coverband::Collectors::Base.instance.record_coverage
11
12
  @app.call(env)
12
13
  ensure
13
- Coverband::Base.instance.report_coverage
14
+ Coverband::Collectors::Base.instance.report_coverage
14
15
  end
15
-
16
16
  end
17
17
  end
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coverband
2
4
  module Reporters
3
5
  class Base
4
-
5
6
  def self.report(store, options = {})
6
7
  roots = get_roots
7
8
  additional_coverage_data = options.fetch(:additional_scov_data) { [] }
@@ -32,7 +33,7 @@ module Coverband
32
33
  protected
33
34
 
34
35
  def self.fix_file_names(report_hash, roots)
35
- fixed_report = {} #normalize names across servers
36
+ fixed_report = {} # normalize names across servers
36
37
  report_hash.each_pair do |key, values|
37
38
  filename = filename_from_key(key, roots)
38
39
  fixed_report[filename] = values
@@ -46,12 +47,10 @@ module Coverband
46
47
  merged = []
47
48
  longest = first.length > second.length ? first : second
48
49
 
49
- longest.each_with_index do |line, index|
50
- if first[index] || second[index]
51
- merged[index] = (first[index].to_i + second[index].to_i)
52
- else
53
- merged[index] = nil
54
- end
50
+ longest.each_with_index do |_line, index|
51
+ merged[index] = if first[index] || second[index]
52
+ (first[index].to_i + second[index].to_i)
53
+ end
55
54
  end
56
55
 
57
56
  merged
@@ -61,12 +60,12 @@ module Coverband
61
60
  # expects = {"file.rb" => [0,2,4,nil,0,1,2]}
62
61
  def self.merge_existing_coverage(scov_style_report, existing_coverage)
63
62
  existing_coverage.each_pair do |file_key, existing_lines|
64
- next if Coverband.configuration.ignore.any?{ |i| file_key.match(i) }
65
- if current_line_hits = scov_style_report[file_key]
66
- scov_style_report[file_key] = merge_arrays(current_line_hits, existing_lines)
67
- else
68
- scov_style_report[file_key] = existing_lines
69
- end
63
+ next if Coverband.configuration.ignore.any? { |i| file_key.match(i) }
64
+ scov_style_report[file_key] = if current_line_hits = scov_style_report[file_key]
65
+ merge_arrays(current_line_hits, existing_lines)
66
+ else
67
+ existing_lines
68
+ end
70
69
  end
71
70
  scov_style_report
72
71
  end
@@ -87,18 +86,17 @@ module Coverband
87
86
  # {"/Users/danmayer/projects/hearno/script/tester.rb"=>[1, nil, 1, 2, nil, nil, nil]}
88
87
  def self.line_hash(store, key, roots)
89
88
  filename = filename_from_key(key, roots)
90
- if File.exists?(filename)
91
-
92
- count = File.foreach(filename).inject(0) { |c, line| c + 1 }
89
+ if File.exist?(filename)
90
+ count = File.foreach(filename).inject(0) { |c, _line| c + 1 }
93
91
  line_array = Array.new(count, nil)
94
92
 
95
93
  lines_hit = store.covered_lines_for_file(key)
96
94
  if lines_hit.is_a?(Array)
97
- line_array.each_with_index{|_,index| line_array[index] = 1 if lines_hit.include?((index + 1)) }
95
+ line_array.each_with_index { |_, index| line_array[index] = 1 if lines_hit.include?((index + 1)) }
98
96
  else
99
- line_array.each_with_index{|_,index| line_array[index] = (line_array[index].to_i + lines_hit[(index + 1).to_s].to_i) if lines_hit.keys.include?((index + 1).to_s) }
97
+ line_array.each_with_index { |_, index| line_array[index] = (line_array[index].to_i + lines_hit[(index + 1).to_s].to_i) if lines_hit.keys.include?((index + 1).to_s) }
100
98
  end
101
- {filename => line_array}
99
+ { filename => line_array }
102
100
  else
103
101
  Coverband.configuration.logger.info "file #{filename} not found in project"
104
102
  nil
@@ -115,19 +113,18 @@ module Coverband
115
113
  # this logic should be pushed to base report
116
114
  ###
117
115
  store.covered_files.each do |key|
118
- next if Coverband.configuration.ignore.any?{ |i| key.match(i) }
116
+ next if Coverband.configuration.ignore.any? { |i| key.match(i) }
119
117
  line_data = line_hash(store, key, roots)
120
118
 
121
- if line_data
122
- line_key = line_data.keys.first
123
- previous_line_hash = scov_style_report[line_key]
124
-
125
- if previous_line_hash
126
- line_data[line_key] = merge_arrays(line_data[line_key], previous_line_hash)
127
- end
119
+ next unless line_data
120
+ line_key = line_data.keys.first
121
+ previous_line_hash = scov_style_report[line_key]
128
122
 
129
- scov_style_report.merge!(line_data)
123
+ if previous_line_hash
124
+ line_data[line_key] = merge_arrays(line_data[line_key], previous_line_hash)
130
125
  end
126
+
127
+ scov_style_report.merge!(line_data)
131
128
  end
132
129
 
133
130
  scov_style_report = fix_file_names(scov_style_report, roots)
@@ -143,8 +140,6 @@ module Coverband
143
140
 
144
141
  scov_style_report
145
142
  end
146
-
147
143
  end
148
144
  end
149
145
  end
150
-
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Coverband
2
4
  module Reporters
3
5
  class ConsoleReport < Base
4
-
5
6
  def self.report(store, options = {})
6
7
  scov_style_report = super(store, options)
7
8
 
@@ -10,8 +11,6 @@ module Coverband
10
11
  end
11
12
  scov_style_report
12
13
  end
13
-
14
14
  end
15
15
  end
16
16
  end
17
-