minitest 5.16.3 → 5.23.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.
data/lib/minitest/test.rb CHANGED
@@ -18,6 +18,10 @@ module Minitest
18
18
 
19
19
  PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, SystemExit] # :nodoc:
20
20
 
21
+ SETUP_METHODS = %w[ before_setup setup after_setup ] # :nodoc:
22
+
23
+ TEARDOWN_METHODS = %w[ before_teardown teardown after_teardown ] # :nodoc:
24
+
21
25
  # :stopdoc:
22
26
  class << self; attr_accessor :io_lock; end
23
27
  self.io_lock = Mutex.new
@@ -48,9 +52,10 @@ module Minitest
48
52
  end
49
53
 
50
54
  ##
51
- # Call this at the top of your tests when you want to run your
52
- # tests in parallel. In doing so, you're admitting that you rule
53
- # and your tests are awesome.
55
+ # Call this at the top of your tests (inside the +Minitest::Test+
56
+ # subclass or +describe+ block) when you want to run your tests in
57
+ # parallel. In doing so, you're admitting that you rule and your
58
+ # tests are awesome.
54
59
 
55
60
  def self.parallelize_me!
56
61
  include Minitest::Parallel::Test
@@ -76,16 +81,6 @@ module Minitest
76
81
  end
77
82
  end
78
83
 
79
- ##
80
- # Defines the order to run tests (:random by default). Override
81
- # this or use a convenience method to change it for your tests.
82
-
83
- def self.test_order
84
- :random
85
- end
86
-
87
- TEARDOWN_METHODS = %w[ before_teardown teardown after_teardown ] # :nodoc:
88
-
89
84
  ##
90
85
  # Runs a single test with setup/teardown hooks.
91
86
 
@@ -93,7 +88,9 @@ module Minitest
93
88
  with_info_handler do
94
89
  time_it do
95
90
  capture_exceptions do
96
- before_setup; setup; after_setup
91
+ SETUP_METHODS.each do |hook|
92
+ self.send hook
93
+ end
97
94
 
98
95
  self.send self.name
99
96
  end
@@ -145,7 +142,7 @@ module Minitest
145
142
  # end
146
143
  # end
147
144
  #
148
- # class MiniTest::Test
145
+ # class Minitest::Test
149
146
  # include MyMinitestPlugin
150
147
  # end
151
148
 
@@ -208,7 +205,7 @@ module Minitest
208
205
  neuter_exception e
209
206
  end
210
207
 
211
- def neuter_exception e
208
+ def neuter_exception e # :nodoc:
212
209
  bt = e.backtrace
213
210
  msg = e.message.dup
214
211
 
@@ -219,7 +216,7 @@ module Minitest
219
216
  new_exception RuntimeError, msg, bt, true # but if this raises, we die
220
217
  end
221
218
 
222
- def new_exception klass, msg, bt, kill = false
219
+ def new_exception klass, msg, bt, kill = false # :nodoc:
223
220
  ne = klass.new msg
224
221
  ne.set_backtrace bt
225
222
 
@@ -249,4 +246,4 @@ module Minitest
249
246
  end # Test
250
247
  end
251
248
 
252
- require "minitest/unit" unless defined?(MiniTest) # compatibility layer only
249
+ require "minitest/unit" if ENV["MT_COMPAT"] # compatibility layer only
@@ -29,6 +29,10 @@ module Minitest # :nodoc:
29
29
  # end
30
30
  #
31
31
  # Customize the name and only run unit tests.
32
+ #
33
+ # NOTE: To hook this task up to the default, make it a dependency:
34
+ #
35
+ # task default: :unit
32
36
 
33
37
  class TestTask < Rake::TaskLib
34
38
  WINDOWS = RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ # :nodoc:
@@ -110,7 +114,7 @@ module Minitest # :nodoc:
110
114
  self.test_globs = ["test/**/test_*.rb",
111
115
  "test/**/*_test.rb"]
112
116
  self.test_prelude = nil
113
- self.verbose = Rake.application.options.trace
117
+ self.verbose = Rake.application.options.trace || Rake.verbose == true
114
118
  self.warning = true
115
119
  end
116
120
 
@@ -157,8 +161,6 @@ module Minitest # :nodoc:
157
161
  end
158
162
 
159
163
  def define # :nodoc:
