simplecov 1.3.0 → 1.3.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d476eb77902a3475da8b97f4af5d61fc3ff9fd0829221447b78b82581ce7ddff
4
- data.tar.gz: 5ad815e1bdc6c010579d550fbea11aaa860c2c92f2f4f77cca3824a611f1b38e
3
+ metadata.gz: c96bd7c97b97593cd5f90f20315a755ddbb9e25bd2b43ca54cc19fdf6c0540ac
4
+ data.tar.gz: 23b1b6392bfcf9119b2a5c575adf54568c47ef2c5838e11cbb77bffd72d61666
5
5
  SHA512:
6
- metadata.gz: 7c3de21fa0caf77d6bb4154094be10b9bca3c30155b27b8a4679791bea6221dd09c9cd88eeb0ca3dc0e4959646459cf10d21ec8db234a1ad0f06197bdaa1a0e3
7
- data.tar.gz: 6264dd029949453bdaf3a94ad893b647f40424eccbae4d0e9485be26e28d6c5ff92bdcff0a4e0f7acbb95b9afa5710df4ef6bca1230c888cbe4f99122ce63cb0
6
+ metadata.gz: 3fb49f89386b147aa212d3b8814fb4afef021f394a2231c1e0649b5c35ea4e4ed14ba96cb9b8deacf1d6825629f56c55f900e3decaea287bff81f8a05d12b83f
7
+ data.tar.gz: a57ae0348b43869e6084866da885132c118acf88d83d967992d78aec222f8b6fab1ff6ab6572d3cc1ecdb2e701650aac9837ba45e963e64312ca8d3cdaaad1bd
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "English"
4
+
5
+ module SimpleCov
6
+ module CLI
7
+ module Affected
8
+ # Starts the runner at the repository root, since the selection's paths
9
+ # are relative to it, and answers the runner's exit status.
10
+ module SpawnedRunner
11
+ extend self
12
+
13
+ def call(command, root)
14
+ _, status = Process.wait2(spawn(*command, chdir: root))
15
+ status.exitstatus || 1
16
+ end
17
+ end
18
+
19
+ # JRuby on Windows answers Process.wait2 with a nil status and ignores
20
+ # the chdir option Kernel#system takes, so there the runner starts
21
+ # inside Dir.chdir, and Kernel#system reports its status.
22
+ module SystemRunner
23
+ extend self
24
+
25
+ def call(command, root)
26
+ ran = Dir.chdir(root) { system(*command) }
27
+ raise Errno::ENOENT, command.first if ran.nil?
28
+
29
+ $CHILD_STATUS.exitstatus || 1
30
+ end
31
+ end
32
+
33
+ # simplecov:disable branch — fixed by the running engine and OS
34
+ RUNNER = (RUBY_ENGINE.eql?("jruby") && Gem.win_platform?) ? SystemRunner : SpawnedRunner
35
+ # simplecov:enable branch
36
+ end
37
+ end
38
+ end
@@ -5,6 +5,7 @@ require "optparse"
5
5
  require_relative "command_helpers"
6
6
  require_relative "tests"
7
7
  require_relative "affected/changed_files"
8
+ require_relative "affected/runner"
8
9
  require_relative "affected/selection"
9
10
 
10
11
  module SimpleCov
@@ -133,12 +134,10 @@ module SimpleCov
133
134
  run_command(opts.fetch(:run) + selection.fetch(:tests), opts.fetch(:root), stderr)
134
135
  end
135
136
 
136
- # The selection's paths are relative to the repository root, so the runner
137
- # starts there. The command is named explicitly in the failure because not
138
- # every engine's exception message carries it.
137
+ # The command is named explicitly in the failure because not every
138
+ # engine's exception message carries it.
139
139
  def run_command(command, root, stderr)
140
- _, status = Process.wait2(spawn(*command, chdir: root))
141
- status.exitstatus || 1
140
+ RUNNER.call(command, root)
142
141
  rescue SystemCallError => e
143
142
  stderr.puts("simplecov affected: cannot run #{command.first.inspect} (#{e})")
144
143
  127
@@ -3,6 +3,7 @@
3
3
  require "fileutils"
