coverband 4.1.1 → 4.2.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -4
  3. data/.travis.yml +2 -4
  4. data/README.md +8 -33
  5. data/Rakefile +1 -1
  6. data/changes.md +10 -26
  7. data/coverband.gemspec +0 -4
  8. data/lib/coverband.rb +0 -5
  9. data/lib/coverband/adapters/base.rb +4 -20
  10. data/lib/coverband/adapters/file_store.rb +0 -8
  11. data/lib/coverband/adapters/redis_store.rb +2 -33
  12. data/lib/coverband/at_exit.rb +1 -1
  13. data/lib/coverband/collectors/coverage.rb +1 -7
  14. data/lib/coverband/configuration.rb +2 -30
  15. data/lib/coverband/integrations/background.rb +1 -6
  16. data/lib/coverband/integrations/resque.rb +2 -7
  17. data/lib/coverband/reporters/base.rb +38 -8
  18. data/lib/coverband/reporters/html_report.rb +1 -1
  19. data/lib/coverband/reporters/web.rb +1 -13
  20. data/lib/coverband/utils/file_groups.rb +12 -20
  21. data/lib/coverband/utils/html_formatter.rb +1 -9
  22. data/lib/coverband/utils/railtie.rb +1 -1
  23. data/lib/coverband/utils/tasks.rb +0 -9
  24. data/lib/coverband/version.rb +1 -1
  25. data/test/benchmarks/benchmark.rake +1 -36
  26. data/test/test_helper.rb +12 -5
  27. data/test/{coverband/adapters/base_test.rb → unit/adapters_base_test.rb} +1 -1
  28. data/test/{coverband/adapters/file_store_test.rb → unit/adapters_file_store_test.rb} +1 -1
  29. data/test/{coverband/adapters/redis_store_test.rb → unit/adapters_redis_store_test.rb} +1 -1
  30. data/test/{coverband → unit}/at_exit_test.rb +0 -0
  31. data/test/{coverband/integrations → unit}/background_test.rb +1 -1
  32. data/test/unit/collectors_coverage_test.rb +48 -0
  33. data/test/{coverband → unit}/configuration_test.rb +3 -13
  34. data/test/{coverband → unit}/coverband_test.rb +0 -0
  35. data/test/{dog.rb → unit/dog.rb} +0 -0
  36. data/test/{integration → unit}/full_stack_test.rb +2 -4
  37. data/test/{coverband/integrations → unit}/middleware_test.rb +1 -1
  38. data/test/{coverband/integrations/rack_server_check_test.rb → unit/rack_server_checkout_test.rb} +1 -1
  39. data/test/{integration → unit}/rails_full_stack_test.rb +3 -4
  40. data/test/{integration → unit}/rails_gems_full_stack_test.rb +1 -2
  41. data/test/unit/reports_base_test.rb +117 -0
  42. data/test/{coverband/reporters/console_test.rb → unit/reports_console_test.rb} +5 -6
  43. data/test/{coverband/reporters/html_test.rb → unit/reports_html_test.rb} +1 -1
  44. data/test/{coverband/reporters/web_test.rb → unit/reports_web_test.rb} +2 -2
  45. data/test/{coverband/integrations → unit}/resque_worker_test.rb +8 -9
  46. data/test/{coverband/integrations → unit}/test_resque_job.rb +0 -0
  47. data/test/unit/utils/file_groups_test.rb +31 -0
  48. data/test/{coverband → unit}/utils/file_list_test.rb +0 -0
  49. data/test/{coverband → unit}/utils/gem_list_test.rb +0 -0
  50. data/test/{coverband → unit}/utils/lines_classifier_test.rb +0 -0
  51. data/test/{coverband → unit}/utils/result_test.rb +0 -0
  52. data/test/{coverband → unit}/utils/s3_report_test.rb +0 -0
  53. data/test/{coverband → unit}/utils/source_file_line_test.rb +0 -0
  54. data/test/{coverband → unit}/utils/source_file_test.rb +0 -0
  55. data/views/layout.erb +0 -4
  56. metadata +61 -100
  57. data/lib/coverband/utils/file_path_helper.rb +0 -57
  58. data/public/favicon.png +0 -0
  59. data/test/benchmarks/coverage_fork.sh +0 -37
  60. data/test/coverband/collectors/coverage_test.rb +0 -67
  61. data/test/coverband/reporters/base_test.rb +0 -168
  62. data/test/coverband/utils/file_groups_test.rb +0 -55
  63. data/test/rails4_dummy/tmp/.keep +0 -0
  64. data/test/unique_files.rb +0 -23
  65. data/views/settings.erb +0 -35
