simplecov 1.0.2 → 1.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 +4 -4
- data/README.md +14 -0
- data/lib/simplecov/combine/methods_combiner.rb +11 -10
- data/lib/simplecov/configuration/formatting.rb +12 -6
- data/lib/simplecov/formatter/multi_formatter.rb +1 -1
- data/lib/simplecov/formatter.rb +7 -0
- data/lib/simplecov/result.rb +1 -1
- data/lib/simplecov/result_adapter.rb +48 -16
- data/lib/simplecov/source_file/ruby_data_parser.rb +19 -1
- data/lib/simplecov/version.rb +1 -1
- data/sig/simplecov.rbs +6 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ddfad3987b6f04c755c08c2b251d111ca75eff0e14aef5156e9d30f049087e96
|
|
4
|
+
data.tar.gz: 97efb84c53398cc92a3e2106d2428351f3f1edc37cc4a7c34abeb2859503a0fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ac211a4d93fe0db4e87f4ddddff178566e84ef2d04234ffba49382da236668f718ef43d2b68b01977e1aa17dec86823dbc70cb8adf7eb116a9f24f06a99d78c2
|
|
7
|
+
data.tar.gz: 5259cbb289b03d111995433363370d3e91729da55c9aef5834a0626cfd11fc93fa6614fab3f764e8faaa667439a6716cb1e842f343c58875fd83c023977f6c99
|
data/README.md
CHANGED
|
@@ -1042,6 +1042,20 @@ SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
|
|
|
1042
1042
|
`SimpleCov.result.format!` then invokes `SimpleCov::Formatter::YourFormatter.new.format(result)`, where `result` is a
|
|
1043
1043
|
`SimpleCov::Result`. Do whatever you wish with it.
|
|
1044
1044
|
|
|
1045
|
+
### Passing options to formatters
|
|
1046
|
+
|
|
1047
|
+
Anywhere a formatter class is accepted, a ready-built instance works too — that's how you reach constructor options.
|
|
1048
|
+
The built-in HTML and JSON formatters take `silent: true` to suppress the "Coverage report generated" status line on
|
|
1049
|
+
stderr, and `output_dir:` to write the report somewhere other than `SimpleCov.coverage_path`:
|
|
1050
|
+
|
|
1051
|
+
```ruby
|
|
1052
|
+
SimpleCov.start do
|
|
1053
|
+
formatter SimpleCov::Formatter::HTMLFormatter.new(silent: true)
|
|
1054
|
+
end
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
Instances mix freely with classes in `formatters` lists as well.
|
|
1058
|
+
|
|
1045
1059
|
### Using multiple formatters
|
|
1046
1060
|
|
|
1047
1061
|
As of SimpleCov 0.9 you can specify multiple result formats. The HTML and JSON formatters are built in; other
|
|
@@ -16,14 +16,15 @@ module SimpleCov
|
|
|
16
16
|
#
|
|
17
17
|
# Method coverage maps `[class, name, start_line, start_col, end_line,
|
|
18
18
|
# end_col]` keys to hit counts. Keys are matched on their SOURCE
|
|
19
|
-
# identity —
|
|
20
|
-
# Ruby records one entry per
|
|
21
|
-
# defined onto different classes
|
|
22
|
-
# different
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
# methods that only appear
|
|
19
|
+
# identity — the location, ignoring the class and name elements —
|
|
20
|
+
# because Ruby records one entry per defined method: the same
|
|
21
|
+
# `define_method` block defined onto different classes, or under
|
|
22
|
+
# different names, in different processes arrives with different
|
|
23
|
+
# receivers or names for the same source method, and matching on the
|
|
24
|
+
# full key would keep both, letting a never-called copy's 0 shadow a
|
|
25
|
+
# covered method after merge (issue #1234). Combining sums the hit
|
|
26
|
+
# counts for matching methods and preserves methods that only appear
|
|
27
|
+
# in one result.
|
|
27
28
|
#
|
|
28
29
|
# @return [Hash]
|
|
29
30
|
#
|
|
@@ -41,8 +42,8 @@ module SimpleCov
|
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
def source_identity(key)
|
|
44
|
-
_class_name, *
|
|
45
|
-
|
|
45
|
+
_class_name, _method_name, *location = SourceFile::RubyDataParser.call(key)
|
|
46
|
+
location
|
|
46
47
|
end
|
|
47
48
|
end
|
|
48
49
|
end
|
|
@@ -9,11 +9,16 @@ module SimpleCov
|
|
|
9
9
|
attr_writer :formatter, :print_error_status
|
|
10
10
|
|
|
11
11
|
#
|
|
12
|
-
# Gets or sets the configured formatter.
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
12
|
+
# Gets or sets the configured formatter. Accepts a formatter class
|
|
13
|
+
# (instantiated fresh for every report) or a ready-built instance,
|
|
14
|
+
# which is how constructor options are passed — e.g.
|
|
15
|
+
# `formatter SimpleCov::Formatter::HTMLFormatter.new(silent: true)`
|
|
16
|
+
# to suppress the "Coverage report generated" status line (see
|
|
17
|
+
# #1240). Pass `false` (or `nil`) to opt out of formatting
|
|
18
|
+
# entirely — worker processes in big parallel CI setups (see #964)
|
|
19
|
+
# only need their `.resultset.json` on disk so a final
|
|
20
|
+
# `SimpleCov.collate` job can produce the report; running them
|
|
21
|
+
# without a formatter saves the per-job HTML/multi-formatter
|
|
17
22
|
# overhead.
|
|
18
23
|
#
|
|
19
24
|
def formatter(formatter = :__no_arg__)
|
|
@@ -40,7 +45,8 @@ module SimpleCov
|
|
|
40
45
|
|
|
41
46
|
# Sets the configured formatters. Equivalent to `formatters [...]`.
|
|
42
47
|
# Accepts a single formatter as well as an Array, matching the pre-1.0 behavior
|
|
43
|
-
# where `MultiFormatter.new` normalized its input.
|
|
48
|
+
# where `MultiFormatter.new` normalized its input. Elements may be
|
|
49
|
+
# formatter classes or ready-built instances; see `formatter`.
|
|
44
50
|
def formatters=(formatters)
|
|
45
51
|
formatters = Array(formatters)
|
|
46
52
|
@formatter = formatters.empty? ? nil : SimpleCov::Formatter::MultiFormatter.new(formatters)
|
|
@@ -10,7 +10,7 @@ module SimpleCov
|
|
|
10
10
|
module InstanceMethods
|
|
11
11
|
def format(result)
|
|
12
12
|
formatters.map do |formatter|
|
|
13
|
-
formatter.
|
|
13
|
+
Formatter.instance_for(formatter).format(result)
|
|
14
14
|
rescue StandardError => e
|
|
15
15
|
warn("Formatter #{formatter} failed with #{e.class}: #{e.message} (#{(_ = e.backtrace).first})")
|
|
16
16
|
nil
|
data/lib/simplecov/formatter.rb
CHANGED
|
@@ -6,6 +6,13 @@ module SimpleCov
|
|
|
6
6
|
# and can be wired up via `SimpleCov.formatter=`.
|
|
7
7
|
# TODO: Documentation on how to build your own formatters
|
|
8
8
|
module Formatter
|
|
9
|
+
# Formatters can be configured either as classes (instantiated
|
|
10
|
+
# fresh for every report) or as ready-built instances — the only
|
|
11
|
+
# way to reach constructor options like
|
|
12
|
+
# `HTMLFormatter.new(silent: true)`. See #1240.
|
|
13
|
+
def self.instance_for(formatter)
|
|
14
|
+
formatter.is_a?(Class) ? formatter.new : formatter
|
|
15
|
+
end
|
|
9
16
|
end
|
|
10
17
|
end
|
|
11
18
|
|
data/lib/simplecov/result.rb
CHANGED
|
@@ -102,7 +102,7 @@ module SimpleCov
|
|
|
102
102
|
formatter = SimpleCov.formatter
|
|
103
103
|
return nil if formatter.nil?
|
|
104
104
|
|
|
105
|
-
formatter.
|
|
105
|
+
Formatter.instance_for(formatter).format(self)
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
# Defines when this result has been created. Defaults to Time.now
|
|
@@ -60,37 +60,69 @@ module SimpleCov
|
|
|
60
60
|
SINGLETON_WRAPPER_PATTERN = /\A#<Class:([A-Z_][\w:]*)>\z/
|
|
61
61
|
private_constant :SINGLETON_WRAPPER_PATTERN
|
|
62
62
|
|
|
63
|
-
# Ruby's method coverage records one entry per
|
|
64
|
-
# location: a block handed to `define_method` /
|
|
65
|
-
# from a shared code path
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
63
|
+
# Ruby's method coverage records one entry per DEFINED METHOD, not per
|
|
64
|
+
# source location: a block handed to `define_method` /
|
|
65
|
+
# `define_singleton_method` from a shared code path yields a separate
|
|
66
|
+
# `[receiver, name, location]` entry for every class it's defined on
|
|
67
|
+
# (a module's `included` hook defining onto each descendant) AND for
|
|
68
|
+
# every name it's defined under (a builder looping `define_method key`
|
|
69
|
+
# over a container), all pointing at the same source. A file-based
|
|
70
|
+
# report can only express "was the method at this location ever
|
|
71
|
+
# executed", so entries are aggregated by location alone, summing
|
|
72
|
+
# hits — otherwise each receiver or name whose generated copy never
|
|
73
|
+
# ran shows as a phantom uncovered method on a line whose line
|
|
74
|
+
# coverage is 100%. Regular `def`s map one location to one name, so
|
|
75
|
+
# they are unaffected. The first entry's (normalized) key is kept for
|
|
76
|
+
# display. See issue #1234.
|
|
73
77
|
def normalize_method_keys(cover_statistic)
|
|
74
78
|
methods = cover_statistic[:methods]
|
|
75
79
|
return unless methods
|
|
76
80
|
|
|
77
81
|
aggregated = {} #: Hash[untyped, [untyped, Integer]]
|
|
78
82
|
methods.each_with_object(aggregated) do |(key, count), memo|
|
|
79
|
-
|
|
80
|
-
retained_key, existing = memo[
|
|
81
|
-
memo[
|
|
83
|
+
location = key[2..] #: Array[untyped]
|
|
84
|
+
retained_key, existing = memo[location] || [normalize_method_key(key), 0]
|
|
85
|
+
memo[location] = [retained_key, existing + count]
|
|
82
86
|
end
|
|
83
87
|
cover_statistic[:methods] = aggregated.values.to_h
|
|
84
88
|
end
|
|
85
89
|
|
|
86
90
|
def normalize_method_key(key)
|
|
87
91
|
normalized_key = key.dup
|
|
88
|
-
normalized_key[0] = key[0]
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
normalized_key[0] = class_display_name(key[0])
|
|
93
|
+
.gsub(ADDRESS_PATTERN, ADDRESS_PLACEHOLDER)
|
|
94
|
+
.sub(SINGLETON_WRAPPER_PATTERN, '\1')
|
|
91
95
|
normalized_key
|
|
92
96
|
end
|
|
93
97
|
|
|
98
|
+
# Rendering a class name can execute user code: a singleton class's
|
|
99
|
+
# `to_s` renders its attached object via `#inspect`, which a module can
|
|
100
|
+
# shadow with an incompatible signature (Liquid::Utils defines
|
|
101
|
+
# `inspect(value, max_depth = 2)` as a module_function, so rendering
|
|
102
|
+
# `#<Class:Liquid::Utils>` raises ArgumentError). A coverage report
|
|
103
|
+
# must never crash the host suite over that, so on failure rebuild the
|
|
104
|
+
# singleton wrapper from `Module#name` via bound methods (which cannot
|
|
105
|
+
# be shadowed), falling back to the address form, which
|
|
106
|
+
# ADDRESS_PATTERN then normalizes. See issue #1236.
|
|
107
|
+
def class_display_name(klass)
|
|
108
|
+
klass.to_s
|
|
109
|
+
rescue StandardError
|
|
110
|
+
singleton_wrapper_name(klass) || Object.instance_method(:to_s).bind_call(klass)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def singleton_wrapper_name(klass)
|
|
114
|
+
return nil unless klass.is_a?(Class) && klass.singleton_class?
|
|
115
|
+
|
|
116
|
+
attached = klass.attached_object
|
|
117
|
+
# simplecov:disable branch — CRuby only reaches the rescue via a
|
|
118
|
+
# Module/Class attached object (instance singletons render from the
|
|
119
|
+
# class name chain without calling user inspect), so the non-Module
|
|
120
|
+
# arm is defensive.
|
|
121
|
+
name = Module.instance_method(:name).bind_call(attached) if attached.is_a?(Module)
|
|
122
|
+
# simplecov:enable branch
|
|
123
|
+
name && "#<Class:#{name}>"
|
|
124
|
+
end
|
|
125
|
+
|
|
94
126
|
# Ruby's eval coverage records a fresh set of branch entries for every
|
|
95
127
|
# COMPILE of an eval'd string: a template rendered through multiple view
|
|
96
128
|
# classes (e.g. hanami-view compiles each template once per view) yields
|
|
@@ -15,10 +15,28 @@ module SimpleCov
|
|
|
15
15
|
|
|
16
16
|
# Tests use the real data structures (except for integration tests)
|
|
17
17
|
# so no need to put them through here.
|
|
18
|
+
#
|
|
19
|
+
# String parses are memoized: `Combine::BranchesCombiner` and
|
|
20
|
+
# `Combine::MethodsCombiner` derive a merge identity from every key of
|
|
21
|
+
# both sides on every pairwise merge, so collating N resultsets parses
|
|
22
|
+
# each key string N-1 times — and Ripper dominates the wall time of a
|
|
23
|
+
# large collate.
|
|
24
|
+
# Key strings repeat across folds and within report building, while the
|
|
25
|
+
# set of unique keys is bounded by the project's branch and method
|
|
26
|
+
# count, so a permanent cache stays small. Cached arrays are frozen
|
|
27
|
+
# element-wise (String class names included, not just the tuple):
|
|
28
|
+
# every caller destructures without mutating, and sharing one array
|
|
29
|
+
# across callers must stay that way. The cache key needs no such
|
|
30
|
+
# care — `Hash#[]=` dups and freezes String keys on its own.
|
|
18
31
|
def call(structure)
|
|
19
32
|
return structure if structure.is_a?(Array)
|
|
20
33
|
|
|
21
|
-
|
|
34
|
+
string = structure.to_s
|
|
35
|
+
parse_cache[string] ||= parse_array_string(string).each(&:freeze).freeze
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def parse_cache
|
|
39
|
+
@parse_cache ||= {} #: Hash[String, untyped]
|
|
22
40
|
end
|
|
23
41
|
|
|
24
42
|
# Parse a string like '[:if, 0, 3, 4, 3, 21]' or
|
data/lib/simplecov/version.rb
CHANGED
data/sig/simplecov.rbs
CHANGED
|
@@ -1217,13 +1217,18 @@ end
|
|
|
1217
1217
|
module SimpleCov
|
|
1218
1218
|
# Namespace for result formatters. A formatter is any class whose
|
|
1219
1219
|
# instances respond to `#format(result)` and that can be instantiated
|
|
1220
|
-
# with no arguments
|
|
1220
|
+
# with no arguments, or a ready-built instance responding to
|
|
1221
|
+
# `#format(result)`; wire one up via `SimpleCov.formatter=` or
|
|
1221
1222
|
# `SimpleCov.formatters=`.
|
|
1222
1223
|
module Formatter
|
|
1223
1224
|
# Structural interface for custom formatter instances.
|
|
1224
1225
|
interface _Formatter
|
|
1225
1226
|
def format: (SimpleCov::Result result) -> untyped
|
|
1226
1227
|
end
|
|
1228
|
+
|
|
1229
|
+
# Normalizes a configured formatter — class or ready-built
|
|
1230
|
+
# instance — into an instance. See #1240.
|
|
1231
|
+
def self.instance_for: (untyped formatter) -> untyped
|
|
1227
1232
|
end
|
|
1228
1233
|
end
|
|
1229
1234
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simplecov
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Erik Berlin
|
|
@@ -154,9 +154,9 @@ licenses:
|
|
|
154
154
|
metadata:
|
|
155
155
|
bug_tracker_uri: https://github.com/simplecov-ruby/simplecov/issues
|
|
156
156
|
changelog_uri: https://github.com/simplecov-ruby/simplecov/blob/main/CHANGELOG.md
|
|
157
|
-
documentation_uri: https://www.rubydoc.info/gems/simplecov/1.0.
|
|
157
|
+
documentation_uri: https://www.rubydoc.info/gems/simplecov/1.0.3
|
|
158
158
|
mailing_list_uri: https://groups.google.com/forum/#!forum/simplecov
|
|
159
|
-
source_code_uri: https://github.com/simplecov-ruby/simplecov/tree/v1.0.
|
|
159
|
+
source_code_uri: https://github.com/simplecov-ruby/simplecov/tree/v1.0.3
|
|
160
160
|
rubygems_mfa_required: 'true'
|
|
161
161
|
rdoc_options: []
|
|
162
162
|
require_paths:
|
|
@@ -172,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
172
172
|
- !ruby/object:Gem::Version
|
|
173
173
|
version: '0'
|
|
174
174
|
requirements: []
|
|
175
|
-
rubygems_version: 4.0.
|
|
175
|
+
rubygems_version: 4.0.17
|
|
176
176
|
specification_version: 4
|
|
177
177
|
summary: Code coverage for Ruby
|
|
178
178
|
test_files: []
|