coverband 2.0.3 → 3.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.rubocop.yml +73 -0
  4. data/.travis.yml +0 -3
  5. data/README.md +3 -3
  6. data/changes.md +65 -49
  7. data/coverband.gemspec +1 -1
  8. data/lib/coverband.rb +9 -6
  9. data/lib/coverband/adapters/base.rb +22 -2
  10. data/lib/coverband/adapters/file_store.rb +11 -11
  11. data/lib/coverband/adapters/redis_store.rb +22 -57
  12. data/lib/coverband/collectors/coverage.rb +57 -53
  13. data/lib/coverband/configuration.rb +6 -14
  14. data/lib/coverband/integrations/background.rb +7 -0
  15. data/lib/coverband/{middleware.rb → integrations/middleware.rb} +1 -3
  16. data/lib/coverband/reporters/base.rb +37 -82
  17. data/lib/coverband/reporters/console_report.rb +3 -0
  18. data/lib/coverband/reporters/simple_cov_report.rb +4 -3
  19. data/lib/coverband/reporters/web.rb +38 -35
  20. data/lib/coverband/utils/s3_report_writer.rb +59 -0
  21. data/lib/coverband/{tasks.rb → utils/tasks.rb} +0 -0
  22. data/lib/coverband/version.rb +1 -1
  23. data/test/benchmarks/benchmark.rake +3 -3
  24. data/test/test_helper.rb +18 -17
  25. data/test/unit/adapters_base_test.rb +29 -0
  26. data/test/unit/adapters_file_store_test.rb +2 -2
  27. data/test/unit/adapters_redis_store_test.rb +14 -51
  28. data/test/unit/collectors_coverage_test.rb +3 -107
  29. data/test/unit/configuration_test.rb +2 -9
  30. data/test/unit/full_stack_test.rb +47 -0
  31. data/test/unit/middleware_test.rb +21 -57
  32. data/test/unit/reports_base_test.rb +12 -71
  33. data/test/unit/reports_console_test.rb +9 -22
  34. data/test/unit/reports_simple_cov_test.rb +3 -37
  35. data/test/unit/reports_web_test.rb +4 -0
  36. data/test/unit/{s3_report_writer_test.rb → utils_s3_report_writer_test.rb} +1 -1
  37. metadata +29 -18
  38. data/lib/coverband/adapters/memory_cache_store.rb +0 -53
  39. data/lib/coverband/collectors/base.rb +0 -126
  40. data/lib/coverband/collectors/trace.rb +0 -122
  41. data/lib/coverband/s3_report_writer.rb +0 -49
  42. data/test/unit/adapters_memory_cache_store_test.rb +0 -66
  43. data/test/unit/collectors_base_test.rb +0 -104
  44. data/test/unit/collectors_trace_test.rb +0 -106
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d6a0e6c24d29f4b432cd6a514b5c1bbca2e699d
4
- data.tar.gz: d2ef15b6d6b100396b265068acdf1d4d83394823
3
+ metadata.gz: ff8f54b9c75fc882bc548834903c232af3ff9e74
4
+ data.tar.gz: 72cbf124e7484c096bc8316f8940a5d1128ba4de
5
5
  SHA512:
6
- metadata.gz: fd0b35c4a1abceebcda0791290dc71e0fc382c4980c87d09639371a2d4f048b0d79b3938b18c3facbe5e78fcf25c2f1aa8d3766eaca761dd590ecc71138410d9
7
- data.tar.gz: d0104388613abebf8575d4701b8cda4ff0caaaa5f14d57c72535c7cf68d178f432ac584a7f9c700e625b724c542b78827d5964ab24573e81562bf0b5688e8dd0
6
+ metadata.gz: f964fca6d95868d170694bb4dfb5a064b9d8df717e63a5d79b941dda8beaa6e6acbc3b149f98891030538da8c5b88d46a89edfbd9d97e668230e802fa222102e
7
+ data.tar.gz: de616d37895b9446425e2947d435db919776019f8658d45183430de3c95675e40591e49bcf0539a5306389f1ee52f5ae15372fa2521b13a71ead5a738ff79340
data/.gitignore CHANGED
@@ -5,7 +5,6 @@
5
5
  .yardoc