160
- default_tasks = []
161
-
162
164
  desc "Run the test suite. Use N, X, A, and TESTOPTS to add flags/args."
163
165
  task name do
164
166
  ruby make_test_cmd, verbose:verbose
@@ -243,11 +245,6 @@ module Minitest # :nodoc:
243
245
  "sort -n -k2 -t=",
244
246
  "tail -25"].join " | "
245
247
  end
246
-
247
- default_tasks << name
248
-
249
- desc "Run the default task(s)."
250
- task :default => default_tasks
251
248
  end
252
249
 
253
250
  ##
@@ -277,8 +274,8 @@ module Minitest # :nodoc:
277
274
  end
278
275
  end
279
276
 
280
- class Work < Queue
281
- def initialize jobs = []
277
+ class Work < Queue # :nodoc:
278
+ def initialize jobs = [] # :nodoc:
282
279
  super()
283
280
 
284
281
  jobs.each do |job|
@@ -289,9 +286,8 @@ class Work < Queue
289
286
  end
290
287
  end
291
288
 
292
- class Integer
289
+ class Integer # :nodoc:
293
290
  def threads_do(jobs) # :nodoc:
294
- require "thread"
295
291
  q = Work.new jobs
296
292
 
297
293
  self.times.map {
data/lib/minitest.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  require "optparse"
2
- require "thread"
3
- require "mutex_m"
4
- require "minitest/parallel"
5
2
  require "stringio"
6
3
  require "etc"
7
4
 
5
+ require_relative "minitest/parallel"
6
+ require_relative "minitest/compress"
7
+
8
8
  ##
9
9
  # :include: README.rdoc
10
10
 
11
11
  module Minitest
12
- VERSION = "5.16.3" # :nodoc:
12
+ VERSION = "5.23.1" # :nodoc:
13
13
 
14
14
  @@installed_at_exit ||= false
15
15
  @@after_run = []
@@ -32,7 +32,7 @@ module Minitest
32
32
 
33
33
  cattr_accessor :parallel_executor
34
34
 
35
- warn "DEPRECATED: use MT_CPU instead of N for parallel test runs" if ENV["N"]
35
+ warn "DEPRECATED: use MT_CPU instead of N for parallel test runs" if ENV["N"] && ENV["N"].to_i > 0
36
36
  n_threads = (ENV["MT_CPU"] || ENV["N"] || Etc.nprocessors).to_i
37
37
 
38
38
  self.parallel_executor = Parallel::Executor.new n_threads
@@ -60,6 +60,9 @@ module Minitest
60
60
  cattr_accessor :info_signal
61
61
  self.info_signal = "INFO"
62
62
 
63
+ cattr_accessor :allow_fork
64
+ self.allow_fork = false
65
+
63
66
  ##
64
67
  # Registers Minitest to run at process exit
65
68
 
@@ -75,7 +78,7 @@ module Minitest
75
78
 
76
79
  pid = Process.pid
77
80
  at_exit {
78
- next if Process.pid != pid
81
+ next if !Minitest.allow_fork && Process.pid != pid
79
82
  @@after_run.reverse_each(&:call)
80
83
  exit exit_code || false
81
84
  }
@@ -131,7 +134,7 @@ module Minitest
131
134
  # Minitest.run(args)
132
135
  # Minitest.__run(reporter, options)
133
136
  # Runnable.runnables.each
134
- # runnable.run(reporter, options)
137
+ # runnable_klass.run(reporter, options)
135
138
  # self.runnable_methods.each
136
139
  # self.run_one_method(self, runnable_method, reporter)
137
140
  # Minitest.run_one_method(klass, runnable_method)
@@ -147,7 +150,7 @@ module Minitest
147
150
 
148
151
  reporter = CompositeReporter.new
149
152
  reporter << SummaryReporter.new(options[:io], options)
150
- reporter << ProgressReporter.new(options[:io], options)
153
+ reporter << ProgressReporter.new(options[:io], options) unless options[:quiet]
151
154
 
152
155
  self.reporter = reporter # this makes it available to plugins
153
156
  self.init_plugins options
@@ -161,11 +164,32 @@ module Minitest
161
164
  warn "Interrupted. Exiting..."
162
165
  end
163
166
  self.parallel_executor.shutdown
167
+
168
+ # might have been removed/replaced during init_plugins:
169
+ summary = reporter.reporters.grep(SummaryReporter).first
170
+
164
171
  reporter.report
165
172
 
173
+ return empty_run! options if summary && summary.count == 0
166
174
  reporter.passed?
167
175
  end
168
176
 
177
+ def self.empty_run! options # :nodoc:
178
+ filter = options[:filter]
179
+ return true unless filter # no filter, but nothing ran == success
180
+
181
+ warn "Nothing ran for filter: %s" % [filter]
182
+
183
+ require "did_you_mean" # soft dependency, punt if it doesn't load
184
+
185
+ ms = Runnable.runnables.flat_map(&:runnable_methods)
186
+ cs = DidYouMean::SpellChecker.new(dictionary: ms).correct filter
187
+
188
+ warn DidYouMean::Formatter.message_for cs unless cs.empty?
189
+ rescue LoadError
190
+ # do nothing
191
+ end
192
+
169
193
  ##
170
194
  # Internal run method. Responsible for telling all Runnable
171
195
  # sub-classes to run.
@@ -209,6 +233,10 @@ module Minitest
209
233
  options[:verbose] = true
210
234
  end
211
235
 
236
+ opts.on "-q", "--quiet", "Quiet. Show no progress processing files." do
237
+ options[:quiet] = true
238
+ end
239
+
212
240
  opts.on "--show-skips", "Show skipped at the end of run." do
213
241
  options[:show_skips] = true
214
242
  end
@@ -225,6 +253,20 @@ module Minitest
225
253
  options[:skip] = s.chars.to_a
226
254
  end
227
255
 
256
+ ruby27plus = ::Warning.respond_to?(:[]=)
257
+
258
+ opts.on "-W[error]", String, "Turn Ruby warnings into errors" do |s|
259
+ options[:Werror] = true
260
+ case s
261
+ when "error", "all", nil then
262
+ require "minitest/error_on_warning"
263
+ $VERBOSE = true
264
+ ::Warning[:deprecated] = true if ruby27plus
265
+ else
266
+ ::Warning[s.to_sym] = true if ruby27plus # check validity of category
267
+ end
268
+ end
269
+
228
270
  unless extensions.empty?
229
271
  opts.separator ""
230
272
  opts.separator "Known extensions: #{extensions.join(", ")}"
@@ -331,19 +373,15 @@ module Minitest
331
373
  # reporter to record.
332
374
 
333
375
  def self.run reporter, options = {}
334
- filter = options[:filter] || "/./"
335
- filter = Regexp.new $1 if filter.is_a?(String) && filter =~ %r%/(.*)/%
336
-
337
- filtered_methods = self.runnable_methods.find_all { |m|
338
- filter === m || filter === "#{self}##{m}"
339
- }
376
+ pos = options[:filter]
377
+ neg = options[:exclude]
340
378
 
341
- exclude = options[:exclude]
342
- exclude = Regexp.new $1 if exclude =~ %r%/(.*)/%
379
+ pos = Regexp.new $1 if pos.is_a?(String) && pos =~ %r%/(.*)/%
380
+ neg = Regexp.new $1 if neg.is_a?(String) && neg =~ %r%/(.*)/%
343
381
 
344
- filtered_methods.delete_if { |m|
345
- exclude === m || exclude === "#{self}##{m}"
346
- }
382
+ filtered_methods = self.runnable_methods
383
+ .select { |m| !pos || pos === m || pos === "#{self}##{m}" }
384
+ .reject { |m| neg && (neg === m || neg === "#{self}##{m}") }
347
385
 
348
386
  return if filtered_methods.empty?
349
387
 
@@ -365,6 +403,14 @@ module Minitest
365
403
  reporter.record Minitest.run_one_method(klass, method_name)
366
404
  end
367
405
 
406
+ ##
407
+ # Defines the order to run tests (:random by default). Override
408
+ # this or use a convenience method to change it for your tests.
409
+
410
+ def self.test_order
411
+ :random
412
+ end
413
+
368
414
  def self.with_info_handler reporter, &block # :nodoc:
369
415
  handler = lambda do
370
416
  unless reporter.passed? then
@@ -432,6 +478,31 @@ module Minitest
432
478
  self.name = name
433
479
  self.failures = []
434
480
  self.assertions = 0
481
+ # lazy initializer for metadata
482
+ end
483
+
484
+ ##
485
+ # Metadata you attach to the test results that get sent to the reporter.
486
+ #
487
+ # Lazily initializes to a hash, to keep memory down.
488
+ #
489
+ # NOTE: this data *must* be plain (read: marshal-able) data!
490
+ # Hashes! Arrays! Strings!
491
+
492
+ def metadata
493
+ @metadata ||= {}
494
+ end
495
+
496
+ ##
497
+ # Sets metadata, mainly used for +Result.from+.
498
+
499
+ attr_writer :metadata
500
+
501
+ ##
502
+ # Returns true if metadata exists.
503
+
504
+ def metadata?
505
+ defined? @metadata
435
506
  end
436
507
 
437
508
  ##
@@ -483,12 +554,14 @@ module Minitest
483
554
  not self.failure
484
555
  end
485
556
 
557
+ BASE_DIR = "#{Dir.pwd}/" # :nodoc:
558
+
486
559
  ##
487
560
  # The location identifier of this test. Depends on a method
488
561
  # existing called class_name.
489
562
 
490
563
  def location
491
- loc = " [#{self.failure.location}]" unless passed? or error?
564
+ loc = " [#{self.failure.location.delete_prefix BASE_DIR}]" unless passed? or error?
492
565
  "#{self.class_name}##{self.name}#{loc}"
493
566
  end
494
567
 
@@ -552,6 +625,7 @@ module Minitest
552
625
  r.assertions = o.assertions
553
626
  r.failures = o.failures.dup
554
627
  r.time = o.time
628
+ r.metadata = o.metadata if o.metadata?
555
629
 
556
630
  r.source_location = o.method(o.name).source_location rescue ["unknown", -1]
557
631
 
@@ -576,7 +650,10 @@ module Minitest
576
650
  # you want. Go nuts.
577
651
 
578
652
  class AbstractReporter
579
- include Mutex_m
653
+
654
+ def initialize # :nodoc:
655
+ @mutex = Mutex.new
656
+ end
580
657
 
581
658
  ##
582
659
  # Starts reporting on the run.
@@ -612,6 +689,10 @@ module Minitest
612
689
  def passed?
613
690
  true
614
691
  end
692
+
693
+ def synchronize(&block) # :nodoc:
694
+ @mutex.synchronize(&block)
695
+ end
615
696
  end
616
697
 
617
698
  class Reporter < AbstractReporter # :nodoc:
@@ -715,6 +796,11 @@ module Minitest
715
796
 
716
797
  attr_accessor :errors
717
798
 
799
+ ##
800
+ # Total number of tests that warned.
801
+
802
+ attr_accessor :warnings
803
+
718
804
  ##
719
805
  # Total number of tests that where skipped.
720
806
 
@@ -730,6 +816,7 @@ module Minitest
730
816
  self.total_time = nil
731
817
  self.failures = nil
732
818
  self.errors = nil
819
+ self.warnings = nil
733
820
  self.skips = nil
734
821
  end
735
822
 
@@ -758,6 +845,7 @@ module Minitest
758
845
  self.total_time = Minitest.clock_time - start_time
759
846
  self.failures = aggregate[Assertion].size
760
847
  self.errors = aggregate[UnexpectedError].size
848
+ self.warnings = aggregate[UnexpectedWarning].size
761
849
  self.skips = aggregate[Skip].size
762
850
  end
763
851
  end
@@ -785,7 +873,7 @@ module Minitest
785
873
  io.puts "# Running:"
786
874
  io.puts
787
875
 
788
- self.sync = io.respond_to? :"sync=" # stupid emacs
876
+ self.sync = io.respond_to? :"sync="
789
877
  self.old_sync, io.sync = io.sync, true if self.sync
790
878
  end
791
879
 
@@ -823,7 +911,7 @@ module Minitest
823
911
  end
824
912
 
825
913
  def to_s # :nodoc:
826
- aggregated_results(StringIO.new(binary_string)).string
914
+ aggregated_results(StringIO.new(''.b)).string
827
915
  end
828
916
 
829
917
  def summary # :nodoc:
@@ -833,17 +921,11 @@ module Minitest
833
921
  results.any?(&:skipped?) unless
834
922
  options[:verbose] or options[:show_skips] or ENV["MT_NO_SKIP_MSG"]
835
923
 
924
+ extra.prepend ", %d warnings" % [warnings] if options[:Werror]
925
+
836
926
  "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
837
927
  [count, assertions, failures, errors, skips, extra]
838
928
  end
839
-
840
- private
841
-
842
- if '<3'.respond_to? :b
843
- def binary_string; ''.b; end
844
- else
845
- def binary_string; ''.force_encoding(Encoding::ASCII_8BIT); end
846
- end
847
929
  end
848
930
 
849
931
  ##
@@ -901,6 +983,8 @@ module Minitest
901
983
  # Represents run failures.
902
984
 
903
985
  class Assertion < Exception
986
+ RE = /in [`'](?:[^']+[#.])?(?:assert|refute|flunk|pass|fail|raise|must|wont)/ # :nodoc:
987
+
904
988
  def error # :nodoc:
905
989
  self
906
990
  end
@@ -909,12 +993,11 @@ module Minitest
909
993
  # Where was this run before an assertion was raised?
910
994
 
911
995
  def location
912
- last_before_assertion = ""
913
- self.backtrace.reverse_each do |s|
914
- break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
915
- last_before_assertion = s
916
- end
917
- last_before_assertion.sub(/:in .*$/, "")
996
+ bt = Minitest.filter_backtrace self.backtrace
997
+ idx = bt.rindex { |s| s.match? RE } || -1 # fall back to first item
998
+ loc = bt[idx+1] || bt.last || "unknown:-1"
999
+
1000
+ loc.sub(/:in .*$/, "")
918
1001
  end
919
1002
 
920
1003
  def result_code # :nodoc:
@@ -939,11 +1022,21 @@ module Minitest
939
1022
  # Assertion wrapping an unexpected error that was raised during a run.
940
1023
 
941
1024
  class UnexpectedError < Assertion
1025
+ include Minitest::Compress
1026
+
942
1027
  # TODO: figure out how to use `cause` instead
943
1028
  attr_accessor :error # :nodoc:
944
1029
 
945
1030
  def initialize error # :nodoc:
946
1031
  super "Unexpected exception"
1032
+
1033
+ if SystemStackError === error then
1034
+ bt = error.backtrace
1035
+ new_bt = compress bt
1036
+ error = error.exception "#{bt.size} -> #{new_bt.size}"
1037
+ error.set_backtrace new_bt
1038
+ end
1039
+
947
1040
  self.error = error
948
1041
  end
949
1042
 
@@ -951,8 +1044,11 @@ module Minitest
951
1044
  self.error.backtrace
952
1045
  end
953
1046
 
1047
+ BASE_RE = %r%#{Dir.pwd}/% # :nodoc:
1048
+
954
1049
  def message # :nodoc:
955
- bt = Minitest.filter_backtrace(self.backtrace).join "\n "
1050
+ bt = Minitest.filter_backtrace(self.backtrace).join("\n ")
1051
+ .gsub(BASE_RE, "")
956
1052
  "#{self.error.class}: #{self.error.message}\n #{bt}"
957
1053
  end
958
1054
 
@@ -961,6 +1057,15 @@ module Minitest
961
1057
  end
962
1058
  end
963
1059
 
1060
+ ##
1061
+ # Assertion raised on warning when running in -Werror mode.
1062
+
1063
+ class UnexpectedWarning < Assertion
1064
+ def result_label # :nodoc:
1065
+ "Warning"
1066
+ end
1067
+ end
1068
+
964
1069
  ##
965
1070
  # Provides a simple set of guards that you can use in your tests
966
1071
  # to skip execution if it is not applicable. These methods are
@@ -1034,7 +1139,13 @@ module Minitest
1034
1139
 
1035
1140
  class BacktraceFilter
1036
1141
 
1037
- MT_RE = %r%lib/minitest% #:nodoc:
1142
+ MT_RE = %r%lib/minitest|internal:warning% #:nodoc:
1143
+
1144
+ attr_accessor :regexp
1145
+
1146
+ def initialize regexp = MT_RE
1147
+ self.regexp = regexp
1148
+ end
1038
1149
 
1039
1150
  ##
1040
1151
  # Filter +bt+ to something useful. Returns the whole thing if
@@ -1045,9 +1156,9 @@ module Minitest
1045
1156
 
1046
1157
  return bt.dup if $DEBUG || ENV["MT_DEBUG"]
1047
1158
 
1048
- new_bt = bt.take_while { |line| line !~ MT_RE }
1049
- new_bt = bt.select { |line| line !~ MT_RE } if new_bt.empty?
1050
- new_bt = bt.dup if new_bt.empty?
1159
+ new_bt = bt.take_while { |line| line.to_s !~ regexp }
1160
+ new_bt = bt.select { |line| line.to_s !~ regexp } if new_bt.empty?
1161
+ new_bt = bt.dup if new_bt.empty?
1051
1162
 
1052
1163
  new_bt
1053
1164
  end
@@ -8,24 +8,36 @@ class Minitest::Test
8
8
  end
9
9
 
10
10
  def with_empty_backtrace_filter
11
- original = Minitest.backtrace_filter
12
-
13
- obj = Minitest::BacktraceFilter.new
14
- def obj.filter _bt
15
- []
11
+ with_backtrace_filter Minitest::BacktraceFilter.new %r%.% do
12
+ yield
16
13
  end
14
+ end
15
+
16
+ def with_backtrace_filter filter
17
+ original = Minitest.backtrace_filter
17
18
 
18
19
  Minitest::Test.io_lock.synchronize do # try not to trounce in parallel
19
20
  begin
20
- Minitest.backtrace_filter = obj
21
+ Minitest.backtrace_filter = filter
21
22
  yield
22
23
  ensure
23
24
  Minitest.backtrace_filter = original
24
25
  end
25
26
  end
26
27
  end
27
- end
28
28
 
29
+ def error_on_warn?
30
+ defined?(Minitest::ErrorOnWarning)
31
+ end
32
+
33
+ def assert_deprecation re = /DEPRECATED/
34
+ assert_output "", re do
35
+ yield
36
+ end
37
+ rescue Minitest::UnexpectedWarning => e # raised if -Werror was used
38
+ assert_match re, e.message
39
+ end
40
+ end
29
41
 
30
42
  class FakeNamedTest < Minitest::Test
31
43
  @@count = 0
@@ -55,7 +67,7 @@ class MetaMetaMetaTestCase < Minitest::Test
55
67
  def run_tu_with_fresh_reporter flags = %w[--seed 42]
56
68
  options = Minitest.process_args flags
57
69
 
58
- @output = StringIO.new("".encode('UTF-8'))
70
+ @output = StringIO.new("".encode(Encoding::UTF_8))
59
71
 
60
72
  self.reporter = Minitest::CompositeReporter.new
61
73
  reporter << Minitest::SummaryReporter.new(@output, options)
@@ -105,15 +117,20 @@ class MetaMetaMetaTestCase < Minitest::Test
105
117
  output.gsub!(/0x[A-Fa-f0-9]+/, "0xXXX")
106
118
  output.gsub!(/ +$/, "")
107
119
 
120
+ file = ->(s) { s.start_with?("/") ? "FULLFILE" : "FILE" }
121
+
108
122
  if windows? then
109
123
  output.gsub!(/\[(?:[A-Za-z]:)?[^\]:]+:\d+\]/, "[FILE:LINE]")
110
- output.gsub!(/^(\s+)(?:[A-Za-z]:)?[^:]+:\d+:in/, '\1FILE:LINE:in')
124
+ output.gsub!(/^(\s+)(?:[A-Za-z]:)?[^:]+:\d+:in [`']/, '\1FILE:LINE:in \'')
111
125
  else
112
- output.gsub!(/\[[^\]:]+:\d+\]/, "[FILE:LINE]")
113
- output.gsub!(/^(\s+)[^:]+:\d+:in/, '\1FILE:LINE:in')
126
+ output.gsub!(/\[([^\]:]+):\d+\]/) { "[#{file[$1]}:LINE]" }
127
+ output.gsub!(/^(\s+)([^:]+):\d+:in [`']/) { "#{$1}#{file[$2]}:LINE:in '" }
114
128
  end
115
129
 
116
- output.gsub!(/( at )[^:]+:\d+/, '\1[FILE:LINE]')
130
+ output.gsub!(/in [`']block in (?:([^']+)[#.])?/, "in 'block in")
131
+ output.gsub!(/in [`'](?:([^']+)[#.])?/, "in '")
132
+
133
+ output.gsub!(/( at )[^:]+:\d+/) { "#{$1}[#{file[$2]}:LINE]" } # eval?
117
134
 
118
135
  output
119
136
  end