4
4
  require "optparse"
5
5
  require_relative "command_helpers"
6
+ require_relative "real_path"
6
7
 
7
8
  module SimpleCov
8
9
  module CLI
@@ -68,7 +69,7 @@ module SimpleCov
68
69
  end
69
70
 
70
71
  def canonical_path(path)
71
- File.realpath(path)
72
+ REAL_PATHS.realpath(path)
72
73
  rescue SystemCallError
73
74
  File.expand_path(path)
74
75
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../coverage_json"
4
+ require_relative "real_path"
4
5
 
5
6
  module SimpleCov
6
7
  module CLI
@@ -55,7 +56,7 @@ module SimpleCov
55
56
 
56
57
  # A key whose file no longer exists keeps its literal spelling.
57
58
  def normalize(key)
58
- File.realdirpath(key)
59
+ REAL_PATHS.realdirpath(key)
59
60
  rescue SystemCallError
60
61
  key
61
62
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ module CLI
5
+ # The JDK's answer to File.realpath and File.realdirpath, for JRuby. On
6
+ # Windows JRuby's own versions follow no symlinks, and realdirpath accepts
7
+ # a directory that does not exist, so the CLI's containment and identity
8
+ # checks would compare paths that were never resolved. Path#toRealPath has
9
+ # neither problem on any OS. The path is expanded first because the JDK
10
+ # resolves a relative path against the JVM's working directory, which
11
+ # Dir.chdir does not move.
12
+ #
13
+ # simplecov:disable — JRuby-only; this suite's coverage is measured on CRuby
14
+ module JDKRealPath
15
+ extend self
16
+
17
+ # mutant:disable — JRuby-only, unreachable from the engine mutant runs on
18
+ def realpath(path)
19
+ expanded = File.expand_path(path)
20
+ java.nio.file.Paths.get(expanded).toRealPath.toString.tr("\\", "/") # steep:ignore NoMethod
21
+ rescue java.nio.file.NoSuchFileException, java.nio.file.NotDirectoryException # steep:ignore NoMethod
22
+ raise Errno::ENOENT, expanded
23
+ rescue java.nio.file.AccessDeniedException # steep:ignore NoMethod
24
+ raise Errno::EACCES, expanded
25
+ rescue java.nio.file.InvalidPathException # steep:ignore NoMethod
26
+ raise Errno::EINVAL, expanded
27
+ end
28
+
29
+ # mutant:disable — JRuby-only, unreachable from the engine mutant runs on
30
+ def realdirpath(path)
31
+ expanded = File.expand_path(path)
32
+ return realpath(expanded) if File.exist?(expanded)
33
+
34
+ File.join(realpath(File.dirname(expanded)), File.basename(expanded))
35
+ end
36
+ end
37
+ # simplecov:enable
38
+
39
+ REAL_PATHS = RUBY_ENGINE.eql?("jruby") ? JDKRealPath : File # simplecov:disable branch — fixed by the running engine
40
+ end
41
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../real_path"
4
+
3
5
  module SimpleCov
4
6
  module CLI
5
7
  module Serve
@@ -93,7 +95,7 @@ module SimpleCov
93
95
  # added, it must happen BEFORE the `inside?` check.
94
96
  def resolve(request_path, root)
95
97
  path = request_path.split("?").first.to_s.delete_prefix("/")
96
- absolute_root = File.realpath(root)
98
+ absolute_root = REAL_PATHS.realpath(root)
97
99
  candidate = File.expand_path(path, absolute_root)
98
100
  # Rejected before touching disk, so traversal and absolute-path attempts
99
101
  # are 403, not 404.
@@ -104,10 +106,10 @@ module SimpleCov
104
106
 
105
107
  # Symlinks are resolved last and re-checked: a file inside root could be
106
108
  # a symlink pointing outside.
107
- real = File.realpath(candidate)
109
+ real = REAL_PATHS.realpath(candidate)
108
110
  inside?(real, absolute_root) ? real : :forbidden
109
111
  rescue Errno::ENOENT
110
- # TOCTOU: candidate vanished between File.file? and File.realpath.
112
+ # TOCTOU: candidate vanished between File.file? and resolving it.
111
113
  nil
