diff --git a/spec/ruby/command_line/dash_upper_w_spec.rb b/spec/ruby/command_line/dash_upper_w_spec.rb index 31bb976ad2..8343bc08e4 100644 --- a/spec/ruby/command_line/dash_upper_w_spec.rb +++ b/spec/ruby/command_line/dash_upper_w_spec.rb @@ -18,3 +18,25 @@ end describe "The -W command line option with 2" do it_behaves_like :command_line_verbose, "-W2" end + +ruby_version_is "2.7" do + describe "The -W command line option with :no-deprecated" do + it "suppresses deprecation warnings" do + result = ruby_exe('$; = ""', options: '-w', args: '2>&1') + result.should =~ /is deprecated/ + + result = ruby_exe('$; = ""', options: '-w -W:no-deprecated', args: '2>&1') + result.should == "" + end + end + + describe "The -W command line option with :no-experimental" do + it "suppresses experimental warnings" do + result = ruby_exe('0 in a', args: '2>&1') + result.should =~ /is experimental/ + + result = ruby_exe('0 in a', options: '-W:no-experimental', args: '2>&1') + result.should == "" + end + end +end diff --git a/spec/ruby/command_line/feature_spec.rb b/spec/ruby/command_line/feature_spec.rb index 8848249c64..16e106b37d 100644 --- a/spec/ruby/command_line/feature_spec.rb +++ b/spec/ruby/command_line/feature_spec.rb @@ -37,11 +37,13 @@ describe "The --enable and --disable flags" do ruby_exe("p 'foo'.frozen?", options: "--disable-frozen-string-literal").chomp.should == "false" end - it "can be used with all for enable" do - e = "p [defined?(Gem), defined?(DidYouMean), $VERBOSE, 'foo'.frozen?]" - env = {'RUBYOPT' => '-w'} - # Use a single variant here because it can be quite slow as it might enable jit, etc - ruby_exe(e, options: "--enable-all", env: env).chomp.should == "[\"constant\", \"constant\", true, true]" + platform_is_not :darwin do # frequently hangs for >60s on GitHub Actions macos-latest + it "can be used with all for enable" do + e = "p [defined?(Gem), defined?(DidYouMean), $VERBOSE, 'foo'.frozen?]" + env = {'RUBYOPT' => '-w'} + # Use a single variant here because it can be quite slow as it might enable jit, etc + ruby_exe(e, options: "--enable-all", env: env).chomp.should == "[\"constant\", \"constant\", true, true]" + end end it "can be used with all for disable" do diff --git a/spec/ruby/command_line/rubyopt_spec.rb b/spec/ruby/command_line/rubyopt_spec.rb index 2db42f77ef..ee4e594a1c 100644 --- a/spec/ruby/command_line/rubyopt_spec.rb +++ b/spec/ruby/command_line/rubyopt_spec.rb @@ -59,6 +59,26 @@ describe "Processing RUBYOPT" do ruby_exe("p $VERBOSE", escape: true).chomp.should == "true" end + ruby_version_is "2.7" do + it "suppresses deprecation warnings for '-W:no-deprecated'" do + ENV["RUBYOPT"] = '-W:no-deprecated' + result = ruby_exe('$; = ""', args: '2>&1') + result.should == "" + end + + it "suppresses experimental warnings for '-W:no-experimental'" do + ENV["RUBYOPT"] = '-W:no-experimental' + result = ruby_exe('0 in a', args: '2>&1') + result.should == "" + end + + it "suppresses deprecation and experimental warnings for '-W:no-deprecated -W:no-experimental'" do + ENV["RUBYOPT"] = '-W:no-deprecated -W:no-experimental' + result = ruby_exe('($; = "") in a', args: '2>&1') + result.should == "" + end + end + it "requires the file for '-r'" do f = fixture __FILE__, "rubyopt" ENV["RUBYOPT"] = "-r#{f}" diff --git a/spec/ruby/core/array/minmax_spec.rb b/spec/ruby/core/array/minmax_spec.rb new file mode 100644 index 0000000000..e11fe63347 --- /dev/null +++ b/spec/ruby/core/array/minmax_spec.rb @@ -0,0 +1,14 @@ +require_relative '../../spec_helper' +require_relative '../../shared/enumerable/minmax' + +describe "Array#minmax" do + before :each do + @enum = [6, 4, 5, 10, 8] + @empty_enum = [] + @incomparable_enum = [BasicObject.new, BasicObject.new] + @incompatible_enum = [11, "22"] + @strs = ["333", "2", "60", "55555", "1010", "111"] + end + + it_behaves_like :enumerable_minmax, :minmax +end diff --git a/spec/ruby/core/class/new_spec.rb b/spec/ruby/core/class/new_spec.rb index 989d674558..f863766c1a 100644 --- a/spec/ruby/core/class/new_spec.rb +++ b/spec/ruby/core/class/new_spec.rb @@ -96,11 +96,12 @@ describe "Class.new" do it "raises a TypeError when given a non-Class" do error_msg = /superclass must be a Class/ - -> { Class.new("") }.should raise_error(TypeError, error_msg) - -> { Class.new(1) }.should raise_error(TypeError, error_msg) - -> { Class.new(:symbol) }.should raise_error(TypeError, error_msg) - -> { Class.new(mock('o')) }.should raise_error(TypeError, error_msg) - -> { Class.new(Module.new) }.should raise_error(TypeError, error_msg) + -> { Class.new("") }.should raise_error(TypeError, error_msg) + -> { Class.new(1) }.should raise_error(TypeError, error_msg) + -> { Class.new(:symbol) }.should raise_error(TypeError, error_msg) + -> { Class.new(mock('o')) }.should raise_error(TypeError, error_msg) + -> { Class.new(Module.new) }.should raise_error(TypeError, error_msg) + -> { Class.new(BasicObject.new) }.should raise_error(TypeError, error_msg) end end diff --git a/spec/ruby/core/encoding/list_spec.rb b/spec/ruby/core/encoding/list_spec.rb index 2a2078974e..8efd94ab9c 100644 --- a/spec/ruby/core/encoding/list_spec.rb +++ b/spec/ruby/core/encoding/list_spec.rb @@ -12,7 +12,7 @@ describe "Encoding.list" do end it "returns each encoding only once" do - orig = Encoding.list.map {|e| e.name} + orig = Encoding.list.map { |e| e.name } orig.should == orig.uniq end @@ -33,7 +33,17 @@ describe "Encoding.list" do end it "includes dummy encodings" do - Encoding.list.select {|e| e.dummy?}.should_not == [] + Encoding.list.select { |e| e.dummy? }.should_not == [] + end + + it 'includes UTF-8 encoding' do + Encoding.list.should.include?(Encoding::UTF_8) + end + + ruby_version_is "2.7" do + it 'includes CESU-8 encoding' do + Encoding.list.should.include?(Encoding::CESU_8) + end end # TODO: Find example that illustrates this diff --git a/spec/ruby/core/enumerable/minmax_spec.rb b/spec/ruby/core/enumerable/minmax_spec.rb index 29f1ecf82c..f5f17ef079 100644 --- a/spec/ruby/core/enumerable/minmax_spec.rb +++ b/spec/ruby/core/enumerable/minmax_spec.rb @@ -1,41 +1,17 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative '../../shared/enumerable/minmax' describe "Enumerable#minmax" do before :each do @enum = EnumerableSpecs::Numerous.new(6, 4, 5, 10, 8) - + @empty_enum = EnumerableSpecs::Empty.new + @incomparable_enum = EnumerableSpecs::Numerous.new(BasicObject.new, BasicObject.new) + @incompatible_enum = EnumerableSpecs::Numerous.new(11,"22") @strs = EnumerableSpecs::Numerous.new("333", "2", "60", "55555", "1010", "111") end - it "min should return the minimum element" do - @enum.minmax.should == [4, 10] - @strs.minmax.should == ["1010", "60" ] - end - - it "returns [nil, nil] for an empty Enumerable" do - EnumerableSpecs::Empty.new.minmax.should == [nil, nil] - end - - it "raises an ArgumentError when elements are incomparable" do - -> do - EnumerableSpecs::Numerous.new(11,"22").minmax - end.should raise_error(ArgumentError) - -> do - EnumerableSpecs::Numerous.new(11,12,22,33).minmax{|a, b| nil} - end.should raise_error(ArgumentError) - end - - it "raises a NoMethodError for elements without #<=>" do - -> do - EnumerableSpecs::Numerous.new(BasicObject.new, BasicObject.new).minmax - end.should raise_error(NoMethodError) - end - - it "returns the minimum when using a block rule" do - @enum.minmax {|a,b| b <=> a }.should == [10, 4] - @strs.minmax {|a,b| a.length <=> b.length }.should == ["2", "55555"] - end + it_behaves_like :enumerable_minmax, :minmax it "gathers whole arrays as elements when each yields multiple" do multi = EnumerableSpecs::YieldsMulti.new diff --git a/spec/ruby/core/env/shared/key.rb b/spec/ruby/core/env/shared/key.rb index fcb3a9b8c5..93396d2aca 100644 --- a/spec/ruby/core/env/shared/key.rb +++ b/spec/ruby/core/env/shared/key.rb @@ -9,15 +9,23 @@ describe :env_key, shared: true do it "returns the index associated with the passed value" do ENV["foo"] = "bar" - ENV.send(@method, "bar").should == "foo" + suppress_warning { + ENV.send(@method, "bar").should == "foo" + } end it "returns nil if the passed value is not found" do ENV.delete("foo") - ENV.send(@method, "foo").should be_nil + suppress_warning { + ENV.send(@method, "foo").should be_nil + } end it "raises TypeError if the argument is not a String and does not respond to #to_str" do - -> { ENV.send(@method, Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into String") + -> { + suppress_warning { + ENV.send(@method, Object.new) + } + }.should raise_error(TypeError, "no implicit conversion of Object into String") end end diff --git a/spec/ruby/core/file/shared/path.rb b/spec/ruby/core/file/shared/path.rb index d964acc855..0a5abe33f0 100644 --- a/spec/ruby/core/file/shared/path.rb +++ b/spec/ruby/core/file/shared/path.rb @@ -15,6 +15,23 @@ describe :file_path, shared: true do @file.send(@method).should be_an_instance_of(String) end + it "returns a different String on every call" do + @file = File.new @path + path1 = @file.send(@method) + path2 = @file.send(@method) + path1.should == path2 + path1.should_not.equal?(path2) + end + + it "returns a mutable String" do + @file = File.new @path.dup.freeze + path = @file.send(@method) + path.should == @path + path.should_not.frozen? + path << "test" + @file.send(@method).should == @path + end + it "calls to_str on argument and returns exact value" do path = mock('path') path.should_receive(:to_str).and_return(@path) diff --git a/spec/ruby/core/hash/element_reference_spec.rb b/spec/ruby/core/hash/element_reference_spec.rb index 2eb65d3789..e271f37ea6 100644 --- a/spec/ruby/core/hash/element_reference_spec.rb +++ b/spec/ruby/core/hash/element_reference_spec.rb @@ -117,4 +117,18 @@ describe "Hash#[]" do key = HashSpecs::KeyWithPrivateHash.new { key => 42 }[key].should == 42 end + + it "does not dispatch to hash for Boolean, Integer, Float, String, or Symbol" do + code = <<-EOC + load '#{fixture __FILE__, "name.rb"}' + hash = { true => 42, false => 42, 1 => 42, 2.0 => 42, "hello" => 42, :ok => 42 } + [true, false, 1, 2.0, "hello", :ok].each do |value| + raise "incorrect value" unless hash[value] == 42 + end + puts "Ok." + EOC + result = ruby_exe(code, args: "2>&1") + result.should == "Ok.\n" + end + end diff --git a/spec/ruby/core/hash/fixtures/name.rb b/spec/ruby/core/hash/fixtures/name.rb new file mode 100644 index 0000000000..b203bf6ae4 --- /dev/null +++ b/spec/ruby/core/hash/fixtures/name.rb @@ -0,0 +1,30 @@ +class TrueClass + def hash + raise "TrueClass#hash should not be called" + end +end +class FalseClass + def hash + raise "FalseClass#hash should not be called" + end +end +class Integer + def hash + raise "Integer#hash should not be called" + end +end +class Float + def hash + raise "Float#hash should not be called" + end +end +class String + def hash + raise "String#hash should not be called" + end +end +class Symbol + def hash + raise "Symbol#hash should not be called" + end +end diff --git a/spec/ruby/core/hash/shared/store.rb b/spec/ruby/core/hash/shared/store.rb index 84ffb41e33..eca0e5a8e8 100644 --- a/spec/ruby/core/hash/shared/store.rb +++ b/spec/ruby/core/hash/shared/store.rb @@ -95,4 +95,21 @@ describe :hash_store, shared: true do hash.each { hash.send(@method, 1, :foo) } hash.should == {1 => :foo, 3 => 4, 5 => 6} end + + it "does not dispatch to hash for Boolean, Integer, Float, String, or Symbol" do + code = <<-EOC + load '#{fixture __FILE__, "name.rb"}' + hash = {} + [true, false, 1, 2.0, "hello", :ok].each do |value| + hash[value] = 42 + raise "incorrect value" unless hash[value] == 42 + hash[value] = 43 + raise "incorrect value" unless hash[value] == 43 + end + puts "OK" + puts hash.size + EOC + result = ruby_exe(code, args: "2>&1") + result.should == "OK\n6\n" + end end diff --git a/spec/ruby/core/integer/chr_spec.rb b/spec/ruby/core/integer/chr_spec.rb index a8755eeb84..9f105e4241 100644 --- a/spec/ruby/core/integer/chr_spec.rb +++ b/spec/ruby/core/integer/chr_spec.rb @@ -240,4 +240,17 @@ describe "Integer#chr with an encoding argument" do -> { integer.chr(encoding_name) }.should raise_error(RangeError) end end + + ruby_version_is "2.7" do + it 'returns a String encoding self interpreted as a codepoint in the CESU-8 encoding' do + # see more details here https://en.wikipedia.org/wiki/CESU-8 + # code points from U+0000 to U+FFFF is encoded in the same way as in UTF-8 + 0x0045.chr(Encoding::CESU_8).bytes.should == 0x0045.chr(Encoding::UTF_8).bytes + + # code points in range from U+10000 to U+10FFFF is CESU-8 data containing a 6-byte surrogate pair, + # which decodes to a 4-byte UTF-8 string + 0x10400.chr(Encoding::CESU_8).bytes.should != 0x10400.chr(Encoding::UTF_8).bytes + 0x10400.chr(Encoding::CESU_8).bytes.to_a.should == [0xED, 0xA0, 0x81, 0xED, 0xB0, 0x80] + end + end end diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb index 6c6895e317..4e0322025c 100644 --- a/spec/ruby/core/kernel/shared/require.rb +++ b/spec/ruby/core/kernel/shared/require.rb @@ -160,6 +160,14 @@ describe :kernel_require_basic, shared: true do ScratchPad.recorded.should == [:loaded] end + it "accepts an Object with #to_path in $LOAD_PATH" do + obj = mock("to_path") + obj.should_receive(:to_path).at_least(:once).and_return(CODE_LOADING_DIR) + $LOAD_PATH << obj + @object.send(@method, "load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + it "does not require file twice after $LOAD_PATH change" do $LOAD_PATH << CODE_LOADING_DIR @object.require("load_fixture.rb").should be_true diff --git a/spec/ruby/core/kernel/taint_spec.rb b/spec/ruby/core/kernel/taint_spec.rb index 060e73963e..22b2c4d0a4 100644 --- a/spec/ruby/core/kernel/taint_spec.rb +++ b/spec/ruby/core/kernel/taint_spec.rb @@ -44,4 +44,19 @@ describe "Kernel#taint" do end end end + + ruby_version_is "2.7"..."3.0" do + it "is a no-op" do + o = Object.new + o.taint + o.should_not.tainted? + 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 end diff --git a/spec/ruby/core/kernel/tainted_spec.rb b/spec/ruby/core/kernel/tainted_spec.rb index dbd6bc939a..022938bfc1 100644 --- a/spec/ruby/core/kernel/tainted_spec.rb +++ b/spec/ruby/core/kernel/tainted_spec.rb @@ -11,4 +11,21 @@ describe "Kernel#tainted?" do p.should.tainted? end end + + ruby_version_is "2.7"..."3.0" do + it "is a no-op" do + o = mock('o') + p = mock('p') + p.taint + o.should_not.tainted? + p.should_not.tainted? + 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 end diff --git a/spec/ruby/core/kernel/trust_spec.rb b/spec/ruby/core/kernel/trust_spec.rb index 1d209ea1dc..399983f74d 100644 --- a/spec/ruby/core/kernel/trust_spec.rb +++ b/spec/ruby/core/kernel/trust_spec.rb @@ -24,4 +24,20 @@ describe "Kernel#trust" do o.trust.should equal(o) end end + + ruby_version_is "2.7"..."3.0" do + it "is a no-op" do + o = Object.new.untrust + o.should_not.untrusted? + o.trust + o.should_not.untrusted? + 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 end diff --git a/spec/ruby/core/kernel/untaint_spec.rb b/spec/ruby/core/kernel/untaint_spec.rb index 171d32b356..44d87da5d5 100644 --- a/spec/ruby/core/kernel/untaint_spec.rb +++ b/spec/ruby/core/kernel/untaint_spec.rb @@ -24,4 +24,20 @@ describe "Kernel#untaint" do o.untaint.should equal(o) end end + + ruby_version_is "2.7"..."3.0" do + it "is a no-op" do + o = Object.new.taint + o.should_not.tainted? + o.untaint + o.should_not.tainted? + 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 end diff --git a/spec/ruby/core/kernel/untrust_spec.rb b/spec/ruby/core/kernel/untrust_spec.rb index fca7c9ea47..aff0fec57b 100644 --- a/spec/ruby/core/kernel/untrust_spec.rb +++ b/spec/ruby/core/kernel/untrust_spec.rb @@ -24,4 +24,19 @@ describe "Kernel#untrust" do o.untrust.should equal(o) end end + + ruby_version_is "2.7"..."3.0" do + it "is a no-op" do + o = Object.new + o.untrust + o.should_not.untrusted? + 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 end diff --git a/spec/ruby/core/kernel/untrusted_spec.rb b/spec/ruby/core/kernel/untrusted_spec.rb index 65cbffa3ad..5347c90093 100644 --- a/spec/ruby/core/kernel/untrusted_spec.rb +++ b/spec/ruby/core/kernel/untrusted_spec.rb @@ -27,4 +27,20 @@ describe "Kernel#untrusted?" do -> { d.untrust }.should_not raise_error(RuntimeError) end end + + ruby_version_is "2.7"..."3.0" do + it "is a no-op" do + o = mock('o') + o.should_not.untrusted? + o.untrust + o.should_not.untrusted? + 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 end diff --git a/spec/ruby/core/kernel/warn_spec.rb b/spec/ruby/core/kernel/warn_spec.rb index de08cd8cff..fcba164f8f 100644 --- a/spec/ruby/core/kernel/warn_spec.rb +++ b/spec/ruby/core/kernel/warn_spec.rb @@ -197,4 +197,35 @@ describe "Kernel#warn" do -> { warn(**h) }.should_not complain(verbose: true) -> { warn('foo', **h) }.should complain("foo\n") end + + it "does not call Warning.warn if self is the Warning module" do + # RubyGems redefines Kernel#warn so we need to use a subprocess and disable RubyGems here + code = <<-RUBY + def Warning.warn(*args, **kwargs) + raise 'should not be called' + end + Kernel.instance_method(:warn).bind(Warning).call('Kernel#warn spec edge case') + RUBY + out = ruby_exe(code, args: "2>&1", options: "--disable-gems") + out.should == "Kernel#warn spec edge case\n" + $?.should.success? + end + + it "avoids recursion if Warning#warn is redefined and calls super" do + # This works because of the spec above, which is the workaround for it. + # Note that redefining Warning#warn is a mistake which would naturally end in infinite recursion, + # Warning.extend Module.new { def warn } should be used instead. + # RubyGems redefines Kernel#warn so we need to use a subprocess and disable RubyGems here + code = <<-RUBY + module Warning + def warn(*args, **kwargs) + super + end + end + warn "avoid infinite recursion" + RUBY + out = ruby_exe(code, args: "2>&1", options: "--disable-gems") + out.should == "avoid infinite recursion\n" + $?.should.success? + end end diff --git a/spec/ruby/core/method/fixtures/classes.rb b/spec/ruby/core/method/fixtures/classes.rb index f3b7ff921c..be96f65e25 100644 --- a/spec/ruby/core/method/fixtures/classes.rb +++ b/spec/ruby/core/method/fixtures/classes.rb @@ -50,6 +50,8 @@ module MethodSpecs def one_req(a); end def two_req(a, b); end + def one_req_named(a:); end + def zero_with_block(&blk); end def one_req_with_block(a, &blk); end def two_req_with_block(a, b, &blk); end @@ -59,6 +61,8 @@ module MethodSpecs def one_req_two_opt(a, b=nil, c=nil); end def two_req_one_opt(a, b, c=nil); end + def one_opt_named(a: nil); end + def one_opt_with_block(a=nil, &blk); end def one_req_one_opt_with_block(a, b=nil, &blk); end def one_req_two_opt_with_block(a, b=nil, c=nil, &blk); end @@ -71,6 +75,8 @@ module MethodSpecs def two_req_one_opt_with_splat(a, b, c=nil, *d); end def one_req_two_opt_with_splat(a, b=nil, c=nil, *d); end + def zero_with_double_splat(**a); end + def zero_with_splat_and_block(*a, &blk); end def one_req_with_splat_and_block(a, *b, &blk); end def two_req_with_splat_and_block(a, b, *c, &blk); end diff --git a/spec/ruby/core/method/shared/to_s.rb b/spec/ruby/core/method/shared/to_s.rb index 0c0edc2f8c..1fbee870d6 100644 --- a/spec/ruby/core/method/shared/to_s.rb +++ b/spec/ruby/core/method/shared/to_s.rb @@ -4,7 +4,7 @@ require_relative '../fixtures/classes' describe :method_to_s, shared: true do before :each do @m = MethodSpecs::MySub.new.method :bar - @string = @m.send(@method).sub(/0x\w+/, '0xXXXXXX') + @string = @m.send(@method) end it "returns a String" do @@ -24,6 +24,21 @@ describe :method_to_s, shared: true do @string.should =~ /\#bar/ end + ruby_version_is "2.7" do + it "returns a String containing method arguments" do + obj = MethodSpecs::Methods.new + obj.method(:zero).send(@method).should.include?("()") + obj.method(:one_req).send(@method).should.include?("(a)") + obj.method(:one_req_named).send(@method).should.include?("(a:)") + obj.method(:zero_with_block).send(@method).should.include?("(&blk)") + obj.method(:one_opt).send(@method).should.include?("(a=...)") + obj.method(:one_opt_named).send(@method).should.include?("(a: ...)") + obj.method(:zero_with_splat).send(@method).should.include?("(*a)") + obj.method(:zero_with_double_splat).send(@method).should.include?("(**a)") + obj.method(:one_req_one_opt_with_splat_and_block).send(@method).should.include?("(a, b=..., *c, &blk)") + end + end + it "returns a String containing the Module the method is defined in" do @string.should =~ /MethodSpecs::MyMod/ end @@ -32,13 +47,21 @@ describe :method_to_s, shared: true do @string.should =~ /MethodSpecs::MySub/ end + it "returns a String including all details" do + @string.should.start_with? "#\.bar/ + @string = @m.send(@method).sub(/0x\h+/, '0xXXXXXX') + @string.should.start_with? "#.bar" end end diff --git a/spec/ruby/core/method/source_location_spec.rb b/spec/ruby/core/method/source_location_spec.rb index dd81b02c77..051739c57d 100644 --- a/spec/ruby/core/method/source_location_spec.rb +++ b/spec/ruby/core/method/source_location_spec.rb @@ -86,6 +86,24 @@ describe "Method#source_location" do method.source_location[1].should == line end + it "works for core methods where it returns nil or { Warning[:noop] }.should raise_error(ArgumentError, /unknown category: noop/) + end + end +end diff --git a/spec/ruby/core/warning/element_set_spec.rb b/spec/ruby/core/warning/element_set_spec.rb new file mode 100644 index 0000000000..ee83656cb4 --- /dev/null +++ b/spec/ruby/core/warning/element_set_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../spec_helper' + +ruby_version_is '2.7' do + describe "Warning.[]=" do + it "emits and suppresses warnings for :deprecated" do + ruby_exe('Warning[:deprecated] = true; $; = ""', args: "2>&1").should =~ /is deprecated/ + ruby_exe('Warning[:deprecated] = false; $; = ""', args: "2>&1").should == "" + end + + it "emits and suppresses warnings for :experimental" do + ruby_exe('Warning[:experimental] = true; eval("0 in a")', args: "2>&1").should =~ /is experimental/ + ruby_exe('Warning[:experimental] = false; eval("0 in a")', args: "2>&1").should == "" + end + + it "raises for unknown category" do + -> { Warning[:noop] = false }.should raise_error(ArgumentError, /unknown category: noop/) + end + end +end diff --git a/spec/ruby/fixtures/code_loading.rb b/spec/ruby/fixtures/code_loading.rb index d6cf0edb47..decd56a358 100644 --- a/spec/ruby/fixtures/code_loading.rb +++ b/spec/ruby/fixtures/code_loading.rb @@ -12,7 +12,22 @@ module CodeLoadingSpecs end end + def self.preload_rubygems + # Require RubyGems eagerly, to ensure #require is already the RubyGems + # version and RubyGems is only loaded once, before starting #require/#autoload specs + # which snapshot $LOADED_FEATURES and could cause RubyGems to load twice. + # #require specs also snapshot #require, and could end up redefining #require as the original core Kernel#require. + @rubygems ||= begin + require "rubygems" + true + rescue LoadError + true + end + end + def self.spec_setup + preload_rubygems + @saved_loaded_features = $LOADED_FEATURES.clone @saved_load_path = $LOAD_PATH.clone ScratchPad.record [] diff --git a/spec/ruby/language/case_spec.rb b/spec/ruby/language/case_spec.rb index 1475e20f75..410cb9afb1 100644 --- a/spec/ruby/language/case_spec.rb +++ b/spec/ruby/language/case_spec.rb @@ -424,4 +424,13 @@ describe "The 'case'-construct with no target expression" do :called end.should == :called end + + # Homogeneous cases are often optimized to avoid === using a jump table, and should be tested separately. + # See https://github.com/jruby/jruby/issues/6440 + it "handles homogeneous cases" do + case + when 1; 'foo' + when 2; 'bar' + end.should == 'foo' + end end diff --git a/spec/ruby/language/class_spec.rb b/spec/ruby/language/class_spec.rb index 4ff6e65181..fa30e22c3a 100644 --- a/spec/ruby/language/class_spec.rb +++ b/spec/ruby/language/class_spec.rb @@ -285,6 +285,16 @@ describe "A class definition extending an object (sclass)" do }.should raise_error(TypeError) end + it "raises a TypeError when trying to extend non-Class" do + error_msg = /superclass must be a Class/ + -> { class TestClass < ""; end }.should raise_error(TypeError, error_msg) + -> { class TestClass < 1; end }.should raise_error(TypeError, error_msg) + -> { class TestClass < :symbol; end }.should raise_error(TypeError, error_msg) + -> { class TestClass < mock('o'); end }.should raise_error(TypeError, error_msg) + -> { class TestClass < Module.new; end }.should raise_error(TypeError, error_msg) + -> { class TestClass < BasicObject.new; end }.should raise_error(TypeError, error_msg) + end + ruby_version_is ""..."3.0" do it "allows accessing the block of the original scope" do suppress_warning do diff --git a/spec/ruby/language/fixtures/super.rb b/spec/ruby/language/fixtures/super.rb index 6a024cae23..218f1e4970 100644 --- a/spec/ruby/language/fixtures/super.rb +++ b/spec/ruby/language/fixtures/super.rb @@ -455,6 +455,52 @@ module SuperSpecs end end + module ZSuperWithRestAndPost + class A + def m(*args, a, b) + args + end + + def m_modified(*args, a, b) + args + end + end + + class B < A + def m(*args, a, b) + super + end + + def m_modified(*args, a, b) + args[1] = 14 + super + end + end + end + + module ZSuperWithRestOthersAndPost + class A + def m(a, *args, b) + args + end + + def m_modified(a, *args, b) + args + end + end + + class B < A + def m(a, *args, b) + super + end + + def m_modified(a, *args, b) + args[1] = 14 + super + end + end + end + module ZSuperWithRestReassigned class A def a(*args) diff --git a/spec/ruby/language/heredoc_spec.rb b/spec/ruby/language/heredoc_spec.rb index 1ec21a2ec0..95df8457e4 100644 --- a/spec/ruby/language/heredoc_spec.rb +++ b/spec/ruby/language/heredoc_spec.rb @@ -59,6 +59,22 @@ HERE s.encoding.should == Encoding::US_ASCII end + ruby_version_is "2.7" do + it 'raises SyntaxError if quoted HEREDOC identifier is ending not on same line' do + -> { + eval %{<<"HERE\n"\nraises syntax error\nHERE} + }.should raise_error(SyntaxError) + end + end + + ruby_version_is ""..."2.7" do + it 'prints a warning if quoted HEREDOC identifier is ending not on same line' do + -> { + eval %{<<"HERE\n"\nit warns\nHERE} + }.should complain(/here document identifier ends with a newline/) + end + end + it "allows HEREDOC with <<~'identifier', allowing to indent identifier and content" do require_relative 'fixtures/squiggly_heredoc' SquigglyHeredocSpecs.message.should == "character density, n.:\n The number of very weird people in the office.\n" diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb index dd4ea51572..462a182b3d 100644 --- a/spec/ruby/language/method_spec.rb +++ b/spec/ruby/language/method_spec.rb @@ -596,10 +596,21 @@ describe "A method" do m(a: 1, b: 2).should == { a: 1, b: 2 } m(*[]).should == {} m(**{}).should == {} - m(**{a: 1, b: 2}, **{a: 4, c: 7}).should == { a: 4, b: 2, c: 7 } + suppress_warning { + eval "m(**{a: 1, b: 2}, **{a: 4, c: 7})" + }.should == { a: 4, b: 2, c: 7 } -> { m(2) }.should raise_error(ArgumentError) end + ruby_version_is "2.7" do + evaluate <<-ruby do + def m(**k); k end; + ruby + + m("a" => 1).should == { "a" => 1 } + end + end + evaluate <<-ruby do def m(&b) b end ruby @@ -1626,6 +1637,20 @@ describe "A method" do result.should == [1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l] end + ruby_version_is "2.7" do + evaluate <<-ruby do + def m(a, **nil); a end; + ruby + + m({a: 1}).should == {a: 1} + m({"a" => 1}).should == {"a" => 1} + + -> { m(a: 1) }.should raise_error(ArgumentError) + -> { m(**{a: 1}) }.should raise_error(ArgumentError) + -> { m("a" => 1) }.should raise_error(ArgumentError) + end + end + ruby_version_is ''...'3.0' do evaluate <<-ruby do def m(a, b = nil, c = nil, d, e: nil, **f) @@ -1665,6 +1690,32 @@ describe "A method" do end end + ruby_version_is '2.7' do + context 'when passing an empty keyword splat to a method that does not accept keywords' do + evaluate <<-ruby do + def m(*a); a; end + ruby + + h = {} + m(**h).should == [] + end + end + end + + ruby_version_is '2.7'...'3.0' do + context 'when passing an empty keyword splat to a method that does not accept keywords' do + evaluate <<-ruby do + def m(a); a; end + ruby + h = {} + + -> do + m(**h).should == {} + end.should complain(/warning: Passing the keyword argument as the last hash parameter is deprecated/) + end + end + end + ruby_version_is ''...'3.0' do context "assigns keyword arguments from a passed Hash without modifying it" do evaluate <<-ruby do @@ -1683,6 +1734,18 @@ describe "A method" do end ruby_version_is '3.0' do + context 'when passing an empty keyword splat to a method that does not accept keywords' do + evaluate <<-ruby do + def m(a); a; end + ruby + h = {} + + -> do + m(**h).should == {} + end.should raise_error(ArgumentError) + end + end + context "raises ArgumentError if passing hash as keyword arguments" do evaluate <<-ruby do def m(a: nil); a; end diff --git a/spec/ruby/language/predefined_spec.rb b/spec/ruby/language/predefined_spec.rb index 5ce4e77906..764c96e838 100644 --- a/spec/ruby/language/predefined_spec.rb +++ b/spec/ruby/language/predefined_spec.rb @@ -933,6 +933,14 @@ describe "Global variable $-d" do end describe "Global variable $VERBOSE" do + before :each do + @verbose = $VERBOSE + end + + after :each do + $VERBOSE = @verbose + end + it "converts truthy values to true" do [true, 1, 0, [], ""].each do |true_value| $VERBOSE = true_value diff --git a/spec/ruby/language/safe_spec.rb b/spec/ruby/language/safe_spec.rb index 53ab4f9561..f3a7efc953 100644 --- a/spec/ruby/language/safe_spec.rb +++ b/spec/ruby/language/safe_spec.rb @@ -135,4 +135,18 @@ describe "The $SAFE variable" do }.call end end + + ruby_version_is "2.7"..."3.0" do + it "warn when access" do + -> { + $SAFE + }.should complain(/\$SAFE will become a normal global variable in Ruby 3.0/) + end + + it "warn when set" do + -> { + $SAFE = 1 + }.should complain(/\$SAFE will become a normal global variable in Ruby 3.0/) + end + end end diff --git a/spec/ruby/language/singleton_class_spec.rb b/spec/ruby/language/singleton_class_spec.rb index df735018af..705d9f3548 100644 --- a/spec/ruby/language/singleton_class_spec.rb +++ b/spec/ruby/language/singleton_class_spec.rb @@ -157,6 +157,23 @@ describe "A constant on a singleton class" do end end +describe "Defining yield in singleton class" do + ruby_version_is "2.7"..."3.0" do + it 'emits a deprecation warning' do + code = <<~RUBY + def m + class << Object.new + yield + end + end + m { :ok } + RUBY + + -> { eval(code) }.should complain(/warning: `yield' in class syntax will not be supported from Ruby 3.0/) + end + end +end + describe "Defining instance methods on a singleton class" do before :each do @k = ClassSpecs::K.new diff --git a/spec/ruby/language/super_spec.rb b/spec/ruby/language/super_spec.rb index 3e94155bf3..1ac5c5e1be 100644 --- a/spec/ruby/language/super_spec.rb +++ b/spec/ruby/language/super_spec.rb @@ -293,6 +293,21 @@ describe "The super keyword" do it "without explicit arguments passes arguments and rest arguments" do SuperSpecs::ZSuperWithRestAndOthers::B.new.m(1, 2, 3, 4, 5).should == [3, 4, 5] + SuperSpecs::ZSuperWithRestAndOthers::B.new.m(1, 2).should == [] + end + + it "without explicit arguments passes arguments, rest arguments, and post arguments" do + SuperSpecs::ZSuperWithRestAndPost::B.new.m(1, 2, 3, 4, 5).should == [1, 2, 3] + SuperSpecs::ZSuperWithRestOthersAndPost::B.new.m(1, 2, 3, 4, 5).should == [2, 3, 4] + SuperSpecs::ZSuperWithRestAndPost::B.new.m(1, 2).should == [] + SuperSpecs::ZSuperWithRestOthersAndPost::B.new.m(1, 2).should == [] + end + + it "without explicit arguments passes arguments, rest arguments including modifications, and post arguments" do + SuperSpecs::ZSuperWithRestAndPost::B.new.m_modified(1, 2, 3, 4, 5).should == [1, 14, 3] + SuperSpecs::ZSuperWithRestOthersAndPost::B.new.m_modified(1, 2, 3, 4, 5).should == [2, 14, 4] + SuperSpecs::ZSuperWithRestAndPost::B.new.m_modified(1, 2).should == [nil, 14] + SuperSpecs::ZSuperWithRestOthersAndPost::B.new.m_modified(1, 2).should == [nil, 14] end it "without explicit arguments passes arguments and rest arguments including any modifications" do diff --git a/spec/ruby/language/variables_spec.rb b/spec/ruby/language/variables_spec.rb index 8b67289df4..ce072baa2c 100644 --- a/spec/ruby/language/variables_spec.rb +++ b/spec/ruby/language/variables_spec.rb @@ -796,3 +796,32 @@ describe 'Local variable shadowing' do end end end + +describe 'Allowed characters' do + ruby_version_is "2.6" do + # new feature in 2.6 -- https://bugs.ruby-lang.org/issues/13770 + it 'does not allow non-ASCII upcased characters at the beginning' do + -> do + eval <<-CODE + def test + ἍBB = 1 + end + CODE + end.should raise_error(SyntaxError, /dynamic constant assignment/) + end + end + + it 'allows non-ASCII lowercased characters at the beginning' do + result = nil + + eval <<-CODE + def test + μ = 1 + end + + result = test + CODE + + result.should == 1 + end +end diff --git a/spec/ruby/library/yaml/dump_spec.rb b/spec/ruby/library/yaml/dump_spec.rb index 9e884b0fd7..3107a8f51d 100644 --- a/spec/ruby/library/yaml/dump_spec.rb +++ b/spec/ruby/library/yaml/dump_spec.rb @@ -37,9 +37,12 @@ describe "YAML.dump" do it "dumps an OpenStruct" do require "ostruct" os = OpenStruct.new("age" => 20, "name" => "John") - os2 = YAML.load(YAML.dump(os)) - os2.age.should == 20 - os2.name.should == "John" + yaml_dump = YAML.dump(os) + + [ + "--- !ruby/object:OpenStruct\nage: 20\nname: John\n", + "--- !ruby/object:OpenStruct\ntable:\n :age: 20\n :name: John\n", + ].should.include?(yaml_dump) end it "dumps a File without any state" do diff --git a/spec/ruby/optional/capi/ext/thread_spec.c b/spec/ruby/optional/capi/ext/thread_spec.c index 743828b523..53eae456b2 100644 --- a/spec/ruby/optional/capi/ext/thread_spec.c +++ b/spec/ruby/optional/capi/ext/thread_spec.c @@ -73,6 +73,14 @@ static void* blocking_gvl_func_for_udf_io(void *data) { } } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +static VALUE thread_spec_rb_thread_call_without_gvl_native_function(VALUE self) { + pid_t ret = (pid_t) (long) rb_thread_call_without_gvl((void *(*)(void *)) getpid, 0, RUBY_UBF_IO, 0); + return LONG2FIX(ret); +} +#pragma GCC diagnostic pop + /* Returns true if the thread is interrupted. */ static VALUE thread_spec_rb_thread_call_without_gvl_with_ubf_io(VALUE self) { int fds[2]; @@ -134,6 +142,7 @@ void Init_thread_spec(void) { VALUE cls = rb_define_class("CApiThreadSpecs", rb_cObject); rb_define_method(cls, "rb_thread_alone", thread_spec_rb_thread_alone, 0); rb_define_method(cls, "rb_thread_call_without_gvl", thread_spec_rb_thread_call_without_gvl, 0); + rb_define_method(cls, "rb_thread_call_without_gvl_native_function", thread_spec_rb_thread_call_without_gvl_native_function, 0); rb_define_method(cls, "rb_thread_call_without_gvl_with_ubf_io", thread_spec_rb_thread_call_without_gvl_with_ubf_io, 0); rb_define_method(cls, "rb_thread_current", thread_spec_rb_thread_current, 0); rb_define_method(cls, "rb_thread_local_aref", thread_spec_rb_thread_local_aref, 2); diff --git a/spec/ruby/optional/capi/thread_spec.rb b/spec/ruby/optional/capi/thread_spec.rb index df454d1ea8..35b58996b8 100644 --- a/spec/ruby/optional/capi/thread_spec.rb +++ b/spec/ruby/optional/capi/thread_spec.rb @@ -121,6 +121,10 @@ describe "C-API Thread function" do thr.value.should be_true end + it "runs a native function with the global lock unlocked" do + @t.rb_thread_call_without_gvl_native_function.should == Process.pid + end + guard -> { platform_is :mingw and ruby_version_is ""..."2.7" } do it "runs a C function with the global lock unlocked and unlocks IO with the generic RUBY_UBF_IO" do thr = Thread.new do diff --git a/spec/ruby/shared/enumerable/minmax.rb b/spec/ruby/shared/enumerable/minmax.rb new file mode 100644 index 0000000000..8af2626d2a --- /dev/null +++ b/spec/ruby/shared/enumerable/minmax.rb @@ -0,0 +1,24 @@ +describe :enumerable_minmax, shared: true do + it "min should return the minimum element" do + @enum.minmax.should == [4, 10] + @strs.minmax.should == ["1010", "60"] + end + + it "returns the minimum when using a block rule" do + @enum.minmax {|a,b| b <=> a }.should == [10, 4] + @strs.minmax {|a,b| a.length <=> b.length }.should == ["2", "55555"] + end + + it "returns [nil, nil] for an empty Enumerable" do + @empty_enum.minmax.should == [nil, nil] + end + + it "raises a NoMethodError for elements without #<=>" do + -> { @incomparable_enum.minmax }.should raise_error(NoMethodError) + end + + it "raises an ArgumentError when elements are incompatible" do + -> { @incompatible_enum.minmax }.should raise_error(ArgumentError) + -> { @enum.minmax{ |a, b| nil } }.should raise_error(ArgumentError) + end +end