6
6
  .idea/
7
7
  .ruby-version
8
- .rubocop.yml
9
8
  Gemfile.lock
10
9
  InstalledFiles
11
10
  _yardoc
@@ -0,0 +1,73 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+ Exclude:
4
+ - docs/**/*
5
+ Documentation:
6
+ Enabled: false
7
+ Metrics/MethodLength:
8
+ Enabled: false
9
+ Metrics/LineLength:
10
+ Max: 120
11
+ Metrics/BlockNesting:
12
+ Max: 5
13
+ Bundler/OrderedGems:
14
+ Enabled: false
15
+ Metrics/CyclomaticComplexity:
16
+ Enabled: false
17
+ Metrics/PerceivedComplexity:
18
+ Enabled: false
19
+ Metrics/AbcSize:
20
+ Enabled: false
21
+ Metrics/ClassLength:
22
+ Enabled: false
23
+ Metrics/BlockLength:
24
+ Enabled: false
25
+ Metrics/BlockNesting:
26
+ Max: 10
27
+ Metrics/ModuleLength:
28
+ Enabled: false
29
+ Metrics/ParameterLists:
30
+ Max: 10
31
+ Naming/AccessorMethodName:
32
+ Enabled: false
33
+ Naming/PredicateName:
34
+ Enabled: false
35
+ Style/FormatStringToken:
36
+ Enabled: false
37
+ Style/TernaryParentheses:
38
+ Enabled: false
39
+ Style/MutableConstant:
40
+ Enabled: false
41
+ Style/FrozenStringLiteralComment:
42
+ Enabled: true
43
+ Style/GuardClause:
44
+ Enabled: true
45
+ Style/NumericPredicate:
46
+ Enabled: false
47
+ Style/NumericLiterals:
48
+ Enabled: false
49
+ Style/NumericLiteralPrefix:
50
+ Enabled: false
51
+ Style/ClassAndModuleChildren:
52
+ Enabled: false
53
+ Style/MethodMissingSuper:
54
+ Enabled: true
55
+ Style/MissingRespondToMissing:
56
+ Enabled: false
57
+ Performance/Casecmp:
58
+ Enabled: false
59
+ Layout/EmptyLinesAroundArguments:
60
+ Enabled: true
61
+ Layout/MultilineMethodCallIndentation:
62
+ Enabled: true
63
+ Layout/MultilineOperationIndentation:
64
+ Enabled: true
65
+ Lint/RescueException:
66
+ Enabled: true
67
+ Lint/ShadowingOuterLocalVariable:
68
+ Enabled: true
69
+ Style/FormatString:
70
+ Enabled: true
71
+ Security/Eval:
72
+ Enabled: true
73
+
@@ -1,8 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "2.0"
4
- - "2.1"
5
- - "2.2"
6
3
  - "2.3"
7
4
  - "2.4"
8
5
  - "2.5"
data/README.md CHANGED
@@ -179,7 +179,7 @@ module MyApplication
179
179
  # any files that get loaded as part of railties will have no coverage
180
180
  config.before_initialize do
181
181
  require 'coverage'
182
- Coverband::Collectors::Base.instance.start
182
+ Coverband::Collectors::Coverage.instance.start
183
183
  end
184
184
 
185
185
  end
@@ -380,9 +380,9 @@ require 'rails'
380
380
  # Capture code coverage during our cron jobs
381
381
  class CoverageRunner < ::Rails::Railtie
382
382
  runner do
383
- Coverband::Collectors::Base.instance.start
383
+ Coverband::Collectors::Coverage.instance.start
384
384
  at_exit do
385
- Coverband::Collectors::Base.instance.report_coverage
385
+ Coverband::Collectors::Coverage.instance.report_coverage
386
386
  end
387
387
  end
388
388
  end
data/changes.md CHANGED
@@ -4,22 +4,41 @@
4
4
 