112
114
  end
113
115
 
data/lib/simplecov/cli.rb CHANGED
@@ -4,6 +4,7 @@ require "optparse"
4
4
  require_relative "version"
5
5
  require_relative "color"
6
6
  require_relative "cli/dotfile"
7
+ require_relative "cli/real_path"
7
8
  require_relative "cli/badge"
8
9
  require_relative "cli/clean"
9
10
  require_relative "cli/completions"
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "prism"
3
4
  require_relative "prism_compat"
4
5
  require_relative "condition_folding"
5
6
  require_relative "location_conventions"
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "prism"
4
-
5
3
  module SimpleCov
6
4
  # Static enumeration of the branches and methods Ruby's `Coverage` library
7
5
  # would have reported if a file had been loaded with `branches: true` /
@@ -14,14 +12,21 @@ module SimpleCov
14
12
  # Position info comes from Prism's reported source locations; it doesn't
15
13
  # always match `Coverage`'s byte-for-byte, but lines are reliable and
16
14
  # downstream consumers that key off line numbers see the data they expect.
15
+ #
16
+ # Prism loads on the first extraction rather than with SimpleCov, so a
17
+ # line-only run never needs it. Where it cannot load at all (JRuby on
18
+ # Windows, whose FFI backend fails to open libprism), extraction answers nil
19
+ # like any other failure.
17
20
  module StaticCoverageExtractor
18
21
  extend self
19
22
 
20
23
  # Parse `source` and return `{"branches" => {...}, "methods" => {...}}`
21
- # matching the shape `Coverage.result[path]` produces. Returns nil on parse
22
- # failure, which callers treat as "couldn't extract, fall back to empty
23
- # hashes".
24
+ # matching the shape `Coverage.result[path]` produces. Returns nil when
25
+ # Prism cannot load or parsing fails, which callers treat as "couldn't
26
+ # extract, fall back to empty hashes".
24
27
  def call(source)
28
+ return nil unless prism_loaded?
29
+
25
30
  result = Prism.parse(source)
26
31
  return nil if result.failure?
27
32
 
@@ -49,8 +54,8 @@ module SimpleCov
49
54
  # Coincidental line-sharing between a real branch and an eval-generated one
50
55
  # keeps both, an acceptable false-negative for an opt-in filter.
51
56
  #
52
- # Returns nil when parsing fails, signaling callers to keep every Coverage
53
- # entry.
57
+ # Returns nil when Prism cannot load or parsing fails, signaling callers to
58
+ # keep every Coverage entry.
54
59
  def real_source_positions(source)
55
60
  extracted = call(source)
56
61
  return nil unless extracted
@@ -61,6 +66,14 @@ module SimpleCov
61
66
  }
62
67
  end
63
68
 
69
+ # Memoized, because a failed load leaves nothing in $LOADED_FEATURES and
70
+ # would otherwise be retried for every file.
71
+ def prism_loaded?
72
+ return @prism_loaded if instance_variable_defined?(:@prism_loaded)
73
+
74
+ @prism_loaded = load_prism
75
+ end
76
+
64
77
  # Both keys carry their start line third, after the parts that vary between
65
78
  # recordings. Read through a parameter list rather than an index, because
66
79
  # every spelling of an index answers the same for a tuple of this fixed
@@ -72,7 +85,14 @@ module SimpleCov
72
85
  def method_identity(_class_name, name, start_line, *)
73
86
  [name, start_line]
74
87
  end
88
+
89
+ private
90
+
91
+ def load_prism
92
+ require_relative "static_coverage_extractor/visitor"
93
+ true
94
+ rescue LoadError
95
+ false
96
+ end
75
97
  end
76
98
  end
77
-
78
- require_relative "static_coverage_extractor/visitor"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCov
4
- VERSION = "1.3.0"
4
+ VERSION = "1.3.1"
5
5
  end
@@ -64,20 +64,29 @@ module SimpleCov
64
64
  segments.fetch(-2).to_sym
65
65
  end
66
66
 
