diff --git a/lib/power_assert.rb b/lib/power_assert.rb index 8de209e..5475c71 100644 --- a/lib/power_assert.rb +++ b/lib/power_assert.rb @@ -37,13 +37,15 @@ def app_caller_locations end def app_context? - top_frame = caller_locations.drop_while {|i| i.path.start_with?(POWER_ASSERT_LIB_DIR) }.first + top_frame = caller_locations.drop_while {|i| i.path&.start_with?(POWER_ASSERT_LIB_DIR) }.first top_frame and ! internal_file?(top_frame.path) end private def internal_file?(file) + return false unless file + INTERNAL_LIB_DIRS.find do |_, dir| file.start_with?(dir) end diff --git a/test/nil_path_location_test.rb b/test/nil_path_location_test.rb new file mode 100644 index 0000000..8b1a312 --- /dev/null +++ b/test/nil_path_location_test.rb @@ -0,0 +1,32 @@ +require_relative 'test_helper' + +class TestNilPathLocation < Test::Unit::TestCase + include PowerAssertTestHelper + + t do + obj = [] + def obj.foo; self; end + enum = Enumerator.new {|y| y << obj.foo } + def enum.inspect; '#'; end + assert_equal < +END + enum.next.foo + } + end + + t do + # Yielding on a non-target thread keeps the `TracePoint` for `:call` and `:c_call` events enabled while + # the `Enumerator`'s fiber runs, so that `app_context?` walks caller locations containing a frame without a path. + enum = Enumerator.new {|y| y << 1 } + message = ::PowerAssert.start(-> { enum.next }) do |pa| + Thread.new { pa.yield }.join + pa.message + end + assert_equal '', message + end +end