5
5
  Will be the fully modern release that drops maintenance legacy support in favor of increased performance, ease of use, and maintainability.
6
6
 
7
- * expects to drop Tracepoint collection engine
8
- * expects to drop anything below Ruby 2.3
9
- * Release will be aimed as significantly simplifying ease of use
10
- * expects to drop the concept of baseline recordings
11
- * improve support for eager-loading
12
- * add built-in support for easy loading via Railties
13
- * expects to add safe list support to force reload files one wants coverage on that may happen outside of the standard load order
14
- * built in support for activejob, sidekiq, and other common frameworks
15
- * code route tracing (entry point to all code executed for example /some_path -> code coverage of that path)
7
+ - expects to drop Tracepoint collection engine
8
+ - expects to drop anything below Ruby 2.3
9
+ - Release will be aimed as significantly simplifying ease of use
10
+ - near zero config setup for Rails apps
11
+ - add built-in support for easy loading via Railties
12
+ - built in support for activejob, sidekiq, and other common frameworks
13
+ - reduced configuration options
14
+ - options on reporting
15
+ - background reporting
16
+ - or middleware reporting
17
+ - improved web reporting
18
+ - no longer relying directly on HTML in S3 but dynamically generated from any adapter
19
+ - additional adapters including Memcache, S3, and ActiveRecord
20
+
21
+ ### Coverband_jam_session
22
+
23
+ This is a possible gem to host experimental or more complex features, which would require tuning, configuration, and performance trade offs
24
+
25
+ - additional adapters (tracepoint, ruby-profiler, etc)
26
+ - code route tracing (entry point to all code executed for example /some_path -> code coverage of that path)
27
+ - tagging of reported Coverage
28
+ - allow only to collect coverage based on route (limiting or scoped coverage)
29
+ - coverage over some other variable like a set of alpha users
16
30
 
17
31
  # Alpha
18
32
 
19
- ### 3.0.0
33
+ ### Coverband 3.0
34
+
35
+ * drops Tracepoint
36
+ * drops Ruby <= 2.3.0
37
+ * rewrites redis store, for 60X perf
38
+ * drops various other features not needed without Tracepoint
39
+ * standardizes on Coverage array format vs sparse hash
20
40
 
21
- * drop Tracepoint
22
- * background thread reporter
41
+ # Released
23
42
 
24
43
  # 2.0.3
25
44
 
@@ -28,9 +47,6 @@ Will be the fully modern release that drops maintenance legacy support in favor
28
47
  * various additional benchmarks @danmayer
29
48
  * Filter out files with no coverage thanks @kbaum
30
49
 
31
-
32
- # Released
33
-
34
50
  ### 2.0.2
35
51
 
36
52
  * fix possible nil error on files that changed since initial recording @viktor-silakov
@@ -45,50 +61,50 @@ Will be the fully modern release that drops maintenance legacy support in favor
45
61
 
46
62
  ### 2.0.1
47
63
 
48
- * add support for fine grained S3 configuration via Coverband config, thanks @a0s
49
- * https://github.com/danmayer/coverband/pull/98
50
- * Using the file argument to self.configure in lib/coverband.rb, thanks @ThomasOwens
51
- * https://github.com/danmayer/coverband/pull/100
52
- * added redis improvements allowing namespace and TTL thx @oded-zahavi
53
- * fix warnings about duplicate method definition
54
- * Add support for safe_reload_files based on full file path
55
- * Add support for Sinatra admin control endpoints
56
- * improved documentation
64
+ - add support for fine grained S3 configuration via Coverband config, thanks @a0s
65
+ - https://github.com/danmayer/coverband/pull/98
66
+ - Using the file argument to self.configure in lib/coverband.rb, thanks @ThomasOwens
67
+ - https://github.com/danmayer/coverband/pull/100
68
+ - added redis improvements allowing namespace and TTL thx @oded-zahavi
69
+ - fix warnings about duplicate method definition
70
+ - Add support for safe_reload_files based on full file path
71
+ - Add support for Sinatra admin control endpoints
72
+ - improved documentation
57
73
 
