simplecov 1.2.0 → 1.3.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/README.md +1 -1
- data/lib/simplecov/cli/annotations.rb +166 -0
- data/lib/simplecov/cli/patch/output.rb +16 -0
- data/lib/simplecov/cli/patch.rb +8 -2
- data/lib/simplecov/cli/uncovered/misses.rb +5 -9
- data/lib/simplecov/cli/uncovered.rb +6 -20
- data/lib/simplecov/cli/usage.rb +2 -1
- data/lib/simplecov/configuration/eval_coverage.rb +1 -1
- data/lib/simplecov/configuration/view_coverage.rb +2 -3
- data/lib/simplecov/coverage_violations.rb +4 -2
- data/lib/simplecov/directive/erb.rb +44 -0
- data/lib/simplecov/directive/haml.rb +33 -0
- data/lib/simplecov/directive/indented_template.rb +37 -0
- data/lib/simplecov/directive/slim.rb +35 -0
- data/lib/simplecov/directive/template.rb +29 -0
- data/lib/simplecov/directive.rb +1 -0
- data/lib/simplecov/formatter/html_formatter/public/index.html +12 -12
- data/lib/simplecov/production.rb +1 -1
- data/lib/simplecov/profiles/rails.rb +4 -4
- data/lib/simplecov/profiles/strict.rb +3 -3
- data/lib/simplecov/result.rb +6 -0
- data/lib/simplecov/result_processing.rb +20 -5
- data/lib/simplecov/simulate_coverage.rb +2 -2
- data/lib/simplecov/source_file/builder_context.rb +7 -0
- data/lib/simplecov/source_file/skip_chunks.rb +11 -2
- data/lib/simplecov/source_file/statistics.rb +16 -6
- data/lib/simplecov/static_coverage_extractor/condition_folding.rb +9 -42
- data/lib/simplecov/static_coverage_extractor/method_collector.rb +1 -5
- data/lib/simplecov/static_coverage_extractor/visitor.rb +4 -7
- data/lib/simplecov/static_coverage_extractor.rb +6 -22
- data/lib/simplecov/version.rb +1 -1
- data/man/simplecov.1 +8 -3
- data/sig/simplecov.rbs +5 -0
- metadata +10 -4
|
@@ -8,14 +8,9 @@ module SimpleCov
|
|
|
8
8
|
# must not synthesize one either: the arm would be a phantom no loaded run
|
|
9
9
|
# can hit, the same unmergeable-tuple failure as #1226 / #1233.
|
|
10
10
|
module ConditionFolding
|
|
11
|
-
# CRuby 3.4 rebuilt the fold on the Prism compiler, and the parse.y fold
|
|
12
|
-
# replaced
|
|
13
|
-
# 3.2/3.3; parentheses were transparent for every literal on 3.2; and on
|
|
14
|
-
# 3.2 the dead arm's branch table entries survive the fold while its
|
|
15
|
-
# `def`s still never register.
|
|
11
|
+
# CRuby 3.4 rebuilt the fold on the Prism compiler, and the parse.y fold
|
|
12
|
+
# it replaced still differs on 3.3, where `__FILE__` folds.
|
|
16
13
|
FOLDS_SOURCE_FILE = Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.4")
|
|
17
|
-
PARENS_ALWAYS_TRANSPARENT = Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3.3")
|
|
18
|
-
DEAD_ARM_BRANCHES_SURVIVE = PARENS_ALWAYS_TRANSPARENT
|
|
19
14
|
# Container literals in discarded position are eliminated from 3.3 on, but
|
|
20
15
|
# 3.3's compile.c elides a container whose contents are merely
|
|
21
16
|
# effect-free (`[x]`), while the Prism compiler demands fully static
|
|
@@ -42,8 +37,7 @@ module SimpleCov
|
|
|
42
37
|
|
|
43
38
|
# CRuby folds `if nil`, `if "x"`, and `if -> {}` but keeps a real branch
|
|
44
39
|
# for `if (nil)`, `if ("x")`, and `if (-> {})`, while every other literal
|
|
45
|
-
# folds parenthesized or not.
|
|
46
|
-
# always transparent.
|
|
40
|
+
# folds parenthesized or not.
|
|
47
41
|
PAREN_OPAQUE_TYPES = [
|
|
48
42
|
::Prism::NilNode, ::Prism::StringNode, ::Prism::LambdaNode, ::Prism::SourceFileNode
|
|
49
43
|
].freeze
|
|
@@ -58,42 +52,18 @@ module SimpleCov
|
|
|
58
52
|
::Prism::SourceLineNode, ::Prism::SourceFileNode, ::Prism::SourceEncodingNode, ::Prism::RegularExpressionNode
|
|
59
53
|
].freeze
|
|
60
54
|
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
# raise or run hooks is never eliminated and keeps the branch real.
|
|
64
|
-
PRISM_ERA_ELIMINABLE_READS = [
|
|
65
|
-
::Prism::LocalVariableReadNode, ::Prism::InstanceVariableReadNode, ::Prism::DefinedNode
|
|
66
|
-
].freeze
|
|
67
|
-
|
|
68
|
-
# simplecov:disable branch — which arm runs is fixed by the running Ruby's version
|
|
55
|
+
# Non-literal reads that are also eliminated when discarded. Anything that
|
|
56
|
+
# can raise or run hooks is never eliminated and keeps the branch real.
|
|
69
57
|
ELIMINABLE_READ_TYPES = [
|
|
70
|
-
::Prism::SelfNode,
|
|
71
|
-
|
|
58
|
+
::Prism::SelfNode, ::Prism::LocalVariableReadNode,
|
|
59
|
+
::Prism::InstanceVariableReadNode, ::Prism::DefinedNode
|
|
72
60
|
].freeze
|
|
73
|
-
# simplecov:enable branch
|
|
74
61
|
|
|
75
62
|
private
|
|
76
63
|
|
|
77
|
-
#
|
|
78
|
-
# instrumented branches before eliminating dead code) while its methods
|
|
79
|
-
# never register, so the dead arm is visited with method collection
|
|
80
|
-
# suppressed.
|
|
64
|
+
# `visit` is nil-safe, so a missing arm just visits nothing.
|
|
81
65
|
def visit_folded_arms(verdict, truthy_arm, falsy_arm)
|
|
82
|
-
|
|
83
|
-
visit(live)
|
|
84
|
-
visit_dead_arm(dead) if DEAD_ARM_BRANCHES_SURVIVE
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def visit_dead_arm(arm)
|
|
88
|
-
# Save/restore rather than set/clear: a fold nested inside this dead arm
|
|
89
|
-
# re-enters here and must not clear suppression for the rest of it.
|
|
90
|
-
previous = @suppress_methods
|
|
91
|
-
begin
|
|
92
|
-
@suppress_methods = true
|
|
93
|
-
visit(arm)
|
|
94
|
-
ensure
|
|
95
|
-
@suppress_methods = previous
|
|
96
|
-
end
|
|
66
|
+
visit(verdict.equal?(:truthy) ? truthy_arm : falsy_arm)
|
|
97
67
|
end
|
|
98
68
|
|
|
99
69
|
# `:truthy` or `:falsy` when the compiler folds, nil when it doesn't. The
|
|
@@ -115,8 +85,6 @@ module SimpleCov
|
|
|
115
85
|
def foldable?(node, unwrapped)
|
|
116
86
|
return false unless STATIC_CONDITION_TYPES.any? { |type| unwrapped.instance_of?(type) }
|
|
117
87
|
|
|
118
|
-
return true if PARENS_ALWAYS_TRANSPARENT
|
|
119
|
-
|
|
120
88
|
unwrapped.equal?(node) || PAREN_OPAQUE_TYPES.none? { |type| unwrapped.instance_of?(type) }
|
|
121
89
|
end
|
|
122
90
|
|
|
@@ -151,7 +119,6 @@ module SimpleCov
|
|
|
151
119
|
|
|
152
120
|
def static_container_literal?(node)
|
|
153
121
|
return true if STATIC_LITERAL_LEAF_TYPES.any? { |type| node.instance_of?(type) }
|
|
154
|
-
return false if PARENS_ALWAYS_TRANSPARENT
|
|
155
122
|
|
|
156
123
|
static_container?(node)
|
|
157
124
|
end
|
|
@@ -17,12 +17,8 @@ module SimpleCov
|
|
|
17
17
|
|
|
18
18
|
# `def name(...)` and `def self.name(...)` both produce DefNode. The class
|
|
19
19
|
# context is the surrounding lexical class or module, or `Object` at the top
|
|
20
|
-
# level, matching `Coverage`'s convention.
|
|
21
|
-
# dead arms, where nested branches stay instrumented but a `def` never
|
|
22
|
-
# registers.
|
|
20
|
+
# level, matching `Coverage`'s convention.
|
|
23
21
|
def visit_def_node(node)
|
|
24
|
-
return super if @suppress_methods
|
|
25
|
-
|
|
26
22
|
class_name = @class_stack.last || "Object"
|
|
27
23
|
key = [class_name, node.name, node.start_line, node.start_column, node.end_line, node.end_column]
|
|
28
24
|
@methods[key] = 0
|
|
@@ -12,7 +12,7 @@ module SimpleCov
|
|
|
12
12
|
# Ruby's `Coverage` reports. Tuple ids are sequential across the file like
|
|
13
13
|
# `Coverage`'s, but the numbering order can differ. That's fine: the
|
|
14
14
|
# combiners intern on source span and the report output drops ids, so
|
|
15
|
-
# nothing downstream compares them.
|
|
15
|
+
# nothing downstream compares them.
|
|
16
16
|
class Visitor < Prism::Visitor
|
|
17
17
|
include MethodCollector
|
|
18
18
|
include LocationConventions
|
|
@@ -31,7 +31,6 @@ module SimpleCov
|
|
|
31
31
|
@next_id = 0
|
|
32
32
|
@class_stack = []
|
|
33
33
|
@value_positions = nil
|
|
34
|
-
@suppress_methods = false
|
|
35
34
|
end
|
|
36
35
|
|
|
37
36
|
# On legacy Rubies the location of an empty branch arm depends on whether
|
|
@@ -48,11 +47,9 @@ module SimpleCov
|
|
|
48
47
|
# Coverage synthesizes a `:else` arm attributed to the whole condition's
|
|
49
48
|
# range, and so do we.
|
|
50
49
|
#
|
|
51
|
-
# A folded condition emits no tuple, and
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
# loaded run can produce. On 3.2 the dead arm is visited too, branches
|
|
55
|
-
# only.
|
|
50
|
+
# A folded condition emits no tuple, and only its live arm is descended
|
|
51
|
+
# into: the compiler eliminates the dead arm's entire subtree, so a branch
|
|
52
|
+
# or method nested there would be a phantom no loaded run can produce.
|
|
56
53
|
def visit_if_node(node)
|
|
57
54
|
verdict = folded_condition(node.predicate)
|
|
58
55
|
return visit_folded_arms(verdict, node.statements, PrismCompat.subsequent(node)) if verdict
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
require "prism"
|
|
5
|
-
rescue LoadError
|
|
6
|
-
# Prism isn't available on this Ruby (older than 3.3 without the gem).
|
|
7
|
-
# `StaticCoverageExtractor.available?` will return false and callers fall
|
|
8
|
-
# back to the previous "empty hashes" behavior.
|
|
9
|
-
end
|
|
3
|
+
require "prism"
|
|
10
4
|
|
|
11
5
|
module SimpleCov
|
|
12
6
|
# Static enumeration of the branches and methods Ruby's `Coverage` library
|
|
@@ -23,17 +17,11 @@ module SimpleCov
|
|
|
23
17
|
module StaticCoverageExtractor
|
|
24
18
|
extend self
|
|
25
19
|
|
|
26
|
-
def available?
|
|
27
|
-
defined?(Prism) ? true : false
|
|
28
|
-
end
|
|
29
|
-
|
|
30
20
|
# Parse `source` and return `{"branches" => {...}, "methods" => {...}}`
|
|
31
21
|
# matching the shape `Coverage.result[path]` produces. Returns nil on parse
|
|
32
|
-
# failure
|
|
33
|
-
#
|
|
22
|
+
# failure, which callers treat as "couldn't extract, fall back to empty
|
|
23
|
+
# hashes".
|
|
34
24
|
def call(source)
|
|
35
|
-
return nil unless available?
|
|
36
|
-
|
|
37
25
|
result = Prism.parse(source)
|
|
38
26
|
return nil if result.failure?
|
|
39
27
|
|
|
@@ -61,8 +49,8 @@ module SimpleCov
|
|
|
61
49
|
# Coincidental line-sharing between a real branch and an eval-generated one
|
|
62
50
|
# keeps both, an acceptable false-negative for an opt-in filter.
|
|
63
51
|
#
|
|
64
|
-
# Returns nil when
|
|
65
|
-
#
|
|
52
|
+
# Returns nil when parsing fails, signaling callers to keep every Coverage
|
|
53
|
+
# entry.
|
|
66
54
|
def real_source_positions(source)
|
|
67
55
|
extracted = call(source)
|
|
68
56
|
return nil unless extracted
|
|
@@ -87,8 +75,4 @@ module SimpleCov
|
|
|
87
75
|
end
|
|
88
76
|
end
|
|
89
77
|
|
|
90
|
-
|
|
91
|
-
# The `else` arm (Prism missing) is unreachable on engines where the dogfood
|
|
92
|
-
# report runs; the Visitor class only matters when Prism is loadable.
|
|
93
|
-
require_relative "static_coverage_extractor/visitor" if SimpleCov::StaticCoverageExtractor.available?
|
|
94
|
-
# simplecov:enable branch
|
|
78
|
+
require_relative "static_coverage_extractor/visitor"
|
data/lib/simplecov/version.rb
CHANGED
data/man/simplecov.1
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH SIMPLECOV 1 "2026-
|
|
1
|
+
.TH SIMPLECOV 1 "2026-09-12" "simplecov 1.3.0" "User Commands"
|
|
2
2
|
.SH NAME
|
|
3
3
|
simplecov \- coverage reports and questions from the terminal
|
|
4
4
|
.SH SYNOPSIS
|
|
@@ -168,8 +168,9 @@ line, branch, or method (default: line)
|
|
|
168
168
|
.B \-\-missing
|
|
169
169
|
Append the missed line ranges to each row
|
|
170
170
|
.TP
|
|
171
|
-
.B \-\-annotate
|
|
172
|
-
Emit
|
|
171
|
+
.B \-\-annotate KIND
|
|
172
|
+
Emit CI annotations instead of rows: github, gitlab, rdjson, azure, teamcity,
|
|
173
|
+
or buildkite
|
|
173
174
|
.TP
|
|
174
175
|
.B \-\-json
|
|
175
176
|
Emit results as a JSON array (for CI)
|
|
@@ -252,6 +253,10 @@ Exit non\-zero when patch coverage on any measured criterion is below N%
|
|
|
252
253
|
.B \-\-find\-renames
|
|
253
254
|
Follow a renamed file instead of counting the moved file as all\-new
|
|
254
255
|
.TP
|
|
256
|
+
.B \-\-annotate KIND
|
|
257
|
+
Emit CI annotations instead of rows: github, gitlab, rdjson, azure, teamcity,
|
|
258
|
+
or buildkite
|
|
259
|
+
.TP
|
|
255
260
|
.B \-\-json
|
|
256
261
|
Emit results as a JSON array (for CI)
|
|
257
262
|
.SS ratchet
|
data/sig/simplecov.rbs
CHANGED
|
@@ -123,6 +123,8 @@ module SimpleCov
|
|
|
123
123
|
|
|
124
124
|
def self.tracked_file_paths: () -> Array[String]
|
|
125
125
|
|
|
126
|
+
def self.warn_templates_tracked: (Array[String] templates) -> void
|
|
127
|
+
|
|
126
128
|
def self.inject_unloaded_files: (Hash[String, untyped] result, Array[String] candidate_paths, ?synthesize: bool?, ?lines: bool?) -> [Hash[String, untyped], Set[String]]
|
|
127
129
|
|
|
128
130
|
def self.collect_own_coverage: (standalone: bool) -> void
|
|
@@ -669,6 +671,7 @@ module SimpleCov
|
|
|
669
671
|
def source_file_for: (String path) -> SourceFile?
|
|
670
672
|
def coverage_for: (String path) -> Hash[criterion, CoverageStatistics]?
|
|
671
673
|
def groups: () -> Hash[String, FileList]
|
|
674
|
+
def configured_group?: (String group_name) -> bool
|
|
672
675
|
def format!: () -> untyped
|
|
673
676
|
def created_at: () -> Time
|
|
674
677
|
def command_name: () -> String
|
|
@@ -953,6 +956,8 @@ module SimpleCov
|
|
|
953
956
|
|
|
954
957
|
def method_statistics: () -> Hash[criterion, CoverageStatistics]
|
|
955
958
|
|
|
959
|
+
def unaccounted?: (SourceFile source_file, String table) -> bool
|
|
960
|
+
|
|
956
961
|
def coverage_statistics: (Array[Line | Branch | Method] covered, Array[Line | Branch | Method] missed, ?omitted: Integer, ?percent: Float?) -> CoverageStatistics
|
|
957
962
|
end
|
|
958
963
|
end
|
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.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Erik Berlin
|
|
@@ -44,6 +44,7 @@ files:
|
|
|
44
44
|
- lib/simplecov/cli/affected.rb
|
|
45
45
|
- lib/simplecov/cli/affected/changed_files.rb
|
|
46
46
|
- lib/simplecov/cli/affected/selection.rb
|
|
47
|
+
- lib/simplecov/cli/annotations.rb
|
|
47
48
|
- lib/simplecov/cli/badge.rb
|
|
48
49
|
- lib/simplecov/cli/badge/svg.rb
|
|
49
50
|
- lib/simplecov/cli/clean.rb
|
|
@@ -125,6 +126,11 @@ files:
|
|
|
125
126
|
- lib/simplecov/defaults.rb
|
|
126
127
|
- lib/simplecov/deprecation.rb
|
|
127
128
|
- lib/simplecov/directive.rb
|
|
129
|
+
- lib/simplecov/directive/erb.rb
|
|
130
|
+
- lib/simplecov/directive/haml.rb
|
|
131
|
+
- lib/simplecov/directive/indented_template.rb
|
|
132
|
+
- lib/simplecov/directive/slim.rb
|
|
133
|
+
- lib/simplecov/directive/template.rb
|
|
128
134
|
- lib/simplecov/exit_codes.rb
|
|
129
135
|
- lib/simplecov/exit_codes/baseline_check.rb
|
|
130
136
|
- lib/simplecov/exit_codes/check.rb
|
|
@@ -233,9 +239,9 @@ licenses:
|
|
|
233
239
|
metadata:
|
|
234
240
|
bug_tracker_uri: https://github.com/simplecov-ruby/simplecov/issues
|
|
235
241
|
changelog_uri: https://github.com/simplecov-ruby/simplecov/blob/main/CHANGELOG.md
|
|
236
|
-
documentation_uri: https://www.rubydoc.info/gems/simplecov/1.
|
|
242
|
+
documentation_uri: https://www.rubydoc.info/gems/simplecov/1.3.0
|
|
237
243
|
mailing_list_uri: https://groups.google.com/forum/#!forum/simplecov
|
|
238
|
-
source_code_uri: https://github.com/simplecov-ruby/simplecov/tree/v1.
|
|
244
|
+
source_code_uri: https://github.com/simplecov-ruby/simplecov/tree/v1.3.0
|
|
239
245
|
rubygems_mfa_required: 'true'
|
|
240
246
|
rdoc_options: []
|
|
241
247
|
require_paths:
|
|
@@ -244,7 +250,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
244
250
|
requirements:
|
|
245
251
|
- - ">="
|
|
246
252
|
- !ruby/object:Gem::Version
|
|
247
|
-
version: '3.
|
|
253
|
+
version: '3.3'
|
|
248
254
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
249
255
|
requirements:
|
|
250
256
|
- - ">="
|