diff options
Diffstat (limited to 'spec/ruby/core/thread/backtrace')
18 files changed, 737 insertions, 0 deletions
diff --git a/spec/ruby/core/thread/backtrace/limit_spec.rb b/spec/ruby/core/thread/backtrace/limit_spec.rb new file mode 100644 index 0000000000..b55ca67ea0 --- /dev/null +++ b/spec/ruby/core/thread/backtrace/limit_spec.rb @@ -0,0 +1,13 @@ +require_relative '../../../spec_helper' + +describe "Thread::Backtrace.limit" do + it "returns maximum backtrace length set by --backtrace-limit command-line option" do + out = ruby_exe("print Thread::Backtrace.limit", options: "--backtrace-limit=2") + out.should == "2" + end + + it "returns -1 when --backtrace-limit command-line option is not set" do + out = ruby_exe("print Thread::Backtrace.limit") + out.should == "-1" + end +end diff --git a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb new file mode 100644 index 0000000000..6d9482f2ae --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb @@ -0,0 +1,93 @@ +require_relative '../../../../spec_helper' +require_relative 'fixtures/classes' + +describe 'Thread::Backtrace::Location#absolute_path' do + before :each do + @frame = ThreadBacktraceLocationSpecs.locations[0] + end + + it 'returns the absolute path of the call frame' do + @frame.absolute_path.should == File.realpath(__FILE__) + end + + it 'returns an absolute path when using a relative main script path' do + script = fixture(__FILE__, 'absolute_path_main.rb') + Dir.chdir(File.dirname(script)) do + ruby_exe('absolute_path_main.rb').should == "absolute_path_main.rb\n#{script}\n" + end + end + + it 'returns the correct absolute path when using a relative main script path and changing CWD' do + script = fixture(__FILE__, 'subdir/absolute_path_main_chdir.rb') + sibling = fixture(__FILE__, 'subdir/sibling.rb') + subdir = File.dirname script + Dir.chdir(fixture(__FILE__)) do + ruby_exe('subdir/absolute_path_main_chdir.rb').should == "subdir/absolute_path_main_chdir.rb\n#{subdir}\n#{subdir}\n#{script}\n#{sibling}\n" + end + end + + context "when used in eval with a given filename" do + it "returns nil with absolute_path" do + code = "caller_locations(0)[0].absolute_path" + + eval(code, nil, "foo.rb").should == nil + eval(code, nil, "foo/bar.rb").should == nil + end + end + + context "when used in #method_added" do + it "returns the user filename that defined the method" do + path = fixture(__FILE__, "absolute_path_method_added.rb") + load path + locations = ScratchPad.recorded + locations[0].absolute_path.should == path + # Make sure it's from the class body, not from the file top-level + locations[0].label.should.include? 'MethodAddedAbsolutePath' + end + end + + context "when used in a core method" do + it "returns nil" do + location = nil + tap { location = caller_locations(1, 1)[0] } + location.label.should =~ /\A(?:Kernel#)?tap\z/ + if location.path.start_with?("<internal:") + location.absolute_path.should == nil + else + location.absolute_path.should == File.realpath(__FILE__) + end + end + end + + context "canonicalization" do + platform_is_not :windows do + before :each do + @file = fixture(__FILE__, "absolute_path.rb") + @symlink = tmp("symlink.rb") + File.symlink(@file, @symlink) + ScratchPad.record [] + end + + after :each do + rm_r @symlink + end + + it "returns a canonical path without symlinks, even when __FILE__ does not" do + realpath = File.realpath(@symlink) + realpath.should_not == @symlink + + load @symlink + ScratchPad.recorded.should == [@symlink, realpath] + end + + it "returns a canonical path without symlinks, even when __FILE__ is removed" do + realpath = File.realpath(@symlink) + realpath.should_not == @symlink + + ScratchPad << -> { rm_r(@symlink) } + load @symlink + ScratchPad.recorded.should == [@symlink, realpath] + end + end + end +end diff --git a/spec/ruby/core/thread/backtrace/location/base_label_spec.rb b/spec/ruby/core/thread/backtrace/location/base_label_spec.rb new file mode 100644 index 0000000000..739f62f42f --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/base_label_spec.rb @@ -0,0 +1,49 @@ +require_relative '../../../../spec_helper' +require_relative 'fixtures/classes' + +describe 'Thread::Backtrace::Location#base_label' do + before :each do + @frame = ThreadBacktraceLocationSpecs.locations[0] + end + + it 'returns the base label of the call frame' do + @frame.base_label.should == '<top (required)>' + end + + describe 'when call frame is inside a block' do + before :each do + @frame = ThreadBacktraceLocationSpecs.block_location[0] + end + + it 'returns the name of the method that contains the block' do + @frame.base_label.should == 'block_location' + end + end + + it "is <module:A> for a module body" do + module ThreadBacktraceLocationSpecs + module ModuleLabel + ScratchPad.record caller_locations(0, 1)[0].base_label + end + end + ScratchPad.recorded.should == '<module:ModuleLabel>' + end + + it "is <class:A> for a class body" do + module ThreadBacktraceLocationSpecs + class ClassLabel + ScratchPad.record caller_locations(0, 1)[0].base_label + end + end + ScratchPad.recorded.should == '<class:ClassLabel>' + end + + it "is 'singleton class' for a singleton class body" do + module ThreadBacktraceLocationSpecs + class << Object.new + ScratchPad.record caller_locations(0, 1)[0].base_label + end + end + ScratchPad.recorded.should =~ /\A(singleton class|<singleton class>)\z/ + end +end diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path.rb b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path.rb new file mode 100644 index 0000000000..875e97ffac --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path.rb @@ -0,0 +1,4 @@ +action = ScratchPad.recorded.pop +ScratchPad << __FILE__ +action.call if action +ScratchPad << caller_locations(0)[0].absolute_path diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path_main.rb b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path_main.rb new file mode 100644 index 0000000000..d2b23393d4 --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path_main.rb @@ -0,0 +1,2 @@ +puts __FILE__ +puts caller_locations(0)[0].absolute_path diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path_method_added.rb b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path_method_added.rb new file mode 100644 index 0000000000..26d6298a19 --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/fixtures/absolute_path_method_added.rb @@ -0,0 +1,10 @@ +module ThreadBacktraceLocationSpecs + class MethodAddedAbsolutePath + def self.method_added(name) + ScratchPad.record caller_locations + end + + def foo + end + end +end diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/classes.rb b/spec/ruby/core/thread/backtrace/location/fixtures/classes.rb new file mode 100644 index 0000000000..103c36b3a0 --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/fixtures/classes.rb @@ -0,0 +1,139 @@ +# These are top-level def on purpose to test those cases + +def label_top_method = ThreadBacktraceLocationSpecs::LABEL.call + +def self.label_sdef_method_of_main = ThreadBacktraceLocationSpecs::LABEL.call + +class << self + def label_sclass_method_of_main = ThreadBacktraceLocationSpecs::LABEL.call +end + +module ThreadBacktraceLocationSpecs + MODULE_LOCATION = caller_locations(0) rescue nil + INSTANCE = Object.new.extend(self) + LABEL = -> { caller_locations(1, 1)[0].label } + + def self.locations + caller_locations + end + + def instance_method_location + caller_locations(0) + end + + def self.method_location + caller_locations(0) + end + + def self.block_location + 1.times do + return caller_locations(0) + end + end + + def instance_block_location + 1.times do + return caller_locations(0) + end + end + + def self.locations_inside_nested_blocks + first_level_location = nil + second_level_location = nil + third_level_location = nil + + 1.times do + first_level_location = locations[0] + 1.times do + second_level_location = locations[0] + 1.times do + third_level_location = locations[0] + end + end + end + + [first_level_location, second_level_location, third_level_location] + end + + def instance_locations_inside_nested_block + loc = nil + 1.times do + 1.times do + loc = caller_locations(0) + end + end + loc + end + + def original_method = LABEL.call + alias_method :aliased_method, :original_method + + module M + class C + def regular_instance_method = LABEL.call + + def self.sdef_class_method = LABEL.call + + class << self + def sclass_method = LABEL.call + + def block_in_sclass_method + -> { + -> { LABEL.call }.call + }.call + end + end + block_in_sclass_method + end + end + + class M::D + def scoped_method = LABEL.call + + def self.sdef_scoped_method = LABEL.call + + class << self + def sclass_scoped_method = LABEL.call + end + + module ::ThreadBacktraceLocationSpecs + def top = LABEL.call + end + + class ::ThreadBacktraceLocationSpecs::Nested + def top_nested = LABEL.call + + class C + def top_nested_c = LABEL.call + end + end + end + + SOME_OBJECT = Object.new + SOME_OBJECT.instance_exec do + def unknown_def_singleton_method = LABEL.call + + def self.unknown_sdef_singleton_method = LABEL.call + end + + M.module_eval do + def module_eval_method = LABEL.call + + def self.sdef_module_eval_method = LABEL.call + end + + def ThreadBacktraceLocationSpecs.string_class_method = LABEL.call + + module M + def ThreadBacktraceLocationSpecs.nested_class_method = LABEL.call + end + + module M + module_function def mod_function = LABEL.call + end + + expr = self + def expr.sdef_expression = LABEL.call + + def expr.block_in_sdef_expression = -> { LABEL.call }.call +end diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/locations_in_main.rb b/spec/ruby/core/thread/backtrace/location/fixtures/locations_in_main.rb new file mode 100644 index 0000000000..b124c8161c --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/fixtures/locations_in_main.rb @@ -0,0 +1,5 @@ +1.times do + puts Thread.current.backtrace_locations(1..1)[0].label +end + +require_relative 'locations_in_required' diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/locations_in_required.rb b/spec/ruby/core/thread/backtrace/location/fixtures/locations_in_required.rb new file mode 100644 index 0000000000..5f5ed89e98 --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/fixtures/locations_in_required.rb @@ -0,0 +1,3 @@ +1.times do + puts Thread.current.backtrace_locations(1..1)[0].label +end diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/main.rb b/spec/ruby/core/thread/backtrace/location/fixtures/main.rb new file mode 100644 index 0000000000..bde208a059 --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/fixtures/main.rb @@ -0,0 +1,5 @@ +def backtrace_location_example + caller_locations[0].path +end + +print backtrace_location_example diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/path.rb b/spec/ruby/core/thread/backtrace/location/fixtures/path.rb new file mode 100644 index 0000000000..fba34cb0bc --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/fixtures/path.rb @@ -0,0 +1,2 @@ +ScratchPad << __FILE__ +ScratchPad << caller_locations(0)[0].path diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/subdir/absolute_path_main_chdir.rb b/spec/ruby/core/thread/backtrace/location/fixtures/subdir/absolute_path_main_chdir.rb new file mode 100644 index 0000000000..33c8fb36ef --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/fixtures/subdir/absolute_path_main_chdir.rb @@ -0,0 +1,11 @@ +puts __FILE__ +puts __dir__ +Dir.chdir __dir__ + +# Check __dir__ is still correct after chdir +puts __dir__ + +puts caller_locations(0)[0].absolute_path + +# require_relative also needs to know the absolute path of the current file so we test it here too +require_relative 'sibling' diff --git a/spec/ruby/core/thread/backtrace/location/fixtures/subdir/sibling.rb b/spec/ruby/core/thread/backtrace/location/fixtures/subdir/sibling.rb new file mode 100644 index 0000000000..2a854ddccd --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/fixtures/subdir/sibling.rb @@ -0,0 +1 @@ +puts __FILE__ diff --git a/spec/ruby/core/thread/backtrace/location/inspect_spec.rb b/spec/ruby/core/thread/backtrace/location/inspect_spec.rb new file mode 100644 index 0000000000..4df88a2f33 --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/inspect_spec.rb @@ -0,0 +1,13 @@ +require_relative '../../../../spec_helper' +require_relative 'fixtures/classes' + +describe 'Thread::Backtrace::Location#inspect' do + before :each do + @frame = ThreadBacktraceLocationSpecs.locations[0] + @line = __LINE__ - 1 + end + + it 'converts the call frame to a String' do + @frame.inspect.should.include?("#{__FILE__}:#{@line}:in ") + end +end diff --git a/spec/ruby/core/thread/backtrace/location/label_spec.rb b/spec/ruby/core/thread/backtrace/location/label_spec.rb new file mode 100644 index 0000000000..5f6a7b73df --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/label_spec.rb @@ -0,0 +1,227 @@ +require_relative '../../../../spec_helper' +require_relative 'fixtures/classes' + +describe 'Thread::Backtrace::Location#label' do + it 'returns the base label of the call frame' do + ThreadBacktraceLocationSpecs.locations[0].label.should.include?('<top (required)>') + end + + it 'returns the method name for a method location' do + ThreadBacktraceLocationSpecs.method_location[0].label.should =~ /\A(?:ThreadBacktraceLocationSpecs\.)?method_location\z/ + end + + it 'returns the block name for a block location' do + ThreadBacktraceLocationSpecs.block_location[0].label.should =~ /\Ablock in (?:ThreadBacktraceLocationSpecs\.)?block_location\z/ + end + + it 'returns the module name for a module location' do + ThreadBacktraceLocationSpecs::MODULE_LOCATION[0].label.should == "<module:ThreadBacktraceLocationSpecs>" + end + + it 'includes the nesting level of a block as part of the location label' do + first_level_location, second_level_location, third_level_location = + ThreadBacktraceLocationSpecs.locations_inside_nested_blocks + + first_level_location.label.should =~ /\Ablock in (?:ThreadBacktraceLocationSpecs\.)?locations_inside_nested_blocks\z/ + second_level_location.label.should =~ /\Ablock \(2 levels\) in (?:ThreadBacktraceLocationSpecs\.)?locations_inside_nested_blocks\z/ + third_level_location.label.should =~ /\Ablock \(3 levels\) in (?:ThreadBacktraceLocationSpecs\.)?locations_inside_nested_blocks\z/ + end + + it 'sets the location label for a top-level block differently depending on it being in the main file or a required file' do + path = fixture(__FILE__, "locations_in_main.rb") + main_label, required_label = ruby_exe(path).lines + + main_label.should == "block in <main>\n" + required_label.should == "block in <top (required)>\n" + end + + it "return the same name as the caller for eval" do + this = caller_locations(0)[0].label + eval("caller_locations(0)[0]").label.should == this + + b = binding + b.eval("caller_locations(0)[0]").label.should == this + + b.local_variable_set(:binding_var1, 1) + b.eval("caller_locations(0)[0]").label.should == this + + b.local_variable_set(:binding_var2, 2) + b.eval("caller_locations(0)[0]").label.should == this + + b.local_variable_set(:binding_var2, 2) + eval("caller_locations(0)[0]", b).label.should == this + end + + ruby_version_is "3.4" do + describe "is Module#method for" do + it "a core method defined natively" do + BasicObject.instance_method(:instance_exec).should_not.source_location + loc = nil + loc = instance_exec { caller_locations(1, 1)[0] } + loc.label.should == "BasicObject#instance_exec" + end + + it "a core method defined in Ruby" do + Kernel.instance_method(:tap).should.source_location + loc = nil + tap { loc = caller_locations(1, 1)[0] } + loc.label.should == "Kernel#tap" + end + + it "an instance method defined in Ruby" do + ThreadBacktraceLocationSpecs::INSTANCE.instance_method_location[0].label.should == "ThreadBacktraceLocationSpecs#instance_method_location" + end + + it "a block in an instance method defined in Ruby" do + ThreadBacktraceLocationSpecs::INSTANCE.instance_block_location[0].label.should == "block in ThreadBacktraceLocationSpecs#instance_block_location" + end + + it "a nested block in an instance method defined in Ruby" do + ThreadBacktraceLocationSpecs::INSTANCE.instance_locations_inside_nested_block[0].label.should == "block (2 levels) in ThreadBacktraceLocationSpecs#instance_locations_inside_nested_block" + end + + it "a method defined via module_exec" do + ThreadBacktraceLocationSpecs.module_exec do + def in_module_exec + caller_locations(0) + end + end + ThreadBacktraceLocationSpecs::INSTANCE.in_module_exec[0].label.should == "ThreadBacktraceLocationSpecs#in_module_exec" + end + + it "a method defined via module_eval" do + ThreadBacktraceLocationSpecs.module_eval <<~RUBY + def in_module_eval + caller_locations(0) + end + RUBY + ThreadBacktraceLocationSpecs::INSTANCE.in_module_eval[0].label.should == "ThreadBacktraceLocationSpecs#in_module_eval" + end + end + + describe "is Module.method for" do + it "a singleton method defined in Ruby" do + ThreadBacktraceLocationSpecs.method_location[0].label.should == "ThreadBacktraceLocationSpecs.method_location" + end + + it "a block in a singleton method defined in Ruby" do + ThreadBacktraceLocationSpecs.block_location[0].label.should == "block in ThreadBacktraceLocationSpecs.block_location" + end + + it "a nested block in a singleton method defined in Ruby" do + ThreadBacktraceLocationSpecs.locations_inside_nested_blocks[2].label.should == "block (3 levels) in ThreadBacktraceLocationSpecs.locations_inside_nested_blocks" + end + + it "a singleton method defined via def Const.method" do + def ThreadBacktraceLocationSpecs.def_singleton + caller_locations(0) + end + ThreadBacktraceLocationSpecs.def_singleton[0].label.should == "ThreadBacktraceLocationSpecs.def_singleton" + end + end + + it "shows the original method name for an aliased method" do + ThreadBacktraceLocationSpecs::INSTANCE.aliased_method.should == "ThreadBacktraceLocationSpecs#original_method" + end + + # A wide variety of cases. + # These show interesting cases when trying to determine the name statically/at parse time + describe "is correct for" do + base = ThreadBacktraceLocationSpecs + + it "M::C#regular_instance_method" do + base::M::C.new.regular_instance_method.should == "#{base}::M::C#regular_instance_method" + end + + it "M::C.sdef_class_method" do + base::M::C.sdef_class_method.should == "#{base}::M::C.sdef_class_method" + end + + it "M::C.sclass_method" do + base::M::C.sclass_method.should == "#{base}::M::C.sclass_method" + end + + it "M::C.block_in_sclass_method" do + base::M::C.block_in_sclass_method.should == "block (2 levels) in #{base}::M::C.block_in_sclass_method" + end + + it "M::D#scoped_method" do + base::M::D.new.scoped_method.should == "#{base}::M::D#scoped_method" + end + + it "M::D.sdef_scoped_method" do + base::M::D.sdef_scoped_method.should == "#{base}::M::D.sdef_scoped_method" + end + + it "M::D.sclass_scoped_method" do + base::M::D.sclass_scoped_method.should == "#{base}::M::D.sclass_scoped_method" + end + + it "ThreadBacktraceLocationSpecs#top" do + ThreadBacktraceLocationSpecs::INSTANCE.top.should == "ThreadBacktraceLocationSpecs#top" + end + + it "ThreadBacktraceLocationSpecs::Nested#top_nested" do + ThreadBacktraceLocationSpecs::Nested.new.top_nested.should == "ThreadBacktraceLocationSpecs::Nested#top_nested" + end + + it "ThreadBacktraceLocationSpecs::Nested::C#top_nested_c" do + ThreadBacktraceLocationSpecs::Nested::C.new.top_nested_c.should == "ThreadBacktraceLocationSpecs::Nested::C#top_nested_c" + end + + it "Object#label_top_method" do + label_top_method.should == "Object#label_top_method" + end + + it "main.label_sdef_method_of_main" do + main = TOPLEVEL_BINDING.receiver + main.label_sdef_method_of_main.should == "label_sdef_method_of_main" + end + + it "main.label_sclass_method_of_main" do + main = TOPLEVEL_BINDING.receiver + main.label_sclass_method_of_main.should == "label_sclass_method_of_main" + end + + it "unknown_def_singleton_method" do + base::SOME_OBJECT.unknown_def_singleton_method.should == "unknown_def_singleton_method" + end + + it "unknown_sdef_singleton_method" do + base::SOME_OBJECT.unknown_sdef_singleton_method.should == "unknown_sdef_singleton_method" + end + + it "M#module_eval_method" do + Object.new.extend(base::M).module_eval_method.should == "#{base}::M#module_eval_method" + end + + it "M.sdef_module_eval_method" do + base::M.sdef_module_eval_method.should == "#{base}::M.sdef_module_eval_method" + end + + it "ThreadBacktraceLocationSpecs.string_class_method" do + ThreadBacktraceLocationSpecs.string_class_method.should == "ThreadBacktraceLocationSpecs.string_class_method" + end + + it "ThreadBacktraceLocationSpecs.nested_class_method" do + ThreadBacktraceLocationSpecs.nested_class_method.should == "ThreadBacktraceLocationSpecs.nested_class_method" + end + + it "M#mod_function" do + Object.new.extend(base::M).send(:mod_function).should == "#{base}::M#mod_function" + end + + it "M.mod_function" do + base::M.mod_function.should == "#{base}::M.mod_function" + end + + it "sdef_expression" do + base.sdef_expression.should == "#{base}.sdef_expression" + end + + it "block_in_sdef_expression" do + base.block_in_sdef_expression.should == "block in #{base}.block_in_sdef_expression" + end + end + end +end diff --git a/spec/ruby/core/thread/backtrace/location/lineno_spec.rb b/spec/ruby/core/thread/backtrace/location/lineno_spec.rb new file mode 100644 index 0000000000..10457f80f0 --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/lineno_spec.rb @@ -0,0 +1,23 @@ +require_relative '../../../../spec_helper' +require_relative 'fixtures/classes' + +describe 'Thread::Backtrace::Location#lineno' do + before :each do + @frame = ThreadBacktraceLocationSpecs.locations[0] + @line = __LINE__ - 1 + end + + it 'returns the line number of the call frame' do + @frame.lineno.should == @line + end + + it 'should be the same line number as in #to_s, including for core methods' do + # Get the caller_locations from a call made into a core library method + locations = [:non_empty].map { caller_locations }[0] + + locations.each do |location| + line_number = location.to_s[/:(\d+):/, 1] + location.lineno.should == Integer(line_number) + end + end +end diff --git a/spec/ruby/core/thread/backtrace/location/path_spec.rb b/spec/ruby/core/thread/backtrace/location/path_spec.rb new file mode 100644 index 0000000000..75f76833a9 --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/path_spec.rb @@ -0,0 +1,124 @@ +require_relative '../../../../spec_helper' +require_relative 'fixtures/classes' + +describe 'Thread::Backtrace::Location#path' do + context 'outside a main script' do + it 'returns an absolute path' do + frame = ThreadBacktraceLocationSpecs.locations[0] + + frame.path.should == __FILE__ + end + end + + context 'in a main script' do + before do + @script = fixture(__FILE__, 'main.rb') + end + + context 'when the script is in the working directory' do + before do + @directory = File.dirname(@script) + end + + context 'when using a relative script path' do + it 'returns a path relative to the working directory' do + Dir.chdir(@directory) { + ruby_exe('main.rb') + }.should == 'main.rb' + end + end + + context 'when using an absolute script path' do + it 'returns an absolute path' do + Dir.chdir(@directory) { + ruby_exe(@script) + }.should == @script + end + end + end + + context 'when the script is in a sub directory of the working directory' do + context 'when using a relative script path' do + it 'returns a path relative to the working directory' do + path = 'fixtures/main.rb' + directory = __dir__ + Dir.chdir(directory) { + ruby_exe(path) + }.should == path + end + end + + context 'when using an absolute script path' do + it 'returns an absolute path' do + ruby_exe(@script).should == @script + end + end + end + + context 'when the script is outside of the working directory' do + before :each do + @parent_dir = tmp('path_outside_pwd') + @sub_dir = File.join(@parent_dir, 'sub') + @script = File.join(@parent_dir, 'main.rb') + source = fixture(__FILE__, 'main.rb') + + mkdir_p(@sub_dir) + + cp(source, @script) + end + + after :each do + rm_r(@parent_dir) + end + + context 'when using a relative script path' do + it 'returns a path relative to the working directory' do + Dir.chdir(@sub_dir) { + ruby_exe('../main.rb') + }.should == '../main.rb' + end + end + + context 'when using an absolute path' do + it 'returns an absolute path' do + ruby_exe(@script).should == @script + end + end + end + end + + it 'should be the same path as in #to_s, including for core methods' do + # Get the caller_locations from a call made into a core library method + locations = [:non_empty].map { caller_locations }[0] + + locations.each do |location| + filename = location.to_s[/^(.+):\d+:/, 1] + path = location.path + + path.should == filename + end + end + + context "canonicalization" do + platform_is_not :windows do + before :each do + @file = fixture(__FILE__, "path.rb") + @symlink = tmp("symlink.rb") + File.symlink(@file, @symlink) + ScratchPad.record [] + end + + after :each do + rm_r @symlink + end + + it "returns a non-canonical path with symlinks, the same as __FILE__" do + realpath = File.realpath(@symlink) + realpath.should_not == @symlink + + load @symlink + ScratchPad.recorded.should == [@symlink, @symlink] + end + end + end +end diff --git a/spec/ruby/core/thread/backtrace/location/to_s_spec.rb b/spec/ruby/core/thread/backtrace/location/to_s_spec.rb new file mode 100644 index 0000000000..983ce4c3f8 --- /dev/null +++ b/spec/ruby/core/thread/backtrace/location/to_s_spec.rb @@ -0,0 +1,13 @@ +require_relative '../../../../spec_helper' +require_relative 'fixtures/classes' + +describe 'Thread::Backtrace::Location#to_s' do + before :each do + @frame = ThreadBacktraceLocationSpecs.locations[0] + @line = __LINE__ - 1 + end + + it 'converts the call frame to a String' do + @frame.to_s.should.include?("#{__FILE__}:#{@line}:in ") + end +end |