58
74
  ### 2.0.0
59
75
 
60
76
  Major release with various backwards compatibility breaking changes (generally related to the configuration). The 2.0 lifecycle will act as a mostly easy upgrade that supports past users looking to move to the much faster new Coverage Adapter.
61
77
 
62
- * Continues to support Ruby 2.0 and up
63
- * supports multiple collect engines, introducing the concept of multiple collector adapters
64
- * extends the concepts of multiple storage adapters, enabling additional authors to help support Kafka, graphite, other adapters
65
- * old require based loading, but working towards deprecating the entire baseline concept
66
- * Introduces massive performance enhancements by moving to Ruby `Coverage` based collection
67
- * Opposed to sampling this is now a reporting frequency, when using `Coverage` collector
68
- * Reduced configuration complexity
69
- * Refactoring the code preparing for more varied storage and reporting options
70
- * Drop Redis as a gem runtime_dependency
78
+ - Continues to support Ruby 2.0 and up
79
+ - supports multiple collect engines, introducing the concept of multiple collector adapters
80
+ - extends the concepts of multiple storage adapters, enabling additional authors to help support Kafka, graphite, other adapters
81
+ - old require based loading, but working towards deprecating the entire baseline concept
82
+ - Introduces massive performance enhancements by moving to Ruby `Coverage` based collection
83
+ - Opposed to sampling this is now a reporting frequency, when using `Coverage` collector
84
+ - Reduced configuration complexity
85
+ - Refactoring the code preparing for more varied storage and reporting options
86
+ - Drop Redis as a gem runtime_dependency
71
87
 
72
88
  ### 1.5.0
73
89
 
74
90
  This is a significant release with significant refactoring a stepping stone for a 2.0 release.
75
91
 
76
- * staging a changes.md document!
77
- * refactored out full abstraction for stores
78
- * supports hit counts vs binary covered / not covered for lines
79
- * this will let you find density of code usage just not if it was used
80
- * this is a slight performance hit, so you can fall back to the old system if you want `redisstore.new(@redis, array: true)`
81
- * this is the primary new feature in 1.5.0
82
- * Redis has configurable base name, so I can safely change storage formats between releases
83
- * improved documentation
84
- * supports `SimpleCov.root`
85
- * show files that were never touched
86
- * apply coverband filters to ignore files in report not just collection
87
- * improved test coverage
88
- * improved benchmarks including support for multiple stores ;)
92
+ - staging a changes.md document!
93
+ - refactored out full abstraction for stores
94
+ - supports hit counts vs binary covered / not covered for lines
95
+ - this will let you find density of code usage just not if it was used
96
+ - this is a slight performance hit, so you can fall back to the old system if you want `redisstore.new(@redis, array: true)`
97
+ - this is the primary new feature in 1.5.0
98
+ - Redis has configurable base name, so I can safely change storage formats between releases
99
+ - improved documentation
100
+ - supports `SimpleCov.root`
101
+ - show files that were never touched
102
+ - apply coverband filters to ignore files in report not just collection
103
+ - improved test coverage
104
+ - improved benchmarks including support for multiple stores ;)
89
105
 
90
106
  ### 1.3.1
91
107
 
92
- * This was a small fix release addressing some issues
93
- * mostly readme updates
94
- * last release prior to having a changes document!
108
+ - This was a small fix release addressing some issues
109
+ - mostly readme updates
110
+ - last release prior to having a changes document!
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency 'benchmark-ips'
32
32
  # add when debugging
33
33
  # require 'byebug'; byebug
34
- #spec.add_development_dependency 'byebug'
34
+ spec.add_development_dependency 'byebug'
35
35
  # deprecate when dropping support for older ruby
36
36
  spec.add_runtime_dependency 'json'
37
37
  # todo make an optional dependency for simplecov reports
@@ -7,17 +7,15 @@ require 'coverband/version'
7
7
  require 'coverband/configuration'
8
8
  require 'coverband/adapters/base'
9
9
  require 'coverband/adapters/redis_store'
