minitest-heat 0.0.10 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,197 +2,178 @@
2
2
 
3
3
  module Minitest
4
4
  module Heat
5
- # Convenience methods for determining the file and line number where the problem occurred.
6
- # There are several layers of specificity to help make it easy to communicate the relative
7
- # location of the failure:
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
- TestDefinition = Struct.new(:pathname, :line_number) do
18
- def initialize(pathname, line_number)
19
- @pathname = Pathname(pathname)
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
- attr_reader :test_definition_location, :backtrace
13
+ attr_accessor :raw_pathname, :raw_line_number, :raw_container
26
14
 
27
- def initialize(test_definition_location, backtrace = [])
28
- @test_definition_location = TestDefinition.new(*test_definition_location)
29
- @backtrace = Backtrace.new(backtrace)
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
- # Prints the pathname and line number of the location most likely to be the source of the
33
- # test failure
28
+ # Generates a formatted string describing the line of code similar to the original backtrace
34
29
  #
35
- # @return [String] ex. 'path/to/file.rb:12'
30
+ # @return [String] a consistently-formatted, human-readable string about the line of code
36
31
  def to_s
37
- "#{most_relevant_file}:#{most_relevant_failure_line}"
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
- # Knows if the failure is contained within the test. For example, if there's bad code in a
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 [Boolean] true if final file in the backtrace is the same as the test location file
49
- def broken_test?
50
- !test_file.nil? && test_file == final_file
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
- # Knows if the failure occurred in the actual project source code—as opposed to the test or
54
- # an external piece of code like a gem.
45
+ # A short relative pathname and line number pair
55
46
  #