67
- # The single place ActionView is named. The handler lookup is the one the
68
- # resolver performs, so a template language the project has registered a
69
- # handler for compiles here the way it does when rendered. Nil when
70
- # nothing is registered for the extension, which is how a default glob
71
- # naming `.haml` behaves in a project that has no Haml: ActionView would
72
- # hand back the raw handler and the template would report as static text.
67
+ # Nil when nothing is registered for the extension, which is how a default
68
+ # glob naming `.haml` behaves in a project that has no Haml.
69
+ # `handler_for_extension` is not that lookup: it falls back to the raw
70
+ # handler, and the template would report as static text.
73
71
  def build_template(path, source)
74
72
  extension = File.extname(path).delete_prefix(".")
75
- handler = ActionView::Template.registered_template_handler(extension)
73
+ handler = handler_for(extension)
76
74
  return nil unless handler
77
75
 
78
76
  no_locals = [] #: Array[Symbol]
79
77
  ActionView::Template.new(source, path, handler, locals: no_locals, format: format_for(path))
80
78
  end
79
+
80
+ # Rails main removed `Template.registered_template_handler` when the
81
+ # registry moved onto `Template::Handlers`. Read that hash directly so an
82
+ # unregistered extension stays nil. Older Rails still have the method.
83
+ def handler_for(extension)
84
+ if ActionView::Template.respond_to?(:registered_template_handler)
85
+ ActionView::Template.registered_template_handler(extension)
86
+ else
87
+ ActionView::Template::Handlers.template_handlers[extension.to_sym]
88
+ end
89
+ end
81
90
  end
82
91
  end
83
92
  end
data/man/simplecov.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH SIMPLECOV 1 "2026-09-12" "simplecov 1.3.0" "User Commands"
1
+ .TH SIMPLECOV 1 "2026-09-22" "simplecov 1.3.1" "User Commands"
2
2
  .SH NAME
3
3
  simplecov \- coverage reports and questions from the terminal
4
4
  .SH SYNOPSIS
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.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Berlin
@@ -43,6 +43,7 @@ files:
43
43
  - lib/simplecov/cli.rb
44
44
  - lib/simplecov/cli/affected.rb
45
45
  - lib/simplecov/cli/affected/changed_files.rb
46
+ - lib/simplecov/cli/affected/runner.rb
46
47
  - lib/simplecov/cli/affected/selection.rb
47
48
  - lib/simplecov/cli/annotations.rb
48
49
  - lib/simplecov/cli/badge.rb
@@ -68,6 +69,7 @@ files:
68
69
  - lib/simplecov/cli/patch/output.rb
69
70
  - lib/simplecov/cli/ratchet.rb
70
71
  - lib/simplecov/cli/ratchet/output.rb
72
+ - lib/simplecov/cli/real_path.rb
71
73
  - lib/simplecov/cli/report.rb
72
74
  - lib/simplecov/cli/run.rb
73
75
  - lib/simplecov/cli/serve.rb
@@ -239,9 +241,9 @@ licenses:
239
241
  metadata:
240
242
  bug_tracker_uri: https://github.com/simplecov-ruby/simplecov/issues
241
243
  changelog_uri: https://github.com/simplecov-ruby/simplecov/blob/main/CHANGELOG.md
242
- documentation_uri: https://www.rubydoc.info/gems/simplecov/1.3.0
244
+ documentation_uri: https://www.rubydoc.info/gems/simplecov/1.3.1
243
245
  mailing_list_uri: https://groups.google.com/forum/#!forum/simplecov
244
- source_code_uri: https://github.com/simplecov-ruby/simplecov/tree/v1.3.0
246
+ source_code_uri: https://github.com/simplecov-ruby/simplecov/tree/v1.3.1
245
247
  rubygems_mfa_required: 'true'
246
248
  rdoc_options: []
247
249
  require_paths:
@@ -257,7 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
257
259
  - !ruby/object:Gem::Version
258
260
  version: '0'
259
261
  requirements: []
260
- rubygems_version: 4.0.20
262
+ rubygems_version: 4.0.21
261
263
  specification_version: 4
262
264
  summary: Code coverage for Ruby
263
265
  test_files: []