10
- require 'coverband/adapters/memory_cache_store'
11
10
  require 'coverband/adapters/file_store'
12
- require 'coverband/collectors/base'
13
- require 'coverband/collectors/trace'
11
+ require 'coverband/utils/s3_report_writer'
14
12
  require 'coverband/collectors/coverage'
15
13
  require 'coverband/reporters/base'
16
14
  require 'coverband/reporters/simple_cov_report'
17
15
  require 'coverband/reporters/console_report'
18
16
  require 'coverband/reporters/web'
19
- require 'coverband/middleware'
20
- require 'coverband/s3_report_writer'
17
+ require 'coverband/integrations/middleware'
18
+ require 'coverband/integrations/background'
21
19
 
22
20
  module Coverband
23
21
  CONFIG_FILE = './config/coverband.rb'
@@ -35,11 +33,16 @@ module Coverband
35
33
  elsif File.exist?(configuration_file)
36
34
  require configuration_file
37
35
  else
38
- raise ArgumentError, "configure requires a block, the existance of a #{CONFIG_FILE} in your project, or a path to a config file passed in to configure"
36
+ msg = "configure requires a block, #{CONFIG_FILE} in project, or file path passed in configure"
37
+ raise ArgumentError, msg
39
38
  end
40
39
  end
41
40
 
42
41
  def self.configuration
43
42
  self.configuration_data ||= Configuration.new
44
43
  end
44
+
45
+ def self.start
46
+ Coverband::Collectors::Coverage.instance
47
+ end
45
48
  end
@@ -11,7 +11,7 @@ module Coverband
11
11
  raise 'abstract'
12
12
  end
13
13
 
14
- def save_report(report)
14
+ def save_report(_report)
15
15
  raise 'abstract'
16
16
  end
17
17
 
@@ -23,9 +23,29 @@ module Coverband
23
23
  raise 'abstract'
24
24
  end
25
25
 
26
- def covered_lines_for_file(file)
26
+ def covered_lines_for_file(_file)
27
27
  raise 'abstract'
28
28
  end
29
+
30
+ protected
31
+
32
+ def merge_reports(new_report, old_report)
33
+ keys = (new_report.keys + old_report.keys).uniq
34
+ keys.each do |file|
35
+ new_report[file] = if new_report[file] && old_report[file]
36
+ array_add(new_report[file], old_report[file])
37
+ elsif new_report[file]
38
+ new_report[file]
39
+ else
40
+ old_report[file]
41
+ end
42
+ end
43
+ new_report
44
+ end
45
+
46
+ def array_add(latest, original)
47
+ latest.map.with_index { |v, i| (v && original[i]) ? v + original[i] : nil }
48
+ end
29
49
  end
30
50
  end
31
51
  end
@@ -2,6 +2,11 @@
2
2
 
3
3
  module Coverband
4
4
  module Adapters
5
+ ###
6
+ # FilesStore store a merged coverage file to local disk
7
+ # Generally this is for testing and development
8
+ # Not recommended for production deployment
9
+ ###
5
10
  class FileStore < Base
6
11
  attr_accessor :path
7
12
 
@@ -17,17 +22,8 @@ module Coverband
17
22
  end
18
23
 
19
24
  def save_report(report)
20
- results = existing_data(path)
21
- report.each_pair do |file, values|
22
- if results.key?(file)
23
- # convert the keys to "3" opposed to 3
24
- values = JSON.parse(values.to_json)
25
- results[file].merge!(values) { |_k, old_v, new_v| old_v.to_i + new_v.to_i }
26
- else
27
- results[file] = values
28
- end
29
- end
30
- File.open(path, 'w') { |f| f.write(results.to_json) }
25
+ merge_reports(report, coverage)
26
+ save_coverage(report)
31
27
  end
32
28
 
33
29
  def coverage
@@ -46,6 +42,10 @@ module Coverband
46
42
 
47
43
  private
48
44
 
45
+ def save_coverage(report)
46
+ File.open(path, 'w') { |f| f.write(report.to_json) }
47
+ end
48
+
49
49
  def existing_data(path)
