diff options
Diffstat (limited to 'spec/ruby/core/kernel')
45 files changed, 1420 insertions, 397 deletions
diff --git a/spec/ruby/core/kernel/Float_spec.rb b/spec/ruby/core/kernel/Float_spec.rb index 015bcb33d6..9c436b05f7 100644 --- a/spec/ruby/core/kernel/Float_spec.rb +++ b/spec/ruby/core/kernel/Float_spec.rb @@ -41,7 +41,7 @@ describe :kernel_float, shared: true do end it "converts Strings to floats without calling #to_f" do - string = "10" + string = +"10" string.should_not_receive(:to_f) @object.send(:Float, string).should == 10.0 end @@ -157,6 +157,26 @@ describe :kernel_float, shared: true do @object.send(:Float, "1\t\n").should == 1.0 end + ruby_version_is ""..."3.4" do + it "raises ArgumentError if a fractional part is missing" do + -> { @object.send(:Float, "1.") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "+1.") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "-1.") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "1.e+0") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "1.e-2") }.should raise_error(ArgumentError) + end + end + + ruby_version_is "3.4" do + it "allows String representation without a fractional part" do + @object.send(:Float, "1.").should == 1.0 + @object.send(:Float, "+1.").should == 1.0 + @object.send(:Float, "-1.").should == -1.0 + @object.send(:Float, "1.e+0").should == 1.0 + @object.send(:Float, "1.e-2").should be_close(0.01, TOLERANCE) + end + end + %w(e E).each do |e| it "raises an ArgumentError if #{e} is the trailing character" do -> { @object.send(:Float, "2#{e}") }.should raise_error(ArgumentError) @@ -204,59 +224,107 @@ describe :kernel_float, shared: true do end end - describe "for hexadecimal literals with binary exponent" do - %w(p P).each do |p| - it "interprets the fractional part (on the left side of '#{p}') in hexadecimal" do - @object.send(:Float, "0x10#{p}0").should == 16.0 - end - - it "interprets the exponent (on the right of '#{p}') in decimal" do - @object.send(:Float, "0x1#{p}10").should == 1024.0 - end - - it "raises an ArgumentError if #{p} is the trailing character" do - -> { @object.send(:Float, "0x1#{p}") }.should raise_error(ArgumentError) - end - - it "raises an ArgumentError if #{p} is the leading character" do - -> { @object.send(:Float, "0x#{p}1") }.should raise_error(ArgumentError) - end + context "for hexadecimal literals" do + it "interprets the 0x prefix as hexadecimal" do + @object.send(:Float, "0x10").should == 16.0 + @object.send(:Float, "0x0F").should == 15.0 + @object.send(:Float, "0x0f").should == 15.0 + end - it "returns Infinity for '0x1#{p}10000'" do - @object.send(:Float, "0x1#{p}10000").should == Float::INFINITY - end + it "interprets negative hex value" do + @object.send(:Float, "-0x10").should == -16.0 + end - it "returns 0 for '0x1#{p}-10000'" do - @object.send(:Float, "0x1#{p}-10000").should == 0 - end + it "accepts embedded _ if the number does not contain a-f" do + @object.send(:Float, "0x1_0").should == 16.0 + end - it "allows embedded _ in a number on either side of the #{p}" do - @object.send(:Float, "0x1_0#{p}10").should == 16384.0 - @object.send(:Float, "0x10#{p}1_0").should == 16384.0 - @object.send(:Float, "0x1_0#{p}1_0").should == 16384.0 + ruby_version_is ""..."3.4.3" do + it "does not accept embedded _ if the number contains a-f" do + -> { @object.send(:Float, "0x1_0a") }.should raise_error(ArgumentError) + @object.send(:Float, "0x1_0a", exception: false).should be_nil end + end - it "raises an exception if a space is embedded on either side of the '#{p}'" do - -> { @object.send(:Float, "0x1 0#{p}10") }.should raise_error(ArgumentError) - -> { @object.send(:Float, "0x10#{p}1 0") }.should raise_error(ArgumentError) + ruby_version_is "3.4.3" do + it "accepts embedded _ if the number contains a-f" do + @object.send(:Float, "0x1_0a").should == 0x10a.to_f end + end - it "raises an exception if there's a leading _ on either side of the '#{p}'" do - -> { @object.send(:Float, "0x_10#{p}10") }.should raise_error(ArgumentError) - -> { @object.send(:Float, "0x10#{p}_10") }.should raise_error(ArgumentError) - end + it "does not accept _ before, after or inside the 0x prefix" do + -> { @object.send(:Float, "_0x10") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "0_x10") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "0x_10") }.should raise_error(ArgumentError) + @object.send(:Float, "_0x10", exception: false).should be_nil + @object.send(:Float, "0_x10", exception: false).should be_nil + @object.send(:Float, "0x_10", exception: false).should be_nil + end - it "raises an exception if there's a trailing _ on either side of the '#{p}'" do - -> { @object.send(:Float, "0x10_#{p}10") }.should raise_error(ArgumentError) - -> { @object.send(:Float, "0x10#{p}10_") }.should raise_error(ArgumentError) - end + it "parses negative hexadecimal string as negative float" do + @object.send(:Float, "-0x7b").should == -123.0 + end - it "allows hexadecimal points on the left side of the '#{p}'" do - @object.send(:Float, "0x1.8#{p}0").should == 1.5 + ruby_version_is "3.4" do + it "accepts a fractional part" do + @object.send(:Float, "0x0.8").should == 0.5 end + end - it "raises an ArgumentError if there's a decimal point on the right side of the '#{p}'" do - -> { @object.send(:Float, "0x1#{p}1.0") }.should raise_error(ArgumentError) + describe "with binary exponent" do + %w(p P).each do |p| + it "interprets the fractional part (on the left side of '#{p}') in hexadecimal" do + @object.send(:Float, "0x10#{p}0").should == 16.0 + end + + it "interprets the exponent (on the right of '#{p}') in decimal" do + @object.send(:Float, "0x1#{p}10").should == 1024.0 + end + + it "raises an ArgumentError if #{p} is the trailing character" do + -> { @object.send(:Float, "0x1#{p}") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if #{p} is the leading character" do + -> { @object.send(:Float, "0x#{p}1") }.should raise_error(ArgumentError) + end + + it "returns Infinity for '0x1#{p}10000'" do + @object.send(:Float, "0x1#{p}10000").should == Float::INFINITY + end + + it "returns 0 for '0x1#{p}-10000'" do + @object.send(:Float, "0x1#{p}-10000").should == 0 + end + + it "allows embedded _ in a number on either side of the #{p}" do + @object.send(:Float, "0x1_0#{p}10").should == 16384.0 + @object.send(:Float, "0x10#{p}1_0").should == 16384.0 + @object.send(:Float, "0x1_0#{p}1_0").should == 16384.0 + end + + it "raises an exception if a space is embedded on either side of the '#{p}'" do + -> { @object.send(:Float, "0x1 0#{p}10") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "0x10#{p}1 0") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a leading _ on either side of the '#{p}'" do + -> { @object.send(:Float, "0x_10#{p}10") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "0x10#{p}_10") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a trailing _ on either side of the '#{p}'" do + -> { @object.send(:Float, "0x10_#{p}10") }.should raise_error(ArgumentError) + -> { @object.send(:Float, "0x10#{p}10_") }.should raise_error(ArgumentError) + end + + it "allows hexadecimal points on the left side of the '#{p}'" do + @object.send(:Float, "0x1.8#{p}0").should == 1.5 + end + + it "raises an ArgumentError if there's a decimal point on the right side of the '#{p}'" do + -> { @object.send(:Float, "0x1#{p}1.0") }.should raise_error(ArgumentError) + end end end end @@ -280,7 +348,7 @@ describe :kernel_float, shared: true do nan2.should equal(nan) end - it "returns the identical Infinity if to_f is called and it returns Infinity" do + it "returns the identical Infinity if #to_f is called and it returns Infinity" do infinity = infinity_value (infinity_to_f = mock('Infinity')).should_receive(:to_f).once.and_return(infinity) infinity2 = @object.send(:Float, infinity_to_f) diff --git a/spec/ruby/core/kernel/Integer_spec.rb b/spec/ruby/core/kernel/Integer_spec.rb index c37733e88d..74dd3e0dd2 100644 --- a/spec/ruby/core/kernel/Integer_spec.rb +++ b/spec/ruby/core/kernel/Integer_spec.rb @@ -586,6 +586,21 @@ describe :kernel_integer_string_base, shared: true do Integer("777", obj).should == 0777 end + # https://bugs.ruby-lang.org/issues/19349 + ruby_version_is ''...'3.3' do + it "ignores the base if it is not an integer and does not respond to #to_i" do + Integer("777", "8").should == 777 + end + end + + ruby_version_is '3.3' do + it "raises a TypeError if it is not an integer and does not respond to #to_i" do + -> { + Integer("777", "8") + }.should raise_error(TypeError, "no implicit conversion of String into Integer") + end + end + describe "when passed exception: false" do describe "and valid argument" do it "returns an Integer number" do diff --git a/spec/ruby/core/kernel/Rational_spec.rb b/spec/ruby/core/kernel/Rational_spec.rb index 2d1051db7f..cc11a35451 100644 --- a/spec/ruby/core/kernel/Rational_spec.rb +++ b/spec/ruby/core/kernel/Rational_spec.rb @@ -1,6 +1,236 @@ require_relative '../../spec_helper' -require_relative '../../shared/rational/Rational' +require_relative '../rational/fixtures/rational' describe "Kernel.Rational" do - it_behaves_like :kernel_Rational, :Rational + describe "passed Integer" do + # Guard against the Mathn library + guard -> { !defined?(Math.rsqrt) } do + it "returns a new Rational number with 1 as the denominator" do + Rational(1).should eql(Rational(1, 1)) + Rational(-3).should eql(Rational(-3, 1)) + Rational(bignum_value).should eql(Rational(bignum_value, 1)) + end + end + end + + describe "passed two integers" do + it "returns a new Rational number" do + rat = Rational(1, 2) + rat.numerator.should == 1 + rat.denominator.should == 2 + rat.should be_an_instance_of(Rational) + + rat = Rational(-3, -5) + rat.numerator.should == 3 + rat.denominator.should == 5 + rat.should be_an_instance_of(Rational) + + rat = Rational(bignum_value, 3) + rat.numerator.should == bignum_value + rat.denominator.should == 3 + rat.should be_an_instance_of(Rational) + end + + it "reduces the Rational" do + rat = Rational(2, 4) + rat.numerator.should == 1 + rat.denominator.should == 2 + + rat = Rational(3, 9) + rat.numerator.should == 1 + rat.denominator.should == 3 + end + end + + describe "when passed a String" do + it "converts the String to a Rational using the same method as String#to_r" do + r = Rational(13, 25) + s_r = ".52".to_r + r_s = Rational(".52") + + r_s.should == r + r_s.should == s_r + end + + it "scales the Rational value of the first argument by the Rational value of the second" do + Rational(".52", ".6").should == Rational(13, 15) + Rational(".52", "1.6").should == Rational(13, 40) + end + + it "does not use the same method as Float#to_r" do + r = Rational(3, 5) + f_r = 0.6.to_r + r_s = Rational("0.6") + + r_s.should == r + r_s.should_not == f_r + end + end + + describe "when passed a Numeric" do + it "calls #to_r to convert the first argument to a Rational" do + num = RationalSpecs::SubNumeric.new(2) + + Rational(num).should == Rational(2) + end + end + + describe "when passed a Complex" do + context "[Complex]" do + it "returns a Rational from the real part if the imaginary part is 0" do + Rational(Complex(1, 0)).should == Rational(1) + end + + it "raises a RangeError if the imaginary part is not 0" do + -> { Rational(Complex(1, 2)) }.should raise_error(RangeError, "can't convert 1+2i into Rational") + end + end + + context "[Numeric, Complex]" do + it "uses the real part if the imaginary part is 0" do + Rational(1, Complex(2, 0)).should == Rational(1, 2) + end + + it "divides a numerator by the Complex denominator if the imaginary part is not 0" do + Rational(1, Complex(2, 1)).should == Complex(2/5r, -1/5r) + end + end + end + + context "when passed neither a Numeric nor a String" do + it "converts to Rational with #to_r method" do + obj = Object.new + def obj.to_r; 1/2r; end + + Rational(obj).should == 1/2r + end + + it "tries to convert to Integer with #to_int method if it does not respond to #to_r" do + obj = Object.new + def obj.to_int; 1; end + + Rational(obj).should == 1r + end + + it "raises TypeError if it neither responds to #to_r nor #to_int method" do + -> { Rational([]) }.should raise_error(TypeError, "can't convert Array into Rational") + -> { Rational({}) }.should raise_error(TypeError, "can't convert Hash into Rational") + -> { Rational(nil) }.should raise_error(TypeError, "can't convert nil into Rational") + end + + it "swallows exception raised in #to_int method" do + object = Object.new + def object.to_int() raise NoMethodError; end + + -> { Rational(object) }.should raise_error(TypeError) + -> { Rational(object, 1) }.should raise_error(TypeError) + -> { Rational(1, object) }.should raise_error(TypeError) + end + + it "raises TypeError if #to_r does not return Rational" do + obj = Object.new + def obj.to_r; []; end + + -> { Rational(obj) }.should raise_error(TypeError, "can't convert Object to Rational (Object#to_r gives Array)") + end + end + + it "raises a ZeroDivisionError if the second argument is 0" do + -> { Rational(1, 0) }.should raise_error(ZeroDivisionError, "divided by 0") + -> { Rational(1, 0.0) }.should raise_error(ZeroDivisionError, "divided by 0") + end + + it "raises a TypeError if the first argument is nil" do + -> { Rational(nil) }.should raise_error(TypeError, "can't convert nil into Rational") + end + + it "raises a TypeError if the second argument is nil" do + -> { Rational(1, nil) }.should raise_error(TypeError, "can't convert nil into Rational") + end + + it "raises a TypeError if the first argument is a Symbol" do + -> { Rational(:sym) }.should raise_error(TypeError) + end + + it "raises a TypeError if the second argument is a Symbol" do + -> { Rational(1, :sym) }.should raise_error(TypeError) + end + + describe "when passed exception: false" do + describe "and [non-Numeric]" do + it "swallows an error" do + Rational(:sym, exception: false).should == nil + Rational("abc", exception: false).should == nil + end + + it "swallows an exception raised in #to_r" do + obj = Object.new + def obj.to_r; raise; end + Rational(obj, exception: false).should == nil + end + + it "swallows an exception raised in #to_int" do + obj = Object.new + def obj.to_int; raise; end + Rational(obj, exception: false).should == nil + end + end + + describe "and [non-Numeric, Numeric]" do + it "swallows an error" do + Rational(:sym, 1, exception: false).should == nil + Rational("abc", 1, exception: false).should == nil + end + + it "swallows an exception raised in #to_r" do + obj = Object.new + def obj.to_r; raise; end + Rational(obj, 1, exception: false).should == nil + end + + it "swallows an exception raised in #to_int" do + obj = Object.new + def obj.to_int; raise; end + Rational(obj, 1, exception: false).should == nil + end + end + + describe "and [anything, non-Numeric]" do + it "swallows an error" do + Rational(:sym, :sym, exception: false).should == nil + Rational("abc", :sym, exception: false).should == nil + end + + it "swallows an exception raised in #to_r" do + obj = Object.new + def obj.to_r; raise; end + Rational(obj, obj, exception: false).should == nil + end + + it "swallows an exception raised in #to_int" do + obj = Object.new + def obj.to_int; raise; end + Rational(obj, obj, exception: false).should == nil + end + end + + describe "and non-Numeric String arguments" do + it "swallows an error" do + Rational("a", "b", exception: false).should == nil + Rational("a", 0, exception: false).should == nil + Rational(0, "b", exception: false).should == nil + end + end + + describe "and nil arguments" do + it "swallows an error" do + Rational(nil, exception: false).should == nil + Rational(nil, nil, exception: false).should == nil + end + end + end + + it "freezes its result" do + Rational(1).frozen?.should == true + end end diff --git a/spec/ruby/core/kernel/String_spec.rb b/spec/ruby/core/kernel/String_spec.rb index 47ee797be5..7caec6eda5 100644 --- a/spec/ruby/core/kernel/String_spec.rb +++ b/spec/ruby/core/kernel/String_spec.rb @@ -78,7 +78,7 @@ describe :kernel_String, shared: true do end it "returns the same object if it is already a String" do - string = "Hello" + string = +"Hello" string.should_not_receive(:to_s) string2 = @object.send(@method, string) string.should equal(string2) diff --git a/spec/ruby/core/kernel/autoload_spec.rb b/spec/ruby/core/kernel/autoload_spec.rb index 0404caec6d..5edb70541d 100644 --- a/spec/ruby/core/kernel/autoload_spec.rb +++ b/spec/ruby/core/kernel/autoload_spec.rb @@ -7,7 +7,9 @@ require_relative 'fixtures/classes' autoload :KSAutoloadA, "autoload_a.rb" autoload :KSAutoloadB, fixture(__FILE__, "autoload_b.rb") -autoload :KSAutoloadCallsRequire, "main_autoload_not_exist.rb" +define_autoload_KSAutoloadCallsRequire = -> { + autoload :KSAutoloadCallsRequire, "main_autoload_not_exist.rb" +} def check_autoload(const) autoload? const @@ -43,6 +45,7 @@ describe "Kernel#autoload" do end it "calls main.require(path) to load the file" do + define_autoload_KSAutoloadCallsRequire.call main = TOPLEVEL_BINDING.eval("self") main.should_receive(:require).with("main_autoload_not_exist.rb") # The constant won't be defined since require is mocked to do nothing diff --git a/spec/ruby/core/kernel/block_given_spec.rb b/spec/ruby/core/kernel/block_given_spec.rb index b00bfabfc3..aece4c821d 100644 --- a/spec/ruby/core/kernel/block_given_spec.rb +++ b/spec/ruby/core/kernel/block_given_spec.rb @@ -5,15 +5,20 @@ describe :kernel_block_given, shared: true do it "returns true if and only if a block is supplied" do @object.accept_block {}.should == true @object.accept_block_as_argument {}.should == true + @object.accept_block_inside_block {}.should == true + @object.accept_block_as_argument_inside_block {}.should == true @object.accept_block.should == false @object.accept_block_as_argument.should == false + @object.accept_block_inside_block.should == false + @object.accept_block_as_argument_inside_block.should == false end # Clarify: Based on http://www.ruby-forum.com/topic/137822 it appears # that Matz wanted this to be true in 1.9. it "returns false when a method defined by define_method is called with a block" do @object.defined_block {}.should == false + @object.defined_block_inside_block {}.should == false end end diff --git a/spec/ruby/core/kernel/caller_locations_spec.rb b/spec/ruby/core/kernel/caller_locations_spec.rb index 5994b28fa3..a917dba504 100644 --- a/spec/ruby/core/kernel/caller_locations_spec.rb +++ b/spec/ruby/core/kernel/caller_locations_spec.rb @@ -71,14 +71,40 @@ describe 'Kernel#caller_locations' do end guard -> { Kernel.instance_method(:tap).source_location } do - it "includes core library methods defined in Ruby" do - file, line = Kernel.instance_method(:tap).source_location - file.should.start_with?('<internal:') - - loc = nil - tap { loc = caller_locations(1, 1)[0] } - loc.label.should == "tap" - loc.path.should.start_with? "<internal:" + ruby_version_is ""..."3.4" do + it "includes core library methods defined in Ruby" do + file, line = Kernel.instance_method(:tap).source_location + file.should.start_with?('<internal:') + + loc = nil + tap { loc = caller_locations(1, 1)[0] } + loc.label.should == "tap" + loc.path.should.start_with? "<internal:" + end + end + + ruby_version_is "3.4"..."4.0" do + it "includes core library methods defined in Ruby" do + file, line = Kernel.instance_method(:tap).source_location + file.should.start_with?('<internal:') + + loc = nil + tap { loc = caller_locations(1, 1)[0] } + loc.label.should == "Kernel#tap" + loc.path.should.start_with? "<internal:" + end + end + + ruby_version_is "4.0" do + it "does not include core library methods defined in Ruby" do + file, line = Kernel.instance_method(:tap).source_location + file.should.start_with?('<internal:') + + loc = nil + tap { loc = caller_locations(1, 1)[0] } + loc.label.should == "Kernel#tap" + loc.path.should == __FILE__ + end end end end diff --git a/spec/ruby/core/kernel/caller_spec.rb b/spec/ruby/core/kernel/caller_spec.rb index f1ff7044b8..7cd703de5a 100644 --- a/spec/ruby/core/kernel/caller_spec.rb +++ b/spec/ruby/core/kernel/caller_spec.rb @@ -38,33 +38,72 @@ describe 'Kernel#caller' do it "returns an Array with the block given to #at_exit at the base of the stack" do path = fixture(__FILE__, "caller_at_exit.rb") lines = ruby_exe(path).lines - lines.should == [ - "#{path}:6:in `foo'\n", - "#{path}:2:in `block in <main>'\n" - ] + lines.size.should == 2 + lines[0].should =~ /\A#{path}:6:in [`'](?:Object#)?foo'\n\z/ + lines[1].should =~ /\A#{path}:2:in [`']block in <main>'\n\z/ + end + + it "can be called with a range" do + locations1 = caller(0) + locations2 = caller(2..4) + locations1[2..4].should == locations2 end it "works with endless ranges" do locations1 = KernelSpecs::CallerTest.locations(0) locations2 = KernelSpecs::CallerTest.locations(eval("(2..)")) - locations2.map(&:to_s).should == locations1[2..-1].map(&:to_s) + locations2.should == locations1[2..-1] end it "works with beginless ranges" do locations1 = KernelSpecs::CallerTest.locations(0) locations2 = KernelSpecs::CallerTest.locations((..5)) - locations2.map(&:to_s)[eval("(2..)")].should == locations1[(..5)].map(&:to_s)[eval("(2..)")] + locations2[eval("(2..)")].should == locations1[(..5)][eval("(2..)")] + end + + it "can be called with a range whose end is negative" do + locations1 = caller(0) + locations2 = caller(2..-1) + locations3 = caller(2..-2) + locations1[2..-1].should == locations2 + locations1[2..-2].should == locations3 + end + + it "must return nil if omitting more locations than available" do + caller(100).should == nil + caller(100..-1).should == nil + end + + it "must return [] if omitting exactly the number of locations available" do + omit = caller(0).length + caller(omit).should == [] + end + + it "must return the same locations when called with 1..-1 and when called with no arguments" do + caller.should == caller(1..-1) end guard -> { Kernel.instance_method(:tap).source_location } do - it "includes core library methods defined in Ruby" do - file, line = Kernel.instance_method(:tap).source_location - file.should.start_with?('<internal:') - - loc = nil - tap { loc = caller(1, 1)[0] } - loc.should.end_with? "in `tap'" - loc.should.start_with? "<internal:" + ruby_version_is ""..."4.0" do + it "includes core library methods defined in Ruby" do + file, line = Kernel.instance_method(:tap).source_location + file.should.start_with?('<internal:') + + loc = nil + tap { loc = caller(1, 1)[0] } + loc.should =~ /\A<internal:.*in [`'](?:Kernel#)?tap'\z/ + end + end + + ruby_version_is "4.0" do + it "includes core library methods defined in Ruby" do + file, line = Kernel.instance_method(:tap).source_location + file.should.start_with?('<internal:') + + loc = nil + tap { loc = caller(1, 1)[0] } + loc.should =~ /\A#{ __FILE__ }:.*in [`'](?:Kernel#)?tap'\z/ + end end end end diff --git a/spec/ruby/core/kernel/catch_spec.rb b/spec/ruby/core/kernel/catch_spec.rb index 4060172429..9f59d3b384 100644 --- a/spec/ruby/core/kernel/catch_spec.rb +++ b/spec/ruby/core/kernel/catch_spec.rb @@ -35,7 +35,7 @@ describe "Kernel.catch" do end it "raises an ArgumentError if a String with different identity is thrown" do - -> { catch("exit") { throw "exit" } }.should raise_error(ArgumentError) + -> { catch("exit".dup) { throw "exit".dup } }.should raise_error(ArgumentError) end it "catches a Symbol when thrown a matching Symbol" do diff --git a/spec/ruby/core/kernel/class_spec.rb b/spec/ruby/core/kernel/class_spec.rb index 2725bde19b..b1d9df1671 100644 --- a/spec/ruby/core/kernel/class_spec.rb +++ b/spec/ruby/core/kernel/class_spec.rb @@ -19,7 +19,7 @@ describe "Kernel#class" do end it "returns the first non-singleton class" do - a = "hello" + a = +"hello" def a.my_singleton_method; end a.class.should equal(String) end diff --git a/spec/ruby/core/kernel/eval_spec.rb b/spec/ruby/core/kernel/eval_spec.rb index 3dfc863368..e027294347 100644 --- a/spec/ruby/core/kernel/eval_spec.rb +++ b/spec/ruby/core/kernel/eval_spec.rb @@ -135,7 +135,7 @@ describe "Kernel#eval" do it "includes file and line information in syntax error" do expected = 'speccing.rb' -> { - eval('if true',TOPLEVEL_BINDING, expected) + eval('if true', TOPLEVEL_BINDING, expected) }.should raise_error(SyntaxError) { |e| e.message.should =~ /#{expected}:1:.+/ } @@ -144,7 +144,7 @@ describe "Kernel#eval" do it "evaluates string with given filename and negative linenumber" do expected_file = 'speccing.rb' -> { - eval('if true',TOPLEVEL_BINDING, expected_file, -100) + eval('if true', TOPLEVEL_BINDING, expected_file, -100) }.should raise_error(SyntaxError) { |e| e.message.should =~ /#{expected_file}:-100:.+/ } @@ -175,6 +175,75 @@ describe "Kernel#eval" do end end + context "parameter forwarding" do + it "allows anonymous rest parameter forwarding" do + object = Object.new + def object.foo(a, b, c) + [a, b, c] + end + def object.bar(*) + eval "foo(*)" + end + + object.bar(1, 2, 3).should == [1, 2, 3] + end + + it "allows anonymous keyword parameters forwarding" do + object = Object.new + def object.foo(a:, b:, c:) + [a, b, c] + end + def object.bar(**) + eval "foo(**)" + end + + object.bar(a: 1, b: 2, c: 3).should == [1, 2, 3] + end + + it "allows anonymous block parameter forwarding" do + object = Object.new + def object.foo(&block) + block.call + end + def object.bar(&) + eval "foo(&)" + end + + object.bar { :foobar }.should == :foobar + end + + it "allows ... forwarding" do + object = Object.new + def object.foo(a, b:, &block) + [a, b, block.call] + end + def object.bar(...) + eval "foo(...)" + end + + object.bar(1, b: 2) { 3 }.should == [1, 2, 3] + end + + it "allows parameter forwarding to super" do + m = Module.new do + def foo(a, b:, &block) + [a, b, block.call] + end + end + + c = Class.new do + include m + + def foo(a, b:, &block) + eval "super" + end + end + + object = c.new + object.foo(1, b: 2) { 3 }.should == [1, 2, 3] + end + end + ruby_version_is "3.3" do it "uses (eval at __FILE__:__LINE__) if none is provided" do eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})" @@ -261,6 +330,39 @@ describe "Kernel#eval" do end end + it "makes flip-flop operator work correctly" do + ScratchPad.record [] + + eval "10.times { |i| ScratchPad << i if (i == 4)...(i == 4) }" + ScratchPad.recorded.should == [4, 5, 6, 7, 8, 9] + + ScratchPad.clear + end + + it "returns nil if given an empty string" do + eval("").should == nil + end + + context "with shebang" do + it "ignores shebang with ruby interpreter" do + pid = eval(<<~CODE.b) + #!/usr/bin/env ruby + Process.pid + CODE + + pid.should == Process.pid + end + + it "ignores shebang with non-ruby interpreter" do + pid = eval(<<~CODE.b) + #!/usr/bin/env puma + Process.pid + CODE + + pid.should == Process.pid + end + end + # See language/magic_comment_spec.rb for more magic comments specs describe "with a magic encoding comment" do it "uses the magic comment encoding for the encoding of literal strings" do @@ -280,6 +382,8 @@ CODE eval(code) EvalSpecs.constants(false).should include(:"VÏ€") EvalSpecs::VÏ€.should == 3.14 + ensure + EvalSpecs.send(:remove_const, :VÏ€) end it "allows an emacs-style magic comment encoding" do @@ -293,6 +397,8 @@ CODE eval(code) EvalSpecs.constants(false).should include(:"VÏ€emacs") EvalSpecs::VÏ€emacs.should == 3.14 + ensure + EvalSpecs.send(:remove_const, :VÏ€emacs) end it "allows spaces before the magic encoding comment" do @@ -306,6 +412,8 @@ CODE eval(code) EvalSpecs.constants(false).should include(:"VÏ€spaces") EvalSpecs::VÏ€spaces.should == 3.14 + ensure + EvalSpecs.send(:remove_const, :VÏ€spaces) end it "allows a shebang line before the magic encoding comment" do @@ -320,6 +428,8 @@ CODE eval(code) EvalSpecs.constants(false).should include(:"VÏ€shebang") EvalSpecs::VÏ€shebang.should == 3.14 + ensure + EvalSpecs.send(:remove_const, :VÏ€shebang) end it "allows a shebang line and some spaces before the magic encoding comment" do @@ -334,15 +444,16 @@ CODE eval(code) EvalSpecs.constants(false).should include(:"VÏ€shebang_spaces") EvalSpecs::VÏ€shebang_spaces.should == 3.14 + ensure + EvalSpecs.send(:remove_const, :VÏ€shebang_spaces) end it "allows a magic encoding comment and a subsequent frozen_string_literal magic comment" do - # Make sure frozen_string_literal is not default true - eval("'foo'".b).frozen?.should be_false + frozen_string_default = "test".frozen? code = <<CODE.b # encoding: UTF-8 -# frozen_string_literal: true +# frozen_string_literal: #{!frozen_string_default} class EvalSpecs VÏ€string = "frozen" end @@ -352,7 +463,9 @@ CODE EvalSpecs.constants(false).should include(:"VÏ€string") EvalSpecs::VÏ€string.should == "frozen" EvalSpecs::VÏ€string.encoding.should == Encoding::UTF_8 - EvalSpecs::VÏ€string.frozen?.should be_true + EvalSpecs::VÏ€string.frozen?.should == !frozen_string_default + ensure + EvalSpecs.send(:remove_const, :VÏ€string) end it "allows a magic encoding comment and a frozen_string_literal magic comment on the same line in emacs style" do @@ -368,11 +481,14 @@ CODE EvalSpecs::VÏ€same_line.should == "frozen" EvalSpecs::VÏ€same_line.encoding.should == Encoding::UTF_8 EvalSpecs::VÏ€same_line.frozen?.should be_true + ensure + EvalSpecs.send(:remove_const, :VÏ€same_line) end it "ignores the magic encoding comment if it is after a frozen_string_literal magic comment" do + frozen_string_default = "test".frozen? code = <<CODE.b -# frozen_string_literal: true +# frozen_string_literal: #{!frozen_string_default} # encoding: UTF-8 class EvalSpecs VÏ€frozen_first = "frozen" @@ -386,23 +502,26 @@ CODE value = EvalSpecs.const_get(binary_constant) value.should == "frozen" value.encoding.should == Encoding::BINARY - value.frozen?.should be_true + value.frozen?.should == !frozen_string_default + ensure + EvalSpecs.send(:remove_const, binary_constant) end it "ignores the frozen_string_literal magic comment if it appears after a token and warns if $VERBOSE is true" do + frozen_string_default = "test".frozen? code = <<CODE some_token_before_magic_comment = :anything -# frozen_string_literal: true +# frozen_string_literal: #{!frozen_string_default} class EvalSpecs VÏ€string_not_frozen = "not frozen" end CODE - -> { eval(code) }.should complain(/warning: `frozen_string_literal' is ignored after any tokens/, verbose: true) - EvalSpecs::VÏ€string_not_frozen.frozen?.should be_false + -> { eval(code) }.should complain(/warning: [`']frozen_string_literal' is ignored after any tokens/, verbose: true) + EvalSpecs::VÏ€string_not_frozen.frozen?.should == frozen_string_default EvalSpecs.send :remove_const, :VÏ€string_not_frozen -> { eval(code) }.should_not complain(verbose: false) - EvalSpecs::VÏ€string_not_frozen.frozen?.should be_false + EvalSpecs::VÏ€string_not_frozen.frozen?.should == frozen_string_default EvalSpecs.send :remove_const, :VÏ€string_not_frozen end end diff --git a/spec/ruby/core/kernel/exec_spec.rb b/spec/ruby/core/kernel/exec_spec.rb index 1b4a7ae6f4..3d9520ad67 100644 --- a/spec/ruby/core/kernel/exec_spec.rb +++ b/spec/ruby/core/kernel/exec_spec.rb @@ -7,12 +7,12 @@ describe "Kernel#exec" do end it "runs the specified command, replacing current process" do - ruby_exe('exec "echo hello"; puts "fail"', escape: true).should == "hello\n" + ruby_exe('exec "echo hello"; puts "fail"').should == "hello\n" end end describe "Kernel.exec" do it "runs the specified command, replacing current process" do - ruby_exe('Kernel.exec "echo hello"; puts "fail"', escape: true).should == "hello\n" + ruby_exe('Kernel.exec "echo hello"; puts "fail"').should == "hello\n" end end diff --git a/spec/ruby/core/kernel/extend_spec.rb b/spec/ruby/core/kernel/extend_spec.rb index 47b22f3a18..6342d8cae1 100644 --- a/spec/ruby/core/kernel/extend_spec.rb +++ b/spec/ruby/core/kernel/extend_spec.rb @@ -76,4 +76,16 @@ describe "Kernel#extend" do -> { @frozen.extend @module }.should raise_error(FrozenError) end end + + it "updated class methods of a module when it extends self and includes another module" do + a = Module.new do + extend self + end + b = Module.new do + def foo; :foo; end + end + + a.include b + a.foo.should == :foo + end end diff --git a/spec/ruby/core/kernel/fixtures/classes.rb b/spec/ruby/core/kernel/fixtures/classes.rb index 541a4c075e..0e2b81988f 100644 --- a/spec/ruby/core/kernel/fixtures/classes.rb +++ b/spec/ruby/core/kernel/fixtures/classes.rb @@ -219,10 +219,28 @@ module KernelSpecs block_given? end + def self.accept_block_inside_block() + yield_self { + block_given? + } + end + + def self.accept_block_as_argument_inside_block(&block) + yield_self { + block_given? + } + end + class << self define_method(:defined_block) do block_given? end + + define_method(:defined_block_inside_block) do + yield_self { + block_given? + } + end end end @@ -235,10 +253,28 @@ module KernelSpecs self.send(:block_given?) end + def self.accept_block_inside_block + yield_self { + self.send(:block_given?) + } + end + + def self.accept_block_as_argument_inside_block(&block) + yield_self { + self.send(:block_given?) + } + end + class << self define_method(:defined_block) do self.send(:block_given?) end + + define_method(:defined_block_inside_block) do + yield_self { + self.send(:block_given?) + } + end end end @@ -251,10 +287,28 @@ module KernelSpecs Kernel.block_given? end + def self.accept_block_inside_block + yield_self { + Kernel.block_given? + } + end + + def self.accept_block_as_argument_inside_block(&block) + yield_self { + Kernel.block_given? + } + end + class << self define_method(:defined_block) do Kernel.block_given? end + + define_method(:defined_block_inside_block) do + yield_self { + Kernel.block_given? + } + end end end diff --git a/spec/ruby/core/kernel/format_spec.rb b/spec/ruby/core/kernel/format_spec.rb index e8b031e480..1d0c000c15 100644 --- a/spec/ruby/core/kernel/format_spec.rb +++ b/spec/ruby/core/kernel/format_spec.rb @@ -12,4 +12,36 @@ describe "Kernel.format" do it "is accessible as a module function" do Kernel.format("%s", "hello").should == "hello" end + + describe "when $VERBOSE is true" do + it "warns if too many arguments are passed" do + code = <<~RUBY + $VERBOSE = true + format("test", 1) + RUBY + + ruby_exe(code, args: "2>&1").should include("warning: too many arguments for format string") + end + + it "does not warns if too many keyword arguments are passed" do + code = <<~RUBY + $VERBOSE = true + format("test %{test}", test: 1, unused: 2) + RUBY + + ruby_exe(code, args: "2>&1").should_not include("warning") + end + + ruby_bug "#20593", ""..."3.4" do + it "doesn't warns if keyword arguments are passed and none are used" do + code = <<~RUBY + $VERBOSE = true + format("test", test: 1) + format("test", {}) + RUBY + + ruby_exe(code, args: "2>&1").should_not include("warning") + end + end + end end diff --git a/spec/ruby/core/kernel/initialize_copy_spec.rb b/spec/ruby/core/kernel/initialize_copy_spec.rb index fe08d184ad..d71ca9f60f 100644 --- a/spec/ruby/core/kernel/initialize_copy_spec.rb +++ b/spec/ruby/core/kernel/initialize_copy_spec.rb @@ -1,11 +1,18 @@ require_relative '../../spec_helper' describe "Kernel#initialize_copy" do + it "returns self" do + obj = Object.new + obj.send(:initialize_copy, obj).should.equal?(obj) + end + it "does nothing if the argument is the same as the receiver" do obj = Object.new obj.send(:initialize_copy, obj).should.equal?(obj) - obj.freeze + + obj = Object.new.freeze obj.send(:initialize_copy, obj).should.equal?(obj) + 1.send(:initialize_copy, 1).should.equal?(1) end diff --git a/spec/ruby/core/kernel/inspect_spec.rb b/spec/ruby/core/kernel/inspect_spec.rb index 1f9ce834ab..1fa66cab98 100644 --- a/spec/ruby/core/kernel/inspect_spec.rb +++ b/spec/ruby/core/kernel/inspect_spec.rb @@ -28,4 +28,63 @@ describe "Kernel#inspect" do end obj.inspect.should be_kind_of(String) end + + ruby_version_is "4.0" do + it "calls #instance_variables_to_inspect private method to know which variables to display" do + obj = Object.new + obj.instance_eval do + @host = "localhost" + @user = "root" + @password = "hunter2" + end + obj.singleton_class.class_eval do + private def instance_variables_to_inspect = %i[@host @user @does_not_exist] + end + + inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00') + inspected.should == '#<Object:0x00 @host="localhost", @user="root">' + + obj = Object.new + obj.instance_eval do + @host = "localhost" + @user = "root" + @password = "hunter2" + end + obj.singleton_class.class_eval do + private def instance_variables_to_inspect = [] + end + + inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00') + inspected.should == "#<Object:0x00>" + end + + it "displays all instance variables if #instance_variables_to_inspect returns nil" do + obj = Object.new + obj.instance_eval do + @host = "localhost" + @user = "root" + @password = "hunter2" + end + obj.singleton_class.class_eval do + private def instance_variables_to_inspect = nil + end + + inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00') + inspected.should == %{#<Object:0x00 @host="localhost", @user="root", @password="hunter2">} + end + + it "raises an error if #instance_variables_to_inspect returns an invalid value" do + obj = Object.new + obj.instance_eval do + @host = "localhost" + @user = "root" + @password = "hunter2" + end + obj.singleton_class.class_eval do + private def instance_variables_to_inspect = {} + end + + ->{ obj.inspect }.should raise_error(TypeError, "Expected #instance_variables_to_inspect to return an Array or nil, but it returned Hash") + end + end end diff --git a/spec/ruby/core/kernel/is_a_spec.rb b/spec/ruby/core/kernel/is_a_spec.rb index dc69766f83..bd8c96529a 100644 --- a/spec/ruby/core/kernel/is_a_spec.rb +++ b/spec/ruby/core/kernel/is_a_spec.rb @@ -2,5 +2,5 @@ require_relative '../../spec_helper' require_relative 'shared/kind_of' describe "Kernel#is_a?" do - it_behaves_like :kernel_kind_of , :is_a? + it_behaves_like :kernel_kind_of, :is_a? end diff --git a/spec/ruby/core/kernel/kind_of_spec.rb b/spec/ruby/core/kernel/kind_of_spec.rb index 734035620c..c988edccb5 100644 --- a/spec/ruby/core/kernel/kind_of_spec.rb +++ b/spec/ruby/core/kernel/kind_of_spec.rb @@ -2,5 +2,5 @@ require_relative '../../spec_helper' require_relative 'shared/kind_of' describe "Kernel#kind_of?" do - it_behaves_like :kernel_kind_of , :kind_of? + it_behaves_like :kernel_kind_of, :kind_of? end diff --git a/spec/ruby/core/kernel/lambda_spec.rb b/spec/ruby/core/kernel/lambda_spec.rb index 2f7abc749c..565536ac0d 100644 --- a/spec/ruby/core/kernel/lambda_spec.rb +++ b/spec/ruby/core/kernel/lambda_spec.rb @@ -26,42 +26,44 @@ describe "Kernel.lambda" do l.lambda?.should be_true end - it "creates a lambda-style Proc if given a literal block via Kernel.public_send" do - suppress_warning do - l = Kernel.public_send(:lambda) { 42 } - l.lambda?.should be_true + ruby_version_is ""..."3.3" do + it "creates a lambda-style Proc if given a literal block via Kernel.public_send" do + suppress_warning do + l = Kernel.public_send(:lambda) { 42 } + l.lambda?.should be_true + end end - end - it "returns the passed Proc if given an existing Proc" do - some_proc = proc {} - l = suppress_warning {lambda(&some_proc)} - l.should equal(some_proc) - l.lambda?.should be_false - end + it "returns the passed Proc if given an existing Proc" do + some_proc = proc {} + l = suppress_warning {lambda(&some_proc)} + l.should equal(some_proc) + l.lambda?.should be_false + end - it "creates a lambda-style Proc when called with zsuper" do - suppress_warning do - l = KernelSpecs::LambdaSpecs::ForwardBlockWithZSuper.new.lambda { 42 } - l.lambda?.should be_true - l.call.should == 42 + it "creates a lambda-style Proc when called with zsuper" do + suppress_warning do + l = KernelSpecs::LambdaSpecs::ForwardBlockWithZSuper.new.lambda { 42 } + l.lambda?.should be_true + l.call.should == 42 - lambda { l.call(:extra) }.should raise_error(ArgumentError) + lambda { l.call(:extra) }.should raise_error(ArgumentError) + end end - end - it "returns the passed Proc if given an existing Proc through super" do - some_proc = proc { } - l = KernelSpecs::LambdaSpecs::SuperAmpersand.new.lambda(&some_proc) - l.should equal(some_proc) - l.lambda?.should be_false - end + it "returns the passed Proc if given an existing Proc through super" do + some_proc = proc { } + l = KernelSpecs::LambdaSpecs::SuperAmpersand.new.lambda(&some_proc) + l.should equal(some_proc) + l.lambda?.should be_false + end - it "does not create lambda-style Procs when captured with #method" do - kernel_lambda = method(:lambda) - l = suppress_warning {kernel_lambda.call { 42 }} - l.lambda?.should be_false - l.call(:extra).should == 42 + it "does not create lambda-style Procs when captured with #method" do + kernel_lambda = method(:lambda) + l = suppress_warning {kernel_lambda.call { 42 }} + l.lambda?.should be_false + l.call(:extra).should == 42 + end end it "checks the arity of the call when no args are specified" do @@ -137,8 +139,16 @@ describe "Kernel.lambda" do end context "when called without a literal block" do - it "warns when proc isn't a lambda" do - -> { lambda(&proc{}) }.should complain("#{__FILE__}:#{__LINE__}: warning: lambda without a literal block is deprecated; use the proc without lambda instead\n") + ruby_version_is ""..."3.3" do + it "warns when proc isn't a lambda" do + -> { lambda(&proc{}) }.should complain("#{__FILE__}:#{__LINE__}: warning: lambda without a literal block is deprecated; use the proc without lambda instead\n") + end + end + + ruby_version_is "3.3" do + it "raises when proc isn't a lambda" do + -> { lambda(&proc{}) }.should raise_error(ArgumentError, /the lambda method requires a literal block/) + end end it "doesn't warn when proc is lambda" do diff --git a/spec/ruby/core/kernel/match_spec.rb b/spec/ruby/core/kernel/match_spec.rb index aa25006163..cd6330fe91 100644 --- a/spec/ruby/core/kernel/match_spec.rb +++ b/spec/ruby/core/kernel/match_spec.rb @@ -1,30 +1,7 @@ require_relative '../../spec_helper' describe "Kernel#=~" do - ruby_version_is ''...'3.2' do - it "returns nil matching any object" do - o = Object.new - - suppress_warning do - (o =~ /Object/).should be_nil - (o =~ 'Object').should be_nil - (o =~ Object).should be_nil - (o =~ Object.new).should be_nil - (o =~ nil).should be_nil - (o =~ true).should be_nil - end - end - - it "is deprecated" do - -> do - Object.new =~ /regexp/ - end.should complain(/deprecated Object#=~ is called on Object/, verbose: true) - end - end - - ruby_version_is '3.2' do - it "is no longer defined" do - Object.new.should_not.respond_to?(:=~) - end + it "is no longer defined" do + Object.new.should_not.respond_to?(:=~) end end diff --git a/spec/ruby/core/kernel/not_match_spec.rb b/spec/ruby/core/kernel/not_match_spec.rb index 906f18df2c..082e56fed7 100644 --- a/spec/ruby/core/kernel/not_match_spec.rb +++ b/spec/ruby/core/kernel/not_match_spec.rb @@ -14,6 +14,10 @@ describe "Kernel#!~" do (obj !~ :foo).should == false end + it "raises NoMethodError if self does not respond to #=~" do + -> { Object.new !~ :foo }.should raise_error(NoMethodError) + end + it 'can be overridden in subclasses' do obj = KernelSpecs::NotMatch.new (obj !~ :bar).should == :foo diff --git a/spec/ruby/core/kernel/open_spec.rb b/spec/ruby/core/kernel/open_spec.rb index 99793294c5..b967d5044b 100644 --- a/spec/ruby/core/kernel/open_spec.rb +++ b/spec/ruby/core/kernel/open_spec.rb @@ -27,44 +27,66 @@ describe "Kernel#open" do open(@name, "r") { |f| f.gets }.should == @content end - platform_is_not :windows, :wasi do - it "opens an io when path starts with a pipe" do - @io = open("|date") - begin - @io.should be_kind_of(IO) - @io.read - ensure - @io.close + ruby_version_is ""..."4.0" do + platform_is_not :windows, :wasi do + it "opens an io when path starts with a pipe" do + suppress_warning do # https://bugs.ruby-lang.org/issues/19630 + @io = open("|date") + end + begin + @io.should be_kind_of(IO) + @io.read + ensure + @io.close + end end - end - it "opens an io when called with a block" do - @output = open("|date") { |f| f.read } - @output.should_not == '' - end + it "opens an io when called with a block" do + suppress_warning do # https://bugs.ruby-lang.org/issues/19630 + @output = open("|date") { |f| f.read } + end + @output.should_not == '' + end - it "opens an io for writing" do - -> do - bytes = open("|cat", "w") { |io| io.write(".") } - bytes.should == 1 - end.should output_to_fd(".") + it "opens an io for writing" do + suppress_warning do # https://bugs.ruby-lang.org/issues/19630 + -> { + bytes = open("|cat", "w") { |io| io.write(".") } + bytes.should == 1 + }.should output_to_fd(".") + end + end end - end - platform_is :windows do - it "opens an io when path starts with a pipe" do - @io = open("|date /t") - begin - @io.should be_kind_of(IO) - @io.read - ensure - @io.close + platform_is :windows do + it "opens an io when path starts with a pipe" do + suppress_warning do # https://bugs.ruby-lang.org/issues/19630 + @io = open("|date /t") + end + begin + @io.should be_kind_of(IO) + @io.read + ensure + @io.close + end + end + + it "opens an io when called with a block" do + suppress_warning do # https://bugs.ruby-lang.org/issues/19630 + @output = open("|date /t") { |f| f.read } + end + @output.should_not == '' end end - it "opens an io when called with a block" do - @output = open("|date /t") { |f| f.read } - @output.should_not == '' + ruby_version_is "3.3" do + # https://bugs.ruby-lang.org/issues/19630 + it "warns about deprecation given a path with a pipe" do + cmd = "|echo ok" + -> { + open(cmd) { |f| f.read } + }.should complain(/Kernel#open with a leading '\|'/) + end end end @@ -86,7 +108,7 @@ describe "Kernel#open" do ScratchPad.clear end - it "calls #to_path to covert the argument to a String before calling #to_str" do + it "calls #to_path to convert the argument to a String before calling #to_str" do obj = mock("open to_path") obj.should_receive(:to_path).at_least(1).times.and_return(@name) obj.should_not_receive(:to_str) diff --git a/spec/ruby/core/kernel/printf_spec.rb b/spec/ruby/core/kernel/printf_spec.rb index d8f93ce429..61bf955c25 100644 --- a/spec/ruby/core/kernel/printf_spec.rb +++ b/spec/ruby/core/kernel/printf_spec.rb @@ -31,6 +31,13 @@ describe "Kernel.printf" do object.should_receive(:write).with("string") Kernel.printf(object, "%s", "string") end + + it "calls #to_str to convert the format object to a String" do + object = mock('format string') + object.should_receive(:to_str).and_return("to_str: %i") + $stdout.should_receive(:write).with("to_str: 42") + Kernel.printf($stdout, object, 42) + end end describe "Kernel.printf" do diff --git a/spec/ruby/core/kernel/public_send_spec.rb b/spec/ruby/core/kernel/public_send_spec.rb index 4dae419ff9..b684b1729c 100644 --- a/spec/ruby/core/kernel/public_send_spec.rb +++ b/spec/ruby/core/kernel/public_send_spec.rb @@ -105,11 +105,11 @@ describe "Kernel#public_send" do end it "includes `public_send` in the backtrace when passed not enough arguments" do - -> { public_send() }.should raise_error(ArgumentError) { |e| e.backtrace[0].should.include?("`public_send'") } + -> { public_send() }.should raise_error(ArgumentError) { |e| e.backtrace[0].should =~ /[`'](?:Kernel#)?public_send'/ } end it "includes `public_send` in the backtrace when passed a single incorrect argument" do - -> { public_send(Object.new) }.should raise_error(TypeError) { |e| e.backtrace[0].should.include?("`public_send'") } + -> { public_send(Object.new) }.should raise_error(TypeError) { |e| e.backtrace[0].should =~ /[`'](?:Kernel#)?public_send'/ } end it_behaves_like :basicobject_send, :public_send diff --git a/spec/ruby/core/kernel/raise_spec.rb b/spec/ruby/core/kernel/raise_spec.rb index 4f190c120b..fcd011d4e6 100644 --- a/spec/ruby/core/kernel/raise_spec.rb +++ b/spec/ruby/core/kernel/raise_spec.rb @@ -44,7 +44,242 @@ describe "Kernel#raise" do it "raises an ArgumentError when only cause is given" do cause = StandardError.new - -> { raise(cause: cause) }.should raise_error(ArgumentError) + -> { raise(cause: cause) }.should raise_error(ArgumentError, "only cause is given with no arguments") + end + + it "raises an ArgumentError when only cause is given even if it has nil value" do + -> { raise(cause: nil) }.should raise_error(ArgumentError, "only cause is given with no arguments") + end + + it "raises a TypeError when given cause is not an instance of Exception" do + -> { raise "message", cause: Object.new }.should raise_error(TypeError, "exception object expected") + end + + it "doesn't raise a TypeError when given cause is nil" do + -> { raise "message", cause: nil }.should raise_error(RuntimeError, "message") + end + + it "allows cause equal an exception" do + e = RuntimeError.new("message") + -> { raise e, cause: e }.should raise_error(e) + end + + it "doesn't set given cause when it equals an exception" do + e = RuntimeError.new("message") + + begin + raise e, cause: e + rescue + end + + e.cause.should == nil + end + + it "raises ArgumentError when exception is part of the cause chain" do + -> { + begin + raise "Error 1" + rescue => e1 + begin + raise "Error 2" + rescue => e2 + begin + raise "Error 3" + rescue => e3 + raise e1, cause: e3 + end + end + end + }.should raise_error(ArgumentError, "circular causes") + end + + it "re-raises a rescued exception" do + -> do + begin + raise StandardError, "aaa" + rescue Exception + begin + raise ArgumentError + rescue ArgumentError + end + + # should raise StandardError "aaa" + raise + end + end.should raise_error(StandardError, "aaa") + end + + it "re-raises a previously rescued exception without overwriting the cause" do + begin + begin + begin + begin + raise "Error 1" + rescue => e1 + raise "Error 2" + end + rescue => e2 + raise "Error 3" + end + rescue + e2.cause.should == e1 + raise e2 + end + rescue => e + e.cause.should == e1 + end + end + + it "re-raises a previously rescued exception with overwriting the cause when it's explicitly specified with :cause option" do + e4 = RuntimeError.new("Error 4") + + begin + begin + begin + begin + raise "Error 1" + rescue => e1 + raise "Error 2" + end + rescue => e2 + raise "Error 3" + end + rescue + e2.cause.should == e1 + raise e2, cause: e4 + end + rescue => e + e.cause.should == e4 + end + end + + it "re-raises a previously rescued exception without overwriting the cause when it's explicitly specified with :cause option and has nil value" do + begin + begin + begin + begin + raise "Error 1" + rescue => e1 + raise "Error 2" + end + rescue => e2 + raise "Error 3" + end + rescue + e2.cause.should == e1 + raise e2, cause: nil + end + rescue => e + e.cause.should == e1 + end + end + + it "re-raises a previously rescued exception without setting a cause implicitly" do + begin + begin + raise "Error 1" + rescue => e1 + raise + end + rescue => e + e.should == e1 + e.cause.should == nil + end + end + + it "re-raises a previously rescued exception that has a cause without setting a cause implicitly" do + begin + begin + raise "Error 1" + rescue => e1 + begin + raise "Error 2" + rescue => e2 + raise + end + end + rescue => e + e.should == e2 + e.cause.should == e1 + end + end + + it "re-raises a previously rescued exception that doesn't have a cause and isn't a cause of any other exception with setting a cause implicitly" do + begin + begin + raise "Error 1" + rescue => e1 + begin + raise "Error 2" + rescue => e2 + raise "Error 3" + end + end + rescue => e + e.message.should == "Error 3" + e.cause.should == e2 + end + end + + it "re-raises a previously rescued exception that doesn't have a cause and is a cause of other exception without setting a cause implicitly" do + begin + begin + raise "Error 1" + rescue => e1 + begin + raise "Error 2" + rescue => e2 + e1.cause.should == nil + e2.cause.should == e1 + raise e1 + end + end + rescue => e + e.should == e1 + e.cause.should == nil + end + end + + it "re-raises a previously rescued exception that doesn't have a cause and is a cause of other exception (that wasn't raised explicitly) without setting a cause implicitly" do + begin + begin + raise "Error 1" + rescue => e1 + begin + foo # raises NameError + rescue => e2 + e1.cause.should == nil + e2.cause.should == e1 + raise e1 + end + end + rescue => e + e.should == e1 + e.cause.should == nil + end + end + + it "re-raises a previously rescued exception that has a cause but isn't a cause of any other exception without setting a cause implicitly" do + begin + begin + raise "Error 1" + rescue => e1 + begin + raise "Error 2" + rescue => e2 + begin + raise "Error 3", cause: RuntimeError.new("Error 4") + rescue => e3 + e2.cause.should == e1 + e3.cause.should_not == e2 + raise e2 + end + end + end + rescue => e + e.should == e2 + e.cause.should == e1 + end end end diff --git a/spec/ruby/core/kernel/require_relative_spec.rb b/spec/ruby/core/kernel/require_relative_spec.rb index 5999855de6..6188d13a4e 100644 --- a/spec/ruby/core/kernel/require_relative_spec.rb +++ b/spec/ruby/core/kernel/require_relative_spec.rb @@ -5,9 +5,9 @@ describe "Kernel#require_relative with a relative path" do before :each do CodeLoadingSpecs.spec_setup @dir = "../../fixtures/code" - @abs_dir = File.realpath(@dir, File.dirname(__FILE__)) + @abs_dir = File.realpath(@dir, __dir__) @path = "#{@dir}/load_fixture.rb" - @abs_path = File.realpath(@path, File.dirname(__FILE__)) + @abs_path = File.realpath(@path, __dir__) end after :each do @@ -92,7 +92,7 @@ describe "Kernel#require_relative with a relative path" do it "raises a LoadError that includes the missing path" do missing_path = "#{@dir}/nonexistent.rb" - expanded_missing_path = File.expand_path(missing_path, File.dirname(__FILE__)) + expanded_missing_path = File.expand_path(missing_path, __dir__) -> { require_relative(missing_path) }.should raise_error(LoadError) { |e| e.message.should include(expanded_missing_path) e.path.should == expanded_missing_path @@ -277,7 +277,7 @@ end describe "Kernel#require_relative with an absolute path" do before :each do CodeLoadingSpecs.spec_setup - @dir = File.expand_path "../../fixtures/code", File.dirname(__FILE__) + @dir = File.expand_path "../../fixtures/code", __dir__ @abs_dir = @dir @path = File.join @dir, "load_fixture.rb" @abs_path = @path diff --git a/spec/ruby/core/kernel/require_spec.rb b/spec/ruby/core/kernel/require_spec.rb index 896afb840a..60d17242fe 100644 --- a/spec/ruby/core/kernel/require_spec.rb +++ b/spec/ruby/core/kernel/require_spec.rb @@ -16,9 +16,10 @@ describe "Kernel#require" do Kernel.should have_private_instance_method(:require) end - provided = %w[complex enumerator rational thread ruby2_keywords] - ruby_version_is "3.1" do - provided << "fiber" + provided = %w[complex enumerator fiber rational thread ruby2_keywords] + ruby_version_is "4.0" do + provided << "set" + provided << "pathname" end it "#{provided.join(', ')} are already required" do @@ -26,14 +27,19 @@ describe "Kernel#require" do features = out.lines.map { |line| File.basename(line.chomp, '.*') } # Ignore CRuby internals - features -= %w[encdb transdb windows_1252] + features -= %w[encdb transdb windows_1252 windows_31j] features.reject! { |feature| feature.end_with?('-fake') } features.sort.should == provided.sort - code = provided.map { |f| "puts require #{f.inspect}\n" }.join + requires = provided + ruby_version_is "4.0" do + requires = requires.map { |f| f == "pathname" ? "pathname.so" : f } + end + + code = requires.map { |f| "puts require #{f.inspect}\n" }.join required = ruby_exe(code, options: '--disable-gems') - required.should == "false\n" * provided.size + required.should == "false\n" * requires.size end it_behaves_like :kernel_require_basic, :require, CodeLoadingSpecs::Method.new diff --git a/spec/ruby/core/kernel/select_spec.rb b/spec/ruby/core/kernel/select_spec.rb index e0d82f3079..df23414b28 100644 --- a/spec/ruby/core/kernel/select_spec.rb +++ b/spec/ruby/core/kernel/select_spec.rb @@ -10,9 +10,9 @@ end describe "Kernel.select" do it 'does not block when timeout is 0' do IO.pipe do |read, write| - IO.select([read], [], [], 0).should == nil + select([read], [], [], 0).should == nil write.write 'data' - IO.select([read], [], [], 0).should == [[read], [], []] + select([read], [], [], 0).should == [[read], [], []] end end end diff --git a/spec/ruby/core/kernel/shared/load.rb b/spec/ruby/core/kernel/shared/load.rb index 5c41c19bf6..62c5c7be9b 100644 --- a/spec/ruby/core/kernel/shared/load.rb +++ b/spec/ruby/core/kernel/shared/load.rb @@ -1,5 +1,6 @@ main = self +# The big difference is Kernel#load does not attempt to add an extension to the passed path, unlike Kernel#require describe :kernel_load, shared: true do before :each do CodeLoadingSpecs.spec_setup @@ -10,22 +11,31 @@ describe :kernel_load, shared: true do CodeLoadingSpecs.spec_cleanup end - it "loads a non-extensioned file as a Ruby source file" do - path = File.expand_path "load_fixture", CODE_LOADING_DIR - @object.load(path).should be_true - ScratchPad.recorded.should == [:no_ext] - end + describe "(path resolution)" do + # This behavior is specific to Kernel#load, it differs for Kernel#require + it "loads a non-extensioned file as a Ruby source file" do + path = File.expand_path "load_fixture", CODE_LOADING_DIR + @object.load(path).should be_true + ScratchPad.recorded.should == [:no_ext] + end - it "loads a non .rb extensioned file as a Ruby source file" do - path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR - @object.load(path).should be_true - ScratchPad.recorded.should == [:no_rb_ext] - end + it "loads a non .rb extensioned file as a Ruby source file" do + path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR + @object.load(path).should be_true + ScratchPad.recorded.should == [:no_rb_ext] + end - it "loads from the current working directory" do - Dir.chdir CODE_LOADING_DIR do - @object.load("load_fixture.rb").should be_true - ScratchPad.recorded.should == [:loaded] + it "loads from the current working directory" do + Dir.chdir CODE_LOADING_DIR do + @object.load("load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + end + + # This behavior is specific to Kernel#load, it differs for Kernel#require + it "does not look for a c-extension file when passed a path without extension (when no .rb is present)" do + path = File.join CODE_LOADING_DIR, "a", "load_fixture" + -> { @object.send(@method, path) }.should raise_error(LoadError) end end @@ -155,37 +165,35 @@ describe :kernel_load, shared: true do end describe "when passed a module for 'wrap'" do - ruby_version_is "3.1" do - it "sets the enclosing scope to the supplied module" do - path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR - mod = Module.new - @object.load(path, mod) + it "sets the enclosing scope to the supplied module" do + path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR + mod = Module.new + @object.load(path, mod) - Object.const_defined?(:LoadSpecWrap).should be_false - mod.const_defined?(:LoadSpecWrap).should be_true + Object.const_defined?(:LoadSpecWrap).should be_false + mod.const_defined?(:LoadSpecWrap).should be_true - wrap_module = ScratchPad.recorded[1] - wrap_module.should == mod - end + wrap_module = ScratchPad.recorded[1] + wrap_module.should == mod + end - it "makes constants and instance methods in the source file reachable with the supplied module" do - path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR - mod = Module.new - @object.load(path, mod) + it "makes constants and instance methods in the source file reachable with the supplied module" do + path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR + mod = Module.new + @object.load(path, mod) - mod::LOAD_WRAP_SPECS_TOP_LEVEL_CONSTANT.should == 1 - obj = Object.new - obj.extend(mod) - obj.send(:load_wrap_specs_top_level_method).should == :load_wrap_specs_top_level_method - end + mod::LOAD_WRAP_SPECS_TOP_LEVEL_CONSTANT.should == 1 + obj = Object.new + obj.extend(mod) + obj.send(:load_wrap_specs_top_level_method).should == :load_wrap_specs_top_level_method + end - it "makes instance methods in the source file private" do - path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR - mod = Module.new - @object.load(path, mod) + it "makes instance methods in the source file private" do + path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR + mod = Module.new + @object.load(path, mod) - mod.private_instance_methods.include?(:load_wrap_specs_top_level_method).should == true - end + mod.private_instance_methods.include?(:load_wrap_specs_top_level_method).should == true end end diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb index 61081b200c..52f86f73e5 100644 --- a/spec/ruby/core/kernel/shared/require.rb +++ b/spec/ruby/core/kernel/shared/require.rb @@ -212,6 +212,34 @@ end describe :kernel_require, shared: true do describe "(path resolution)" do + it "loads .rb file when passed absolute path without extension" do + path = File.expand_path "load_fixture", CODE_LOADING_DIR + @object.send(@method, path).should be_true + # This should _not_ be [:no_ext] + ScratchPad.recorded.should == [:loaded] + end + + platform_is :linux, :darwin do + it "loads c-extension file when passed absolute path without extension when no .rb is present" do + # the error message is specific to what dlerror() returns + path = File.join CODE_LOADING_DIR, "a", "load_fixture" + -> { @object.send(@method, path) }.should raise_error(LoadError) + end + end + + platform_is :darwin do + it "loads .bundle file when passed absolute path with .so" do + # the error message is specific to what dlerror() returns + path = File.join CODE_LOADING_DIR, "a", "load_fixture.so" + -> { @object.send(@method, path) }.should raise_error(LoadError) + end + end + + it "does not try an extra .rb if the path already ends in .rb" do + path = File.join CODE_LOADING_DIR, "d", "load_fixture.rb" + -> { @object.send(@method, path) }.should raise_error(LoadError) + end + # For reference see [ruby-core:24155] in which matz confirms this feature is # intentional for security reasons. it "does not load a bare filename unless the current working directory is in $LOAD_PATH" do @@ -268,6 +296,16 @@ describe :kernel_require, shared: true do $LOAD_PATH.replace [File.expand_path("b", CODE_LOADING_DIR), CODE_LOADING_DIR] @object.require("load_fixture").should be_false end + + it "stores the missing path in a LoadError object" do + path = "abcd1234" + + -> { + @object.send(@method, path) + }.should raise_error(LoadError) { |e| + e.path.should == path + } + end end describe "(file extensions)" do @@ -787,4 +825,24 @@ describe :kernel_require, shared: true do e.path.should == path } end + + platform_is :linux, :darwin do + it "does not store the missing path in a LoadError object when c-extension file exists but loading fails and passed absolute path without extension" do + # the error message is specific to what dlerror() returns + path = File.join CODE_LOADING_DIR, "a", "load_fixture" + -> { @object.send(@method, path) }.should raise_error(LoadError) { |e| + e.path.should == nil + } + end + end + + platform_is :darwin do + it "does not store the missing path in a LoadError object when c-extension file exists but loading fails and passed absolute path with extension" do + # the error message is specific to what dlerror() returns + path = File.join CODE_LOADING_DIR, "a", "load_fixture.bundle" + -> { @object.send(@method, path) }.should raise_error(LoadError) { |e| + e.path.should == nil + } + end + end end diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb index 2db50bd686..2b2c6c9b63 100644 --- a/spec/ruby/core/kernel/shared/sprintf.rb +++ b/spec/ruby/core/kernel/shared/sprintf.rb @@ -22,6 +22,7 @@ describe :kernel_sprintf, shared: true do @method.call("%d", "112").should == "112" @method.call("%d", "0127").should == "87" @method.call("%d", "0xc4").should == "196" + @method.call("%d", "0").should == "0" end it "raises TypeError exception if cannot convert to Integer" do @@ -57,6 +58,11 @@ describe :kernel_sprintf, shared: true do it "works well with large numbers" do @method.call("%#{f}", 1234567890987654321).should == "1234567890987654321" end + + it "converts to the empty string if precision is 0 and value is 0" do + @method.call("%.#{f}", 0).should == "" + @method.call("%.0#{f}", 0).should == "" + end end end @@ -289,28 +295,12 @@ describe :kernel_sprintf, shared: true do @method.call("%c", "a").should == "a" end - ruby_version_is ""..."3.2" do - it "raises ArgumentError if argument is a string of several characters" do - -> { - @method.call("%c", "abc") - }.should raise_error(ArgumentError, /%c requires a character/) - end - - it "raises ArgumentError if argument is an empty string" do - -> { - @method.call("%c", "") - }.should raise_error(ArgumentError, /%c requires a character/) - end + it "displays only the first character if argument is a string of several characters" do + @method.call("%c", "abc").should == "a" end - ruby_version_is "3.2" do - it "displays only the first character if argument is a string of several characters" do - @method.call("%c", "abc").should == "a" - end - - it "displays no characters if argument is an empty string" do - @method.call("%c", "").should == "" - end + it "displays no characters if argument is an empty string" do + @method.call("%c", "").should == "" end it "raises TypeError if argument is not String or Integer and cannot be converted to them" do @@ -356,13 +346,13 @@ describe :kernel_sprintf, shared: true do it "raises TypeError if converting to Integer with to_int returns non-Integer" do obj = BasicObject.new - def obj.to_str + def obj.to_int :foo end -> { @method.call("%c", obj) - }.should raise_error(TypeError, /can't convert BasicObject to String/) + }.should raise_error(TypeError, /can't convert BasicObject to Integer/) end end @@ -372,6 +362,10 @@ describe :kernel_sprintf, shared: true do obj.should_receive(:inspect).and_return("<inspect-result>") @method.call("%p", obj).should == "<inspect-result>" end + + it "substitutes 'nil' for nil" do + @method.call("%p", nil).should == "nil" + end end describe "s" do @@ -455,7 +449,7 @@ describe :kernel_sprintf, shared: true do it "is escaped by %" do @method.call("%%").should == "%" - @method.call("%%d", 10).should == "%d" + @method.call("%%d").should == "%d" end end end diff --git a/spec/ruby/core/kernel/shared/sprintf_encoding.rb b/spec/ruby/core/kernel/shared/sprintf_encoding.rb index 9cedb8b662..7ec0fe4c48 100644 --- a/spec/ruby/core/kernel/shared/sprintf_encoding.rb +++ b/spec/ruby/core/kernel/shared/sprintf_encoding.rb @@ -14,14 +14,14 @@ describe :kernel_sprintf_encoding, shared: true do end it "returns a String in the same encoding as the format String if compatible" do - string = "%s".force_encoding(Encoding::KOI8_U) + string = "%s".dup.force_encoding(Encoding::KOI8_U) result = @method.call(string, "dogs") result.encoding.should equal(Encoding::KOI8_U) end it "returns a String in the argument's encoding if format encoding is more restrictive" do - string = "foo %s".force_encoding(Encoding::US_ASCII) - argument = "b\303\274r".force_encoding(Encoding::UTF_8) + string = "foo %s".dup.force_encoding(Encoding::US_ASCII) + argument = "b\303\274r".dup.force_encoding(Encoding::UTF_8) result = @method.call(string, argument) result.encoding.should equal(Encoding::UTF_8) @@ -56,7 +56,7 @@ describe :kernel_sprintf_encoding, shared: true do end it "uses the encoding of the format string to interpret codepoints" do - format = "%c".force_encoding("euc-jp") + format = "%c".dup.force_encoding("euc-jp") result = @method.call(format, 9415601) result.encoding.should == Encoding::EUC_JP diff --git a/spec/ruby/core/kernel/singleton_method_spec.rb b/spec/ruby/core/kernel/singleton_method_spec.rb index 0bdf125ad8..7d63fa7cc6 100644 --- a/spec/ruby/core/kernel/singleton_method_spec.rb +++ b/spec/ruby/core/kernel/singleton_method_spec.rb @@ -1,7 +1,7 @@ require_relative '../../spec_helper' describe "Kernel#singleton_method" do - it "find a method defined on the singleton class" do + it "finds a method defined on the singleton class" do obj = Object.new def obj.foo; end obj.singleton_method(:foo).should be_an_instance_of(Method) @@ -38,4 +38,48 @@ describe "Kernel#singleton_method" do e.class.should == NameError } end + + ruby_bug "#20620", ""..."3.4" do + it "finds a method defined in a module included in the singleton class" do + m = Module.new do + def foo + :foo + end + end + + obj = Object.new + obj.singleton_class.include(m) + + obj.singleton_method(:foo).should be_an_instance_of(Method) + obj.singleton_method(:foo).call.should == :foo + end + + it "finds a method defined in a module prepended in the singleton class" do + m = Module.new do + def foo + :foo + end + end + + obj = Object.new + obj.singleton_class.prepend(m) + + obj.singleton_method(:foo).should be_an_instance_of(Method) + obj.singleton_method(:foo).call.should == :foo + end + + it "finds a method defined in a module that an object is extended with" do + m = Module.new do + def foo + :foo + end + end + + obj = Object.new + obj.extend(m) + + obj.singleton_method(:foo).should be_an_instance_of(Method) + obj.singleton_method(:foo).call.should == :foo + end + end end diff --git a/spec/ruby/core/kernel/sleep_spec.rb b/spec/ruby/core/kernel/sleep_spec.rb index 44b417a92e..e9c600aac4 100644 --- a/spec/ruby/core/kernel/sleep_spec.rb +++ b/spec/ruby/core/kernel/sleep_spec.rb @@ -1,5 +1,5 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' +require_relative '../fiber/fixtures/scheduler' describe "Kernel#sleep" do it "is a private method" do @@ -22,7 +22,7 @@ describe "Kernel#sleep" do sleep(Rational(1, 999)).should >= 0 end - it "accepts any Object that reponds to divmod" do + it "accepts any Object that responds to divmod" do o = Object.new def o.divmod(*); [0, 0.001]; end sleep(o).should >= 0 @@ -52,6 +52,17 @@ describe "Kernel#sleep" do t.value.should == 5 end + it "sleeps with nanosecond precision" do + start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + 100.times do + sleep(0.0001) + end + end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + + actual_duration = end_time - start_time + actual_duration.should > 0.01 # 100 * 0.0001 => 0.01 + end + ruby_version_is ""..."3.3" do it "raises a TypeError when passed nil" do -> { sleep(nil) }.should raise_error(TypeError) @@ -74,6 +85,40 @@ describe "Kernel#sleep" do t.value.should == 5 end end + + context "Kernel.sleep with Fiber scheduler" do + before :each do + Fiber.set_scheduler(FiberSpecs::LoggingScheduler.new) + end + + after :each do + Fiber.set_scheduler(nil) + end + + it "calls the scheduler without arguments when no duration is given" do + sleeper = Fiber.new(blocking: false) do + sleep + end + sleeper.resume + Fiber.scheduler.events.should == [{ event: :kernel_sleep, fiber: sleeper, args: [] }] + end + + it "calls the scheduler with the given duration" do + sleeper = Fiber.new(blocking: false) do + sleep(0.01) + end + sleeper.resume + Fiber.scheduler.events.should == [{ event: :kernel_sleep, fiber: sleeper, args: [0.01] }] + end + + it "does not call the scheduler if the fiber is blocking" do + sleeper = Fiber.new(blocking: true) do + sleep(0.01) + end + sleeper.resume + Fiber.scheduler.events.should == [] + end + end end describe "Kernel.sleep" do diff --git a/spec/ruby/core/kernel/sprintf_spec.rb b/spec/ruby/core/kernel/sprintf_spec.rb index 7adf71be76..5a4a90ff7a 100644 --- a/spec/ruby/core/kernel/sprintf_spec.rb +++ b/spec/ruby/core/kernel/sprintf_spec.rb @@ -3,22 +3,62 @@ require_relative 'fixtures/classes' require_relative 'shared/sprintf' require_relative 'shared/sprintf_encoding' +describe :kernel_sprintf_to_str, shared: true do + it "calls #to_str to convert the format object to a String" do + obj = mock('format string') + obj.should_receive(:to_str).and_return("to_str: %i") + @method.call(obj, 42).should == "to_str: 42" + end +end + describe "Kernel#sprintf" do it_behaves_like :kernel_sprintf, -> format, *args { - sprintf(format, *args) + r = nil + -> { + r = sprintf(format, *args) + }.should_not complain(verbose: true) + r } it_behaves_like :kernel_sprintf_encoding, -> format, *args { - sprintf(format, *args) + r = nil + -> { + r = sprintf(format, *args) + }.should_not complain(verbose: true) + r + } + + it_behaves_like :kernel_sprintf_to_str, -> format, *args { + r = nil + -> { + r = sprintf(format, *args) + }.should_not complain(verbose: true) + r } end describe "Kernel.sprintf" do it_behaves_like :kernel_sprintf, -> format, *args { - Kernel.sprintf(format, *args) + r = nil + -> { + r = Kernel.sprintf(format, *args) + }.should_not complain(verbose: true) + r } it_behaves_like :kernel_sprintf_encoding, -> format, *args { - Kernel.sprintf(format, *args) + r = nil + -> { + r = Kernel.sprintf(format, *args) + }.should_not complain(verbose: true) + r + } + + it_behaves_like :kernel_sprintf_to_str, -> format, *args { + r = nil + -> { + r = Kernel.sprintf(format, *args) + }.should_not complain(verbose: true) + r } end diff --git a/spec/ruby/core/kernel/system_spec.rb b/spec/ruby/core/kernel/system_spec.rb index 9671a650cc..9bc03924dd 100644 --- a/spec/ruby/core/kernel/system_spec.rb +++ b/spec/ruby/core/kernel/system_spec.rb @@ -64,6 +64,23 @@ describe :kernel_system, shared: true do end end + platform_is_not :windows do + before :each do + require 'tmpdir' + @shell_command = File.join(Dir.mktmpdir, "noshebang.cmd") + File.write(@shell_command, %[echo "$PATH"\n], perm: 0o700) + end + + after :each do + File.unlink(@shell_command) + Dir.rmdir(File.dirname(@shell_command)) + end + + it "executes with `sh` if the command is executable but not binary and there is no shebang" do + -> { @object.system(@shell_command) }.should output_to_fd(ENV['PATH'] + "\n") + end + end + before :each do ENV['TEST_SH_EXPANSION'] = 'foo' @shell_var = '$TEST_SH_EXPANSION' diff --git a/spec/ruby/core/kernel/taint_spec.rb b/spec/ruby/core/kernel/taint_spec.rb index 0c16b1dbbf..9a2efbaea0 100644 --- a/spec/ruby/core/kernel/taint_spec.rb +++ b/spec/ruby/core/kernel/taint_spec.rb @@ -2,26 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#taint" do - ruby_version_is ""..."3.2" do - it "is a no-op" do - suppress_warning do - o = Object.new - o.taint - o.should_not.tainted? - end - end - - it "warns in verbose mode" do - -> { - obj = mock("tainted") - obj.taint - }.should complain(/Object#taint is deprecated and will be removed in Ruby 3.2/, verbose: true) - end - end - - ruby_version_is "3.2" do - it "has been removed" do - Object.new.should_not.respond_to?(:taint) - end + it "has been removed" do + Object.new.should_not.respond_to?(:taint) end end diff --git a/spec/ruby/core/kernel/tainted_spec.rb b/spec/ruby/core/kernel/tainted_spec.rb index fcae433069..837eb1dafb 100644 --- a/spec/ruby/core/kernel/tainted_spec.rb +++ b/spec/ruby/core/kernel/tainted_spec.rb @@ -2,28 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#tainted?" do - ruby_version_is ""..."3.2" do - it "is a no-op" do - suppress_warning do - o = mock('o') - p = mock('p') - p.taint - o.should_not.tainted? - p.should_not.tainted? - end - end - - it "warns in verbose mode" do - -> { - o = mock('o') - o.tainted? - }.should complain(/Object#tainted\? is deprecated and will be removed in Ruby 3.2/, verbose: true) - end - end - - ruby_version_is "3.2" do - it "has been removed" do - Object.new.should_not.respond_to?(:tainted?) - end + it "has been removed" do + Object.new.should_not.respond_to?(:tainted?) end end diff --git a/spec/ruby/core/kernel/test_spec.rb b/spec/ruby/core/kernel/test_spec.rb index abb365aed2..d26dc06361 100644 --- a/spec/ruby/core/kernel/test_spec.rb +++ b/spec/ruby/core/kernel/test_spec.rb @@ -3,8 +3,8 @@ require_relative 'fixtures/classes' describe "Kernel#test" do before :all do - @file = File.dirname(__FILE__) + '/fixtures/classes.rb' - @dir = File.dirname(__FILE__) + '/fixtures' + @file = __dir__ + '/fixtures/classes.rb' + @dir = __dir__ + '/fixtures' end it "is a private method" do diff --git a/spec/ruby/core/kernel/trust_spec.rb b/spec/ruby/core/kernel/trust_spec.rb index db6f17e0fb..ef3fa9a3e1 100644 --- a/spec/ruby/core/kernel/trust_spec.rb +++ b/spec/ruby/core/kernel/trust_spec.rb @@ -2,27 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#trust" do - ruby_version_is ""..."3.2" do - it "is a no-op" do - suppress_warning do - o = Object.new.untrust - o.should_not.untrusted? - o.trust - o.should_not.untrusted? - end - end - - it "warns in verbose mode" do - -> { - o = Object.new.untrust - o.trust - }.should complain(/Object#trust is deprecated and will be removed in Ruby 3.2/, verbose: true) - end - end - - ruby_version_is "3.2" do - it "has been removed" do - Object.new.should_not.respond_to?(:trust) - end + it "has been removed" do + Object.new.should_not.respond_to?(:trust) end end diff --git a/spec/ruby/core/kernel/untaint_spec.rb b/spec/ruby/core/kernel/untaint_spec.rb index 26b2aabbe9..47e8544bd4 100644 --- a/spec/ruby/core/kernel/untaint_spec.rb +++ b/spec/ruby/core/kernel/untaint_spec.rb @@ -2,27 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#untaint" do - ruby_version_is ""..."3.2" do - it "is a no-op" do - suppress_warning do - o = Object.new.taint - o.should_not.tainted? - o.untaint - o.should_not.tainted? - end - end - - it "warns in verbose mode" do - -> { - o = Object.new.taint - o.untaint - }.should complain(/Object#untaint is deprecated and will be removed in Ruby 3.2/, verbose: true) - end - end - - ruby_version_is "3.2" do - it "has been removed" do - Object.new.should_not.respond_to?(:untaint) - end + it "has been removed" do + Object.new.should_not.respond_to?(:untaint) end end diff --git a/spec/ruby/core/kernel/untrust_spec.rb b/spec/ruby/core/kernel/untrust_spec.rb index 5310cd8eb4..8787ab3fc9 100644 --- a/spec/ruby/core/kernel/untrust_spec.rb +++ b/spec/ruby/core/kernel/untrust_spec.rb @@ -2,26 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#untrust" do - ruby_version_is ""..."3.2" do - it "is a no-op" do - suppress_warning do - o = Object.new - o.untrust - o.should_not.untrusted? - end - end - - it "warns in verbose mode" do - -> { - o = Object.new - o.untrust - }.should complain(/Object#untrust is deprecated and will be removed in Ruby 3.2/, verbose: true) - end - end - - ruby_version_is "3.2" do - it "has been removed" do - Object.new.should_not.respond_to?(:untrust) - end + it "has been removed" do + Object.new.should_not.respond_to?(:untrust) end end diff --git a/spec/ruby/core/kernel/untrusted_spec.rb b/spec/ruby/core/kernel/untrusted_spec.rb index ea36d6c98c..29261be9c4 100644 --- a/spec/ruby/core/kernel/untrusted_spec.rb +++ b/spec/ruby/core/kernel/untrusted_spec.rb @@ -2,27 +2,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Kernel#untrusted?" do - ruby_version_is ""..."3.2" do - it "is a no-op" do - suppress_warning do - o = mock('o') - o.should_not.untrusted? - o.untrust - o.should_not.untrusted? - end - end - - it "warns in verbose mode" do - -> { - o = mock('o') - o.untrusted? - }.should complain(/Object#untrusted\? is deprecated and will be removed in Ruby 3.2/, verbose: true) - end - end - - ruby_version_is "3.2" do - it "has been removed" do - Object.new.should_not.respond_to?(:untrusted?) - end + it "has been removed" do + Object.new.should_not.respond_to?(:untrusted?) end end diff --git a/spec/ruby/core/kernel/warn_spec.rb b/spec/ruby/core/kernel/warn_spec.rb index 00164ad90b..e03498c6dc 100644 --- a/spec/ruby/core/kernel/warn_spec.rb +++ b/spec/ruby/core/kernel/warn_spec.rb @@ -112,6 +112,12 @@ describe "Kernel#warn" do ruby_exe(file, options: "-rrubygems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n" end + it "doesn't show the caller when the uplevel is `nil`" do + w = KernelSpecs::WarnInNestedCall.new + + -> { w.f4("foo", nil) }.should output(nil, "foo\n") + end + guard -> { Kernel.instance_method(:tap).source_location } do it "skips <internal: core library methods defined in Ruby" do file, line = Kernel.instance_method(:tap).source_location |
