diff options
Diffstat (limited to 'test/ruby/test_backtrace.rb')
| -rw-r--r-- | test/ruby/test_backtrace.rb | 324 |
1 files changed, 303 insertions, 21 deletions
diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb index 3d2caf4e76..dad7dfcb55 100644 --- a/test/ruby/test_backtrace.rb +++ b/test/ruby/test_backtrace.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: false require 'test/unit' -require 'thread' +require 'tempfile' class TestBacktrace < Test::Unit::TestCase def test_exception @@ -14,6 +15,58 @@ class TestBacktrace < Test::Unit::TestCase assert_match(/.+:\d+:.+/, bt[0]) end + def helper_test_exception_backtrace_locations + raise + end + + def test_exception_backtrace_locations + backtrace, backtrace_locations = Fiber.new{ + begin + raise + rescue => e + [e.backtrace, e.backtrace_locations] + end + }.resume + assert_equal(backtrace, backtrace_locations.map{|e| e.to_s}) + + backtrace, backtrace_locations = Fiber.new{ + begin + begin + helper_test_exception_backtrace_locations + rescue + raise + end + rescue => e + [e.backtrace, e.backtrace_locations] + end + }.resume + assert_equal(backtrace, backtrace_locations.map{|e| e.to_s}) + end + + def call_helper_test_exception_backtrace_locations + helper_test_exception_backtrace_locations(:bad_argument) + end + + def test_argument_error_backtrace_locations + backtrace, backtrace_locations = Fiber.new{ + begin + helper_test_exception_backtrace_locations(1) + rescue ArgumentError => e + [e.backtrace, e.backtrace_locations] + end + }.resume + assert_equal(backtrace, backtrace_locations.map{|e| e.to_s}) + + backtrace, backtrace_locations = Fiber.new{ + begin + call_helper_test_exception_backtrace_locations + rescue ArgumentError => e + [e.backtrace, e.backtrace_locations] + end + }.resume + assert_equal(backtrace, backtrace_locations.map{|e| e.to_s}) + end + def test_caller_lev cs = [] Fiber.new{ @@ -26,10 +79,10 @@ class TestBacktrace < Test::Unit::TestCase cs << caller(5) }.call }.resume - assert_equal(3, cs[0].size) - assert_equal(2, cs[1].size) - assert_equal(1, cs[2].size) - assert_equal(0, cs[3].size) + assert_equal(2, cs[0].size) + assert_equal(1, cs[1].size) + assert_equal(0, cs[2].size) + assert_equal(nil, cs[3]) assert_equal(nil, cs[4]) # @@ -49,7 +102,7 @@ class TestBacktrace < Test::Unit::TestCase } end } - bt = Fiber.new{ + Fiber.new{ rec[max] }.resume end @@ -59,21 +112,21 @@ class TestBacktrace < Test::Unit::TestCase rec = lambda{|n| if n < 0 (m*6).times{|lev| - (m*6).times{|n| + (m*6).times{|i| t = caller(0).size - r = caller(lev, n) + r = caller(lev, i) r = r.size if r.respond_to? :size - # STDERR.puts [t, lev, n, r].inspect - if n == 0 - assert_equal(0, r, [t, lev, n, r].inspect) + # STDERR.puts [t, lev, i, r].inspect + if i == 0 + assert_equal(0, r, [t, lev, i, r].inspect) elsif t < lev - assert_equal(nil, r, [t, lev, n, r].inspect) + assert_equal(nil, r, [t, lev, i, r].inspect) else - if t - lev > n - assert_equal(n, r, [t, lev, n, r].inspect) + if t - lev > i + assert_equal(i, r, [t, lev, i, r].inspect) else - assert_equal(t - lev, r, [t, lev, n, r].inspect) + assert_equal(t - lev, r, [t, lev, i, r].inspect) end end } @@ -85,10 +138,103 @@ class TestBacktrace < Test::Unit::TestCase rec[m] end + def test_caller_with_limit + x = nil + c = Class.new do + define_method(:bar) do + x = caller(1, 1) + end + end + [c.new].group_by(&:bar) + assert_equal 1, x.length + assert_equal caller(0), caller(0, nil) + end + def test_caller_with_nil_length assert_equal caller(0), caller(0, nil) end + def test_each_backtrace_location + assert_nil(Thread.each_caller_location {}) + + assert_raise(LocalJumpError) {Thread.each_caller_location} + + i = 0 + cl = caller_locations(1, 1)[0]; ecl = Thread.each_caller_location{|x| i+=1; break x if i == 1} + assert_equal(cl.to_s, ecl.to_s) + assert_kind_of(Thread::Backtrace::Location, ecl) + + i = 0 + ary = [] + cllr = caller_locations(1, 2); last = Thread.each_caller_location{|x| ary << x; i+=1; break x if i == 2} + assert_equal(cllr.map(&:to_s), ary.map(&:to_s)) + assert_kind_of(Thread::Backtrace::Location, last) + + i = 0 + ary = [] + ->{->{ + cllr = caller_locations(1, 2); last = Thread.each_caller_location{|x| ary << x; i+=1; break x if i == 2} + }.()}.() + assert_equal(cllr.map(&:to_s), ary.map(&:to_s)) + assert_kind_of(Thread::Backtrace::Location, last) + + cllr = caller_locations(1, 2); ary = Thread.to_enum(:each_caller_location).to_a[2..3] + assert_equal(cllr.map(&:to_s), ary.map(&:to_s)) + + ecl = Thread.to_enum(:each_caller_location) + assert_raise(StopIteration) { + ecl.next + } + + ary = [] + cl = caller_locations(1, 2); Thread.each_caller_location(1, 2) {|x| ary << x} + assert_equal(cl.map(&:to_s), ary.map(&:to_s)) + end + + def test_caller_locations_first_label + def self.label + caller_locations.first.label + end + + def self.label_caller + label + end + + assert_equal 'label_caller', label_caller + + [1].group_by do + assert_equal 'label_caller', label_caller + end + end + + def test_caller_limit_cfunc_iseq_no_pc + def self.a; [1].group_by { b } end + def self.b + [ + caller_locations(2, 1).first.base_label, + caller_locations(3, 1).first.base_label + ] + end + assert_equal({["each", "group_by"]=>[1]}, a) + end + + def test_caller_location_inspect_cfunc_iseq_no_pc + def self.foo + @res = caller_locations(2, 1).inspect + end + @line = __LINE__ + 1 + [1].map.map { [1].map.map { foo } } + assert_equal("[\"#{__FILE__}:#{@line}:in 'Array#map'\"]", @res) + end + + def test_caller_location_path_cfunc_iseq_no_pc + def self.foo + @res = caller_locations(2, 1)[0].path + end + [1].map.map { [1].map.map { foo } } + assert_equal(__FILE__, @res) + end + def test_caller_locations cs = caller(0); locs = caller_locations(0).map{|loc| loc.to_s @@ -111,6 +257,59 @@ class TestBacktrace < Test::Unit::TestCase } end + def test_caller_locations_path + loc, = caller_locations(0, 1) + assert_equal(__FILE__, loc.path) + Tempfile.create(%w"caller_locations .rb") do |f| + f.puts "caller_locations(0, 1)[0].tap {|loc| puts loc.path}" + f.close + dir, base = File.split(f.path) + assert_in_out_err(["-C", dir, base], "", [base]) + end + end + + def test_caller_locations_absolute_path + loc, = caller_locations(0, 1) + assert_equal(__FILE__, loc.absolute_path) + Tempfile.create(%w"caller_locations .rb") do |f| + f.puts "caller_locations(0, 1)[0].tap {|loc| puts loc.absolute_path}" + f.close + assert_in_out_err(["-C", *File.split(f.path)], "", [File.realpath(f.path)]) + end + end + + def test_caller_locations_lineno + loc, = caller_locations(0, 1) + assert_equal(__LINE__-1, loc.lineno) + Tempfile.create(%w"caller_locations .rb") do |f| + f.puts "caller_locations(0, 1)[0].tap {|loc| puts loc.lineno}" + f.close + assert_in_out_err(["-C", *File.split(f.path)], "", ["1"]) + end + end + + def test_caller_locations_base_label + assert_equal("#{__method__}", caller_locations(0, 1)[0].base_label) + loc, = tap {break caller_locations(0, 1)} + assert_equal("#{__method__}", loc.base_label) + begin + raise + rescue + assert_equal("#{__method__}", caller_locations(0, 1)[0].base_label) + end + end + + def test_caller_locations_label + assert_equal("TestBacktrace##{__method__}", caller_locations(0, 1)[0].label) + loc, = tap {break caller_locations(0, 1)} + assert_equal("block in TestBacktrace##{__method__}", loc.label) + begin + raise + rescue + assert_equal("TestBacktrace##{__method__}", caller_locations(0, 1)[0].label) + end + end + def th_rec q, n=10 if n > 1 th_rec q, n-1 @@ -121,7 +320,7 @@ class TestBacktrace < Test::Unit::TestCase def test_thread_backtrace begin - q = Queue.new + q = Thread::Queue.new th = Thread.new{ th_rec q } @@ -143,12 +342,13 @@ class TestBacktrace < Test::Unit::TestCase assert_equal(n, th.backtrace_locations(0, n + 1).size) ensure q << true + th.join end end def test_thread_backtrace_locations_with_range begin - q = Queue.new + q = Thread::Queue.new th = Thread.new{ th_rec q } @@ -160,6 +360,7 @@ class TestBacktrace < Test::Unit::TestCase assert_equal(bt, locs) ensure q << true + th.join end end @@ -170,8 +371,7 @@ class TestBacktrace < Test::Unit::TestCase alias foo bar end end - /`(.*)'\z/.match e.backtrace[0] - assert_not_match(/\Acore#/, $1) + assert_not_match(/\Acore#/, e.backtrace_locations[0].base_label) end def test_core_backtrace_undef @@ -181,7 +381,89 @@ class TestBacktrace < Test::Unit::TestCase undef foo end end - /`(.*)'\z/.match e.backtrace[0] - assert_not_match(/\Acore#/, $1) + assert_not_match(/\Acore#/, e.backtrace_locations[0].base_label) + end + + def test_core_backtrace_hash_merge + e = assert_raise(TypeError) do + {**1} + end + assert_not_match(/\Acore#/, e.backtrace_locations[0].base_label) + end + + def test_notty_backtrace + err = ["-:1:in '<main>': unhandled exception"] + assert_in_out_err([], "raise", [], err) + + err = ["-:2:in 'Object#foo': foo! (RuntimeError)", + "\tfrom -:4:in '<main>'"] + assert_in_out_err([], <<-"end;", [], err) + def foo + raise "foo!" + end + foo + end; + + err = ["-:7:in 'Object#bar': bar! (RuntimeError)", + "\tfrom -:9:in '<main>'", + "-:2:in 'Object#foo': foo! (RuntimeError)", + "\tfrom -:5:in 'Object#bar'", + "\tfrom -:9:in '<main>'"] + assert_in_out_err([], <<-"end;", [], err) + def foo + raise "foo!" + end + def bar + foo + rescue + raise "bar!" + end + bar + end; + end + + def test_caller_to_enum + err = ["-:3:in 'Object#foo': unhandled exception", "\tfrom -:in 'Enumerator#each'"] + assert_in_out_err([], <<-"end;", [], err, "[ruby-core:91911]") + def foo + return to_enum(__method__) unless block_given? + raise + yield 1 + end + + enum = foo + enum.next + end; + end + + def test_no_receiver_for_anonymous_class + err = ["-:2:in 'bar': unhandled exception", # Not '#<Class:0xXXX>.bar' + "\tfrom -:3:in '<main>'"] + assert_in_out_err([], <<-"end;", [], err) + foo = Class.new + def foo.bar = raise + foo.bar + end; + + err = ["-:3:in 'baz': unhandled exception", # Not '#<Class:0xXXX>::Bar.baz' + "\tfrom -:4:in '<main>'"] + assert_in_out_err([], <<-"end;", [], err) + foo = Class.new + foo::Bar = Class.new + def (foo::Bar).baz = raise + foo::Bar.baz + end; + end + + def test_backtrace_internal_frame + backtrace = tap { break caller_locations(0) } + assert_equal(__FILE__, backtrace[1].path) # not "<internal:kernel>" + assert_equal("Kernel#tap", backtrace[1].label) + end + + def test_backtrace_on_argument_error + lineno = __LINE__; [1, 2].inject(:tap) + rescue ArgumentError + assert_equal("#{ __FILE__ }:#{ lineno }:in 'Kernel#tap'", $!.backtrace[0].to_s) end end |