50
50
  if File.exist?(path)
51
51
  JSON.parse(File.read(path))
@@ -2,94 +2,59 @@
2
2
 
3
3
  module Coverband
4
4
  module Adapters
5
+ ###
6
+ # RedisStore store a merged coverage file to redis
7
+ ###
5
8
  class RedisStore < Base
6
- BASE_KEY = 'coverband2'
9
+ BASE_KEY = 'coverband3'
7
10
 
8
11
  def initialize(redis, opts = {})
9
- @redis = redis
12
+ @redis = redis
10
13
  @ttl = opts[:ttl]
11
14
  @redis_namespace = opts[:redis_namespace]
12
15
  end
13
16
 
14
17
  def clear!
15
- @redis.smembers(base_key).each { |key| @redis.del("#{base_key}.#{key}") }
16
18
  @redis.del(base_key)
17
19
  end
18
20
 
19
- def base_key
20
- @base_key ||= [BASE_KEY, @redis_namespace].compact.join('.')
21
- end
22
-
23
21
  def save_report(report)
24
- store_array(base_key, report.keys)
25
-
26
- file_keys = report.keys.map {|file| "#{base_key}.#{file}"}
27
- existing_records = existing_records(file_keys)
28
- combined_report = combined_report(file_keys, report, existing_records)
29
- pipelined_save(combined_report)
22
+ # Note: This could lead to slight races
23
+ # where multiple processes pull the old coverage and add to it then push
24
+ # the Coverband 2 had the same issue,
25
+ # and the tradeoff has always been acceptable
26
+ merge_reports(report, coverage)
27
+ save_coverage(base_key, report)
30
28
  end
31
29
 
32
30
  def coverage
33
- data = {}
34
- redis.smembers(base_key).each do |key|
35
- data[key] = covered_lines_for_file(key)
36
- end
37
- data
31
+ get_report(base_key)
38
32
  end
39
33
 
40
34
  def covered_files
41
- redis.smembers(base_key)
35
+ coverage.keys
42
36
  end
43
37
 
44
38
  def covered_lines_for_file(file)
45
- @redis.hgetall("#{base_key}.#{file}")
39
+ coverage[file]
46
40
  end
47
41
 
48
42
  private
49
43
 
50
44
  attr_reader :redis
51
-
52
- def pipelined_save(combined_report)
53
- redis.pipelined do
54
- combined_report.each do |file, values|
55
- existing = values[:existing]
56
- new = values[:new]
57
- unless values.empty?
58
- # in redis all file_keys are strings
59
- new_string_values = Hash[new.map {|k, val| [k.to_s, val]}]
60
- new_string_values.merge!(existing) {|_k, old_v, new_v| old_v.to_i + new_v.to_i}
61
- redis.mapped_hmset(file, new_string_values)
62
- redis.expire(file, @ttl) if @ttl
63
- end
64
- end
65
- end
66
- end
67
45
 
68
- def existing_records(file_keys)
69
- redis.pipelined do
70
- file_keys.each do |key|
71
- redis.hgetall(key)
72
- end
73
- end
46
+ def base_key
47
+ @base_key ||= [BASE_KEY, @redis_namespace].compact.join('.')
74
48
  end
75
49
 
76
- def combined_report(file_keys, report, existing_records)
77
- combined_report = {}
78
-
79
- file_keys.each_with_index do |key, i|
80
- combined_report[key] = {
81
- new: report.values[i],
82
- existing: existing_records[i]
83
- }
84
- end
85
-
86
- return combined_report
50
+ def save_coverage(key, data)
51
+ redis.set key, data.to_json
52
+ redis.expire(key, @ttl) if @ttl
87
53
  end
88
54
 
89
- def store_array(key, values)
90
- redis.sadd(key, values) unless values.empty?
91
- redis.expire(key, @ttl) if @ttl
92
- values
55
+ def get_report(key)
56
+ data = redis.get key
57
+ data ? JSON.parse(data) : {}
93
58
  end
94
59
  end
95
60
  end