@@ -5,7 +5,6 @@ module Coverband
5
5
  @semaphore = Mutex.new
6
6
 
7
7
  def self.stop
8
- return unless @thread
9
8
  @semaphore.synchronize do
10
9
  if @thread
11
10
  @thread.exit
@@ -14,10 +13,6 @@ module Coverband
14
13
  end
15
14
  end
16
15
 
17
- def self.running?
18
- !!@thread
19
- end
20
-
21
16
  def self.start
22
17
  return if @thread
23
18
 
@@ -28,7 +23,7 @@ module Coverband
28
23
  sleep_seconds = Coverband.configuration.background_reporting_sleep_seconds
29
24
  @thread = Thread.new do
30
25
  loop do
31
- Coverband.report_coverage(true)
26
+ Coverband::Collectors::Coverage.instance.report_coverage(true)
32
27
  logger&.debug("Coverband: Reported coverage via thread. Sleeping #{sleep_seconds}s") if Coverband.configuration.verbose
33
28
  sleep(sleep_seconds)
34
29
  end
@@ -4,20 +4,15 @@ Resque.after_fork do |job|
4
4
  Coverband.start
5
5
  end
6
6
 
7
- Resque.before_first_fork do
8
- Coverband.configuration.background_reporting_enabled = false
9
- Coverband::Background.stop
10
- Coverband::Collectors::Coverage.instance.report_coverage(true)
11
- end
12
-
13
7
  module Coverband
14
8
  module ResqueWorker
15
9
  def perform
16
10
  super
17
11
  ensure
18
- Coverband.report_coverage(true)
12
+ Coverband::Collectors::Coverage.instance.report_coverage(true)
19
13
  end
20
14
  end
21
15
  end
22
16
 
23
17
  Resque::Job.prepend(Coverband::ResqueWorker)
18
+
@@ -8,10 +8,8 @@ module Coverband
8
8
  ###
9
9
  class Base
10
10
  class << self
11
- include Coverband::Utils::FilePathHelper
12
11
  def report(store, _options = {})
13
- all_roots = Coverband.configuration.all_root_paths
14
- scov_style_report = get_current_scov_data_imp(store, all_roots)
12
+ scov_style_report = get_current_scov_data_imp(store, root_paths)
15
13
 
16
14
  if Coverband.configuration.verbose
17
15
  msg = "report:\n #{scov_style_report.inspect}"
@@ -22,6 +20,17 @@ module Coverband
22
20
 
23
21
  protected
24
22
 
23
+ def root_paths
24
+ roots = Coverband.configuration.root_paths
25
+ roots += Coverband.configuration.gem_paths if Coverband.configuration.track_gems
26
+ roots << "#{current_root}/"
27
+ roots
28
+ end
29
+
30
+ def current_root
31
+ File.expand_path(Coverband.configuration.root)
32
+ end
33
+
25
34
  def fix_file_names(report_hash, roots)
26
35
  if Coverband.configuration.verbose
27
36
  Coverband.configuration.logger.info "fixing root: #{roots.join(', ')}"
@@ -29,11 +38,9 @@ module Coverband
29
38
 
30
39
  # normalize names across servers
31
40
  report_hash.each_with_object({}) do |(key, vals), fixed_report|
32
- filename = relative_path_to_full(key, roots)
33
- fixed_report[filename] = if fixed_report.key?(filename) && fixed_report[filename]['data'] && vals['data']
34
- merged_data = merge_arrays(fixed_report[filename]['data'], vals['data'])
35
- vals['data'] = merged_data
36
- vals
41
+ filename = filename_from_key(key, roots)
42
+ fixed_report[filename] = if fixed_report.key?(filename)
43
+ merge_arrays(fixed_report[filename], vals)
37
44
  else
38
45
  vals
39
46
  end
@@ -55,10 +62,33 @@ module Coverband
55
62
  merged
56
63
  end
57
64
 