56
- # @return [Boolean] true if there's a non-test project file in the stacktrace but it's not
57
- # a result of a broken test
58
- def proper_failure?
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 [String] the relative path to the file from the project root
67
- def final_file
68
- Pathname(final_location.pathname)
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 file most likely to be the source of the underlying problem. Often, the most recent
72
- # backtrace files will be a gem or external library that's failing indirectly as a result
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 [String] the relative path to the file from the project root
77
- def most_relevant_file
78
- Pathname(most_relevant_location.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
- # The final location from the stacktrace that is a test file
69
+ # A safe interface to getting a string representing the path portion of the file
82
70
  #
83
- # @return [String, nil] the relative path to the file from the project root
84
- def test_file
85
- Pathname(final_test_location.pathname)
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
- # The final location from the stacktrace that is within the project directory
89
- #
90
- # @return [String, nil] the relative path to the file from the project root
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
- Pathname(final_source_code_location.pathname)
81
+ def relative_path
82
+ pathname.exist? ? absolute_path.delete_prefix("#{project_root_dir}/") : UNRECOGNIZED
95
83
  end
96
84
 
97
- # The final location of the stacktrace from within the project (source code or test code)
85
+ # A safe interface for getting a string representing the filename portion of the file
98
86
  #
99
- # @return [String] the relative path to the file from the project root
100
- def project_file
101
- return nil if project_location.nil?
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
- # The line number of the `final_file` where the failure originated
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
- # The line number of the `most_relevant_file` where the failure originated
101
+ # Line number identifying the specific line in the file
115
102
  #
116
- # @return [Integer] line number
117
- def most_relevant_failure_line
118
- most_relevant_location.line_number
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 line number of the `test_file` where the test is defined
111
+ # The containing method or block details for the location
122
112
  #
123
- # @return [Integer] line number
124
- def test_definition_line
125
- test_definition_location.line_number
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
- # The line number from within the `test_file` test definition where the failure occurred
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 [Integer] line number
131
- def test_failure_line
132
- final_test_location.line_number
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
- # The line number of the `source_code_file` where the failure originated
132
+ # Determines if a given file is from the project directory
136
133
  #
137
- # @return [Integer] line number
138
- def source_code_failure_line
139
- final_source_code_location&.line_number
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
- # The line number of the `project_file` where the failure originated
139
+ # Determines if a given file follows the standard approaching to naming test files.
143
140
  #
144
- # @return [Integer] line number
145
- def project_failure_line
146
- if !broken_test? && !source_code_file.nil?
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
- # The line number from within the `test_file` test definition where the failure occurred
146
+ # Determines if a given file is a non-test file from the project directory
154
147
  #
155
- # @return [Location] the last location from the backtrace or the test location if a backtrace
156
- # was not passed to the initializer
157
- def final_location
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
- # The file most likely to be the source of the underlying problem. Often, the most recent
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 [Array] file and line number of the most likely source of the problem
167
- def most_relevant_location
168
- [
169
- final_source_code_location,
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
- # Returns the final test location based on the backtrace if present. Otherwise falls back to
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 [Location] the final location from the test files
179
- def final_test_location
180
- backtrace.final_test_location || test_definition_location
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
- # Returns the final source code location based on the backtrace
184
- #
185
- # @return [Location] the final location from the source code files
186
- def final_source_code_location
187
- backtrace.final_source_code_location
169
+ private
170
+
171
+ def project_root_dir
172
+ Dir.pwd
188
173
  end
189
174
 
190
- # Returns the final project location based on the backtrace if present. Otherwise falls back
191
- # to the test location which represents the test definition.
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
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Minitest
4
+ module Heat
5
+ # Convenience methods for determining the file and line number where the problem occurred.
6
+ # There are several layers of specificity to help make it easy to communicate the relative
7
+ # location of the failure:
8
+ # - 'final' represents the final line of the backtrace regardless of where it is
9
+ # - 'test_definition' represents where the test is defined
10
+ # - 'test_failure' represents the last line from the project's tests. It is further differentiated by
11
+ # the line where the test is defined and the actual line of code in the test that geneated
12
+ # the failure or exception
13
+ # - 'source_code' represents the last line from the project's source code
14
+ # - 'project' represents the last source line, but falls back to the last test line
15
+ # - 'most_relevant' represents the most specific file to investigate starting with the source
16
+ # code and then looking to the test code with final line of the backtrace as a fallback
17
+ class Locations
18
+ attr_reader :test_definition, :backtrace
19
+
20
+ def initialize(test_definition_location, backtrace = [])
21
+ test_definition_pathname, test_definition_line_number = test_definition_location
22
+ @test_definition = ::Minitest::Heat::Location.new(pathname: test_definition_pathname, line_number: test_definition_line_number)
23
+
24
+ @backtrace = Backtrace.new(backtrace)
25
+ end
26
+
27
+ # Prints the pathname and line number of the location most likely to be the source of the
28
+ # test failure
29
+ #
30
+ # @return [String] ex. 'path/to/file.rb:12'
31
+ def to_s
32
+ "#{most_relevant.absolute_filename}:#{most_relevant.line_number}"
33
+ end
34
+
35
+ # Knows if the failure is contained within the test. For example, if there's bad code in a
36
+ # test, and it raises an exception, then it's really a broken test rather than a proper
37
+ # faiure.
38
+ #
39
+ # @return [Boolean] true if final file in the backtrace is the same as the test location file
40
+ def broken_test?
41
+ !test_failure.nil? && test_failure == final
42
+ end
43
+
44
+ # Knows if the failure occurred in the actual project source code—as opposed to the test or
45
+ # an external piece of code like a gem.
46
+ #
47
+ # @return [Boolean] true if there's a non-test project file in the stacktrace but it's not
48
+ # a result of a broken test
49
+ def proper_failure?
50
+ !source_code.nil? && !broken_test?
51
+ end
52
+
53
+ # The file most likely to be the source of the underlying problem. Often, the most recent
54
+ # backtrace files will be a gem or external library that's failing indirectly as a result
55
+ # of a problem with local source code (not always, but frequently). In that case, the best
56
+ # first place to focus is on the code you control.
57
+ #
58
+ # @return [Array] file and line number of the most likely source of the problem
59
+ def most_relevant
60
+ [
61
+ source_code,
62
+ test_failure,
63
+ final
64
+ ].compact.first
65
+ end
66
+
67
+ def freshest
68
+ backtrace.recently_modified_locations.first
69
+ end
70
+
71
+ # Returns the final test location based on the backtrace if present. Otherwise falls back to
72
+ # the test location which represents the test definition. The `test_definition` attribute
73
+ # provides the location of where the test is defined. `test_failure` represents the actual
74
+ # line from within the test where the problem occurred
75
+ #
76
+ # @return [Location] the final location from the test files
77
+ def test_failure
78
+ backtrace.test_locations.any? ? backtrace.test_locations.first : test_definition
79
+ end
80
+
81
+ # Returns the final source code location based on the backtrace
82
+ #
83
+ # @return [Location] the final location from the source code files
84
+ def source_code
85
+ backtrace.source_code_locations.first
86
+ end
87
+
88
+ # Returns the final project location based on the backtrace if present. Otherwise falls back
89
+ # to the test location which represents the test definition.
90
+ #
91
+ # @return [Location] the final location from the project files
92
+ def project
93
+ backtrace.project_locations.any? ? backtrace.project_locations.first : test_definition
94
+ end
95
+
96
+ # The line number from within the `test_file` test definition where the failure occurred
97
+ #
98
+ # @return [Location] the last location from the backtrace or the test location if a backtrace
99
+ # was not passed to the initializer
100
+ def final
101
+ backtrace.locations.any? ? backtrace.locations.first : test_definition
102
+ end
103
+ end
104
+ end
105
+ end
@@ -18,10 +18,9 @@ module Minitest
18
18
  # @param type [Symbol] the type of issue that was encountered (i.e. :failure, :error, etc.)
19
19
  #
20
20
  # @return [void]
21
- def add(filename, line_number, type)
22
- @hits[filename] ||= Hit.new(filename)
23
-
24
- @hits[filename].log(type.to_sym, line_number)
21
+ def add(pathname, line_number, type, backtrace: [])
22
+ @hits[pathname.to_s] ||= Hit.new(pathname)
23
+ @hits[pathname.to_s].log(type.to_sym, line_number, backtrace: backtrace)
25
24
  end
26
25
 
27
26
  # Returns a subset of affected files to keep the list from being overwhelming
@@ -3,115 +3,137 @@
3
3
  module Minitest
4
4
  module Heat
5
5
  class Output
6
- # Builds the collection of tokens for a backtrace when an exception occurs
6
+ # Builds the collection of tokens for displaying a backtrace when an exception occurs
7
7
  class Backtrace
8
8
  DEFAULT_LINE_COUNT = 10
9
9
  DEFAULT_INDENTATION_SPACES = 2
10
10
 
11
- attr_accessor :location, :backtrace
11
+ attr_accessor :locations, :backtrace
12
12
 
13
- def initialize(location)
14
- @location = location
15
- @backtrace = location.backtrace
13
+ def initialize(locations)
14
+ @locations = locations
15
+ @backtrace = locations.backtrace
16
16
  @tokens = []
17
17
  end
18
18
 
19
19
  def tokens
20
- # There could be option to expand and display more than one line of source code for the
21
- # final backtrace line if it might be relevant/helpful?
22
-
23
20
  # Iterate over the selected lines from the backtrace
24
- backtrace_entries.each do |backtrace_entry|
25
- # Get the source code for the line from the backtrace
26
- parts = backtrace_line_tokens(backtrace_entry)
27
-
28
- # If it's the most recently modified file in the trace, add the token for that
29
- parts << file_freshness(backtrace_entry) if most_recently_modified?(backtrace_entry)
30
-
31
- @tokens << parts
32
- end
33
-
34
- @tokens
21
+ @tokens = backtrace_locations.map { |location| backtrace_location_tokens(location) }
35
22
  end
36
23
 
24
+ # Determines the number of lines to display from the backtrace.
25
+ #
26
+ # @return [Integer] the number of lines to limit the backtrace to
37
27
  def line_count
28
+ # Defined as a method instead of using the constant directlyr in order to easily support
29
+ # adding options for controlling how many lines are displayed from a backtrace.
30
+ #
31
+ # For example, instead of a fixed number, the backtrace could dynamically calculate how
32
+ # many lines it should displaye in order to get to the origination point. Or it could have
33
+ # a default, but inteligently go back further if the backtrace meets some criteria for
34
+ # displaying more lines.
38
35
  DEFAULT_LINE_COUNT
39
36
  end
40
37
 
41
- # This should probably be smart about what lines are displayed in a backtrace.
42
- # Maybe...
43
- # ...it could intelligently display the full back trace?
44
- # ...only the backtrace from the first/last line of project source?
45
- # ...it behaves a little different when it's a broken test vs. a true exception?
46
- # ...it could be smart about subtly flagging the lines that show up in the heat map frequently?
47
- # ...it could be influenced by a "compact" or "robust" reporter super-style?
48
- # ...it's smart about exceptions that were raised outside of the project?
49
- # ...it's smart about highlighting lines of code differently based on whether it's source code, test code, or external code?
50
- def backtrace_entries
51
- all_entries
38
+ # A subset of parsed lines from the backtrace.
39
+ #
40
+ # @return [Array<Location>] the backtrace locations determined to be most relevant to the
41
+ # context of the underlying issue
42
+ def backtrace_locations
43
+ # This could eventually have additional intelligence to determine what lines are most
44
+ # relevant for a given type of issue. For now, it simply takes the line numbers, but the
45
+ # idea is that long-term, it could adjust that on the fly to keep the line count as low
46
+ # as possible but expand it if necessary to ensure enough context is displayed.
47
+ #
48
+ # - If there's no clear cut details about the source of the error from within the project,
49
+ # it could display the entire backtrace without filtering anything.
50
+ # - It could scan the backtrace to the first appearance of project files and then display
51
+ # all of the lines that occurred after that instance
52
+ # - It coudl filter the lines differently whether the issue originated from a test or from
53
+ # the source code.
54
+ # - It could allow supporting a "compact" or "robust" reporter style so that someone on
55
+ # a smaller screen could easily reduce the information shown so that the results could
56
+ # be higher density even if it means truncating some occasionally useful details
57
+ # - It could be smarter about displaying context/guidance when the full backtrace is from
58
+ # outside the project's code
59
+ #
60
+ # But for now. It just grabs some lines.
61
+ backtrace.locations.take(line_count)
52
62
  end
53
63
 
54
64
  private
55
65
 
56
- def backtrace_line_tokens(backtrace_entry)
66
+ def backtrace_location_tokens(location)
57
67
  [
58
68
  indentation_token,
59
- path_token(backtrace_entry),
60
- *file_and_line_number_tokens(backtrace_entry),
61
- source_code_line_token(backtrace_entry.source_code)
62
- ]
63
- end
64
-
65
- def all_backtrace_entries_from_project?
66
- backtrace_entries.all? { |line| line.path.to_s.include?(project_root_dir) }
67
- end
68
-
69
- def project_root_dir
70
- Dir.pwd
71
- end
72
-
73
- def project_entries
74
- backtrace.project_entries.take(line_count)
69
+ path_token(location),
70
+ *file_and_line_number_tokens(location),
71
+ containining_element_token(location),
72
+ source_code_line_token(location),
73
+ most_recently_modified_token(location),
74
+ ].compact
75
75
  end
76
76
 
77
- def all_entries
78
- backtrace.parsed_entries.take(line_count)
77
+ # Determines if all lines to be displayed are from within the project directory
78
+ #
79
+ # @return [Boolean] true if all lines of the backtrace being displayed are from the project
80
+ def all_backtrace_from_project?
81
+ backtrace_locations.all?(&:project_file?)
79
82
  end
80
83
 
81
- def most_recently_modified?(line)
82
- # If there's more than one line being displayed, and the current line is the freshest
83
- backtrace_entries.size > 1 && line == backtrace.freshest_project_location
84
+ # Determines if the file referenced by a backtrace line is the most recently modified file
85
+ # of all the files referenced in the visible backtrace locations.
86
+ #
87
+ # @param [Location] location the location to examine
88
+ #
89
+ # @return [<type>] <description>
90
+ #
91
+ def most_recently_modified?(location)
92
+ # If there's more than one line being displayed (otherwise, with one line, of course it's
93
+ # the most recently modified because there_aren't any others) and the current line is the
94
+ # same as the freshest location in the backtrace
95
+ backtrace_locations.size > 1 && location == locations.freshest
84
96
  end
85
97
 
86
98
  def indentation_token
87
99
  [:default, ' ' * indentation]
88
100
  end
89
101
 
90
- def path_token(line)
91
- style = line.to_s.include?(Dir.pwd) ? :default : :muted
92
- path = "#{line.path}/"
102
+ def path_token(location)
103
+ # If the line is a project file, help it stand out from the backtrace noise
104
+ style = location.project_file? ? :default : :muted
93
105
 
94
- # If all of the backtrace lines are from the project, no point in the added redundant
95
- # noise of showing the project root directory over and over again
96
- path = path.delete_prefix(project_root_dir) if all_backtrace_entries_from_project?
106
+ # If *all* of the backtrace lines are from the project, no point in the added redundant
107
+ # noise of showing the project root directory over and over again
108
+ path_format = all_backtrace_from_project? ? :relative_path : :absolute_path
97
109
 
98
- [style, path]
110
+ [style, location.send(path_format)]
99
111
  end
100
112
 
101
- def file_and_line_number_tokens(backtrace_entry)
102
- style = backtrace_entry.to_s.include?(Dir.pwd) ? :bold : :muted
113
+ def file_and_line_number_tokens(location)
114
+ style = location.to_s.include?(Dir.pwd) ? :bold : :muted
103
115
  [
104
- [style, backtrace_entry.file],
116
+ [style, location.filename],
105
117
  [:muted, ':'],
106
- [style, backtrace_entry.line_number]
118
+ [style, location.line_number]
107
119
  ]
108
120
  end
109
121
 
110
- def source_code_line_token(source_code)
111
- [:muted, " #{Output::SYMBOLS[:arrow]} `#{source_code.line.strip}`"]
122
+ def source_code_line_token(location)
123
+ return nil unless location.project_file?
124
+
125
+ [:muted, " #{Output::SYMBOLS[:arrow]} `#{location.source_code.line.strip}`"]
126
+ end
127
+
128
+ def containining_element_token(location)
129
+ return nil if !location.project_file? || location.container.nil? || location.container.empty?
130
+
131
+ [:muted, " in #{location.container}"]
112
132
  end
113
133
 
114
- def file_freshness(_line)
134
+ def most_recently_modified_token(location)
135
+ return nil unless most_recently_modified?(location)
136
+
115
137
  [:default, " #{Output::SYMBOLS[:middot]} Most Recently Modified File"]
116
138
  end
117
139
 
@@ -126,10 +148,6 @@ module Minitest
126
148
  def indentation
127
149
  DEFAULT_INDENTATION_SPACES
128
150
  end
129
-
130
- def style_for(path)
131
- path.to_s.include?(Dir.pwd) ? :default : :muted
132
- end
133
151
  end
134
152
  end
135
153
  end