coverband 6.1.7 → 6.2.0
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 +4 -4
- data/.github/workflows/main.yml +10 -3
- data/Gemfile +3 -0
- data/Gemfile.rails7.0 +3 -0
- data/Gemfile.rails7.1 +4 -1
- data/Gemfile.rails7.2 +3 -0
- data/Gemfile.rails8.0 +3 -0
- data/README.md +8 -6
- data/changes.md +21 -0
- data/lib/coverband/adapters/base.rb +21 -6
- data/lib/coverband/adapters/file_store.rb +1 -2
- data/lib/coverband/adapters/hash_redis_store.rb +2 -1
- data/lib/coverband/adapters/redis_store.rb +1 -2
- data/lib/coverband/adapters/web_service_store.rb +1 -2
- data/lib/coverband/collectors/abstract_tracker.rb +4 -4
- data/lib/coverband/collectors/delta.rb +4 -4
- data/lib/coverband/collectors/view_tracker.rb +12 -2
- data/lib/coverband/mcp/tools/get_dead_methods.rb +5 -5
- data/lib/coverband/mcp/tools/get_file_coverage.rb +2 -0
- data/lib/coverband/reporters/json_report.rb +7 -0
- data/lib/coverband/reporters/web.rb +4 -4
- data/lib/coverband/utils/absolute_file_converter.rb +37 -7
- data/lib/coverband/utils/dead_methods.rb +4 -0
- data/lib/coverband/utils/file_list.rb +15 -7
- data/lib/coverband/utils/html_formatter.rb +5 -2
- data/lib/coverband/utils/relative_file_converter.rb +22 -7
- data/lib/coverband/utils/result.rb +0 -2
- data/lib/coverband/utils/source_file/line.rb +84 -0
- data/lib/coverband/utils/source_file.rb +33 -81
- data/lib/coverband/version.rb +1 -1
- data/test/benchmarks/benchmark.rake +4 -0
- data/test/benchmarks/benchmark_delta.rb +54 -0
- data/test/benchmarks/benchmark_file_list.rb +58 -0
- data/test/benchmarks/benchmark_unused_keys.rb +52 -0
- data/test/coverband/collectors/route_tracker_test.rb +3 -3
- data/test/coverband/collectors/translation_tracker_test.rb +1 -1
- data/test/coverband/collectors/view_tracker_test.rb +2 -2
- data/test/coverband/mcp/tools/get_dead_methods_test.rb +20 -10
- data/test/coverband/mcp/tools/get_file_coverage_test.rb +44 -0
- data/test/coverband/reporters/console_test.rb +1 -0
- data/test/coverband/reporters/json_test.rb +3 -3
- data/test/coverband/reporters/web_test.rb +10 -0
- data/test/coverband/utils/absolute_file_converter_test.rb +35 -0
- data/test/coverband/utils/dead_methods_test.rb +20 -0
- data/test/coverband/utils/relative_file_converter_test.rb +26 -0
- data/test/coverband/utils/result_test.rb +19 -0
- data/test/coverband/utils/{source_file_line_test.rb → source_file/line_test.rb} +1 -1
- metadata +6 -3
- data/coverband/log.272267 +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 12802541de3827dd777fba9962270fd2f0a6c7b3debb2ef96c4bcca32f02b48d
|
|
4
|
+
data.tar.gz: 36fa21009ed2ada842ff90ca4a18195c8bfcddc81ad0951c11f613ede11f6606
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 03c7e496735cfe705b926b30140b3d704993b0599e470d290fb9ae672a7b204bf5cfa98adfc73d54eb70df1c71e8cbb9d91a5f85862b86d5c2a1ba1d0d3aa704
|
|
7
|
+
data.tar.gz: 6610a4ec327a69de3bd6878cb256a8f9d605fcf2a4104e6699b6b68acbec3ac9aaaeb957569eaf9ac1e949c58c5a6adbcd75055be464da56a11dde5c873dc4b6
|
data/.github/workflows/main.yml
CHANGED
|
@@ -28,11 +28,18 @@ jobs:
|
|
|
28
28
|
- gemfile: 'rails_8.0'
|
|
29
29
|
ruby: '3.1'
|
|
30
30
|
runs-on: ${{ matrix.os }}-latest
|
|
31
|
+
services:
|
|
32
|
+
redis:
|
|
33
|
+
image: redis:${{ matrix.redis-version }}
|
|
34
|
+
ports:
|
|
35
|
+
- 6379:6379
|
|
36
|
+
options: >-
|
|
37
|
+
--health-cmd "redis-cli ping"
|
|
38
|
+
--health-interval 10s
|
|
39
|
+
--health-timeout 5s
|
|
40
|
+
--health-retries 5
|
|
31
41
|
steps:
|
|
32
42
|
- uses: actions/checkout@v6
|
|
33
|
-
- uses: supercharge/redis-github-action@v2
|
|
34
|
-
with:
|
|
35
|
-
redis-version: ${{ matrix.redis-version }}
|
|
36
43
|
- uses: ruby/setup-ruby@v1
|
|
37
44
|
with:
|
|
38
45
|
ruby-version: ${{ matrix.ruby }}
|
data/Gemfile
CHANGED
data/Gemfile.rails7.0
CHANGED
data/Gemfile.rails7.1
CHANGED
data/Gemfile.rails7.2
CHANGED
data/Gemfile.rails8.0
CHANGED
data/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
<img src="https://raw.github.com/danmayer/coverband/
|
|
1
|
+
<img src="https://raw.github.com/danmayer/coverband/main/docs/assets/logo/heads.svg?sanitize=true" width='300'>
|
|
2
2
|
|
|
3
3
|
# Coverband
|
|
4
4
|
|
|
5
5
|
[](https://github.com/danmayer/coverband/actions)
|
|
6
|
-
[](https://coveralls.io/github/danmayer/coverband?branch=main)
|
|
7
7
|
[](https://codeclimate.com/github/danmayer/coverband/maintainability)
|
|
8
8
|
[](https://discord.gg/KAH38EV)
|
|
9
9
|
|
|
@@ -57,7 +57,7 @@ gem 'coverband'
|
|
|
57
57
|
|
|
58
58
|
With older versions of Coverband, projects would report to redis using rack or sidekiq middleware. After Coverband 4.0, this should no longer be required and could cause performance issues. Reporting to redis is now automatically done within a background thread with no custom code needed.
|
|
59
59
|
|
|
60
|
-
See [changelog](https://github.com/danmayer/coverband/blob/
|
|
60
|
+
See [changelog](https://github.com/danmayer/coverband/blob/main/changes.md).
|
|
61
61
|
|
|
62
62
|
## Rails
|
|
63
63
|
|
|
@@ -77,10 +77,12 @@ run ActionController::Dispatcher.new
|
|
|
77
77
|
|
|
78
78
|
## Coverband Web UI
|
|
79
79
|
|
|
80
|
-

|
|
81
81
|
|
|
82
82
|
> You can check it out locally by running the [Coverband Demo App](https://github.com/danmayer/coverband_rails_example).
|
|
83
83
|
|
|
84
|
+
- View a shared demo at [https://coverband-rails-example.onrender.com/](https://coverband-rails-example.onrender.com/)
|
|
85
|
+
|
|
84
86
|
- View overall coverage information
|
|
85
87
|
|
|
86
88
|
- Drill into individual file coverage
|
|
@@ -208,7 +210,7 @@ Take Coverband for a spin on the live Heroku deployed [Coverband Demo](https://c
|
|
|
208
210
|
|
|
209
211
|
If you need to configure Coverband, this can be done by creating a `config/coverband.rb` file relative to your project root.
|
|
210
212
|
|
|
211
|
-
- See [lib/coverband/configuration.rb](https://github.com/danmayer/coverband/blob/
|
|
213
|
+
- See [lib/coverband/configuration.rb](https://github.com/danmayer/coverband/blob/main/lib/coverband/configuration.rb) for all options
|
|
212
214
|
- By default Coverband will try to store data to Redis \* Redis endpoint is looked for in this order: `ENV['COVERBAND_REDIS_URL']`, `ENV['REDIS_URL']`, or `localhost`
|
|
213
215
|
|
|
214
216
|
Below is an example config file for a Rails 5 app:
|
|
@@ -269,7 +271,7 @@ This feature is enabled by default. To stop this feature, disable the feature in
|
|
|
269
271
|
|
|
270
272
|
`config.track_views = false`
|
|
271
273
|
|
|
272
|
-

|
|
273
275
|
|
|
274
276
|
### Hiding settings
|
|
275
277
|
|
data/changes.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
### 6.2.0
|
|
2
|
+
|
|
3
|
+
* Fix: Correct `Rack::Static` URL handling in the web reporter by using an empty string URL prefix; thanks @alpaca-tc (#635)
|
|
4
|
+
* Fix: Skip missing files during dead method scans to avoid errors in dynamic or removed-file environments; thanks @doug-hall (#632)
|
|
5
|
+
* Fix: Improve MCP dead method formatting for clearer tool output; thanks @doug-hall (#633)
|
|
6
|
+
* Docs: README updates (including `master` → `main` wording updates); thanks @jjb (#627)
|
|
7
|
+
|
|
8
|
+
### 6.1.8
|
|
9
|
+
|
|
10
|
+
* Feature: Added temporal data to GetFileCoverage tool for MCP by @fabienpiette (#626)
|
|
11
|
+
* Optimization: Significant performance improvements to `FileList` avoiding array allocations and reducing redundant iterations (#619, #620, #622, #623)
|
|
12
|
+
* Optimization: Optimize `HTMLFormatter` with template caching (#616, #618)
|
|
13
|
+
* Optimization: Optimize memory usage by removing redundant `dup` calls across `Coverband::Utils::Result` (#617, #621)
|
|
14
|
+
* Optimization: Optimize `array_add` logic with in-place modification avoiding overhead in newer Ruby (#615)
|
|
15
|
+
* Optimization: Optimize `ViewTracker` unused keys and path normalization (#608, #609, #613)
|
|
16
|
+
* Optimization: Optimize `AbstractTracker` to use bulk Redis hset and direct Hash lookups (#610, #612)
|
|
17
|
+
* Optimization: Cache Regexp in `short_name` and optimize `RelativeFileConverter` with `Regexp.union` (#606, #607)
|
|
18
|
+
* Optimization: Optimize delta collector ignore check order (#611)
|
|
19
|
+
* Refactoring: Extract Line class to `Coverband::Utils::SourceFile::Line` (#605, #614)
|
|
20
|
+
* Fix: Improve symlink handling in file converters (#603)
|
|
21
|
+
|
|
1
22
|
### Coverband 6.1.7
|
|
2
23
|
|
|
3
24
|
* add back a running demo site
|
|
@@ -125,14 +125,29 @@ module Coverband
|
|
|
125
125
|
}
|
|
126
126
|
end
|
|
127
127
|
|
|
128
|
-
# TODO: This should have cases reduced
|
|
129
128
|
def array_add(latest, original)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
use_oneshot = Coverband.configuration.use_oneshot_lines_coverage
|
|
130
|
+
if latest.frozen?
|
|
131
|
+
latest.map.with_index do |v, i|
|
|
132
|
+
if v && original[i]
|
|
133
|
+
if use_oneshot
|
|
134
|
+
(v + original[i] >= 1) ? 1 : 0
|
|
135
|
+
else
|
|
136
|
+
v + original[i]
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
134
140
|
else
|
|
135
|
-
latest.
|
|
141
|
+
latest.each_with_index do |v, i|
|
|
142
|
+
latest[i] = if v && original[i]
|
|
143
|
+
if use_oneshot
|
|
144
|
+
(v + original[i] >= 1) ? 1 : 0
|
|
145
|
+
else
|
|
146
|
+
v + original[i]
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
latest
|
|
136
151
|
end
|
|
137
152
|
end
|
|
138
153
|
end
|
|
@@ -29,6 +29,7 @@ module Coverband
|
|
|
29
29
|
|
|
30
30
|
@ttl = opts[:ttl]
|
|
31
31
|
@relative_file_converter = opts[:relative_file_converter] || Utils::RelativeFileConverter
|
|
32
|
+
@short_name_regex = /^#{Coverband.configuration.root}/
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
def supported?
|
|
@@ -171,7 +172,7 @@ module Coverband
|
|
|
171
172
|
end
|
|
172
173
|
|
|
173
174
|
def short_name(filename)
|
|
174
|
-
filename.sub(
|
|
175
|
+
filename.sub(@short_name_regex, ".")
|
|
175
176
|
.gsub(%r{^\./}, "")
|
|
176
177
|
end
|
|
177
178
|
|
|
@@ -63,8 +63,7 @@ module Coverband
|
|
|
63
63
|
# the Coverband 2 had the same issue,
|
|
64
64
|
# and the tradeoff has always been acceptable
|
|
65
65
|
def save_report(report)
|
|
66
|
-
data = report
|
|
67
|
-
data = merge_reports(data, coverage(nil, skip_hash_check: true))
|
|
66
|
+
data = merge_reports(report, coverage(nil, skip_hash_check: true))
|
|
68
67
|
save_coverage(data)
|
|
69
68
|
end
|
|
70
69
|
|
|
@@ -66,10 +66,9 @@ module Coverband
|
|
|
66
66
|
# We set here vs initialize to avoid setting on the primary process vs child processes
|
|
67
67
|
@pid ||= ::Process.pid
|
|
68
68
|
|
|
69
|
-
# TODO: do we need dup
|
|
70
69
|
# TODO: we don't need upstream timestamps, server will track first_seen
|
|
71
70
|
Thread.new do
|
|
72
|
-
data = expand_report(report
|
|
71
|
+
data = expand_report(report)
|
|
73
72
|
full_package = {
|
|
74
73
|
collection_type: "coverage_delta",
|
|
75
74
|
collection_data: {
|
|
@@ -61,8 +61,8 @@ module Coverband
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def unused_keys(used_keys = nil)
|
|
64
|
-
recently_used_keys =
|
|
65
|
-
all_keys.reject { |k| recently_used_keys.
|
|
64
|
+
recently_used_keys = used_keys || self.used_keys
|
|
65
|
+
all_keys.reject { |k| recently_used_keys.key?(k.to_s) }
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def as_json
|
|
@@ -97,8 +97,8 @@ module Coverband
|
|
|
97
97
|
redis_store.set(tracker_time_key, Time.now.to_i) unless @one_time_timestamp || tracker_time_key_exists?
|
|
98
98
|
@one_time_timestamp = true
|
|
99
99
|
reported_time = Time.now.to_i
|
|
100
|
-
@keys_to_record.
|
|
101
|
-
redis_store.hset(tracker_key, key.to_s
|
|
100
|
+
if @keys_to_record.any?
|
|
101
|
+
redis_store.hset(tracker_key, @keys_to_record.each_with_object({}) { |key, h| h[key.to_s] = reported_time })
|
|
102
102
|
end
|
|
103
103
|
@keys_to_record.clear
|
|
104
104
|
rescue => e
|
|
@@ -54,8 +54,8 @@ module Coverband
|
|
|
54
54
|
# on the critical performance path, and any refactoring I come up with
|
|
55
55
|
# would slow down the performance.
|
|
56
56
|
###
|
|
57
|
-
next unless
|
|
58
|
-
file.
|
|
57
|
+
next unless file.start_with?(@@project_directory) &&
|
|
58
|
+
@@ignore_patterns.none? { |pattern| file.match(pattern) }
|
|
59
59
|
|
|
60
60
|
# This handles Coverage branch support, setup by default in
|
|
61
61
|
# simplecov 0.18.x
|
|
@@ -84,8 +84,8 @@ module Coverband
|
|
|
84
84
|
# on the critical performance path, and any refactoring I come up with
|
|
85
85
|
# would slow down the performance.
|
|
86
86
|
###
|
|
87
|
-
next unless
|
|
88
|
-
file.
|
|
87
|
+
next unless file.start_with?(@@project_directory) &&
|
|
88
|
+
@@ignore_patterns.none? { |pattern| file.match(pattern) }
|
|
89
89
|
|
|
90
90
|
@@stubs[file] ||= ::Coverage.line_stub(file)
|
|
91
91
|
transformed_line_counts = coverage[:oneshot_lines].each_with_object(@@stubs[file].dup) { |line_number, line_counts|
|
|
@@ -78,8 +78,17 @@ module Coverband
|
|
|
78
78
|
recently_used_views = used_keys.keys
|
|
79
79
|
unused_views = all_keys - recently_used_views
|
|
80
80
|
# since layouts don't include format we count them used if they match with ANY formats
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
potential_layout_references = recently_used_views.reject { |v| v.end_with?(".erb", ".haml", ".slim") }
|
|
82
|
+
|
|
83
|
+
layout_matcher = if potential_layout_references.any?
|
|
84
|
+
Regexp.union(potential_layout_references)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
unused_views.reject! do |view|
|
|
88
|
+
(layout_matcher && view.include?("/layouts/") && view.match?(layout_matcher)) ||
|
|
89
|
+
@ignore_patterns.any? { |pattern| view.match?(pattern) }
|
|
90
|
+
end
|
|
91
|
+
unused_views
|
|
83
92
|
end
|
|
84
93
|
|
|
85
94
|
def clear_key!(filename)
|
|
@@ -103,6 +112,7 @@ module Coverband
|
|
|
103
112
|
|
|
104
113
|
roots.each do |root|
|
|
105
114
|
normalized = normalized.gsub(root, "")
|
|
115
|
+
break if normalized.length < original_length
|
|
106
116
|
end
|
|
107
117
|
|
|
108
118
|
# Only remove leading slash if we actually modified the path by removing a root
|
|
@@ -28,21 +28,21 @@ module Coverband
|
|
|
28
28
|
|
|
29
29
|
if file_pattern
|
|
30
30
|
dead_methods = dead_methods.select do |method|
|
|
31
|
-
File.fnmatch(file_pattern, method
|
|
31
|
+
File.fnmatch(file_pattern, method.file_path, File::FNM_PATHNAME)
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
# Group by file for easier reading
|
|
36
|
-
grouped = dead_methods.group_by
|
|
36
|
+
grouped = dead_methods.group_by(&:file_path)
|
|
37
37
|
|
|
38
38
|
result = grouped.map do |file_path, methods|
|
|
39
39
|
{
|
|
40
40
|
file: file_path,
|
|
41
41
|
dead_methods: methods.map do |m|
|
|
42
42
|
{
|
|
43
|
-
class_name: m
|
|
44
|
-
method_name: m
|
|
45
|
-
line_number: m
|
|
43
|
+
class_name: m.class_name,
|
|
44
|
+
method_name: m.name,
|
|
45
|
+
line_number: m.first_line_number
|
|
46
46
|
}
|
|
47
47
|
end
|
|
48
48
|
}
|
|
@@ -52,6 +52,8 @@ module Coverband
|
|
|
52
52
|
lines_missed: file_data["lines_missed"],
|
|
53
53
|
runtime_percentage: file_data["runtime_percentage"],
|
|
54
54
|
never_loaded: file_data["never_loaded"],
|
|
55
|
+
first_updated_at: file_data["first_updated_at"],
|
|
56
|
+
last_updated_at: file_data["last_updated_at"],
|
|
55
57
|
coverage: file_data["coverage"]
|
|
56
58
|
}
|
|
57
59
|
end
|
|
@@ -72,6 +72,11 @@ module Coverband
|
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
def format_timestamp(timestamp)
|
|
76
|
+
return nil if timestamp.nil? || timestamp == Coverband::Utils::SourceFile::NOT_AVAILABLE
|
|
77
|
+
timestamp.is_a?(Time) ? timestamp.iso8601 : timestamp.to_s
|
|
78
|
+
end
|
|
79
|
+
|
|
75
80
|
def report_as_json
|
|
76
81
|
return filtered_report_files.to_json if for_merged_report
|
|
77
82
|
|
|
@@ -133,6 +138,8 @@ module Coverband
|
|
|
133
138
|
filename: source_file.filename,
|
|
134
139
|
hash: Digest::SHA1.hexdigest(source_file.filename),
|
|
135
140
|
never_loaded: source_file.never_loaded,
|
|
141
|
+
first_updated_at: format_timestamp(source_file.first_updated_at),
|
|
142
|
+
last_updated_at: format_timestamp(source_file.last_updated_at),
|
|
136
143
|
runtime_percentage: result.runtime_relevant_coverage(source_file),
|
|
137
144
|
lines_of_code: source_file.lines.count,
|
|
138
145
|
lines_covered: source_file.covered_lines.count,
|
|
@@ -32,9 +32,9 @@ module Coverband
|
|
|
32
32
|
|
|
33
33
|
def init_web
|
|
34
34
|
full_path = Gem::Specification.find_by_name("coverband").full_gem_path
|
|
35
|
-
@
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
@file_server = Rack::Files.new(
|
|
36
|
+
File.expand_path("public", full_path)
|
|
37
|
+
)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def check_auth
|
|
@@ -85,7 +85,7 @@ module Coverband
|
|
|
85
85
|
else
|
|
86
86
|
case request_path_info
|
|
87
87
|
when /.*\.(css|js|gif|png)/
|
|
88
|
-
@
|
|
88
|
+
@file_server.get(env)
|
|
89
89
|
when %r{/settings}
|
|
90
90
|
[200, coverband_headers, [settings]]
|
|
91
91
|
when %r{/view_tracker_data}
|
|
@@ -5,7 +5,7 @@ module Coverband
|
|
|
5
5
|
class AbsoluteFileConverter
|
|
6
6
|
def initialize(roots)
|
|
7
7
|
@cache = {}
|
|
8
|
-
@roots = roots
|
|
8
|
+
@roots = convert_roots(roots)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def self.instance
|
|
@@ -24,11 +24,25 @@ module Coverband
|
|
|
24
24
|
@cache[relative_path] ||= begin
|
|
25
25
|
relative_filename = relative_path
|
|
26
26
|
local_filename = relative_filename
|
|
27
|
-
@roots.each do |root|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
@roots.each do |root, root_regexp|
|
|
28
|
+
if relative_filename.match?(root_regexp)
|
|
29
|
+
relative_filename = relative_filename.sub(root_regexp, "./")
|
|
30
|
+
# once we have a relative path break out of the loop
|
|
31
|
+
break
|
|
32
|
+
end
|
|
31
33
|
end
|
|
34
|
+
|
|
35
|
+
if relative_filename == local_filename && File.exist?(local_filename)
|
|
36
|
+
real_filename = File.realpath(local_filename)
|
|
37
|
+
@roots.each do |root, root_regexp|
|
|
38
|
+
if real_filename.match?(root_regexp)
|
|
39
|
+
relative_filename = real_filename.sub(root_regexp, "./")
|
|
40
|
+
# once we have a relative path break out of the loop
|
|
41
|
+
break
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
32
46
|
# the filename for our reports is expected to be a full path.
|
|
33
47
|
# roots.last should be roots << current_root}/
|
|
34
48
|
# a fully expanded path of config.root
|
|
@@ -36,12 +50,28 @@ module Coverband
|
|
|
36
50
|
# above only works for app files
|
|
37
51
|
# we need to rethink some of this logic
|
|
38
52
|
# gems aren't at project root and can have multiple locations
|
|
39
|
-
local_root = @roots.find { |root|
|
|
53
|
+
local_root = @roots.find { |root, _root_regexp|
|
|
40
54
|
File.exist?(relative_filename.gsub("./", root))
|
|
41
|
-
}
|
|
55
|
+
}&.first
|
|
42
56
|
local_root ? relative_filename.gsub("./", local_root) : local_filename
|
|
43
57
|
end
|
|
44
58
|
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def convert_roots(roots)
|
|
63
|
+
roots.flat_map { |root|
|
|
64
|
+
items = []
|
|
65
|
+
expanded = "#{File.expand_path(root)}/"
|
|
66
|
+
items << [expanded, /^#{expanded}/]
|
|
67
|
+
|
|
68
|
+
if File.exist?(root)
|
|
69
|
+
real = "#{File.realpath(root)}/"
|
|
70
|
+
items << [real, /^#{Regexp.escape(real)}/]
|
|
71
|
+
end
|
|
72
|
+
items
|
|
73
|
+
}.uniq
|
|
74
|
+
end
|
|
45
75
|
end
|
|
46
76
|
end
|
|
47
77
|
end
|
|
@@ -43,7 +43,11 @@ module Coverband
|
|
|
43
43
|
# and runtime phases.
|
|
44
44
|
coverage = Coverband.configuration.store.get_coverage_report[Coverband::MERGED_TYPE]
|
|
45
45
|
coverage.flat_map do |file_path, coverage|
|
|
46
|
+
next [] unless File.exist?(file_path)
|
|
47
|
+
|
|
46
48
|
scan(file_path: file_path, coverage: coverage["data"])
|
|
49
|
+
rescue Errno::ENOENT
|
|
50
|
+
[]
|
|
47
51
|
end
|
|
48
52
|
end
|
|
49
53
|
|
|
@@ -14,28 +14,28 @@ module Coverband
|
|
|
14
14
|
def covered_lines
|
|
15
15
|
return 0.0 if empty?
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
@covered_lines ||= sum(&:covered_lines_count)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
# Returns the count of lines that have been missed
|
|
21
21
|
def missed_lines
|
|
22
22
|
return 0.0 if empty?
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
@missed_lines ||= sum(&:missed_lines_count)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# Returns the count of lines that are not relevant for coverage
|
|
28
28
|
def never_lines
|
|
29
29
|
return 0.0 if empty?
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
@never_lines ||= sum(&:never_lines_count)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# Returns the count of skipped lines
|
|
35
35
|
def skipped_lines
|
|
36
36
|
return 0.0 if empty?
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
@skipped_lines ||= sum(&:skipped_lines_count)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
# Computes the coverage based upon lines covered and lines missed for each file
|
|
@@ -46,7 +46,9 @@ module Coverband
|
|
|
46
46
|
|
|
47
47
|
# Returns the overall amount of relevant lines of code across all files in this list
|
|
48
48
|
def lines_of_code
|
|
49
|
-
|
|
49
|
+
return 0.0 if empty?
|
|
50
|
+
|
|
51
|
+
@lines_of_code ||= sum(&:lines_of_code)
|
|
50
52
|
end
|
|
51
53
|
|
|
52
54
|
# Computes the coverage based upon lines covered and lines missed
|
|
@@ -68,11 +70,17 @@ module Coverband
|
|
|
68
70
|
def covered_strength
|
|
69
71
|
return 0.0 if empty? || lines_of_code.zero?
|
|
70
72
|
|
|
71
|
-
Float(
|
|
73
|
+
Float(sum { |f| f.covered_strength * f.lines_of_code } / lines_of_code)
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
def first_seen_at
|
|
75
|
-
|
|
77
|
+
min = nil
|
|
78
|
+
each do |f|
|
|
79
|
+
val = f.first_updated_at
|
|
80
|
+
next if val.is_a?(String)
|
|
81
|
+
min = val if min.nil? || val < min
|
|
82
|
+
end
|
|
83
|
+
min
|
|
76
84
|
end
|
|
77
85
|
end
|
|
78
86
|
end
|
|
@@ -73,9 +73,12 @@ module Coverband
|
|
|
73
73
|
template("data").result(binding)
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
TEMPLATE_CACHE = {}
|
|
77
|
+
private_constant :TEMPLATE_CACHE
|
|
78
|
+
|
|
76
79
|
# Returns the an erb instance for the template of given name
|
|
77
80
|
def template(name)
|
|
78
|
-
ERB.new(File.read(File.
|
|
81
|
+
TEMPLATE_CACHE[name] ||= ERB.new(File.read(File.expand_path("../../../views/#{name}.erb", __dir__)))
|
|
79
82
|
end
|
|
80
83
|
|
|
81
84
|
def output_path
|
|
@@ -119,7 +122,7 @@ module Coverband
|
|
|
119
122
|
|
|
120
123
|
# Returns a table containing the given source files
|
|
121
124
|
def formatted_file_list(title, result, source_files, options = {})
|
|
122
|
-
title_id = title.gsub(/^[^a-zA-Z]+/, "").gsub(/[^a-zA-Z0-9
|
|
125
|
+
title_id = title.gsub(/^[^a-zA-Z]+/, "").gsub(/[^a-zA-Z0-9\-_]/, "")
|
|
123
126
|
# Silence a warning by using the following variable to assign to `_`:
|
|
124
127
|
# "warning: possibly useless use of a variable in void context"
|
|
125
128
|
# The variable is used by ERB via binding.
|
|
@@ -17,24 +17,39 @@ module Coverband
|
|
|
17
17
|
|
|
18
18
|
def initialize(roots)
|
|
19
19
|
@cache = {}
|
|
20
|
-
@
|
|
20
|
+
@roots_regexp = Regexp.union(convert_roots(roots))
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def convert(file)
|
|
24
24
|
@cache[file] ||= begin
|
|
25
|
-
relative_file = file
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
relative_file = file.sub(@roots_regexp, "./")
|
|
26
|
+
|
|
27
|
+
if relative_file == file && !file.start_with?(".") && File.exist?(file)
|
|
28
|
+
real_file = File.realpath(file)
|
|
29
|
+
new_relative_file = real_file.sub(@roots_regexp, "./")
|
|
30
|
+
relative_file = ((new_relative_file == real_file) ? file : new_relative_file)
|
|
29
31
|
end
|
|
32
|
+
|
|
30
33
|
relative_file
|
|
31
34
|
end
|
|
32
35
|
end
|
|
33
36
|
|
|
34
37
|
private
|
|
35
38
|
|
|
36
|
-
def
|
|
37
|
-
|
|
39
|
+
def convert_roots(roots)
|
|
40
|
+
roots.flat_map { |root|
|
|
41
|
+
items = []
|
|
42
|
+
expanded = File.expand_path(root)
|
|
43
|
+
expanded += "/" unless expanded.end_with?("/")
|
|
44
|
+
items << /^#{Regexp.escape(expanded)}/
|
|
45
|
+
|
|
46
|
+
if File.exist?(root)
|
|
47
|
+
real = File.realpath(root)
|
|
48
|
+
real += "/" unless real.end_with?("/")
|
|
49
|
+
items << /^#{Regexp.escape(real)}/
|
|
50
|
+
end
|
|
51
|
+
items
|
|
52
|
+
}.uniq
|
|
38
53
|
end
|
|
39
54
|
end
|
|
40
55
|
end
|
|
@@ -51,8 +51,6 @@ module Coverband
|
|
|
51
51
|
# the line-by-line coverage to zero (if relevant) or nil (comments / whitespace etc).
|
|
52
52
|
def self.add_not_loaded_files(result, tracked_files)
|
|
53
53
|
if tracked_files
|
|
54
|
-
# TODO: Can we get rid of this dup it wastes memory
|
|
55
|
-
result = result.dup
|
|
56
54
|
Dir[tracked_files].each do |file|
|
|
57
55
|
absolute = File.expand_path(file)
|
|
58
56
|
result[absolute] ||= {
|