From 126fd5f15cff0d3bf314d90d8c21a3ae25ae8e68 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 27 Jul 2020 21:41:08 +0200 Subject: Update to ruby/spec@07164da --- spec/ruby/core/array/fill_spec.rb | 3 +- spec/ruby/core/exception/backtrace_spec.rb | 2 +- spec/ruby/core/kernel/__dir___spec.rb | 7 + spec/ruby/core/kernel/at_exit_spec.rb | 31 ++- spec/ruby/core/kernel/fixtures/__dir__.rb | 2 + spec/ruby/core/kernel/fixtures/at_exit.rb | 3 + spec/ruby/core/kernel/fixtures/warn_require.rb | 1 + .../core/kernel/fixtures/warn_require_caller.rb | 2 + spec/ruby/core/kernel/raise_spec.rb | 21 ++ spec/ruby/core/kernel/warn_spec.rb | 13 + spec/ruby/core/module/fixtures/refine.rb | 12 +- spec/ruby/core/module/refine_spec.rb | 300 ++++++++++++++++++++- spec/ruby/core/string/split_spec.rb | 68 ++++- .../backtrace/location/absolute_path_spec.rb | 7 + .../location/fixtures/absolute_path_main.rb | 2 + .../core/thread/backtrace/location/lineno_spec.rb | 10 + .../core/thread/backtrace/location/path_spec.rb | 12 + spec/ruby/core/thread/raise_spec.rb | 2 +- 18 files changed, 480 insertions(+), 18 deletions(-) create mode 100644 spec/ruby/core/kernel/fixtures/__dir__.rb create mode 100644 spec/ruby/core/kernel/fixtures/at_exit.rb create mode 100644 spec/ruby/core/kernel/fixtures/warn_require.rb create mode 100644 spec/ruby/core/kernel/fixtures/warn_require_caller.rb create mode 100644 spec/ruby/core/thread/backtrace/location/fixtures/absolute_path_main.rb (limited to 'spec/ruby/core') diff --git a/spec/ruby/core/array/fill_spec.rb b/spec/ruby/core/array/fill_spec.rb index 96c8862208..6745bc8d09 100644 --- a/spec/ruby/core/array/fill_spec.rb +++ b/spec/ruby/core/array/fill_spec.rb @@ -207,8 +207,9 @@ describe "Array#fill with (filler, index, length)" do not_supported_on :opal do it "raises an ArgumentError or RangeError for too-large sizes" do + error_types = [RangeError, ArgumentError] arr = [1, 2, 3] - -> { arr.fill(10, 1, fixnum_max) }.should raise_error(ArgumentError) + -> { arr.fill(10, 1, fixnum_max) }.should raise_error { |err| error_types.should include(err.class) } -> { arr.fill(10, 1, bignum_value) }.should raise_error(RangeError) end end diff --git a/spec/ruby/core/exception/backtrace_spec.rb b/spec/ruby/core/exception/backtrace_spec.rb index 2d6825180a..3f74c4cefe 100644 --- a/spec/ruby/core/exception/backtrace_spec.rb +++ b/spec/ruby/core/exception/backtrace_spec.rb @@ -43,7 +43,7 @@ describe "Exception#backtrace" do # This regexp is deliberately imprecise to account for the need to abstract out # the paths of the included mspec files and the desire to avoid specifying in any # detail what the in `...' portion looks like. - line.should =~ /^[^ ]+\:\d+(:in `[^`]+')?$/ + line.should =~ /^.+:\d+:in `[^`]+'$/ end end diff --git a/spec/ruby/core/kernel/__dir___spec.rb b/spec/ruby/core/kernel/__dir___spec.rb index 64b439161f..0686b31e97 100644 --- a/spec/ruby/core/kernel/__dir___spec.rb +++ b/spec/ruby/core/kernel/__dir___spec.rb @@ -5,6 +5,13 @@ describe "Kernel#__dir__" do __dir__.should == File.realpath(File.dirname(__FILE__)) end + it "returns the expanded path of the directory when used in the main script" do + fixtures_dir = File.dirname(fixture(__FILE__, '__dir__.rb')) + Dir.chdir(fixtures_dir) do + ruby_exe("__dir__.rb").should == "__dir__.rb\n#{fixtures_dir}\n" + end + end + context "when used in eval with a given filename" do it "returns File.dirname(filename)" do eval("__dir__", nil, "foo.rb").should == "." diff --git a/spec/ruby/core/kernel/at_exit_spec.rb b/spec/ruby/core/kernel/at_exit_spec.rb index 21149f965b..7bdb5391fe 100644 --- a/spec/ruby/core/kernel/at_exit_spec.rb +++ b/spec/ruby/core/kernel/at_exit_spec.rb @@ -23,7 +23,7 @@ describe "Kernel.at_exit" do it "gives access to the last raised exception" do code = <<-EOC at_exit do - puts "The exception matches: \#{$! == $exception}" + puts "The exception matches: \#{$! == $exception} (message=\#{$!.message})" end begin @@ -33,10 +33,35 @@ describe "Kernel.at_exit" do end EOC - result = ruby_exe(code, args: "2>&1", escape: true) - result.should =~ /The exception matches: true/ + result = ruby_exe(code, args: "2>&1") + result.lines.should.include?("The exception matches: true (message=foo)\n") end + it "both exceptions in at_exit and in the main script are printed" do + result = ruby_exe('at_exit { raise "at_exit_error" }; raise "main_script_error"', args: "2>&1") + result.should.include?('at_exit_error (RuntimeError)') + result.should.include?('main_script_error (RuntimeError)') + end + + it "decides the exit status if both at_exit and the main script raise SystemExit" do + ruby_exe('at_exit { exit 43 }; exit 42', args: "2>&1") + $?.exitstatus.should == 43 + end + + it "runs all at_exit even if some raise exceptions" do + code = 'at_exit { STDERR.puts "last" }; at_exit { exit 43 }; at_exit { STDERR.puts "first" }; exit 42' + result = ruby_exe(code, args: "2>&1") + result.should == "first\nlast\n" + $?.exitstatus.should == 43 + end + + it "runs at_exit handlers even if the main script fails to parse" do + script = fixture(__FILE__, "at_exit.rb") + result = ruby_exe('{', options: "-r#{script}", args: "2>&1") + $?.should_not.success? + result.should.include?("at_exit ran\n") + result.should.include?("syntax error") + end end describe "Kernel#at_exit" do diff --git a/spec/ruby/core/kernel/fixtures/__dir__.rb b/spec/ruby/core/kernel/fixtures/__dir__.rb new file mode 100644 index 0000000000..bf9a15e3c8 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/__dir__.rb @@ -0,0 +1,2 @@ +puts __FILE__ +puts __dir__ diff --git a/spec/ruby/core/kernel/fixtures/at_exit.rb b/spec/ruby/core/kernel/fixtures/at_exit.rb new file mode 100644 index 0000000000..9c11a7ad6c --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/at_exit.rb @@ -0,0 +1,3 @@ +at_exit do + STDERR.puts "at_exit ran" +end diff --git a/spec/ruby/core/kernel/fixtures/warn_require.rb b/spec/ruby/core/kernel/fixtures/warn_require.rb new file mode 100644 index 0000000000..c4b0733233 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/warn_require.rb @@ -0,0 +1 @@ +warn 'warn-require-warning', uplevel: 1 diff --git a/spec/ruby/core/kernel/fixtures/warn_require_caller.rb b/spec/ruby/core/kernel/fixtures/warn_require_caller.rb new file mode 100644 index 0000000000..35a0f969f9 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/warn_require_caller.rb @@ -0,0 +1,2 @@ +# Use a different line than just 1 +require "#{__dir__}/warn_require" diff --git a/spec/ruby/core/kernel/raise_spec.rb b/spec/ruby/core/kernel/raise_spec.rb index bf26560246..591daa03cf 100644 --- a/spec/ruby/core/kernel/raise_spec.rb +++ b/spec/ruby/core/kernel/raise_spec.rb @@ -6,6 +6,27 @@ describe "Kernel#raise" do it "is a private method" do Kernel.should have_private_instance_method(:raise) end + + it "re-raises the previously rescued exception if no exception is specified" do + ScratchPad.record nil + + -> do + begin + raise Exception, "outer" + ScratchPad.record :no_abort + rescue Exception + begin + raise StandardError, "inner" + rescue StandardError + end + + raise + ScratchPad.record :no_reraise + end + end.should raise_error(Exception, "outer") + + ScratchPad.recorded.should be_nil + end end describe "Kernel#raise" do diff --git a/spec/ruby/core/kernel/warn_spec.rb b/spec/ruby/core/kernel/warn_spec.rb index 84cfa78672..79e78dd4a3 100644 --- a/spec/ruby/core/kernel/warn_spec.rb +++ b/spec/ruby/core/kernel/warn_spec.rb @@ -101,6 +101,19 @@ describe "Kernel#warn" do -> { w.f4("foo", 3) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f3_call_lineno}: warning: foo|) end + # Test both explicitly without and with RubyGems as RubyGems overrides Kernel#warn + it "shows the caller of #require and not #require itself without RubyGems" do + file = fixture(__FILE__ , "warn_require_caller.rb") + ruby_exe(file, options: "--disable-gems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n" + end + + ruby_version_is "2.6" do + it "shows the caller of #require and not #require itself with RubyGems loaded" do + file = fixture(__FILE__ , "warn_require_caller.rb") + ruby_exe(file, options: "-rrubygems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n" + end + end + it "converts first arg using to_s" do w = KernelSpecs::WarnInNestedCall.new diff --git a/spec/ruby/core/module/fixtures/refine.rb b/spec/ruby/core/module/fixtures/refine.rb index 79e2e80197..e8215aa640 100644 --- a/spec/ruby/core/module/fixtures/refine.rb +++ b/spec/ruby/core/module/fixtures/refine.rb @@ -3,6 +3,10 @@ module ModuleSpecs def foo; "foo" end end + class ClassWithSuperFoo + def foo; [:C] end + end + module PrependedModule def foo; "foo from prepended module"; end end @@ -11,7 +15,11 @@ module ModuleSpecs def foo; "foo from included module"; end end - def self.build_refined_class - Class.new(ClassWithFoo) + def self.build_refined_class(for_super: false) + if for_super + Class.new(ClassWithSuperFoo) + else + Class.new(ClassWithFoo) + end end end diff --git a/spec/ruby/core/module/refine_spec.rb b/spec/ruby/core/module/refine_spec.rb index ca12d5d13b..ebb7111d82 100644 --- a/spec/ruby/core/module/refine_spec.rb +++ b/spec/ruby/core/module/refine_spec.rb @@ -731,16 +731,67 @@ describe "Module#refine" do result.should == "foo" end + it "looks in the refined class from included module" do + refined_class = ModuleSpecs.build_refined_class(for_super: true) + + a = Module.new do + def foo + [:A] + super + end + end + + refinement = Module.new do + refine refined_class do + include a + end + end + + result = nil + Module.new do + using refinement + + result = refined_class.new.foo + end + + result.should == [:A, :C] + end + + it "looks in the refined ancestors from included module" do + refined_class = ModuleSpecs.build_refined_class(for_super: true) + subclass = Class.new(refined_class) + + a = Module.new do + def foo + [:A] + super + end + end + + refinement = Module.new do + refine refined_class do + include a + end + end + + result = nil + Module.new do + using refinement + + result = subclass.new.foo + end + + result.should == [:A, :C] + end + # super in a method of a refinement invokes the method in the refined # class even if there is another refinement which has been activated # in the same context. - it "looks in the refined class even if there is another active refinement" do - refined_class = ModuleSpecs.build_refined_class + it "looks in the refined class first if called from refined method" do + refined_class = ModuleSpecs.build_refined_class(for_super: true) refinement = Module.new do refine refined_class do def foo - "foo from refinement" + [:R1] end end end @@ -748,7 +799,7 @@ describe "Module#refine" do refinement_with_super = Module.new do refine refined_class do def foo - super + [:R2] + super end end end @@ -760,7 +811,246 @@ describe "Module#refine" do result = refined_class.new.foo end - result.should == "foo" + result.should == [:R2, :C] + end + + it "looks only in the refined class even if there is another active refinement" do + refined_class = ModuleSpecs.build_refined_class(for_super: true) + + refinement = Module.new do + refine refined_class do + def bar + "you cannot see me from super because I belong to another active R" + end + end + end + + refinement_with_super = Module.new do + refine refined_class do + def bar + super + end + end + end + + + Module.new do + using refinement + using refinement_with_super + -> { + refined_class.new.bar + }.should raise_error(NoMethodError) + end + end + + it "does't have access to active refinements for C from included module" do + refined_class = ModuleSpecs.build_refined_class + + a = Module.new do + def foo + super + bar + end + end + + refinement = Module.new do + refine refined_class do + include a + + def bar + "bar is not seen from A methods" + end + end + end + + Module.new do + using refinement + -> { + refined_class.new.foo + }.should raise_error(NameError) { |e| e.name.should == :bar } + end + end + + it "does't have access to other active refinements from included module" do + refined_class = ModuleSpecs.build_refined_class + + refinement_integer = Module.new do + refine Integer do + def bar + "bar is not seen from A methods" + end + end + end + + a = Module.new do + def foo + super + 1.bar + end + end + + refinement = Module.new do + refine refined_class do + include a + end + end + + Module.new do + using refinement + using refinement_integer + -> { + refined_class.new.foo + }.should raise_error(NameError) { |e| e.name.should == :bar } + end + end + + # https://bugs.ruby-lang.org/issues/16977 + it "looks in the another active refinement if super called from included modules" do + refined_class = ModuleSpecs.build_refined_class(for_super: true) + + a = Module.new do + def foo + [:A] + super + end + end + + b = Module.new do + def foo + [:B] + super + end + end + + refinement_a = Module.new do + refine refined_class do + include a + end + end + + refinement_b = Module.new do + refine refined_class do + include b + end + end + + result = nil + Module.new do + using refinement_a + using refinement_b + result = refined_class.new.foo + end + + result.should == [:B, :A, :C] + end + + it "looks in the current active refinement from included modules" do + refined_class = ModuleSpecs.build_refined_class(for_super: true) + + a = Module.new do + def foo + [:A] + super + end + end + + b = Module.new do + def foo + [:B] + super + end + end + + refinement = Module.new do + refine refined_class do + def foo + [:LAST] + super + end + end + end + + refinement_a_b = Module.new do + refine refined_class do + include a + include b + end + end + + result = nil + Module.new do + using refinement + using refinement_a_b + result = refined_class.new.foo + end + + result.should == [:B, :A, :LAST, :C] + end + + ruby_version_is ""..."2.8" do + it "looks in the lexical scope refinements before other active refinements" do + refined_class = ModuleSpecs.build_refined_class(for_super: true) + + refinement_local = Module.new do + refine refined_class do + def foo + [:LOCAL] + super + end + end + end + + a = Module.new do + using refinement_local + + def foo + [:A] + super + end + end + + refinement = Module.new do + refine refined_class do + include a + end + end + + result = nil + Module.new do + using refinement + result = refined_class.new.foo + end + + result.should == [:A, :LOCAL, :C] + end + end + + ruby_version_is "2.8" do + # https://bugs.ruby-lang.org/issues/17007 + it "does not look in the lexical scope refinements before other active refinements" do + refined_class = ModuleSpecs.build_refined_class(for_super: true) + + refinement_local = Module.new do + refine refined_class do + def foo + [:LOCAL] + super + end + end + end + + a = Module.new do + using refinement_local + + def foo + [:A] + super + end + end + + refinement = Module.new do + refine refined_class do + include a + end + end + + result = nil + Module.new do + using refinement + result = refined_class.new.foo + end + + result.should == [:A, :C] + end end end diff --git a/spec/ruby/core/string/split_spec.rb b/spec/ruby/core/string/split_spec.rb index 34a50a5f13..7ac8d7815c 100644 --- a/spec/ruby/core/string/split_spec.rb +++ b/spec/ruby/core/string/split_spec.rb @@ -426,12 +426,70 @@ describe "String#split with Regexp" do end ruby_version_is "2.6" do - it "yields each split substrings if a block is given" do - a = [] - returned_object = "chunky bacon".split(" ") { |str| a << str.capitalize } + context "when a block is given" do + it "yields each split substring with default pattern" do + a = [] + returned_object = "chunky bacon".split { |str| a << str.capitalize } + + returned_object.should == "chunky bacon" + a.should == ["Chunky", "Bacon"] + end + + it "yields the string when limit is 1" do + a = [] + returned_object = "chunky bacon".split("", 1) { |str| a << str.capitalize } + + returned_object.should == "chunky bacon" + a.should == ["Chunky bacon"] + end + + it "yields each split letter" do + a = [] + returned_object = "chunky".split("", 0) { |str| a << str.capitalize } + + returned_object.should == "chunky" + a.should == %w(C H U N K Y) + end + + it "yields each split substring with a pattern" do + a = [] + returned_object = "chunky-bacon".split("-", 0) { |str| a << str.capitalize } + + returned_object.should == "chunky-bacon" + a.should == ["Chunky", "Bacon"] + end - returned_object.should == "chunky bacon" - a.should == ["Chunky", "Bacon"] + it "yields each split substring with empty regexp pattern" do + a = [] + returned_object = "chunky".split(//) { |str| a << str.capitalize } + + returned_object.should == "chunky" + a.should == %w(C H U N K Y) + end + + it "yields each split substring with empty regexp pattern and limit" do + a = [] + returned_object = "chunky".split(//, 3) { |str| a << str.capitalize } + + returned_object.should == "chunky" + a.should == %w(C H Unky) + end + + it "yields each split substring with a regexp pattern" do + a = [] + returned_object = "chunky:bacon".split(/:/) { |str| a << str.capitalize } + + returned_object.should == "chunky:bacon" + a.should == ["Chunky", "Bacon"] + end + + it "returns a string as is (and doesn't call block) if it is empty" do + a = [] + returned_object = "".split { |str| a << str.capitalize } + + returned_object.should == "" + a.should == [] + end end describe "for a String subclass" do diff --git a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb index 67f9274f32..90839baa0f 100644 --- a/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb +++ b/spec/ruby/core/thread/backtrace/location/absolute_path_spec.rb @@ -10,6 +10,13 @@ describe 'Thread::Backtrace::Location#absolute_path' 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 + context "when used in eval with a given filename" do it "returns filename" do code = "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/lineno_spec.rb b/spec/ruby/core/thread/backtrace/location/lineno_spec.rb index dc93d32d75..d14cf17514 100644 --- a/spec/ruby/core/thread/backtrace/location/lineno_spec.rb +++ b/spec/ruby/core/thread/backtrace/location/lineno_spec.rb @@ -10,4 +10,14 @@ describe 'Thread::Backtrace::Location#lineno' do it 'returns the absolute path 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 index 93eb4982d8..7863c055d3 100644 --- a/spec/ruby/core/thread/backtrace/location/path_spec.rb +++ b/spec/ruby/core/thread/backtrace/location/path_spec.rb @@ -87,6 +87,18 @@ describe 'Thread::Backtrace::Location#path' do 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 diff --git a/spec/ruby/core/thread/raise_spec.rb b/spec/ruby/core/thread/raise_spec.rb index 88a96d5f4e..27de3cc627 100644 --- a/spec/ruby/core/thread/raise_spec.rb +++ b/spec/ruby/core/thread/raise_spec.rb @@ -203,6 +203,6 @@ describe "Thread#raise on same thread" do Thread.current.raise end end - -> { t.value }.should raise_error(RuntimeError) + -> { t.value }.should raise_error(RuntimeError, '') end end -- cgit v1.2.3