minitest-heat 0.0.10 → 0.0.11
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/Gemfile.lock +6 -6
- data/lib/minitest/heat/backtrace/line_parser.rb +25 -0
- data/lib/minitest/heat/backtrace.rb +21 -59
- data/lib/minitest/heat/hit.rb +18 -6
- data/lib/minitest/heat/issue.rb +11 -11
- data/lib/minitest/heat/location.rb +113 -132
- data/lib/minitest/heat/locations.rb +105 -0
- data/lib/minitest/heat/map.rb +3 -4
- data/lib/minitest/heat/output/backtrace.rb +46 -53
- data/lib/minitest/heat/output/issue.rb +24 -36
- data/lib/minitest/heat/output/map.rb +88 -28
- data/lib/minitest/heat/output.rb +11 -7
- data/lib/minitest/heat/results.rb +11 -6
- data/lib/minitest/heat/source.rb +1 -1
- data/lib/minitest/heat/version.rb +1 -1
- data/lib/minitest/heat.rb +1 -0
- data/lib/minitest/heat_plugin.rb +1 -1
- data/lib/minitest/heat_reporter.rb +12 -5
- metadata +5 -4
- data/lib/minitest/heat/backtrace/line.rb +0 -118
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a2dd8d434e13e9719addb1a2afa9f25c751ca6e5712c752244afb1823ac4740
|
4
|
+
data.tar.gz: 722f33b1145d79510f9007b54663198972c1ce4461a7a79c14a7bfdeb190721e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 955b848541141572e94aeff4b02586253f70db8e313af111e54b91c3e8a64c7c44c0f0f90c672f699f9296dc5f691ea04c7852eb948f6ab680173b8261512eee
|
7
|
+
data.tar.gz: 9c3085267bea53a6734a085c982fd8348442715036ccf2b636de9c997f3dc14662e25a709faab1b4425ffb46ceeb109f43672336fadf0bca7c4e0cbbccd0463c
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
minitest-heat (0.0.
|
4
|
+
minitest-heat (0.0.11)
|
5
5
|
minitest
|
6
6
|
|
7
7
|
GEM
|
@@ -10,7 +10,7 @@ GEM
|
|
10
10
|
ast (2.4.2)
|
11
11
|
awesome_print (1.9.2)
|
12
12
|
coderay (1.1.3)
|
13
|
-
dead_end (
|
13
|
+
dead_end (3.0.2)
|
14
14
|
docile (1.4.0)
|
15
15
|
method_source (1.0.0)
|
16
16
|
minitest (5.14.4)
|
@@ -24,7 +24,7 @@ GEM
|
|
24
24
|
rake (12.3.3)
|
25
25
|
regexp_parser (2.1.1)
|
26
26
|
rexml (3.2.5)
|
27
|
-
rubocop (1.22.
|
27
|
+
rubocop (1.22.3)
|
28
28
|
parallel (~> 1.10)
|
29
29
|
parser (>= 3.0.0.0)
|
30
30
|
rainbow (>= 2.2.2, < 4.0)
|
@@ -33,9 +33,9 @@ GEM
|
|
33
33
|
rubocop-ast (>= 1.12.0, < 2.0)
|
34
34
|
ruby-progressbar (~> 1.7)
|
35
35
|
unicode-display_width (>= 1.4.0, < 3.0)
|
36
|
-
rubocop-ast (1.
|
36
|
+
rubocop-ast (1.13.0)
|
37
37
|
parser (>= 3.0.1.1)
|
38
|
-
rubocop-minitest (0.15.
|
38
|
+
rubocop-minitest (0.15.2)
|
39
39
|
rubocop (>= 0.90, < 2.0)
|
40
40
|
rubocop-rake (0.6.0)
|
41
41
|
rubocop (~> 1.0)
|
@@ -64,4 +64,4 @@ DEPENDENCIES
|
|
64
64
|
simplecov
|
65
65
|
|
66
66
|
BUNDLED WITH
|
67
|
-
2.
|
67
|
+
2.2.30
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Minitest
|
6
|
+
module Heat
|
7
|
+
class Backtrace
|
8
|
+
# Represents a line from a backtrace to provide more convenient access to information about
|
9
|
+
# the relevant file and line number for displaying in test results
|
10
|
+
module LineParser
|
11
|
+
# Parses a line from a backtrace in order to convert it to usable components
|
12
|
+
def self.read(raw_text)
|
13
|
+
raw_pathname, raw_line_number, raw_container = raw_text.split(':')
|
14
|
+
raw_container = raw_container.delete_prefix('in `').delete_suffix("'")
|
15
|
+
|
16
|
+
::Minitest::Heat::Location.new(
|
17
|
+
pathname: raw_pathname,
|
18
|
+
line_number: raw_line_number,
|
19
|
+
container: raw_container
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'backtrace/
|
3
|
+
require_relative 'backtrace/line_parser'
|
4
4
|
|
5
5
|
module Minitest
|
6
6
|
module Heat
|
@@ -9,7 +9,7 @@ module Minitest
|
|
9
9
|
attr_reader :raw_backtrace
|
10
10
|
|
11
11
|
# Creates a more flexible backtrace data structure by parsing the lines of the backtrace to
|
12
|
-
# extract
|
12
|
+
# extract specific subsets of lines for investigating the offending files and line numbers
|
13
13
|
# @param raw_backtrace [Array] the array of lines from the backtrace
|
14
14
|
#
|
15
15
|
# @return [self]
|
@@ -24,80 +24,42 @@ module Minitest
|
|
24
24
|
raw_backtrace.empty?
|
25
25
|
end
|
26
26
|
|
27
|
-
#
|
28
|
-
# dependency or the Ruby core libraries
|
27
|
+
# All lines of the backtrace converted to Backtrace::LineParser's
|
29
28
|
#
|
30
|
-
# @return [Line] the
|
31
|
-
def
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
# The final location from within the project exposed in the backtrace. Could be test files or
|
36
|
-
# source code files
|
37
|
-
#
|
38
|
-
# @return [Line] the final project location from the backtrace parsed as a Backtrace::Line
|
39
|
-
def final_project_location
|
40
|
-
project_entries.first
|
41
|
-
end
|
42
|
-
|
43
|
-
# The most recently modified location from within the project
|
44
|
-
#
|
45
|
-
# @return [Line] the most recently modified project location from the backtrace parsed as a
|
46
|
-
# Backtrace::Line
|
47
|
-
def freshest_project_location
|
48
|
-
recently_modified_entries.first
|
49
|
-
end
|
29
|
+
# @return [Line] the full set of backtrace lines parsed as Backtrace::LineParser instances
|
30
|
+
def locations
|
31
|
+
return [] if raw_backtrace.nil?
|
50
32
|
|
51
|
-
|
52
|
-
#
|
53
|
-
# @return [Line] the final source code location from the backtrace parsed as a Backtrace::Line
|
54
|
-
def final_source_code_location
|
55
|
-
source_code_entries.first
|
33
|
+
@locations ||= raw_backtrace.map { |entry| Backtrace::LineParser.read(entry) }
|
56
34
|
end
|
57
35
|
|
58
|
-
#
|
36
|
+
# All entries from the backtrace within the project and sorted with the most recently modified
|
37
|
+
# files at the beginning
|
59
38
|
#
|
60
|
-
# @return [Line] the
|
61
|
-
def
|
62
|
-
|
39
|
+
# @return [Line] the sorted backtrace lines from the project parsed as Backtrace::LineParser's
|
40
|
+
def recently_modified_locations
|
41
|
+
@recently_modified_locations ||= project_locations.sort_by(&:mtime).reverse
|
63
42
|
end
|
64
43
|
|
65
44
|
# All entries from the backtrace that are files within the project
|
66
45
|
#
|
67
|
-
# @return [Line] the backtrace lines from within the project parsed as Backtrace::
|
68
|
-
def
|
69
|
-
@
|
70
|
-
end
|
71
|
-
|
72
|
-
# All entries from the backtrace within the project and sorted with the most recently modified
|
73
|
-
# files at the beginning
|
74
|
-
#
|
75
|
-
# @return [Line] the sorted backtrace lines from the project parsed as Backtrace::Line's
|
76
|
-
def recently_modified_entries
|
77
|
-
@recently_modified_entries ||= project_entries.sort_by(&:mtime).reverse
|
46
|
+
# @return [Line] the backtrace lines from within the project parsed as Backtrace::LineParser's
|
47
|
+
def project_locations
|
48
|
+
@project_locations ||= locations.select(&:project_file?)
|
78
49
|
end
|
79
50
|
|
80
51
|
# All entries from the backtrace within the project tests
|
81
52
|
#
|
82
|
-
# @return [Line] the backtrace lines from within the project tests parsed as Backtrace::
|
83
|
-
def
|
84
|
-
@
|
53
|
+
# @return [Line] the backtrace lines from within the project tests parsed as Backtrace::LineParser's
|
54
|
+
def test_locations
|
55
|
+
@test_locations ||= project_locations.select(&:test_file?)
|
85
56
|
end
|
86
57
|
|
87
58
|
# All source code entries from the backtrace (i.e. excluding tests)
|
88
59
|
#
|
89
|
-
# @return [Line] the backtrace lines from within the source code parsed as Backtrace::
|
90
|
-
def
|
91
|
-
@
|
92
|
-
end
|
93
|
-
|
94
|
-
# All lines of the backtrace converted to Backtrace::Line's
|
95
|
-
#
|
96
|
-
# @return [Line] the full set of backtrace lines parsed as Backtrace::Line instances
|
97
|
-
def parsed_entries
|
98
|
-
return [] if raw_backtrace.nil?
|
99
|
-
|
100
|
-
@parsed_entries ||= raw_backtrace.map { |entry| Backtrace::Line.parse_backtrace(entry) }
|
60
|
+
# @return [Line] the backtrace lines from within the source code parsed as Backtrace::LineParser's
|
61
|
+
def source_code_locations
|
62
|
+
@source_code_locations ||= project_locations.select(&:source_code_file?)
|
101
63
|
end
|
102
64
|
end
|
103
65
|
end
|
data/lib/minitest/heat/hit.rb
CHANGED
@@ -5,8 +5,10 @@ require 'forwardable'
|
|
5
5
|
module Minitest
|
6
6
|
module Heat
|
7
7
|
# Kind of like an issue, but instead of focusing on a failing test, it covers all issues for a
|
8
|
-
# given file to build a heat map of the affected files
|
8
|
+
# given file to build a heat map of the affected files and line numbers
|
9
9
|
class Hit
|
10
|
+
Trace = Struct.new(:type, :line_number, :locations)
|
11
|
+
|
10
12
|
# So we can sort hot spots by liklihood of being the most important spot to check out before
|
11
13
|
# trying to fix something. These are ranked based on the possibility they represent ripple
|
12
14
|
# effects where fixing one problem could potentially fix multiple other failures.
|
@@ -23,7 +25,7 @@ module Minitest
|
|
23
25
|
slow: 0
|
24
26
|
}.freeze
|
25
27
|
|
26
|
-
attr_reader :pathname, :issues
|
28
|
+
attr_reader :pathname, :issues, :lines
|
27
29
|
|
28
30
|
# Creates an instance of a Hit for the given pathname. It must be the full pathname to
|
29
31
|
# uniquely identify the file or we could run into collisions that muddy the water and
|
@@ -34,16 +36,26 @@ module Minitest
|
|
34
36
|
def initialize(pathname)
|
35
37
|
@pathname = Pathname(pathname)
|
36
38
|
@issues = {}
|
39
|
+
@lines = {}
|
37
40
|
end
|
38
41
|
|
39
42
|
# Adds a record of a given issue type for the line number
|
40
43
|
# @param type [Symbol] one of Issue::TYPES
|
41
44
|
# @param line_number [Integer,String] the line number to record the issue on
|
45
|
+
# @param backtrace: nil [Array<Location>] the project locations from the backtrace
|
42
46
|
#
|
43
|
-
# @return [
|
44
|
-
def log(type, line_number)
|
45
|
-
|
46
|
-
|
47
|
+
# @return [void]
|
48
|
+
def log(type, line_number, backtrace: [])
|
49
|
+
line_number = Integer(line_number)
|
50
|
+
issue_type = type.to_sym
|
51
|
+
|
52
|
+
# Store issues by issue type with an array of line numbers
|
53
|
+
@issues[issue_type] ||= []
|
54
|
+
@issues[issue_type] << line_number
|
55
|
+
|
56
|
+
# Store issues by line number with an array of Traces
|
57
|
+
@lines[line_number.to_s] ||= []
|
58
|
+
@lines[line_number.to_s] << Trace.new(issue_type, line_number, backtrace)
|
47
59
|
end
|
48
60
|
|
49
61
|
# Calcuates an approximate weight to serve as a proxy for which files are most likely to be
|
data/lib/minitest/heat/issue.rb
CHANGED
@@ -18,7 +18,7 @@ module Minitest
|
|
18
18
|
}.freeze
|
19
19
|
|
20
20
|
attr_reader :assertions,
|
21
|
-
:
|
21
|
+
:locations,
|
22
22
|
:message,
|
23
23
|
:test_class,
|
24
24
|
:test_identifier,
|
@@ -27,7 +27,7 @@ module Minitest
|
|
27
27
|
:error,
|
28
28
|
:skipped
|
29
29
|
|
30
|
-
def_delegators :@
|
30
|
+
def_delegators :@locations, :backtrace, :test_definition_line, :test_failure_line
|
31
31
|
|
32
32
|
# Extracts the necessary data from result.
|
33
33
|
# @param result [Minitest::Result] the instance of Minitest::Result to examine
|
@@ -39,7 +39,7 @@ module Minitest
|
|
39
39
|
|
40
40
|
new(
|
41
41
|
assertions: result.assertions,
|
42
|
-
|
42
|
+
test_location: result.source_location,
|
43
43
|
test_class: result.klass,
|
44
44
|
test_identifier: result.name,
|
45
45
|
execution_time: result.time,
|
@@ -47,7 +47,7 @@ module Minitest
|
|
47
47
|
error: result.error?,
|
48
48
|
skipped: result.skipped?,
|
49
49
|
message: exception&.message,
|
50
|
-
backtrace: exception&.backtrace
|
50
|
+
backtrace: exception&.backtrace
|
51
51
|
)
|
52
52
|
end
|
53
53
|
|
@@ -57,7 +57,7 @@ module Minitest
|
|
57
57
|
# @param assertions: 1 [Integer] the number of assertions in the result
|
58
58
|
# @param message: nil [String] exception if there is one
|
59
59
|
# @param backtrace: [] [Array<String>] the array of backtrace lines from an exception
|
60
|
-
# @param
|
60
|
+
# @param test_location: nil [Array<String, Integer>] the locations identifier for a test
|
61
61
|
# @param test_class: nil [String] the class name for the test result's containing class
|
62
62
|
# @param test_identifier: nil [String] the name of the test
|
63
63
|
# @param execution_time: nil [Float] the time it took to run the test
|
@@ -66,11 +66,11 @@ module Minitest
|
|
66
66
|
# @param skipped: false [Boolean] true if the test was skipped
|
67
67
|
#
|
68
68
|
# @return [type] [description]
|
69
|
-
def initialize(assertions: 1,
|
69
|
+
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
70
|
@message = message
|
71
71
|
|
72
72
|
@assertions = Integer(assertions)
|
73
|
-
@
|
73
|
+
@locations = Locations.new(test_location, backtrace)
|
74
74
|
|
75
75
|
@test_class = test_class
|
76
76
|
@test_identifier = test_identifier
|
@@ -135,18 +135,18 @@ module Minitest
|
|
135
135
|
# Determines if the issue is an exception that was raised from directly within a test
|
136
136
|
# definition. In these cases, it's more likely to be a quick fix.
|
137
137
|
#
|
138
|
-
# @return [Boolean] true if the final
|
138
|
+
# @return [Boolean] true if the final locations of the stacktrace was a test file
|
139
139
|
def in_test?
|
140
|
-
|
140
|
+
locations.broken_test?
|
141
141
|
end
|
142
142
|
|
143
143
|
# Determines if the issue is an exception that was raised from directly within the project
|
144
144
|
# codebase.
|
145
145
|
#
|
146
|
-
# @return [Boolean] true if the final
|
146
|
+
# @return [Boolean] true if the final locations of the stacktrace was a file from the project
|
147
147
|
# (as opposed to a dependency or Ruby library)
|
148
148
|
def in_source?
|
149
|
-
|
149
|
+
locations.proper_failure?
|
150
150
|
end
|
151
151
|
|
152
152
|
# Was the result a pass? i.e. Skips aren't passes or failures. Slows are still passes. So this
|
@@ -2,197 +2,178 @@
|
|
2
2
|
|
3
3
|
module Minitest
|
4
4
|
module Heat
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# - 'final' represents the final line of the backtrace regardless of where it is
|
9
|
-
# - 'test' represents the last line from the project's tests. It is further differentiated by
|
10
|
-
# the line where the test is defined and the actual line of code in the test that geneated
|
11
|
-
# the failure or exception
|
12
|
-
# - 'source_code' represents the last line from the project's source code
|
13
|
-
# - 'project' represents the last source line, but falls back to the last test line
|
14
|
-
# - 'most_relevant' represents the most specific file to investigate starting with the source
|
15
|
-
# code and then looking to the test code with final line of the backtrace as a fallback
|
5
|
+
# Consistent structure for extracting information about a given location. In addition to the
|
6
|
+
# pathname to the file and the line number in the file, it can also include information about
|
7
|
+
# the containing method or block and retrieve source code for the location.
|
16
8
|
class Location
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@line_number = Integer(line_number)
|
21
|
-
super
|
22
|
-
end
|
23
|
-
end
|
9
|
+
UNRECOGNIZED = '(Unrecognized File)'
|
10
|
+
UNKNOWN_MODIFICATION_TIME = Time.at(0)
|
11
|
+
UNKNOWN_MODIFICATION_SECONDS = -1
|
24
12
|
|
25
|
-
|
13
|
+
attr_accessor :raw_pathname, :raw_line_number, :raw_container
|
26
14
|
|
27
|
-
|
28
|
-
|
29
|
-
|
15
|
+
# Initialize a new Location
|
16
|
+
#
|
17
|
+
# @param [Pathname, String] pathname: the pathname to the file
|
18
|
+
# @param [Integer] line_number: the line number of the location within the file
|
19
|
+
# @param [String] container: nil the containing method or block for the issue
|
20
|
+
#
|
21
|
+
# @return [self]
|
22
|
+
def initialize(pathname:, line_number:, container: nil)
|
23
|
+
@raw_pathname = pathname
|
24
|
+
@raw_line_number = line_number
|
25
|
+
@raw_container = container
|
30
26
|
end
|
31
27
|
|
32
|
-
#
|
33
|
-
# test failure
|
28
|
+
# Generates a formatted string describing the line of code similar to the original backtrace
|
34
29
|
#
|
35
|
-
# @return [String]
|
30
|
+
# @return [String] a consistently-formatted, human-readable string about the line of code
|
36
31
|
def to_s
|
37
|
-
"#{
|
38
|
-
end
|
39
|
-
|
40
|
-
def local?
|
41
|
-
broken_test? || proper_failure?
|
32
|
+
"#{absolute_path}#{filename}:#{line_number} in `#{container}`"
|
42
33
|
end
|
43
34
|
|
44
|
-
#
|
45
|
-
# test, and it raises an exception, then it's really a broken test rather than a proper
|
46
|
-
# faiure.
|
35
|
+
# Generates a simplified location array with the pathname and line number
|
47
36
|
#
|
48
|
-
# @return [
|
49
|
-
def
|
50
|
-
|
37
|
+
# @return [Array<Pathname, Integer>] a no-frills location pair
|
38
|
+
def to_a
|
39
|
+
[
|
40
|
+
pathname,
|
41
|
+
line_number
|
42
|
+
]
|
51
43
|
end
|
52
44
|
|
53
|
-
#
|
54
|
-
# an external piece of code like a gem.
|
45
|
+
# A short relative pathname and line number pair
|
55
46
|
#
|
56
|
-
# @return [
|
57
|
-
|
58
|
-
|
59
|
-
!source_code_file.nil? && !broken_test?
|
47
|
+
# @return [String] the short filename/line number combo. ex. `dir/file.rb:23`
|
48
|
+
def short
|
49
|
+
"#{relative_filename}:#{line_number}"
|
60
50
|
end
|
61
51
|
|
62
|
-
|
63
|
-
|
64
|
-
# The final location of the stacktrace regardless of whether it's from within the project
|
52
|
+
# Determine if there is a file and text at the given line number
|
65
53
|
#
|
66
|
-
# @return [
|
67
|
-
def
|
68
|
-
|
54
|
+
# @return [Boolean] true if the file exists and has text at the given line number
|
55
|
+
def exists?
|
56
|
+
pathname.exist? && source_code.lines.any?
|
69
57
|
end
|
70
58
|
|
71
|
-
# The
|
72
|
-
#
|
73
|
-
# of a problem with local source code (not always, but frequently). In that case, the best
|
74
|
-
# first place to focus is on the code you control.
|
59
|
+
# The pathanme for the location. Written to be safe and fallbackto the project directory if
|
60
|
+
# an exception is raised ocnverting the value to a pathname
|
75
61
|
#
|
76
|
-
# @return [
|
77
|
-
def
|
78
|
-
Pathname(
|
62
|
+
# @return [Pathname] a pathname instance for the relevant file
|
63
|
+
def pathname
|
64
|
+
Pathname(raw_pathname)
|
65
|
+
rescue ArgumentError
|
66
|
+
Pathname(Dir.pwd)
|
79
67
|
end
|
80
68
|
|
81
|
-
#
|
69
|
+
# A safe interface to getting a string representing the path portion of the file
|
82
70
|
#
|
83
|
-
# @return [String
|
84
|
-
|
85
|
-
|
71
|
+
# @return [String] either the path/directory portion of the file name or '(Unrecognized File)'
|
72
|
+
# if the offending file can't be found for some reason
|
73
|
+
def path
|
74
|
+
pathname.exist? ? pathname.dirname.to_s : UNRECOGNIZED
|
86
75
|
end
|
87
76
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
def source_code_file
|
92
|
-
return nil if final_source_code_location.nil?
|
77
|
+
def absolute_path
|
78
|
+
pathname.exist? ? "#{path}/" : UNRECOGNIZED
|
79
|
+
end
|
93
80
|
|
94
|
-
|
81
|
+
def relative_path
|
82
|
+
pathname.exist? ? absolute_path.delete_prefix("#{project_root_dir}/") : UNRECOGNIZED
|
95
83
|
end
|
96
84
|
|
97
|
-
#
|
85
|
+
# A safe interface for getting a string representing the filename portion of the file
|
98
86
|
#
|
99
|
-
# @return [String] the
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
Pathname(project_location.pathname)
|
87
|
+
# @return [String] either the filename portion of the file or '(Unrecognized File)'
|
88
|
+
# if the offending file can't be found for some reason
|
89
|
+
def filename
|
90
|
+
pathname.exist? ? pathname.basename.to_s : UNRECOGNIZED
|
104
91
|
end
|
105
92
|
|
93
|
+
def absolute_filename
|
94
|
+
pathname.exist? ? pathname.to_s : UNRECOGNIZED
|
95
|
+
end
|
106
96
|
|
107
|
-
|
108
|
-
|
109
|
-
# @return [Integer] line number
|
110
|
-
def final_failure_line
|
111
|
-
final_location.line_number
|
97
|
+
def relative_filename
|
98
|
+
pathname.exist? ? pathname.to_s.delete_prefix("#{project_root_dir}/") : UNRECOGNIZED
|
112
99
|
end
|
113
100
|
|
114
|
-
#
|
101
|
+
# Line number identifying the specific line in the file
|
115
102
|
#
|
116
|
-
# @return [Integer] line number
|
117
|
-
|
118
|
-
|
103
|
+
# @return [Integer] line number for the file
|
104
|
+
#
|
105
|
+
def line_number
|
106
|
+
Integer(raw_line_number)
|
107
|
+
rescue ArgumentError
|
108
|
+
1
|
119
109
|
end
|
120
110
|
|
121
|
-
# The
|
111
|
+
# The containing method or block details for the location
|
122
112
|
#
|
123
|
-
# @return [
|
124
|
-
def
|
125
|
-
|
113
|
+
# @return [String] the containing method of the line of code
|
114
|
+
def container
|
115
|
+
raw_container.nil? ? '(Unknown Container)' : String(raw_container)
|
126
116
|
end
|
127
117
|
|
128
|
-
#
|
118
|
+
# Looks up the source code for the location. Can return multiple lines of source code from
|
119
|
+
# the surrounding lines of code for the primary line
|
120
|
+
#
|
121
|
+
# @param [Integer] max_line_count: 1 the maximum number of lines to return from the source
|
129
122
|
#
|
130
|
-
# @return [
|
131
|
-
def
|
132
|
-
|
123
|
+
# @return [Source] an instance of Source for accessing lines and their line numbers
|
124
|
+
def source_code(max_line_count: 1)
|
125
|
+
Minitest::Heat::Source.new(
|
126
|
+
pathname.to_s,
|
127
|
+
line_number: line_number,
|
128
|
+
max_line_count: max_line_count
|
129
|
+
)
|
133
130
|
end
|
134
131
|
|
135
|
-
#
|
132
|
+
# Determines if a given file is from the project directory
|
136
133
|
#
|
137
|
-
# @return [
|
138
|
-
def
|
139
|
-
|
134
|
+
# @return [Boolean] true if the file is in the project (source code or test)
|
135
|
+
def project_file?
|
136
|
+
path.include?(project_root_dir)
|
140
137
|
end
|
141
138
|
|
142
|
-
#
|
139
|
+
# Determines if a given file follows the standard approaching to naming test files.
|
143
140
|
#
|
144
|
-
# @return [
|
145
|
-
def
|
146
|
-
|
147
|
-
source_code_failure_line
|
148
|
-
else
|
149
|
-
test_failure_line
|
150
|
-
end
|
141
|
+
# @return [Boolean] true if the file name starts with `test_` or ends with `_test.rb`
|
142
|
+
def test_file?
|
143
|
+
filename.to_s.start_with?('test_') || filename.to_s.end_with?('_test.rb')
|
151
144
|
end
|
152
145
|
|
153
|
-
#
|
146
|
+
# Determines if a given file is a non-test file from the project directory
|
154
147
|
#
|
155
|
-
# @return [
|
156
|
-
|
157
|
-
|
158
|
-
backtrace.parsed_entries.any? ? backtrace.final_location : test_definition_location
|
148
|
+
# @return [Boolean] true if the file is in the project but not a test file
|
149
|
+
def source_code_file?
|
150
|
+
project_file? && !test_file?
|
159
151
|
end
|
160
152
|
|
161
|
-
#
|
162
|
-
# backtrace files will be a gem or external library that's failing indirectly as a result
|
163
|
-
# of a problem with local source code (not always, but frequently). In that case, the best
|
164
|
-
# first place to focus is on the code you control.
|
153
|
+
# A safe interface to getting the last modified time for the file in question
|
165
154
|
#
|
166
|
-
# @return [
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
final_test_location,
|
171
|
-
final_location
|
172
|
-
].compact.first
|
155
|
+
# @return [Time] the timestamp for when the file in question was last modified or `Time.at(0)`
|
156
|
+
# if the offending file can't be found for some reason
|
157
|
+
def mtime
|
158
|
+
pathname.exist? ? pathname.mtime : UNKNOWN_MODIFICATION_TIME
|
173
159
|
end
|
174
160
|
|
175
|
-
#
|
176
|
-
# the test location which represents the test definition.
|
161
|
+
# A safe interface to getting the number of seconds since the file was modified
|
177
162
|
#
|
178
|
-
# @return [
|
179
|
-
|
180
|
-
|
163
|
+
# @return [Integer] the number of seconds since the file was modified or `-1` if the offending
|
164
|
+
# file can't be found for some reason
|
165
|
+
def age_in_seconds
|
166
|
+
pathname.exist? ? seconds_ago : UNKNOWN_MODIFICATION_SECONDS
|
181
167
|
end
|
182
168
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
backtrace.final_source_code_location
|
169
|
+
private
|
170
|
+
|
171
|
+
def project_root_dir
|
172
|
+
Dir.pwd
|
188
173
|
end
|
189
174
|
|
190
|
-
|
191
|
-
|
192
|
-
#
|
193
|
-
# @return [Location] the final location from the project files
|
194
|
-
def project_location
|
195
|
-
backtrace.final_project_location || test_definition_location
|
175
|
+
def seconds_ago
|
176
|
+
(Time.now - mtime).to_i
|
196
177
|
end
|
197
178
|
end
|
198
179
|
end
|