minitest-heat 2.2.0 → 2.2.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 +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +1 -1
- data/lib/minitest/heat/hit.rb +1 -1
- data/lib/minitest/heat/issue.rb +28 -4
- data/lib/minitest/heat/location.rb +3 -3
- data/lib/minitest/heat/output/backtrace.rb +1 -1
- data/lib/minitest/heat/output/map.rb +1 -1
- data/lib/minitest/heat/source.rb +8 -3
- data/lib/minitest/heat/version.rb +1 -1
- data/lib/minitest/heat.rb +25 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5002a70b8781d74165f357718b769508a49fdb1f0de8c5321c7d2001c0e5a35c
|
|
4
|
+
data.tar.gz: 964154c41295fbbdf2ddd3d1111b917649d6e27ed9e741dabecab3701d81f3b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de8d4e9468a3cd311517cb4c618d60c0b619f456df3f0f04a1184fd9c6f5aaa5bbb27b7790ad789f917d5f76e3f8dfb23bef247ec9010ced9b417f0c01476dc2
|
|
7
|
+
data.tar.gz: c3deb15724cf7f10656fb9ca5f2ad76079cd4c4ec3aa3a1a9eb907c246bad9197232ea43f78466456b47fa4d3414644613175390946678051f28247c92e33f69
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [2.2.1] - 2026-09-24
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Display failures normally when a relevant source line contains non-ASCII text under a non-UTF-8 locale (such as `LC_ALL=C` or an unrecognized `LC_ALL`), or contains bytes that aren't valid UTF-8, instead of reporting that Heat couldn't display the failure. Source files are read as UTF-8 and invalid bytes are replaced, so a file that declares a non-UTF-8 encoding shows `�` for characters that aren't valid UTF-8.
|
|
7
|
+
- Under a non-UTF-8 locale, report failures whose messages include non-ASCII text read at runtime (such as command output or file contents) instead of losing them, and keep `--heat-json` output valid when they occur.
|
|
8
|
+
- Under a non-UTF-8 locale, report every failure in a project whose path contains non-ASCII characters, including errors that Minitest's own reporter can't display in that situation.
|
|
9
|
+
|
|
3
10
|
## [2.2.0] - 2026-09-16
|
|
4
11
|
|
|
5
12
|
### Added
|
data/Gemfile.lock
CHANGED
data/lib/minitest/heat/hit.rb
CHANGED
data/lib/minitest/heat/issue.rb
CHANGED
|
@@ -35,7 +35,9 @@ module Minitest
|
|
|
35
35
|
# @return [Issue] the instance of the issue to use for examining the result
|
|
36
36
|
def self.from_result(result)
|
|
37
37
|
# Not all results are failures, so we use the safe navigation operator
|
|
38
|
-
|
|
38
|
+
failure = result.failure
|
|
39
|
+
unexpected = failure.is_a?(Minitest::UnexpectedError)
|
|
40
|
+
exception = unexpected ? failure.error : failure&.exception
|
|
39
41
|
|
|
40
42
|
new(
|
|
41
43
|
assertions: result.assertions,
|
|
@@ -46,11 +48,33 @@ module Minitest
|
|
|
46
48
|
passed: result.passed?,
|
|
47
49
|
error: result.error?,
|
|
48
50
|
skipped: result.skipped?,
|
|
49
|
-
message: exception&.message,
|
|
51
|
+
message: unexpected ? unexpected_error_message(exception) : exception&.message,
|
|
50
52
|
backtrace: exception&.backtrace
|
|
51
53
|
)
|
|
52
54
|
end
|
|
53
55
|
|
|
56
|
+
# Matches the format of `Minitest::UnexpectedError#message` without calling it. Minitest
|
|
57
|
+
# builds that message with a binary regular expression, which raises when the project path
|
|
58
|
+
# and the backtrace contain non-ASCII characters under a non-UTF-8 locale.
|
|
59
|
+
# @param error [Exception] the exception wrapped by the unexpected error
|
|
60
|
+
#
|
|
61
|
+
# @return [String] the exception class, message, and filtered backtrace
|
|
62
|
+
def self.unexpected_error_message(error)
|
|
63
|
+
lines = (error.backtrace || []).map { |line| Heat.utf8(line) }
|
|
64
|
+
backtrace = Minitest.filter_backtrace(lines).map { |line| line.gsub(minitest_base_pattern, '') }
|
|
65
|
+
|
|
66
|
+
"#{error.class}: #{Heat.utf8(error.message)}\n #{backtrace.join("\n ")}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Minitest's pattern for the directory it loaded from, rebuilt as UTF-8 so it can match
|
|
70
|
+
# UTF-8 backtraces. Reusing it strips the same prefix as Minitest even if a test changes
|
|
71
|
+
# the working directory.
|
|
72
|
+
#
|
|
73
|
+
# @return [Regexp] the UTF-8 equivalent of `Minitest::UnexpectedError::BASE_RE`
|
|
74
|
+
def self.minitest_base_pattern
|
|
75
|
+
@minitest_base_pattern ||= Regexp.new(Heat.utf8(Minitest::UnexpectedError::BASE_RE.source))
|
|
76
|
+
end
|
|
77
|
+
|
|
54
78
|
# Creates an instance of Issue. In general, the `from_result` approach will be more convenient
|
|
55
79
|
# for standard usage, but for lower-level purposes like testing, the initializer provides3
|
|
56
80
|
# more fine-grained control
|
|
@@ -67,10 +91,10 @@ module Minitest
|
|
|
67
91
|
#
|
|
68
92
|
# @return [type] [description]
|
|
69
93
|
def initialize(assertions: 1, test_location: ['Unrecognized Test File', 1], backtrace: [], execution_time: 0.0, message: nil, test_class: nil, test_identifier: nil, passed: false, error: false, skipped: false)
|
|
70
|
-
@message = message
|
|
94
|
+
@message = Heat.utf8(message)
|
|
71
95
|
|
|
72
96
|
@assertions = Integer(assertions)
|
|
73
|
-
@locations = Locations.new(test_location, backtrace)
|
|
97
|
+
@locations = Locations.new(test_location, backtrace&.map { |line| Heat.utf8(line) })
|
|
74
98
|
|
|
75
99
|
@test_class = test_class
|
|
76
100
|
@test_identifier = test_identifier
|
|
@@ -68,9 +68,9 @@ module Minitest
|
|
|
68
68
|
#
|
|
69
69
|
# @return [Pathname] a pathname instance for the relevant file
|
|
70
70
|
def pathname
|
|
71
|
-
Pathname(raw_pathname)
|
|
71
|
+
Pathname(Heat.utf8(File.expand_path(raw_pathname)))
|
|
72
72
|
rescue ArgumentError
|
|
73
|
-
Pathname(
|
|
73
|
+
Pathname(Heat.project_root)
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
# A safe interface to getting a string representing the path portion of the file
|
|
@@ -163,7 +163,7 @@ module Minitest
|
|
|
163
163
|
|
|
164
164
|
private
|
|
165
165
|
|
|
166
|
-
def project_root_dir =
|
|
166
|
+
def project_root_dir = Heat.project_root
|
|
167
167
|
|
|
168
168
|
def seconds_ago = (Time.now - mtime).to_i
|
|
169
169
|
end
|
|
@@ -102,7 +102,7 @@ module Minitest
|
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def file_and_line_number_tokens(location)
|
|
105
|
-
style = location.to_s.include?(
|
|
105
|
+
style = location.to_s.include?(Heat.project_root) ? :bold : :muted
|
|
106
106
|
[
|
|
107
107
|
[style, location.filename],
|
|
108
108
|
[:muted, ':'],
|
|
@@ -118,7 +118,7 @@ module Minitest
|
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
def pathname(hit)
|
|
121
|
-
directory = hit.pathname.dirname.to_s.delete_prefix("#{
|
|
121
|
+
directory = hit.pathname.dirname.to_s.delete_prefix("#{Heat.project_root}/")
|
|
122
122
|
filename = hit.pathname.basename.to_s
|
|
123
123
|
|
|
124
124
|
[
|
data/lib/minitest/heat/source.rb
CHANGED
|
@@ -44,14 +44,19 @@ module Minitest
|
|
|
44
44
|
|
|
45
45
|
# Reads (and chomps) the lines of the target file
|
|
46
46
|
#
|
|
47
|
-
#
|
|
47
|
+
# Ruby labels file contents with the locale's encoding, which is US-ASCII under `LC_ALL=C` or
|
|
48
|
+
# an unrecognized locale. Ruby source is UTF-8 by default, so lines are read as UTF-8
|
|
49
|
+
# regardless of locale, and bytes that aren't valid UTF-8 are replaced so that displaying
|
|
50
|
+
# them can't raise.
|
|
51
|
+
#
|
|
52
|
+
# @return [Array<String>] the chomped lines of the file as valid UTF-8
|
|
48
53
|
def file_lines
|
|
49
|
-
@raw_lines ||= File.readlines(filename, chomp: true)
|
|
54
|
+
@raw_lines ||= File.readlines(filename, chomp: true, mode: 'rb').map { |line| Heat.utf8(line) }
|
|
50
55
|
# Remove trailing empty lines, checking for nil/empty safely
|
|
51
56
|
@raw_lines.pop while @raw_lines.any? && @raw_lines.last&.strip.to_s.empty?
|
|
52
57
|
|
|
53
58
|
@raw_lines
|
|
54
|
-
rescue Errno::ENOENT, Errno::EACCES, Errno::EISDIR, IOError
|
|
59
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::EISDIR, IOError
|
|
55
60
|
# Occasionally, for a variety of reasons, a file can't be read. In those cases, it's best to
|
|
56
61
|
# return no source code lines rather than have the test suite raise an error unrelated to
|
|
57
62
|
# the code being tested because that gets confusing.
|
data/lib/minitest/heat.rb
CHANGED
|
@@ -48,5 +48,30 @@ module Minitest
|
|
|
48
48
|
def self.configure
|
|
49
49
|
yield(configuration)
|
|
50
50
|
end
|
|
51
|
+
|
|
52
|
+
# Ruby labels text from outside Ruby source (paths, file contents, command output) with the
|
|
53
|
+
# locale's encoding, which is US-ASCII or binary under `LC_ALL=C` or an unrecognized locale.
|
|
54
|
+
# That text is overwhelmingly UTF-8, so unlabeled bytes are read as UTF-8 and text in any other
|
|
55
|
+
# encoding is converted. Anything that isn't valid UTF-8 is replaced so that comparing,
|
|
56
|
+
# displaying or serializing the text can't raise.
|
|
57
|
+
#
|
|
58
|
+
# @param text [String, nil] text from outside Ruby source
|
|
59
|
+
#
|
|
60
|
+
# @return [String, nil] valid UTF-8 text
|
|
61
|
+
def self.utf8(text)
|
|
62
|
+
return text unless text.is_a?(String)
|
|
63
|
+
|
|
64
|
+
case text.encoding
|
|
65
|
+
when Encoding::UTF_8, Encoding::US_ASCII, Encoding::BINARY
|
|
66
|
+
String.new(text, encoding: Encoding::UTF_8).scrub
|
|
67
|
+
else
|
|
68
|
+
text.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# The directory the tests run from, which identifies project files
|
|
73
|
+
#
|
|
74
|
+
# @return [String] the current working directory as UTF-8
|
|
75
|
+
def self.project_root = utf8(Dir.pwd)
|
|
51
76
|
end
|
|
52
77
|
end
|