coverband 2.0.3 → 3.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
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