coverband 2.0.0 → 2.0.3
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.
- checksums.yaml +5 -5
- data/.gitignore +2 -1
- data/README.md +198 -62
- data/Rakefile +7 -0
- data/changes.md +40 -0
- data/config.ru +4 -0
- data/coverband.gemspec +1 -2
- data/lib/coverband/adapters/memory_cache_store.rb +16 -16
- data/lib/coverband/adapters/redis_store.rb +51 -16
- data/lib/coverband/collectors/base.rb +1 -1
- data/lib/coverband/collectors/coverage.rb +20 -8
- data/lib/coverband/collectors/trace.rb +17 -7
- data/lib/coverband/configuration.rb +31 -6
- data/lib/coverband/reporters/base.rb +2 -2
- data/lib/coverband/reporters/simple_cov_report.rb +6 -1
- data/lib/coverband/reporters/web.rb +152 -0
- data/lib/coverband/s3_report_writer.rb +13 -2
- data/lib/coverband/tasks.rb +0 -46
- data/lib/coverband/version.rb +1 -1
- data/lib/coverband.rb +6 -5
- data/test/benchmarks/benchmark.rake +89 -10
- data/test/test_helper.rb +0 -5
- data/test/unit/adapters_memory_cache_store_test.rb +40 -30
- data/test/unit/adapters_redis_store_test.rb +12 -3
- data/test/unit/collectors_base_test.rb +3 -1
- data/test/unit/collectors_coverage_test.rb +3 -3
- data/test/unit/collectors_trace_test.rb +3 -1
- data/test/unit/reports_console_test.rb +10 -1
- data/test/unit/reports_simple_cov_test.rb +11 -2
- data/test/unit/reports_web_test.rb +37 -0
- data/test/unit/s3_report_writer_test.rb +7 -1
- metadata +7 -26
- data/docs/coverband_details.png +0 -0
- data/docs/coverband_index.png +0 -0
- data/docs/coverband_install.gif +0 -0
- data/lib/coverband/baseline.rb +0 -44
- data/lib/coverband/s3_web.rb +0 -25
- data/test/unit/baseline_test.rb +0 -59
- data/test/unit/s3_web_test.rb +0 -28
|
@@ -7,53 +7,88 @@ module Coverband
|
|
|
7
7
|
|
|
8
8
|
def initialize(redis, opts = {})
|
|
9
9
|
@redis = redis
|
|
10
|
+
@ttl = opts[:ttl]
|
|
11
|
+
@redis_namespace = opts[:redis_namespace]
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
def clear!
|
|
13
|
-
@redis.smembers(
|
|
14
|
-
@redis.del(
|
|
15
|
+
@redis.smembers(base_key).each { |key| @redis.del("#{base_key}.#{key}") }
|
|
16
|
+
@redis.del(base_key)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def base_key
|
|
20
|
+
@base_key ||= [BASE_KEY, @redis_namespace].compact.join('.')
|
|
15
21
|
end
|
|
16
22
|
|
|
17
23
|
def save_report(report)
|
|
18
|
-
store_array(
|
|
24
|
+
store_array(base_key, report.keys)
|
|
19
25
|
|
|
20
|
-
report.
|
|
21
|
-
|
|
22
|
-
|
|
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)
|
|
23
30
|
end
|
|
24
31
|
|
|
25
32
|
def coverage
|
|
26
33
|
data = {}
|
|
27
|
-
redis.smembers(
|
|
34
|
+
redis.smembers(base_key).each do |key|
|
|
28
35
|
data[key] = covered_lines_for_file(key)
|
|
29
36
|
end
|
|
30
37
|
data
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
def covered_files
|
|
34
|
-
redis.smembers(
|
|
41
|
+
redis.smembers(base_key)
|
|
35
42
|
end
|
|
36
43
|
|
|
37
44
|
def covered_lines_for_file(file)
|
|
38
|
-
@redis.hgetall("#{
|
|
45
|
+
@redis.hgetall("#{base_key}.#{file}")
|
|
39
46
|
end
|
|
40
47
|
|
|
41
48
|
private
|
|
42
49
|
|
|
43
50
|
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
|
+
|
|
68
|
+
def existing_records(file_keys)
|
|
69
|
+
redis.pipelined do
|
|
70
|
+
file_keys.each do |key|
|
|
71
|
+
redis.hgetall(key)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def combined_report(file_keys, report, existing_records)
|
|
77
|
+
combined_report = {}
|
|
44
78
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
values.merge!(existing) { |_k, old_v, new_v| old_v.to_i + new_v.to_i }
|
|
51
|
-
redis.mapped_hmset(key, values)
|
|
79
|
+
file_keys.each_with_index do |key, i|
|
|
80
|
+
combined_report[key] = {
|
|
81
|
+
new: report.values[i],
|
|
82
|
+
existing: existing_records[i]
|
|
83
|
+
}
|
|
52
84
|
end
|
|
85
|
+
|
|
86
|
+
return combined_report
|
|
53
87
|
end
|
|
54
88
|
|
|
55
89
|
def store_array(key, values)
|
|
56
90
|
redis.sadd(key, values) unless values.empty?
|
|
91
|
+
redis.expire(key, @ttl) if @ttl
|
|
57
92
|
values
|
|
58
93
|
end
|
|
59
94
|
end
|
|
@@ -87,7 +87,7 @@ module Coverband
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def output_file_line_usage
|
|
90
|
-
@logger.
|
|
90
|
+
@logger.debug 'coverband debug coverband file:line usage:'
|
|
91
91
|
@file_line_usage.sort_by { |_key, value| value.length }.each do |pair|
|
|
92
92
|
file = pair.first
|
|
93
93
|
lines = pair.last
|
|
@@ -18,7 +18,7 @@ module Coverband
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
if failed_recently?
|
|
21
|
-
@logger.
|
|
21
|
+
@logger.error 'coverage reporting standing-by because of recent failure' if @verbose
|
|
22
22
|
return
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -31,16 +31,16 @@ module Coverband
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
if @verbose
|
|
34
|
-
@logger.
|
|
34
|
+
@logger.debug "coverband file usage: #{file_usage.inspect}"
|
|
35
35
|
output_file_line_usage if @verbose == 'debug'
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
if @store
|
|
39
|
-
@store.save_report(
|
|
39
|
+
@store.save_report(files_with_line_usage)
|
|
40
40
|
@file_line_usage.clear
|
|
41
41
|
elsif @verbose
|
|
42
|
-
@logger.
|
|
43
|
-
@logger.
|
|
42
|
+
@logger.debug 'coverage report: '
|
|
43
|
+
@logger.debug @file_line_usage.inspect
|
|
44
44
|
end
|
|
45
45
|
# StandardError might be better option
|
|
46
46
|
# coverband previously had RuntimeError here
|
|
@@ -49,15 +49,22 @@ module Coverband
|
|
|
49
49
|
rescue StandardError => err
|
|
50
50
|
failed!
|
|
51
51
|
if @verbose
|
|
52
|
-
@logger.
|
|
53
|
-
@logger.
|
|
52
|
+
@logger.error 'coverage missing'
|
|
53
|
+
@logger.error "error: #{err.inspect} #{err.message}"
|
|
54
|
+
@logger.error err.backtrace
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
57
|
|
|
57
58
|
private
|
|
58
59
|
|
|
60
|
+
def files_with_line_usage
|
|
61
|
+
@file_line_usage.select do |_file_name, coverage|
|
|
62
|
+
coverage.values.any? { |value| value != 0 }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
59
66
|
def array_diff(latest, original)
|
|
60
|
-
latest.map.with_index { |v, i| v ? v - original[i] : nil }
|
|
67
|
+
latest.map.with_index { |v, i| (v && original[i]) ? v - original[i] : nil }
|
|
61
68
|
end
|
|
62
69
|
|
|
63
70
|
def previous_results
|
|
@@ -116,6 +123,11 @@ module Coverband
|
|
|
116
123
|
else
|
|
117
124
|
::Coverage.start
|
|
118
125
|
end
|
|
126
|
+
if Coverband.configuration.safe_reload_files
|
|
127
|
+
Coverband.configuration.safe_reload_files.each do |safe_file|
|
|
128
|
+
load safe_file
|
|
129
|
+
end
|
|
130
|
+
end
|
|
119
131
|
@semaphore = Mutex.new
|
|
120
132
|
@@previous_results = nil
|
|
121
133
|
reset_instance
|
|
@@ -2,8 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module Coverband
|
|
4
4
|
module Collectors
|
|
5
|
+
###
|
|
6
|
+
# NOTE: While this still works it is slower than Coverage.
|
|
7
|
+
# I recommend using the Coverage adapter.
|
|
8
|
+
# As baseline is removed the Trace collector also doesn't have a good way
|
|
9
|
+
# to collect initial code usage during app boot up.
|
|
10
|
+
#
|
|
11
|
+
# I am leaving Trace around as I believe there are some interesting use cases
|
|
12
|
+
# also, it illustrates an alternative collector, and I have some others I would like to implement
|
|
13
|
+
###
|
|
5
14
|
class Trace < Base
|
|
6
|
-
|
|
7
15
|
def reset_instance
|
|
8
16
|
super
|
|
9
17
|
@tracer_set = false
|
|
@@ -21,8 +29,9 @@ module Coverband
|
|
|
21
29
|
rescue RuntimeError => err
|
|
22
30
|
failed!
|
|
23
31
|
if @verbose
|
|
24
|
-
@logger.
|
|
25
|
-
@logger.
|
|
32
|
+
@logger.error 'error stating recording coverage'
|
|
33
|
+
@logger.error "error: #{err.inspect} #{err.message}"
|
|
34
|
+
@logger.error err.backtrace
|
|
26
35
|
end
|
|
27
36
|
end
|
|
28
37
|
|
|
@@ -39,12 +48,12 @@ module Coverband
|
|
|
39
48
|
unset_tracer
|
|
40
49
|
|
|
41
50
|
if failed_recently?
|
|
42
|
-
@logger.
|
|
51
|
+
@logger.error 'coverage reporting standing-by because of recent failure' if @verbose
|
|
43
52
|
return
|
|
44
53
|
end
|
|
45
54
|
|
|
46
55
|
if @verbose
|
|
47
|
-
@logger.
|
|
56
|
+
@logger.debug "coverband file usage: #{file_usage.inspect}"
|
|
48
57
|
output_file_line_usage if @verbose == 'debug'
|
|
49
58
|
end
|
|
50
59
|
|
|
@@ -58,8 +67,9 @@ module Coverband
|
|
|
58
67
|
rescue RuntimeError => err
|
|
59
68
|
failed!
|
|
60
69
|
if @verbose
|
|
61
|
-
@logger.
|
|
62
|
-
@logger.
|
|
70
|
+
@logger.error 'coverage missing'
|
|
71
|
+
@logger.error "error: #{err.inspect} #{err.message}"
|
|
72
|
+
@logger.error err.backtrace
|
|
63
73
|
end
|
|
64
74
|
end
|
|
65
75
|
|
|
@@ -5,9 +5,11 @@ module Coverband
|
|
|
5
5
|
attr_accessor :redis, :root_paths, :root,
|
|
6
6
|
:ignore, :additional_files, :percentage, :verbose,
|
|
7
7
|
:reporter, :startup_delay, :memory_caching,
|
|
8
|
-
:include_gems, :
|
|
9
|
-
:
|
|
10
|
-
|
|
8
|
+
:include_gems, :collector, :disable_on_failure_for,
|
|
9
|
+
:redis_namespace, :redis_ttl,
|
|
10
|
+
:safe_reload_files
|
|
11
|
+
|
|
12
|
+
attr_writer :logger, :s3_region, :s3_bucket, :s3_access_key_id, :s3_secret_access_key
|
|
11
13
|
|
|
12
14
|
def initialize
|
|
13
15
|
@root = Dir.pwd
|
|
@@ -29,12 +31,34 @@ module Coverband
|
|
|
29
31
|
@memory_caching = false
|
|
30
32
|
@store = nil
|
|
31
33
|
@disable_on_failure_for = nil
|
|
34
|
+
@s3_region = nil
|
|
35
|
+
@s3_bucket = nil
|
|
36
|
+
@s3_access_key_id = nil
|
|
37
|
+
@s3_secret_access_key = nil
|
|
38
|
+
@redis_namespace = nil
|
|
39
|
+
@redis_ttl = nil
|
|
32
40
|
end
|
|
33
41
|
|
|
34
42
|
def logger
|
|
35
43
|
@logger ||= Logger.new(STDOUT)
|
|
36
44
|
end
|
|
37
45
|
|
|
46
|
+
def s3_bucket
|
|
47
|
+
@s3_bucket || ENV['AWS_BUCKET']
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def s3_region
|
|
51
|
+
@s3_region || ENV['AWS_REGION']
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def s3_access_key_id
|
|
55
|
+
@s3_access_key_id || ENV['AWS_ACCESS_KEY_ID']
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def s3_secret_access_key
|
|
59
|
+
@s3_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY']
|
|
60
|
+
end
|
|
61
|
+
|
|
38
62
|
def store
|
|
39
63
|
return @store if @store
|
|
40
64
|
raise 'no valid store configured'
|
|
@@ -43,10 +67,11 @@ module Coverband
|
|
|
43
67
|
def store=(store)
|
|
44
68
|
if store.is_a?(Coverband::Adapters::Base)
|
|
45
69
|
@store = store
|
|
46
|
-
elsif defined?(Redis) &&
|
|
47
|
-
@store = Coverband::Adapters::RedisStore.new(redis
|
|
70
|
+
elsif defined?(Redis) && redis && redis.is_a?(Redis)
|
|
71
|
+
@store = Coverband::Adapters::RedisStore.new(redis, ttl: Coverband.configuration.redis_ttl,
|
|
72
|
+
redis_namespace: Coverband.configuration.redis_namespace)
|
|
48
73
|
elsif store.is_a?(String)
|
|
49
|
-
@store = Coverband::Adapters::FileStore.new(
|
|
74
|
+
@store = Coverband::Adapters::FileStore.new(store)
|
|
50
75
|
end
|
|
51
76
|
end
|
|
52
77
|
end
|
|
@@ -9,13 +9,13 @@ module Coverband
|
|
|
9
9
|
|
|
10
10
|
if Coverband.configuration.verbose
|
|
11
11
|
Coverband.configuration.logger.info "fixing root: #{roots.join(', ')}"
|
|
12
|
-
Coverband.configuration.logger.
|
|
12
|
+
Coverband.configuration.logger.debug "additional data:\n #{additional_coverage_data}"
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
scov_style_report = report_scov_with_additional_data(store, additional_coverage_data, roots)
|
|
16
16
|
|
|
17
17
|
if Coverband.configuration.verbose
|
|
18
|
-
Coverband.configuration.logger.
|
|
18
|
+
Coverband.configuration.logger.debug "report:\n #{scov_style_report.inspect}"
|
|
19
19
|
end
|
|
20
20
|
scov_style_report
|
|
21
21
|
end
|
|
@@ -38,7 +38,12 @@ module Coverband
|
|
|
38
38
|
Coverband.configuration.logger.info "report is ready and viewable: open #{SimpleCov.coverage_dir}/index.html"
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
s3_writer_options = {
|
|
42
|
+
region: Coverband.configuration.s3_region,
|
|
43
|
+
access_key_id: Coverband.configuration.s3_access_key_id,
|
|
44
|
+
secret_access_key: Coverband.configuration.s3_secret_access_key
|
|
45
|
+
}
|
|
46
|
+
S3ReportWriter.new(Coverband.configuration.s3_bucket, s3_writer_options).persist! if Coverband.configuration.s3_bucket
|
|
42
47
|
end
|
|
43
48
|
end
|
|
44
49
|
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rack'
|
|
4
|
+
|
|
5
|
+
module Coverband
|
|
6
|
+
module Reporters
|
|
7
|
+
# TODO: move to reports and drop need for S3 allow reading from adapters?
|
|
8
|
+
class Web
|
|
9
|
+
attr_reader :request
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@static = Rack::Static.new(self,
|
|
13
|
+
root: File.expand_path('public', Gem::Specification.find_by_name('simplecov-html').full_gem_path),
|
|
14
|
+
urls: [/.*\.css/, /.*\.js/, /.*\.gif/, /.*\.png/]
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(env)
|
|
19
|
+
@request = Rack::Request.new(env)
|
|
20
|
+
|
|
21
|
+
if request.post?
|
|
22
|
+
case request.path_info
|
|
23
|
+
when /\/collect_update_and_view/
|
|
24
|
+
collect_update_and_view
|
|
25
|
+
when /\/clear/
|
|
26
|
+
clear
|
|
27
|
+
when /\/update_report/
|
|
28
|
+
update_report
|
|
29
|
+
when /\/collect_coverage/
|
|
30
|
+
collect_coverage
|
|
31
|
+
when /\/reload_files/
|
|
32
|
+
reload_files
|
|
33
|
+
else
|
|
34
|
+
[404, {'Content-Type' => 'text/html'}, ['404 error!']]
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
case request.path_info
|
|
38
|
+
when /.*\.(css|js|gif|png)/
|
|
39
|
+
@static.call(env)
|
|
40
|
+
when /\/show/
|
|
41
|
+
[200, {'Content-Type' => 'text/html'}, [show]]
|
|
42
|
+
when /\//
|
|
43
|
+
[200, {'Content-Type' => 'text/html'}, [index]]
|
|
44
|
+
else
|
|
45
|
+
[404, {'Content-Type' => 'text/html'}, ['404 error!']]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# todo move to file or template
|
|
51
|
+
def index
|
|
52
|
+
html = "<html>"
|
|
53
|
+
html += "<strong>Notice:</strong> #{Rack::Utils.escape_html(request.params['notice'])}<br/>" if request.params['notice']
|
|
54
|
+
html += "<ul>"
|
|
55
|
+
html += "<li><a href='#{base_path}'>Coverband Web Admin Index</a></li>"
|
|
56
|
+
html += "<li>#{button("#{base_path}collect_update_and_view",'collect data, update report, & view')}</li>"
|
|
57
|
+
html += "<li><a href='#{base_path}show'>view coverage report</a></li>"
|
|
58
|
+
html += "<li>#{button("#{base_path}collect_coverage",'update coverage data (collect coverage)')}</li>"
|
|
59
|
+
html += "<li>#{button("#{base_path}update_report",'update coverage report (rebuild report)')}</li>"
|
|
60
|
+
html += "<li>#{button("#{base_path}clear",'clear coverage report')}</li>"
|
|
61
|
+
html += "<li>#{button("#{base_path}reload_files",'reload Coverband files')}</li>"
|
|
62
|
+
html += "</ul>"
|
|
63
|
+
html += "<br/>"
|
|
64
|
+
html += "version: #{Coverband::VERSION}<br/>"
|
|
65
|
+
html += "<a href='https://github.com/danmayer/coverband'>Coverband</a>"
|
|
66
|
+
html += "</html>"
|
|
67
|
+
html
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def show
|
|
71
|
+
html = s3.get_object(bucket: Coverband.configuration.s3_bucket, key: 'coverband/index.html').body.read
|
|
72
|
+
# hack the static HTML assets to link to the path where this was mounted
|
|
73
|
+
html = html.gsub("src='", "src='#{base_path}")
|
|
74
|
+
html = html.gsub("href='", "href='#{base_path}")
|
|
75
|
+
html = html.gsub("loading.gif", "#{base_path}loading.gif")
|
|
76
|
+
html = html.gsub("/images/", "#{base_path}images/")
|
|
77
|
+
html
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def collect_update_and_view
|
|
81
|
+
collect_coverage
|
|
82
|
+
update_report
|
|
83
|
+
[301, { 'Location' => "#{base_path}show" }, []]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def update_report
|
|
87
|
+
Coverband::Reporters::SimpleCovReport.report(Coverband.configuration.store, open_report: false)
|
|
88
|
+
notice = 'coverband coverage updated'
|
|
89
|
+
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def collect_coverage
|
|
93
|
+
Coverband::Collectors::Base.instance.report_coverage
|
|
94
|
+
notice = 'coverband coverage collected'
|
|
95
|
+
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def clear
|
|
99
|
+
Coverband.configuration.store.clear!
|
|
100
|
+
notice = 'coverband coverage cleared'
|
|
101
|
+
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def reload_files
|
|
105
|
+
if Coverband.configuration.safe_reload_files
|
|
106
|
+
Coverband.configuration.safe_reload_files.each do |safe_file|
|
|
107
|
+
load safe_file
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
# force reload
|
|
111
|
+
Coverband.configure
|
|
112
|
+
notice = 'coverband files reloaded'
|
|
113
|
+
[301, { 'Location' => "#{base_path}?notice=#{notice}" }, []]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def button(url, title)
|
|
119
|
+
button = "<form action='#{url}' method='post'>"
|
|
120
|
+
button += "<button type='submit'>#{title}</button>"
|
|
121
|
+
button + '</form>'
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# This method should get the root mounted endpoint
|
|
125
|
+
# for example if the app is mounted like so:
|
|
126
|
+
# mount Coverband::S3Web, at: '/coverage'
|
|
127
|
+
# "/coverage/collect_coverage?" become:
|
|
128
|
+
# /coverage/
|
|
129
|
+
def base_path
|
|
130
|
+
request.path.match("\/.*\/") ? request.path.match("\/.*\/")[0] : '/'
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def s3
|
|
134
|
+
begin
|
|
135
|
+
require 'aws-sdk'
|
|
136
|
+
rescue StandardError
|
|
137
|
+
Coverband.configuration.logger.error "coverband requires 'aws-sdk' in order use S3ReportWriter."
|
|
138
|
+
return
|
|
139
|
+
end
|
|
140
|
+
@s3 ||= begin
|
|
141
|
+
client_options = {
|
|
142
|
+
region: Coverband.configuration.s3_region,
|
|
143
|
+
access_key_id: Coverband.configuration.s3_access_key_id,
|
|
144
|
+
secret_access_key: Coverband.configuration.s3_secret_access_key
|
|
145
|
+
}
|
|
146
|
+
client_options = {} if client_options.values.any?(&:nil?)
|
|
147
|
+
Aws::S3::Client.new(client_options)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# possibly move to / make an adapter or more likely a reporter
|
|
3
4
|
class S3ReportWriter
|
|
4
|
-
def initialize(bucket_name)
|
|
5
|
+
def initialize(bucket_name, options = {})
|
|
5
6
|
@bucket_name = bucket_name
|
|
7
|
+
@region = options[:region]
|
|
8
|
+
@access_key_id = options[:access_key_id]
|
|
9
|
+
@secret_access_key = options[:secret_access_key]
|
|
6
10
|
begin
|
|
7
11
|
require 'aws-sdk'
|
|
8
12
|
rescue StandardError
|
|
@@ -29,7 +33,14 @@ class S3ReportWriter
|
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
def s3
|
|
32
|
-
|
|
36
|
+
client_options = {
|
|
37
|
+
region: @region,
|
|
38
|
+
access_key_id: @access_key_id,
|
|
39
|
+
secret_access_key: @secret_access_key
|
|
40
|
+
}
|
|
41
|
+
resource_options = { client: Aws::S3::Client.new(client_options) }
|
|
42
|
+
resource_options = {} if client_options.values.any?(&:nil?)
|
|
43
|
+
Aws::S3::Resource.new(resource_options)
|
|
33
44
|
end
|
|
34
45
|
|
|
35
46
|
def bucket
|
data/lib/coverband/tasks.rb
CHANGED
|
@@ -1,52 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
namespace :coverband do
|
|
4
|
-
def safely_import_files(files_to_cover)
|
|
5
|
-
if files_to_cover.any?
|
|
6
|
-
files = Coverband::Baseline.exclude_files(files_to_cover)
|
|
7
|
-
files.each do |file|
|
|
8
|
-
begin
|
|
9
|
-
require_dependency file
|
|
10
|
-
rescue Exception => err
|
|
11
|
-
if Coverband.configuration.verbose
|
|
12
|
-
Coverband.configuration.logger.info "error adding file to baseline: #{file}"
|
|
13
|
-
Coverband.configuration.logger.info "error: #{err}"
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
desc 'record coverband coverage baseline'
|
|
21
|
-
task :baseline do
|
|
22
|
-
Coverband::Baseline.record do
|
|
23
|
-
if Rake::Task.tasks.any? { |key| key.to_s.match(/environment$/) }
|
|
24
|
-
Coverband.configuration.logger.info 'invoking rake environment'
|
|
25
|
-
Rake::Task['environment'].invoke
|
|
26
|
-
elsif Rake::Task.tasks.any? { |key| key.to_s.match(/env$/) }
|
|
27
|
-
Coverband.configuration.logger.info 'invoking rake env'
|
|
28
|
-
Rake::Task['env'].invoke
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
baseline_files = [File.expand_path('./config/boot.rb', Dir.pwd),
|
|
32
|
-
File.expand_path('./config/application.rb', Dir.pwd),
|
|
33
|
-
File.expand_path('./config/environment.rb', Dir.pwd)]
|
|
34
|
-
|
|
35
|
-
baseline_files.each do |baseline_file|
|
|
36
|
-
require baseline_file if File.exist?(baseline_file)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
safely_import_files(Coverband.configuration.additional_files.flatten)
|
|
40
|
-
|
|
41
|
-
if defined? Rails
|
|
42
|
-
safely_import_files(Dir.glob("#{Rails.root}/app/**/*.rb"))
|
|
43
|
-
if File.exist?("#{Rails.root}/lib")
|
|
44
|
-
safely_import_files(Dir.glob("#{Rails.root}/lib/**/*.rb"))
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
4
|
###
|
|
51
5
|
# note: If your project has set many simplecov filters.
|
|
52
6
|
# You might want to override them and clear the filters.
|
data/lib/coverband/version.rb
CHANGED
data/lib/coverband.rb
CHANGED
|
@@ -12,10 +12,10 @@ require 'coverband/adapters/file_store'
|
|
|
12
12
|
require 'coverband/collectors/base'
|
|
13
13
|
require 'coverband/collectors/trace'
|
|
14
14
|
require 'coverband/collectors/coverage'
|
|
15
|
-
require 'coverband/baseline'
|
|
16
15
|
require 'coverband/reporters/base'
|
|
17
16
|
require 'coverband/reporters/simple_cov_report'
|
|
18
17
|
require 'coverband/reporters/console_report'
|
|
18
|
+
require 'coverband/reporters/web'
|
|
19
19
|
require 'coverband/middleware'
|
|
20
20
|
require 'coverband/s3_report_writer'
|
|
21
21
|
|
|
@@ -27,14 +27,15 @@ module Coverband
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def self.configure(file = nil)
|
|
30
|
+
configuration_file = file || CONFIG_FILE
|
|
31
|
+
|
|
30
32
|
configuration
|
|
31
33
|
if block_given?
|
|
32
34
|
yield(configuration)
|
|
33
|
-
elsif File.exist?(
|
|
34
|
-
|
|
35
|
-
require file
|
|
35
|
+
elsif File.exist?(configuration_file)
|
|
36
|
+
require configuration_file
|
|
36
37
|
else
|
|
37
|
-
raise ArgumentError, "configure requires a block
|
|
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"
|
|
38
39
|
end
|
|
39
40
|
end
|
|
40
41
|
|