65
+ ###
66
+ # This method is responsible for finding the CURRENT LOCAL
67
+ # filename regardless of the paths being different on
68
+ # various servers or deployments
69
+ ###
70
+ def filename_from_key(key, roots)
71
+ relative_filename = key
72
+ local_filename = relative_filename
73
+ roots.each do |root|
74
+ relative_filename = relative_filename.gsub(/^#{root}/, './')
75
+ end
76
+ # the filename for our reports is expected to be a full path.
77
+ # roots.last should be roots << current_root}/
78
+ # a fully expanded path of config.root
79
+ # filename = filename.gsub('./', roots.last)
80
+ # above only works for app files
81
+ # we need to rethink some of this logic
82
+ # gems aren't at project root and can have multiple locations
83
+ local_root = roots.find { |root| File.exist?(relative_filename.gsub('./', root)) }
84
+ local_root ? relative_filename.gsub('./', local_root) : local_filename
85
+ end
86
+
58
87
  ###
59
88
  # why do we need to merge covered files data?
60
89
  # basically because paths on machines or deployed hosts could be different, so
61
90
  # two different keys could point to the same filename or `line_key`
91
+ # this logic should be pushed to base report
62
92
  # TODO: think we are filtering based on ignore while sending to the store
63
93
  # and as we also pull it out here
64
94
  ###
@@ -11,7 +11,7 @@ module Coverband
11
11
  base_path = options.fetch(:base_path) { nil }
12
12
 
13
13
  # list all files, even if not tracked by Coverband (0% coverage)
14
- tracked_glob = "#{Coverband.configuration.current_root}/{app,lib,config}/**/*.{rb}"
14
+ tracked_glob = "#{current_root}/{app,lib,config}/**/*.{rb}"
15
15
  report_files = Coverband::Utils::Result.add_not_loaded_files(scov_style_report, tracked_glob)
16
16
  # apply coverband filters
17
17
  filtered_report_files = {}
@@ -35,10 +35,6 @@ module Coverband
35
35
  case request.path_info
36
36
  when /.*\.(css|js|gif|png)/
37
37
  @static.call(env)
38
- when %r{\/settings}
39
- [200, { 'Content-Type' => 'text/html' }, [settings]]
40
- when %r{\/debug_data}
41
- [200, { 'Content-Type' => 'text/json' }, [debug_data]]
42
38
  when %r{\/$}
43
39
  [200, { 'Content-Type' => 'text/html' }, [index]]
44
40
  else
@@ -57,16 +53,8 @@ module Coverband
57
53
  open_report: false)
58
54
  end
59
55
 
60
- def settings
61
- Coverband::Utils::HTMLFormatter.new(nil, base_path: base_path).format_settings!
62
- end
63
-
64
- def debug_data
65
- Coverband.configuration.store.coverage.to_json
66
- end
67
-
68
56
  def collect_coverage
69
- Coverband.report_coverage(true)
57
+ Coverband::Collectors::Coverage.instance.report_coverage(true)
70
58
  notice = 'coverband coverage collected'
71
59
  [301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
72
60
  end
@@ -8,8 +8,7 @@ module Coverband
8
8
  class FileGroups
9
9
  def initialize(files)
10
10
  @grouped = {}
11
- @files = files
12
- filter_to_groups
11
+ filter_to_groups(files)
13
12
  end
14
13
 
15
14
  def grouped_results
@@ -18,36 +17,29 @@ module Coverband
18
17
 
19
18
  private
20
19
 
21
- def filter_to_groups
20
+ def filter_to_groups(files)
22
21
  grouped_files = []
22
+ grouped_gems = {}
23
+ gem_lists = []
23
24
  Coverband.configuration.groups.each do |name, filter|
24
25
  if name == 'Gems'
25
- gem_lists = gem_files(name, filter)
26
- grouped_files.concat(gem_lists.flatten) if gem_lists.flatten.any?
26
+ grouped_gems = files.select { |source_file| source_file.filename =~ /#{filter}/ }.group_by(&:gem_name)
27
+ gem_lists = grouped_gems.values.map { |gem_files| Coverband::Utils::FileList.new(gem_files) }
28
+ grouped_files.concat(gem_lists.flatten)
29
+ @grouped[name] = Coverband::Utils::GemList.new(gem_lists) if gem_lists.flatten.any?
27
30
  else
28
- app_files(name, filter)
31
+ @grouped[name] = Coverband::Utils::FileList.new(files.select do |source_file|
32
+ source_file.filename =~ /#{filter}/
33
+ end)
29
34
  grouped_files += @grouped[name]
30
35
  end
31
36
  end
32
- if !Coverband.configuration.groups.empty? && !(other_files = @files.reject do |source_file|
37
+ if !Coverband.configuration.groups.empty? && !(other_files = files.reject do |source_file|
33
38
  grouped_files.include?(source_file)
34
39
  end).empty?
35
40
  @grouped['Ungrouped'] = Coverband::Utils::FileList.new(other_files)
36
41
  end
37
42
  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
43
  end
52
44
  end
53
45
  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) if report
21
+ @coverage_result = Coverband::Utils::Result.new(report)
22
22
  end
23
23
 
24
24
  def format!
@@ -29,16 +29,8 @@ module Coverband
29
29
  format_html(@coverage_result)
30
30
  end
31
31
 
32
- def format_settings!
33
- format_settings
34
- end
35
-
36
32
  private
37
33
 
38
- def format_settings
39
- template('settings').result(binding)
40
- end
41
-
42
34
  def format(result)
43
35
  Dir[File.join(File.dirname(__FILE__), '../../../public/*')].each do |path|
44
36
  FileUtils.cp_r(path, asset_output_path)
@@ -7,7 +7,7 @@ module Coverband
7
7
  end
8
8
 
9
9
  config.after_initialize do
10
- Coverband.report_coverage(true)
10
+ Coverband::Collectors::Coverage.instance.report_coverage(true)
11
11
  end
12
12
 
13
13
  rake_tasks do
@@ -25,13 +25,4 @@ namespace :coverband do
25
25
  environment
26
26
  Coverband.configuration.store.clear!
27
27
  end
28
-
29
- ###
30
- # clear data helpful for development or after configuration issues
31
- ###
32
- desc 'upgrade previous Coverband datastore to latest format'
33
- task :migrate do
34
- environment
35
- Coverband.configuration.store.migrate!
36
- end
37
28
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Coverband
4
- VERSION = '4.1.1'
4
+ VERSION = '4.2.0.alpha'
5
5
  end
@@ -134,7 +134,7 @@ namespace :benchmarks do
134
134
  x.config(time: 12, warmup: 5, suite: suite)
135
135
  x.report 'coverband' do
136
136
  work
137
- Coverband.report_coverage(true)
137
+ Coverband::Collectors::Coverage.instance.report_coverage
138
138
  end
139
139
  x.report 'no coverband' do
140
140
  work
@@ -176,10 +176,6 @@ namespace :benchmarks do
176
176
  @file_hash_cache[file] = Digest::MD5.file(__FILE__).hexdigest
177
177
  end
178
178
  end
179
-
180
- def store.full_path_to_relative(file)
181
- file
182
- end
183
179
  end
184
180
 
185
181
  def reporting_speed
@@ -219,43 +215,12 @@ namespace :benchmarks do
219
215
  $stdout = previous_out
220
216
  end
221
217
 
222
- def measure_configure_memory
223
- require 'memory_profiler'
224
- # warmup
225
- 3.times { Coverband.configure }
226
-
227
- previous_out = $stdout
228
- capture = StringIO.new
229
- $stdout = capture
230
-
231
- MemoryProfiler.report do
232
- 10.times do
233
- Coverband.configure do |config|
234
- redis_url = ENV['CACHE_REDIS_URL'] || ENV['REDIS_URL']
235
- config.store = Coverband::Adapters::RedisStore.new(Redis.new(url: redis_url), redis_namespace: 'coverband_data')
236
- end
237
- end
238
- end.pretty_print
239
- data = $stdout.string
240
- $stdout = previous_out
241
- puts data
242
- raise 'leaking memory!!!' unless data.match('Total retained: 0 bytes')
243
- ensure
244
- $stdout = previous_out
245
- end
246
-
247
218
  desc 'runs memory reporting on Redis store'
248
219
  task memory_reporting: [:setup] do
249
220
  puts 'runs memory benchmarking to ensure we dont leak'
250
221
  measure_memory
251
222
  end
252
223
 
253
- desc 'runs memory reporting on configure'
254
- task memory_configure_reporting: [:setup] do
255
- puts 'runs memory benchmarking on configure to ensure we dont leak'
256
- measure_configure_memory
257
- end
258
-
259
224
  desc 'runs memory leak check via Rails tests'
260
225
  task memory_rails: [:setup] do
261
226
  puts 'runs memory rails test to ensure we dont leak'
data/test/test_helper.rb CHANGED
@@ -16,7 +16,6 @@ require 'json'
16
16
  require 'redis'
17
17
  require 'resque'
18
18
  require 'pry-byebug'
19
- require_relative 'unique_files'
20
19
  $VERBOSE = original_verbosity
21
20
 
22
21
  Coveralls.wear!
@@ -80,12 +79,20 @@ def basic_coverage
80
79
  { 'app_path/dog.rb' => example_line }
81
80
  end
82
81
 
83
- def source_fixture(filename)
84
- File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', filename))
82
+ def fake_redis
83
+ @redis ||= begin
84
+ redis = OpenStruct.new
85
+ redis
86
+ end
85
87
  end
86
88
 
87
- def test_root
88
- File.expand_path(File.join(File.dirname(__FILE__)))
89
+ def fake_coverage_report
90
+ file_name = '/Users/danmayer/projects/hearno/script/tester.rb'
91
+ { file_name => [1, nil, 1, 1, nil, nil, nil] }
92
+ end
93
+
94
+ def source_fixture(filename)
95
+ File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', filename))
89
96
  end
90
97
 
91
98
  # Taken from http://stackoverflow.com/questions/4459330/how-do-i-temporarily-redirect-stderr-in-ruby
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require File.expand_path('../../test_helper', File.dirname(__FILE__))
3
+ require File.expand_path('../test_helper', File.dirname(__FILE__))
4
4
 
5
5
  class AdaptersBaseTest < Minitest::Test
6
6
  def setup
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require File.expand_path('../../test_helper', File.dirname(__FILE__))
3
+ require File.expand_path('../test_helper', File.dirname(__FILE__))
4
4
 
5
5
  class AdaptersFileStoreTest < Minitest::Test
6
6
  def setup
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require File.expand_path('../../test_helper', File.dirname(__FILE__))
3
+ require File.expand_path('../test_helper', File.dirname(__FILE__))
4
4
 
5
5
  class RedisTest < Minitest::Test
6
6
  REDIS_STORAGE_FORMAT_VERSION = Coverband::Adapters::RedisStore::REDIS_STORAGE_FORMAT_VERSION
File without changes
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require File.expand_path('../../test_helper', File.dirname(__FILE__))
3
+ require File.expand_path('../test_helper', File.dirname(__FILE__))
4
4
 
5
5
  class BackgroundTest < Minitest::Test
6
6
  def setup
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('../test_helper', File.dirname(__FILE__))
4
+ require File.expand_path('./dog', File.dirname(__FILE__))
5
+
6
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.3.0')
7
+ class CollectorsCoverageTest < Minitest::Test
8
+ attr_accessor :coverband
9
+
10
+ def setup
11
+ super
12
+ Coverband.configure do |config|
13
+ config.store = Coverband::Adapters::RedisStore.new(Redis.new)
14
+ end
15
+ @coverband = Coverband::Collectors::Coverage.instance.reset_instance
16
+ end
17
+
18
+ def teardown
19
+ Thread.current[:coverband_instance] = nil
20
+ Coverband.configure do |config|
21
+ end
22
+ @coverband = Coverband::Collectors::Coverage.instance.reset_instance
23
+ end
24
+
25
+ test 'gets coverage instance' do
26
+ assert_equal Coverband::Collectors::Coverage, coverband.class
27
+ end
28
+
29
+ test 'defaults to a redis store' do
30
+ assert_equal Coverband::Adapters::RedisStore, coverband.instance_variable_get('@store').class
31
+ end
32
+
33
+ test 'report_coverage raises errors in tests' do
34
+ @coverband.reset_instance
35
+ @coverband.expects(:ready_to_report?).raises('Oh no')
36
+ assert_raises RuntimeError do
37
+ @coverband.report_coverage
38
+ end
39
+ end
40
+
41
+ test 'report_coverage does not raise errors in non-test mode' do
42
+ Coverband.configuration.stubs(:test_env).returns(false)
43
+ @coverband.expects(:ready_to_report?).raises('Oh no')
44
+ @coverband.reset_instance
45
+ @coverband.report_coverage
46
+ end
47
+ end
48
+ end