mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	Update to ruby/spec@aaf998f
This commit is contained in:
		
							parent
							
								
									ae650f0372
								
							
						
					
					
						commit
						8db4f25bf4
					
				
					 41 changed files with 821 additions and 56 deletions
				
			
		| 
						 | 
				
			
			@ -39,6 +39,8 @@ CodingUS_ASCII
 | 
			
		|||
CodingUTF_8
 | 
			
		||||
ComparisonTest
 | 
			
		||||
ConstantSpecsIncludedModule
 | 
			
		||||
ConstantSpecsTwo
 | 
			
		||||
ConstantSpecsThree
 | 
			
		||||
ConstantVisibility
 | 
			
		||||
Coverage
 | 
			
		||||
CoverageSpecs
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,13 +18,13 @@ Every example code has a textual description, which presents several advantages:
 | 
			
		|||
 | 
			
		||||
The specs are written with syntax similar to RSpec 2.
 | 
			
		||||
They are run with MSpec, the purpose-built framework for running the Ruby Spec Suite.
 | 
			
		||||
For more information, see the [MSpec](http://github.com/ruby/mspec) project.
 | 
			
		||||
For more information, see the [MSpec](https://github.com/ruby/mspec) project.
 | 
			
		||||
 | 
			
		||||
The specs describe the [language syntax](language/), the [core library](core/), the [standard library](library/), the [C API for extensions](optional/capi) and the [command line flags](command_line/).
 | 
			
		||||
The language specs are grouped by keyword while the core and standard library specs are grouped by class and method.
 | 
			
		||||
 | 
			
		||||
ruby/spec is known to be tested in these implementations for every commit:
 | 
			
		||||
* [MRI](http://rubyci.org/) on 30 platforms and 4 versions
 | 
			
		||||
* [MRI](https://rubyci.org/) on 30 platforms and 4 versions
 | 
			
		||||
* [JRuby](https://github.com/jruby/jruby/tree/master/spec/ruby) for both 1.7 and 9.x
 | 
			
		||||
* [TruffleRuby](https://github.com/oracle/truffleruby/tree/master/spec/ruby)
 | 
			
		||||
* [Opal](https://github.com/opal/opal/tree/master/spec)
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +70,7 @@ Then move to it:
 | 
			
		|||
 | 
			
		||||
    $ cd spec
 | 
			
		||||
 | 
			
		||||
Clone [MSpec](http://github.com/ruby/mspec):
 | 
			
		||||
Clone [MSpec](https://github.com/ruby/mspec):
 | 
			
		||||
 | 
			
		||||
    $ git clone https://github.com/ruby/mspec.git ../mspec
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -152,5 +152,5 @@ This project was originally born from [Rubinius](https://github.com/rubinius/rub
 | 
			
		|||
The revision history of these specs is available [here](https://github.com/ruby/spec/blob/2b886623/CHANGES.before-2008-05-10).
 | 
			
		||||
These specs were later extracted to their own project, RubySpec, with a specific vision and principles.
 | 
			
		||||
At the end of 2014, Brian Shirai, the creator of RubySpec, decided to [end RubySpec](http://rubinius.com/2014/12/31/matz-s-ruby-developers-don-t-use-rubyspec/).
 | 
			
		||||
A couple months later, the different repositories were merged and [the project was revived](http://eregon.github.io/rubyspec/2015/07/29/rubyspec-is-reborn.html).
 | 
			
		||||
A couple months later, the different repositories were merged and [the project was revived](https://eregon.github.io/rubyspec/2015/07/29/rubyspec-is-reborn.html).
 | 
			
		||||
On 12 January 2016, the name was changed to "The Ruby Spec Suite" for clarity and to let the RubySpec ideology rest in peace.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ describe "Array#pack with format 'x'" do
 | 
			
		|||
 | 
			
		||||
  it "does not add a NULL byte when passed the '*' modifier" do
 | 
			
		||||
    [].pack("x*").should == ""
 | 
			
		||||
    [1, 2].pack("Cx*C").should == "\x01\x02"
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,10 +19,18 @@ describe "Array#sample" do
 | 
			
		|||
    [].sample.should be_nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "returns nil for an empty array when called without n and a Random is given" do
 | 
			
		||||
    [].sample(random: Random.new(42)).should be_nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "returns a single value when not passed a count" do
 | 
			
		||||
    [4].sample.should equal(4)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "returns a single value when not passed a count and a Random is given" do
 | 
			
		||||
    [4].sample(random: Random.new(42)).should equal(4)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "returns an empty Array when passed zero" do
 | 
			
		||||
    [4].sample(0).should == []
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,13 @@ describe "Dir.foreach" do
 | 
			
		|||
  it "accepts an encoding keyword for the encoding of the entries" do
 | 
			
		||||
    dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort
 | 
			
		||||
    dirs.each {|dir| dir.encoding.should == Encoding::UTF_8}
 | 
			
		||||
 | 
			
		||||
    dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::UTF_16LE).to_a.sort
 | 
			
		||||
    dirs.each {|dir| dir.encoding.should == Encoding::UTF_16LE}
 | 
			
		||||
 | 
			
		||||
    Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::UTF_16LE) do |f|
 | 
			
		||||
      f.encoding.should == Encoding::UTF_16LE
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is ""..."2.7" do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,4 +66,11 @@ describe :enumerable_inject, shared: true do
 | 
			
		|||
  it "returns nil when fails(legacy rubycon)" do
 | 
			
		||||
    EnumerableSpecs::EachDefiner.new().send(@method) {|acc,x| 999 }.should == nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_bug '#18635', ''...'3.2' do
 | 
			
		||||
    it "raises an ArgumentError when no parameters or block is given" do
 | 
			
		||||
      -> { [1,2].send(@method) }.should raise_error(ArgumentError)
 | 
			
		||||
      -> { {one: 1, two: 2}.send(@method) }.should raise_error(ArgumentError)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,5 +26,13 @@ ruby_version_is "2.7" do
 | 
			
		|||
      (0..Float::INFINITY).lazy.with_index { |i, idx| result << [i * 2, idx] }.first(3)
 | 
			
		||||
      result.should == [[0,0],[2,1],[4,2]]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "resets after a new call to each" do
 | 
			
		||||
      enum = (0..2).lazy.with_index.map { |i, idx| [i, idx] }
 | 
			
		||||
      result = []
 | 
			
		||||
      enum.each { |i, idx| result << [i, idx] }
 | 
			
		||||
      enum.each { |i, idx| result << [i, idx] }
 | 
			
		||||
      result.should == [[0,0], [1,1], [2,2], [0,0], [1,1], [2,2]]
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,13 +15,19 @@ describe "Exception#full_message" do
 | 
			
		|||
  it "supports :highlight option and adds escape sequences to highlight some strings" do
 | 
			
		||||
    e = RuntimeError.new("Some runtime error")
 | 
			
		||||
 | 
			
		||||
    full_message = e.full_message(highlight: true, order: :bottom)
 | 
			
		||||
    full_message.should include "\e[1mTraceback\e[m (most recent call last)"
 | 
			
		||||
    full_message.should include "\e[1mSome runtime error (\e[1;4mRuntimeError\e[m\e[1m)"
 | 
			
		||||
    full_message = e.full_message(highlight: true, order: :top).lines
 | 
			
		||||
    full_message[0].should.end_with? "\e[1mSome runtime error (\e[1;4mRuntimeError\e[m\e[1m)\e[m\n"
 | 
			
		||||
 | 
			
		||||
    full_message = e.full_message(highlight: false, order: :bottom)
 | 
			
		||||
    full_message.should include "Traceback (most recent call last)"
 | 
			
		||||
    full_message.should include "Some runtime error (RuntimeError)"
 | 
			
		||||
    full_message = e.full_message(highlight: true, order: :bottom).lines
 | 
			
		||||
    full_message[0].should == "\e[1mTraceback\e[m (most recent call last):\n"
 | 
			
		||||
    full_message[-1].should.end_with? "\e[1mSome runtime error (\e[1;4mRuntimeError\e[m\e[1m)\e[m\n"
 | 
			
		||||
 | 
			
		||||
    full_message = e.full_message(highlight: false, order: :top).lines
 | 
			
		||||
    full_message[0].should.end_with? "Some runtime error (RuntimeError)\n"
 | 
			
		||||
 | 
			
		||||
    full_message = e.full_message(highlight: false, order: :bottom).lines
 | 
			
		||||
    full_message[0].should == "Traceback (most recent call last):\n"
 | 
			
		||||
    full_message[-1].should.end_with? "Some runtime error (RuntimeError)\n"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "supports :order option and places the error message and the backtrace at the top or the bottom" do
 | 
			
		||||
| 
						 | 
				
			
			@ -35,9 +41,9 @@ describe "Exception#full_message" do
 | 
			
		|||
  it "shows the caller if the exception has no backtrace" do
 | 
			
		||||
    e = RuntimeError.new("Some runtime error")
 | 
			
		||||
    e.backtrace.should == nil
 | 
			
		||||
    full_message = e.full_message(highlight: false, order: :top)
 | 
			
		||||
    full_message.should include("#{__FILE__}:#{__LINE__-1}:in `")
 | 
			
		||||
    full_message.should include("': Some runtime error (RuntimeError)\n")
 | 
			
		||||
    full_message = e.full_message(highlight: false, order: :top).lines
 | 
			
		||||
    full_message[0].should.start_with?("#{__FILE__}:#{__LINE__-1}:in `")
 | 
			
		||||
    full_message[0].should.end_with?("': Some runtime error (RuntimeError)\n")
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "shows the exception class at the end of the first line of the message when the message contains multiple lines" do
 | 
			
		||||
| 
						 | 
				
			
			@ -45,12 +51,24 @@ describe "Exception#full_message" do
 | 
			
		|||
      line = __LINE__; raise "first line\nsecond line"
 | 
			
		||||
    rescue => e
 | 
			
		||||
      full_message = e.full_message(highlight: false, order: :top).lines
 | 
			
		||||
      full_message[0].should include("#{__FILE__}:#{line}:in `")
 | 
			
		||||
      full_message[0].should include(": first line (RuntimeError)\n")
 | 
			
		||||
      full_message[0].should.start_with?("#{__FILE__}:#{line}:in `")
 | 
			
		||||
      full_message[0].should.end_with?(": first line (RuntimeError)\n")
 | 
			
		||||
      full_message[1].should == "second line\n"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "highlights the entire message when the message contains multiple lines" do
 | 
			
		||||
    begin
 | 
			
		||||
      line = __LINE__; raise "first line\nsecond line\nthird line"
 | 
			
		||||
    rescue => e
 | 
			
		||||
      full_message = e.full_message(highlight: true, order: :top).lines
 | 
			
		||||
      full_message[0].should.start_with?("#{__FILE__}:#{line}:in `")
 | 
			
		||||
      full_message[0].should.end_with?(": \e[1mfirst line (\e[1;4mRuntimeError\e[m\e[1m)\e[m\n")
 | 
			
		||||
      full_message[1].should == "\e[1msecond line\e[m\n"
 | 
			
		||||
      full_message[2].should == "\e[1mthird line\e[m\n"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "contains cause of exception" do
 | 
			
		||||
    begin
 | 
			
		||||
      begin
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,48 @@
 | 
			
		|||
require_relative '../../spec_helper'
 | 
			
		||||
 | 
			
		||||
describe "SystemExit" do
 | 
			
		||||
  describe "#initialize" do
 | 
			
		||||
    it "accepts a status and message" do
 | 
			
		||||
      exc = SystemExit.new(42, "message")
 | 
			
		||||
      exc.status.should == 42
 | 
			
		||||
      exc.message.should == "message"
 | 
			
		||||
 | 
			
		||||
      exc = SystemExit.new(true, "message")
 | 
			
		||||
      exc.status.should == 0
 | 
			
		||||
      exc.message.should == "message"
 | 
			
		||||
 | 
			
		||||
      exc = SystemExit.new(false, "message")
 | 
			
		||||
      exc.status.should == 1
 | 
			
		||||
      exc.message.should == "message"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "accepts a status only" do
 | 
			
		||||
      exc = SystemExit.new(42)
 | 
			
		||||
      exc.status.should == 42
 | 
			
		||||
      exc.message.should == "SystemExit"
 | 
			
		||||
 | 
			
		||||
      exc = SystemExit.new(true)
 | 
			
		||||
      exc.status.should == 0
 | 
			
		||||
      exc.message.should == "SystemExit"
 | 
			
		||||
 | 
			
		||||
      exc = SystemExit.new(false)
 | 
			
		||||
      exc.status.should == 1
 | 
			
		||||
      exc.message.should == "SystemExit"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "accepts a message only" do
 | 
			
		||||
      exc = SystemExit.new("message")
 | 
			
		||||
      exc.status.should == 0
 | 
			
		||||
      exc.message.should == "message"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "accepts no arguments" do
 | 
			
		||||
      exc = SystemExit.new
 | 
			
		||||
      exc.status.should == 0
 | 
			
		||||
      exc.message.should == "SystemExit"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "sets the exit status and exits silently when raised" do
 | 
			
		||||
    code = 'raise SystemExit.new(7)'
 | 
			
		||||
    result = ruby_exe(code, args: "2>&1", exit_status: 7)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -77,6 +77,10 @@ describe "File.utime" do
 | 
			
		|||
    File.mtime(@file1).nsec.should.between?(0, 123500000)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "returns the number of filenames in the arguments" do
 | 
			
		||||
    File.utime(@atime.to_f, @mtime.to_f, @file1, @file2).should == 2
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  platform_is :linux do
 | 
			
		||||
    platform_is wordsize: 64 do
 | 
			
		||||
      it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19)" do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,6 +40,20 @@ ruby_version_is "2.7" do
 | 
			
		|||
      kw.should == h
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "copies instance variables" do
 | 
			
		||||
      h = {a: 1}
 | 
			
		||||
      h.instance_variable_set(:@foo, 42)
 | 
			
		||||
      kw = Hash.ruby2_keywords_hash(h)
 | 
			
		||||
      kw.instance_variable_get(:@foo).should == 42
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "copies the hash internals" do
 | 
			
		||||
      h = {a: 1}
 | 
			
		||||
      kw = Hash.ruby2_keywords_hash(h)
 | 
			
		||||
      h[:a] = 2
 | 
			
		||||
      kw[:a].should == 1
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "raises TypeError for non-Hash" do
 | 
			
		||||
      -> { Hash.ruby2_keywords_hash(nil) }.should raise_error(TypeError)
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,6 @@ require_relative '../../../spec_helper'
 | 
			
		|||
require_relative '../fixtures/classes'
 | 
			
		||||
 | 
			
		||||
describe :hash_to_s, shared: true do
 | 
			
		||||
 | 
			
		||||
  it "returns a string representation with same order as each()" do
 | 
			
		||||
    h = { a: [1, 2], b: -2, d: -6, nil => nil }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -95,4 +94,8 @@ describe :hash_to_s, shared: true do
 | 
			
		|||
 | 
			
		||||
    {a: utf_16be}.send(@method).should == '{:a=>"utf_16be \u3042"}'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "works for keys and values whose #inspect return a frozen String" do
 | 
			
		||||
    { true => false }.to_s.should == "{true=>false}"
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,6 +73,11 @@ describe :io_readlines_options_19, shared: true do
 | 
			
		|||
        result = IO.send(@method, @name, 10, &@object)
 | 
			
		||||
        (result ? result : ScratchPad.recorded).should == IOSpecs.lines_limit
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "ignores the object as a limit if it is negative" do
 | 
			
		||||
        result = IO.send(@method, @name, -2, &@object)
 | 
			
		||||
        (result ? result : ScratchPad.recorded).should == IOSpecs.lines
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    describe "when the object is a String" do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,4 +96,19 @@ describe "Kernel#define_singleton_method" do
 | 
			
		|||
      o.define(:foo) { raise "not used" }
 | 
			
		||||
    }.should raise_error(ArgumentError)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "always defines the method with public visibility" do
 | 
			
		||||
    cls = Class.new
 | 
			
		||||
    def cls.define(name, &block)
 | 
			
		||||
      private
 | 
			
		||||
      define_singleton_method(name, &block)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    -> {
 | 
			
		||||
      suppress_warning do
 | 
			
		||||
        cls.define(:foo) { :ok }
 | 
			
		||||
      end
 | 
			
		||||
      cls.foo.should == :ok
 | 
			
		||||
    }.should_not raise_error(NoMethodError)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,10 @@ describe "Math.sqrt" do
 | 
			
		|||
  it "accepts any argument that can be coerced with Float()" do
 | 
			
		||||
    Math.sqrt(MathSpecs::Float.new).should be_close(1.0, TOLERANCE)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "raises a Math::DomainError when given a negative number" do
 | 
			
		||||
    -> { Math.sqrt(-1) }.should raise_error(Math::DomainError)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
describe "Math#sqrt" do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,6 +100,16 @@ describe "Module#const_get" do
 | 
			
		|||
    ConstantSpecs.const_get("::CS_CONST1").should == :const1
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "accepts a toplevel scope qualifier when inherit is false" do
 | 
			
		||||
    ConstantSpecs.const_get("::CS_CONST1", false).should == :const1
 | 
			
		||||
    -> { ConstantSpecs.const_get("CS_CONST1", false) }.should raise_error(NameError)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "returns a constant whose module is defined the the toplevel" do
 | 
			
		||||
    ConstantSpecs.const_get("ConstantSpecsTwo::Foo").should == :cs_two_foo
 | 
			
		||||
    ConstantSpecsThree.const_get("ConstantSpecsTwo::Foo").should == :cs_three_foo
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "accepts a scoped constant name" do
 | 
			
		||||
    ConstantSpecs.const_get("ClassA::CS_CONST10").should == :const10_10
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			@ -140,6 +150,10 @@ describe "Module#const_get" do
 | 
			
		|||
    Object.const_get('CSAutoloadD::InnerModule').name.should == 'CSAutoloadD::InnerModule'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "raises a NameError when the nested constant does not exist on the module but exists in Object" do
 | 
			
		||||
    -> { Object.const_get('ConstantSpecs::CS_CONST1') }.should raise_error(NameError)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "with statically assigned constants" do
 | 
			
		||||
    it "searches the immediate class or module first" do
 | 
			
		||||
      ConstantSpecs::ClassA.const_get(:CS_CONST10).should == :const10_10
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,83 @@ ruby_version_is "2.7" do
 | 
			
		|||
      Hash.ruby2_keywords_hash?(last).should == true
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "makes a copy of the hash and only marks the copy as keyword hash" do
 | 
			
		||||
      obj = Object.new
 | 
			
		||||
      obj.singleton_class.class_exec do
 | 
			
		||||
        def regular(*args)
 | 
			
		||||
          args.last
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        ruby2_keywords def foo(*args)
 | 
			
		||||
          args.last
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      h = {a: 1}
 | 
			
		||||
      ruby_version_is "3.0" do
 | 
			
		||||
        obj.regular(**h).should.equal?(h)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      last = obj.foo(**h)
 | 
			
		||||
      Hash.ruby2_keywords_hash?(last).should == true
 | 
			
		||||
      Hash.ruby2_keywords_hash?(h).should == false
 | 
			
		||||
 | 
			
		||||
      last2 = obj.foo(**last) # last is already marked
 | 
			
		||||
      Hash.ruby2_keywords_hash?(last2).should == true
 | 
			
		||||
      Hash.ruby2_keywords_hash?(last).should == true
 | 
			
		||||
      last2.should_not.equal?(last)
 | 
			
		||||
      Hash.ruby2_keywords_hash?(h).should == false
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "makes a copy and unmark at the call site when calling with marked *args" do
 | 
			
		||||
      obj = Object.new
 | 
			
		||||
      obj.singleton_class.class_exec do
 | 
			
		||||
        ruby2_keywords def foo(*args)
 | 
			
		||||
          args
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def single(arg)
 | 
			
		||||
          arg
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def splat(*args)
 | 
			
		||||
          args.last
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def kwargs(**kw)
 | 
			
		||||
          kw
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      h = { a: 1 }
 | 
			
		||||
      args = obj.foo(**h)
 | 
			
		||||
      marked = args.last
 | 
			
		||||
      Hash.ruby2_keywords_hash?(marked).should == true
 | 
			
		||||
 | 
			
		||||
      after_usage = obj.single(*args)
 | 
			
		||||
      after_usage.should == h
 | 
			
		||||
      after_usage.should_not.equal?(h)
 | 
			
		||||
      after_usage.should_not.equal?(marked)
 | 
			
		||||
      Hash.ruby2_keywords_hash?(after_usage).should == false
 | 
			
		||||
      Hash.ruby2_keywords_hash?(marked).should == true
 | 
			
		||||
 | 
			
		||||
      after_usage = obj.splat(*args)
 | 
			
		||||
      after_usage.should == h
 | 
			
		||||
      after_usage.should_not.equal?(h)
 | 
			
		||||
      after_usage.should_not.equal?(marked)
 | 
			
		||||
      ruby_bug "#18625", ""..."3.3" do # might be fixed in 3.2
 | 
			
		||||
        Hash.ruby2_keywords_hash?(after_usage).should == false
 | 
			
		||||
      end
 | 
			
		||||
      Hash.ruby2_keywords_hash?(marked).should == true
 | 
			
		||||
 | 
			
		||||
      after_usage = obj.kwargs(*args)
 | 
			
		||||
      after_usage.should == h
 | 
			
		||||
      after_usage.should_not.equal?(h)
 | 
			
		||||
      after_usage.should_not.equal?(marked)
 | 
			
		||||
      Hash.ruby2_keywords_hash?(after_usage).should == false
 | 
			
		||||
      Hash.ruby2_keywords_hash?(marked).should == true
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "applies to the underlying method and applies across aliasing" do
 | 
			
		||||
      obj = Object.new
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +157,7 @@ ruby_version_is "2.7" do
 | 
			
		|||
      }.should raise_error(NameError, /undefined method `not_existing'/)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "acceps String as well" do
 | 
			
		||||
    it "accepts String as well" do
 | 
			
		||||
      obj = Object.new
 | 
			
		||||
 | 
			
		||||
      obj.singleton_class.class_exec do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,7 +31,7 @@ describe "Proc#parameters" do
 | 
			
		|||
    end
 | 
			
		||||
 | 
			
		||||
    it "regards named parameters in lambda as optional if lambda: false keyword used" do
 | 
			
		||||
      lambda {|x| }.parameters(lambda: false).first.first.should == :opt
 | 
			
		||||
      -> x { }.parameters(lambda: false).first.first.should == :opt
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -205,6 +205,11 @@ describe "Random#rand with Range" do
 | 
			
		|||
    Random.new(42).rand(0..1.0).should be_kind_of(Float)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "returns a float within a given float range" do
 | 
			
		||||
    Random.new(42).rand(0.0...100.0).should == 37.454011884736246
 | 
			
		||||
    Random.new(42).rand(-100.0...0.0).should == -62.545988115263754
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "raises an ArgumentError when the startpoint lacks #+ and #- methods" do
 | 
			
		||||
    -> do
 | 
			
		||||
      Random.new.rand(Object.new..67)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,4 +5,9 @@ require_relative 'shared/concat'
 | 
			
		|||
describe "String#<<" do
 | 
			
		||||
  it_behaves_like :string_concat, :<<
 | 
			
		||||
  it_behaves_like :string_concat_encoding, :<<
 | 
			
		||||
 | 
			
		||||
  it "raises an ArgumentError when given the incorrect number of arguments" do
 | 
			
		||||
    -> { "hello".send(:<<) }.should raise_error(ArgumentError)
 | 
			
		||||
    -> { "hello".send(:<<, "one", "two") }.should raise_error(ArgumentError)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,6 +75,10 @@ describe "String#<=> with String" do
 | 
			
		|||
    (xff_1 <=> xff_2).should == -1
 | 
			
		||||
    (xff_2 <=> xff_1).should ==  1
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "returns 0 when comparing 2 empty strings but one is not ASCII-compatible" do
 | 
			
		||||
    ("" <=> "".force_encoding('iso-2022-jp')).should == 0
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
# Note: This is inconsistent with Array#<=> which calls #to_ary instead of
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,7 @@ describe "String#lstrip" do
 | 
			
		|||
   "hello".lstrip.should == "hello"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is '3.1' do
 | 
			
		||||
  ruby_version_is '3.0' do
 | 
			
		||||
    it "strips leading \\0" do
 | 
			
		||||
     "\x00hello".lstrip.should == "hello"
 | 
			
		||||
     "\000 \000hello\000 \000".lstrip.should == "hello\000 \000"
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +35,7 @@ describe "String#lstrip!" do
 | 
			
		|||
    a.should == "hello  "
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is '3.1' do
 | 
			
		||||
  ruby_version_is '3.0' do
 | 
			
		||||
    it "strips leading \\0" do
 | 
			
		||||
      a = "\000 \000hello\000 \000"
 | 
			
		||||
      a.lstrip!
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,13 +4,17 @@ require_relative 'fixtures/classes'
 | 
			
		|||
require_relative 'fixtures/utf-8-encoding'
 | 
			
		||||
 | 
			
		||||
describe "String#rindex with object" do
 | 
			
		||||
  it "raises a TypeError if obj isn't a String, Integer or Regexp" do
 | 
			
		||||
  it "raises a TypeError if obj isn't a String or Regexp" do
 | 
			
		||||
    not_supported_on :opal do
 | 
			
		||||
      -> { "hello".rindex(:sym) }.should raise_error(TypeError)
 | 
			
		||||
    end
 | 
			
		||||
    -> { "hello".rindex(mock('x')) }.should raise_error(TypeError)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "raises a TypeError if obj is an Integer" do
 | 
			
		||||
    -> { "hello".rindex(42) }.should raise_error(TypeError)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "doesn't try to convert obj to an integer via to_int" do
 | 
			
		||||
    obj = mock('x')
 | 
			
		||||
    obj.should_not_receive(:to_int)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,4 +31,8 @@ describe :string_eql_value, shared: true do
 | 
			
		|||
    a.send(@method, b).should be_true
 | 
			
		||||
    b.send(@method, a).should be_true
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "returns true when comparing 2 empty strings but one is not ASCII-compatible" do
 | 
			
		||||
    "".send(@method, "".force_encoding('iso-2022-jp')).should == true
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -589,4 +589,11 @@ describe "String#split with Regexp" do
 | 
			
		|||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "raises a TypeError when not called with nil, String, or Regexp" do
 | 
			
		||||
    -> { "hello".split(42) }.should raise_error(TypeError)
 | 
			
		||||
    -> { "hello".split(:ll) }.should raise_error(TypeError)
 | 
			
		||||
    -> { "hello".split(false) }.should raise_error(TypeError)
 | 
			
		||||
    -> { "hello".split(Object.new) }.should raise_error(TypeError)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ describe "String#strip" do
 | 
			
		|||
    "\tgoodbye\r\v\n".strip.should == "goodbye"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is '3.1' do
 | 
			
		||||
  ruby_version_is '3.0' do
 | 
			
		||||
    it "returns a copy of self without leading and trailing NULL bytes and whitespace" do
 | 
			
		||||
      " \x00 goodbye \x00 ".strip.should == "goodbye"
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ describe "String#strip!" do
 | 
			
		|||
    a.should == "hello"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is '3.1' do
 | 
			
		||||
  ruby_version_is '3.0' do
 | 
			
		||||
    it "removes leading and trailing NULL bytes and whitespace" do
 | 
			
		||||
      a = "\000 goodbye \000"
 | 
			
		||||
      a.strip!
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,14 +60,21 @@ describe "Struct.new" do
 | 
			
		|||
    -> { Struct.new(:animal, nil)                  }.should raise_error(TypeError)
 | 
			
		||||
    -> { Struct.new(:animal, true)                 }.should raise_error(TypeError)
 | 
			
		||||
    -> { Struct.new(:animal, ['chris', 'evan'])    }.should raise_error(TypeError)
 | 
			
		||||
    ruby_version_is "3.2" do
 | 
			
		||||
      -> { Struct.new(:animal, { name: 'chris' }) }.should raise_error(TypeError)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is ""..."3.2" do
 | 
			
		||||
    it "raises a ArgumentError if passed a Hash with an unknown key" do
 | 
			
		||||
      -> { Struct.new(:animal, { name: 'chris' }) }.should raise_error(ArgumentError)
 | 
			
		||||
    it "raises a TypeError or ArgumentError if passed a Hash with an unknown key" do
 | 
			
		||||
      # CRuby < 3.2 raises ArgumentError: unknown keyword: :name, but that seems a bug:
 | 
			
		||||
      # https://bugs.ruby-lang.org/issues/18632
 | 
			
		||||
      -> { Struct.new(:animal, { name: 'chris' }) }.should raise_error(StandardError) { |e|
 | 
			
		||||
        [ArgumentError, TypeError].should.include?(e.class)
 | 
			
		||||
      }
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is "3.2" do
 | 
			
		||||
    it "raises a TypeError if passed a Hash with an unknown key" do
 | 
			
		||||
      -> { Struct.new(:animal, { name: 'chris' }) }.should raise_error(TypeError)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,6 +66,26 @@ describe :thread_exit, shared: true do
 | 
			
		|||
    ScratchPad.recorded.should == nil
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "does not reset $!" do
 | 
			
		||||
    ScratchPad.record []
 | 
			
		||||
 | 
			
		||||
    exc = RuntimeError.new("foo")
 | 
			
		||||
    thread = Thread.new do
 | 
			
		||||
      begin
 | 
			
		||||
        raise exc
 | 
			
		||||
      ensure
 | 
			
		||||
        ScratchPad << $!
 | 
			
		||||
        begin
 | 
			
		||||
          Thread.current.send(@method)
 | 
			
		||||
        ensure
 | 
			
		||||
          ScratchPad << $!
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
    thread.join
 | 
			
		||||
    ScratchPad.recorded.should == [exc, exc]
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "cannot be rescued" do
 | 
			
		||||
    thread = Thread.new do
 | 
			
		||||
      begin
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -299,4 +299,14 @@ module ConstantSpecs
 | 
			
		|||
  private_constant :CS_PRIVATE
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
module ConstantSpecsThree
 | 
			
		||||
  module ConstantSpecsTwo
 | 
			
		||||
    Foo = :cs_three_foo
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
module ConstantSpecsTwo
 | 
			
		||||
  Foo = :cs_two_foo
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
include ConstantSpecs::ModuleA
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										307
									
								
								spec/ruby/language/keyword_arguments_spec.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										307
									
								
								spec/ruby/language/keyword_arguments_spec.rb
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,307 @@
 | 
			
		|||
require_relative '../spec_helper'
 | 
			
		||||
 | 
			
		||||
ruby_version_is "3.0" do
 | 
			
		||||
  describe "Keyword arguments" do
 | 
			
		||||
    def target(*args, **kwargs)
 | 
			
		||||
      [args, kwargs]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "are separated from positional arguments" do
 | 
			
		||||
      def m(*args, **kwargs)
 | 
			
		||||
        [args, kwargs]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      empty = {}
 | 
			
		||||
      m(**empty).should == [[], {}]
 | 
			
		||||
      m(empty).should == [[{}], {}]
 | 
			
		||||
 | 
			
		||||
      m(a: 1).should == [[], {a: 1}]
 | 
			
		||||
      m({a: 1}).should == [[{a: 1}], {}]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "when the receiving method has not keyword parameters it treats kwargs as positional" do
 | 
			
		||||
      def m(*a)
 | 
			
		||||
        a
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      m(a: 1).should == [{a: 1}]
 | 
			
		||||
      m({a: 1}).should == [{a: 1}]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "empty kwargs are treated as if they were not passed" do
 | 
			
		||||
      it "when calling a method" do
 | 
			
		||||
        def m(*a)
 | 
			
		||||
          a
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        m(**empty).should == []
 | 
			
		||||
        m(empty).should == [{}]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "when yielding to a block" do
 | 
			
		||||
        def y(*args, **kwargs)
 | 
			
		||||
          yield(*args, **kwargs)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        y(**empty) { |*a| a }.should == []
 | 
			
		||||
        y(empty) { |*a| a }.should == [{}]
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "extra keywords are not allowed without **kwrest" do
 | 
			
		||||
      def m(*a, kw:)
 | 
			
		||||
        a
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      m(kw: 1).should == []
 | 
			
		||||
      -> { m(kw: 1, kw2: 2) }.should raise_error(ArgumentError, 'unknown keyword: :kw2')
 | 
			
		||||
      -> { m(kw: 1, true => false) }.should raise_error(ArgumentError, 'unknown keyword: true')
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "handle * and ** at the same call site" do
 | 
			
		||||
      def m(*a)
 | 
			
		||||
        a
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      m(*[], **{}).should == []
 | 
			
		||||
      m(*[], 42, **{}).should == [42]
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "**" do
 | 
			
		||||
      it "does not copy a non-empty Hash for a method taking (*args)" do
 | 
			
		||||
        def m(*args)
 | 
			
		||||
          args[0]
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        h = {a: 1}
 | 
			
		||||
        m(**h).should.equal?(h)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "copies the given Hash for a method taking (**kwargs)" do
 | 
			
		||||
        def m(**kw)
 | 
			
		||||
          kw
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        m(**empty).should == empty
 | 
			
		||||
        m(**empty).should_not.equal?(empty)
 | 
			
		||||
 | 
			
		||||
        h = {a: 1}
 | 
			
		||||
        m(**h).should == h
 | 
			
		||||
        m(**h).should_not.equal?(h)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "delegation" do
 | 
			
		||||
      it "works with (*args, **kwargs)" do
 | 
			
		||||
        def m(*args, **kwargs)
 | 
			
		||||
          target(*args, **kwargs)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        m(**empty).should == [[], {}]
 | 
			
		||||
        m(empty).should == [[{}], {}]
 | 
			
		||||
 | 
			
		||||
        m(a: 1).should == [[], {a: 1}]
 | 
			
		||||
        m({a: 1}).should == [[{a: 1}], {}]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "works with proc { |*args, **kwargs| }" do
 | 
			
		||||
        m = proc do |*args, **kwargs|
 | 
			
		||||
          target(*args, **kwargs)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        m.(**empty).should == [[], {}]
 | 
			
		||||
        m.(empty).should == [[{}], {}]
 | 
			
		||||
 | 
			
		||||
        m.(a: 1).should == [[], {a: 1}]
 | 
			
		||||
        m.({a: 1}).should == [[{a: 1}], {}]
 | 
			
		||||
 | 
			
		||||
        # no autosplatting for |*args, **kwargs|
 | 
			
		||||
        m.([1, 2]).should == [[[1, 2]], {}]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "works with -> (*args, **kwargs) {}" do
 | 
			
		||||
        m = -> *args, **kwargs do
 | 
			
		||||
          target(*args, **kwargs)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        m.(**empty).should == [[], {}]
 | 
			
		||||
        m.(empty).should == [[{}], {}]
 | 
			
		||||
 | 
			
		||||
        m.(a: 1).should == [[], {a: 1}]
 | 
			
		||||
        m.({a: 1}).should == [[{a: 1}], {}]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "works with (...)" do
 | 
			
		||||
        instance_eval <<~DEF
 | 
			
		||||
        def m(...)
 | 
			
		||||
          target(...)
 | 
			
		||||
        end
 | 
			
		||||
        DEF
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        m(**empty).should == [[], {}]
 | 
			
		||||
        m(empty).should == [[{}], {}]
 | 
			
		||||
 | 
			
		||||
        m(a: 1).should == [[], {a: 1}]
 | 
			
		||||
        m({a: 1}).should == [[{a: 1}], {}]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "works with call(*ruby2_keyword_args)" do
 | 
			
		||||
        class << self
 | 
			
		||||
          ruby2_keywords def m(*args)
 | 
			
		||||
            target(*args)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        m(**empty).should == [[], {}]
 | 
			
		||||
        Hash.ruby2_keywords_hash?(empty).should == false
 | 
			
		||||
        m(empty).should == [[{}], {}]
 | 
			
		||||
        Hash.ruby2_keywords_hash?(empty).should == false
 | 
			
		||||
 | 
			
		||||
        m(a: 1).should == [[], {a: 1}]
 | 
			
		||||
        m({a: 1}).should == [[{a: 1}], {}]
 | 
			
		||||
 | 
			
		||||
        kw = {a: 1}
 | 
			
		||||
 | 
			
		||||
        m(**kw).should == [[], {a: 1}]
 | 
			
		||||
        m(**kw)[1].should == kw
 | 
			
		||||
        m(**kw)[1].should_not.equal?(kw)
 | 
			
		||||
        Hash.ruby2_keywords_hash?(kw).should == false
 | 
			
		||||
        Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
 | 
			
		||||
 | 
			
		||||
        m(kw).should == [[{a: 1}], {}]
 | 
			
		||||
        m(kw)[0][0].should.equal?(kw)
 | 
			
		||||
        Hash.ruby2_keywords_hash?(kw).should == false
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "works with super(*ruby2_keyword_args)" do
 | 
			
		||||
        parent = Class.new do
 | 
			
		||||
          def m(*args, **kwargs)
 | 
			
		||||
            [args, kwargs]
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        child = Class.new(parent) do
 | 
			
		||||
          ruby2_keywords def m(*args)
 | 
			
		||||
            super(*args)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        obj = child.new
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        obj.m(**empty).should == [[], {}]
 | 
			
		||||
        Hash.ruby2_keywords_hash?(empty).should == false
 | 
			
		||||
        obj.m(empty).should == [[{}], {}]
 | 
			
		||||
        Hash.ruby2_keywords_hash?(empty).should == false
 | 
			
		||||
 | 
			
		||||
        obj.m(a: 1).should == [[], {a: 1}]
 | 
			
		||||
        obj.m({a: 1}).should == [[{a: 1}], {}]
 | 
			
		||||
 | 
			
		||||
        kw = {a: 1}
 | 
			
		||||
 | 
			
		||||
        obj.m(**kw).should == [[], {a: 1}]
 | 
			
		||||
        obj.m(**kw)[1].should == kw
 | 
			
		||||
        obj.m(**kw)[1].should_not.equal?(kw)
 | 
			
		||||
        Hash.ruby2_keywords_hash?(kw).should == false
 | 
			
		||||
        Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
 | 
			
		||||
 | 
			
		||||
        obj.m(kw).should == [[{a: 1}], {}]
 | 
			
		||||
        obj.m(kw)[0][0].should.equal?(kw)
 | 
			
		||||
        Hash.ruby2_keywords_hash?(kw).should == false
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "works with zsuper" do
 | 
			
		||||
        parent = Class.new do
 | 
			
		||||
          def m(*args, **kwargs)
 | 
			
		||||
            [args, kwargs]
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        child = Class.new(parent) do
 | 
			
		||||
          ruby2_keywords def m(*args)
 | 
			
		||||
            super
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        obj = child.new
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        obj.m(**empty).should == [[], {}]
 | 
			
		||||
        Hash.ruby2_keywords_hash?(empty).should == false
 | 
			
		||||
        obj.m(empty).should == [[{}], {}]
 | 
			
		||||
        Hash.ruby2_keywords_hash?(empty).should == false
 | 
			
		||||
 | 
			
		||||
        obj.m(a: 1).should == [[], {a: 1}]
 | 
			
		||||
        obj.m({a: 1}).should == [[{a: 1}], {}]
 | 
			
		||||
 | 
			
		||||
        kw = {a: 1}
 | 
			
		||||
 | 
			
		||||
        obj.m(**kw).should == [[], {a: 1}]
 | 
			
		||||
        obj.m(**kw)[1].should == kw
 | 
			
		||||
        obj.m(**kw)[1].should_not.equal?(kw)
 | 
			
		||||
        Hash.ruby2_keywords_hash?(kw).should == false
 | 
			
		||||
        Hash.ruby2_keywords_hash?(obj.m(**kw)[1]).should == false
 | 
			
		||||
 | 
			
		||||
        obj.m(kw).should == [[{a: 1}], {}]
 | 
			
		||||
        obj.m(kw)[0][0].should.equal?(kw)
 | 
			
		||||
        Hash.ruby2_keywords_hash?(kw).should == false
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "works with yield(*ruby2_keyword_args)" do
 | 
			
		||||
        class << self
 | 
			
		||||
          def y(args)
 | 
			
		||||
            yield(*args)
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          ruby2_keywords def m(*outer_args)
 | 
			
		||||
            y(outer_args, &-> *args, **kwargs { target(*args, **kwargs) })
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        m(**empty).should == [[], {}]
 | 
			
		||||
        Hash.ruby2_keywords_hash?(empty).should == false
 | 
			
		||||
        m(empty).should == [[{}], {}]
 | 
			
		||||
        Hash.ruby2_keywords_hash?(empty).should == false
 | 
			
		||||
 | 
			
		||||
        m(a: 1).should == [[], {a: 1}]
 | 
			
		||||
        m({a: 1}).should == [[{a: 1}], {}]
 | 
			
		||||
 | 
			
		||||
        kw = {a: 1}
 | 
			
		||||
 | 
			
		||||
        m(**kw).should == [[], {a: 1}]
 | 
			
		||||
        m(**kw)[1].should == kw
 | 
			
		||||
        m(**kw)[1].should_not.equal?(kw)
 | 
			
		||||
        Hash.ruby2_keywords_hash?(kw).should == false
 | 
			
		||||
        Hash.ruby2_keywords_hash?(m(**kw)[1]).should == false
 | 
			
		||||
 | 
			
		||||
        m(kw).should == [[{a: 1}], {}]
 | 
			
		||||
        m(kw)[0][0].should.equal?(kw)
 | 
			
		||||
        Hash.ruby2_keywords_hash?(kw).should == false
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "does not work with (*args)" do
 | 
			
		||||
        class << self
 | 
			
		||||
          def m(*args)
 | 
			
		||||
            target(*args)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        empty = {}
 | 
			
		||||
        m(**empty).should == [[], {}]
 | 
			
		||||
        m(empty).should == [[{}], {}]
 | 
			
		||||
 | 
			
		||||
        m(a: 1).should == [[{a: 1}], {}]
 | 
			
		||||
        m({a: 1}).should == [[{a: 1}], {}]
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -15,14 +15,14 @@ describe "Logger::LogDevice#close" do
 | 
			
		|||
    rm_r @file_path
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is ""..."2.7" do
 | 
			
		||||
  version_is Logger::VERSION, ""..."1.4.0" do
 | 
			
		||||
    it "closes the LogDevice's stream" do
 | 
			
		||||
      @device.close
 | 
			
		||||
      -> { @device.write("Test") }.should complain(/\Alog writing failed\./)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is "2.7" do
 | 
			
		||||
  version_is Logger::VERSION, "1.4.0" do
 | 
			
		||||
    it "closes the LogDevice's stream" do
 | 
			
		||||
      @device.close
 | 
			
		||||
      -> { @device.write("Test") }.should complain(/\Alog shifting failed\./)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,14 +35,14 @@ describe "Logger::LogDevice#write" do
 | 
			
		|||
    rm_r path
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is ""..."2.7" do
 | 
			
		||||
  version_is Logger::VERSION, ""..."1.4.0" do
 | 
			
		||||
    it "fails if the device is already closed" do
 | 
			
		||||
      @device.close
 | 
			
		||||
      -> { @device.write "foo" }.should complain(/\Alog writing failed\./)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is "2.7" do
 | 
			
		||||
  version_is Logger::VERSION, "1.4.0" do
 | 
			
		||||
    it "fails if the device is already closed" do
 | 
			
		||||
      @device.close
 | 
			
		||||
      -> { @device.write "foo" }.should complain(/\Alog shifting failed\./)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -108,17 +108,37 @@ describe "C-API Class function" do
 | 
			
		|||
 | 
			
		||||
  describe "rb_class_new_instance" do
 | 
			
		||||
    it "allocates and initializes a new object" do
 | 
			
		||||
      o = @s.rb_class_new_instance(0, nil, CApiClassSpecs::Alloc)
 | 
			
		||||
      o = @s.rb_class_new_instance([], CApiClassSpecs::Alloc)
 | 
			
		||||
      o.class.should == CApiClassSpecs::Alloc
 | 
			
		||||
      o.initialized.should be_true
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "passes arguments to the #initialize method" do
 | 
			
		||||
      o = @s.rb_class_new_instance(2, [:one, :two], CApiClassSpecs::Alloc)
 | 
			
		||||
      o = @s.rb_class_new_instance([:one, :two], CApiClassSpecs::Alloc)
 | 
			
		||||
      o.arguments.should == [:one, :two]
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is "3.0" do
 | 
			
		||||
    describe "rb_class_new_instance_kw" do
 | 
			
		||||
      it "passes arguments and keywords to the #initialize method" do
 | 
			
		||||
        obj = @s.rb_class_new_instance_kw([{pos: 1}, {kw: 2}], CApiClassSpecs::KeywordAlloc)
 | 
			
		||||
        obj.args.should == [{pos: 1}]
 | 
			
		||||
        obj.kwargs.should == {kw: 2}
 | 
			
		||||
 | 
			
		||||
        obj = @s.rb_class_new_instance_kw([{}], CApiClassSpecs::KeywordAlloc)
 | 
			
		||||
        obj.args.should == []
 | 
			
		||||
        obj.kwargs.should == {}
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "raises TypeError if the last argument is not a Hash" do
 | 
			
		||||
        -> {
 | 
			
		||||
          @s.rb_class_new_instance_kw([42], CApiClassSpecs::KeywordAlloc)
 | 
			
		||||
        }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "rb_include_module" do
 | 
			
		||||
    it "includes a module into a class" do
 | 
			
		||||
      c = Class.new
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,19 +61,16 @@ static VALUE class_spec_rb_class_new(VALUE self, VALUE super) {
 | 
			
		|||
  return rb_class_new(super);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE class_spec_rb_class_new_instance(VALUE self,
 | 
			
		||||
                                      VALUE nargs, VALUE args,
 | 
			
		||||
                                      VALUE klass) {
 | 
			
		||||
  int c_nargs = FIX2INT(nargs);
 | 
			
		||||
  VALUE *c_args = (VALUE*)alloca(sizeof(VALUE) * c_nargs);
 | 
			
		||||
  int i;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < c_nargs; i++)
 | 
			
		||||
    c_args[i] = rb_ary_entry(args, i);
 | 
			
		||||
 | 
			
		||||
  return rb_class_new_instance(c_nargs, c_args, klass);
 | 
			
		||||
static VALUE class_spec_rb_class_new_instance(VALUE self, VALUE args, VALUE klass) {
 | 
			
		||||
  return rb_class_new_instance(RARRAY_LENINT(args), RARRAY_PTR(args), klass);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef RUBY_VERSION_IS_3_0
 | 
			
		||||
static VALUE class_spec_rb_class_new_instance_kw(VALUE self, VALUE args, VALUE klass) {
 | 
			
		||||
  return rb_class_new_instance_kw(RARRAY_LENINT(args), RARRAY_PTR(args), klass, RB_PASS_KEYWORDS);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static VALUE class_spec_rb_class_real(VALUE self, VALUE object) {
 | 
			
		||||
  if(rb_type_p(object, T_FIXNUM)) {
 | 
			
		||||
    return INT2FIX(rb_class_real(FIX2INT(object)));
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +168,10 @@ void Init_class_spec(void) {
 | 
			
		|||
  rb_define_method(cls, "rb_class_protected_instance_methods", class_spec_rb_class_protected_instance_methods, -1);
 | 
			
		||||
  rb_define_method(cls, "rb_class_private_instance_methods", class_spec_rb_class_private_instance_methods, -1);
 | 
			
		||||
  rb_define_method(cls, "rb_class_new", class_spec_rb_class_new, 1);
 | 
			
		||||
  rb_define_method(cls, "rb_class_new_instance", class_spec_rb_class_new_instance, 3);
 | 
			
		||||
  rb_define_method(cls, "rb_class_new_instance", class_spec_rb_class_new_instance, 2);
 | 
			
		||||
#ifdef RUBY_VERSION_IS_3_0
 | 
			
		||||
  rb_define_method(cls, "rb_class_new_instance_kw", class_spec_rb_class_new_instance_kw, 2);
 | 
			
		||||
#endif
 | 
			
		||||
  rb_define_method(cls, "rb_class_real", class_spec_rb_class_real, 1);
 | 
			
		||||
  rb_define_method(cls, "rb_class_superclass", class_spec_rb_class_superclass, 1);
 | 
			
		||||
  rb_define_method(cls, "rb_cvar_defined", class_spec_cvar_defined, 2);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -142,7 +142,7 @@ VALUE kernel_spec_call_proc_with_raised_exc(VALUE arg_array, VALUE raised_exc) {
 | 
			
		|||
 | 
			
		||||
  argc = 2;
 | 
			
		||||
 | 
			
		||||
  return rb_funcall2(proc, rb_intern("call"), argc, argv);
 | 
			
		||||
  return rb_funcallv(proc, rb_intern("call"), argc, argv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VALUE kernel_spec_rb_rescue(VALUE self, VALUE main_proc, VALUE arg,
 | 
			
		||||
| 
						 | 
				
			
			@ -318,8 +318,22 @@ static VALUE kernel_spec_rb_make_backtrace(VALUE self) {
 | 
			
		|||
  return rb_make_backtrace();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE kernel_spec_rb_funcall3(VALUE self, VALUE obj, VALUE method) {
 | 
			
		||||
  return rb_funcall3(obj, SYM2ID(method), 0, NULL);
 | 
			
		||||
static VALUE kernel_spec_rb_funcallv(VALUE self, VALUE obj, VALUE method, VALUE args) {
 | 
			
		||||
  return rb_funcallv(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef RUBY_VERSION_IS_3_0
 | 
			
		||||
static VALUE kernel_spec_rb_funcallv_kw(VALUE self, VALUE obj, VALUE method, VALUE args) {
 | 
			
		||||
  return rb_funcallv_kw(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args), RB_PASS_KEYWORDS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE kernel_spec_rb_keyword_given_p(int argc, VALUE *args, VALUE self) {
 | 
			
		||||
  return rb_keyword_given_p() ? Qtrue : Qfalse;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static VALUE kernel_spec_rb_funcallv_public(VALUE self, VALUE obj, VALUE method) {
 | 
			
		||||
  return rb_funcallv_public(obj, SYM2ID(method), 0, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE method, VALUE block) {
 | 
			
		||||
| 
						 | 
				
			
			@ -371,7 +385,12 @@ void Init_kernel_spec(void) {
 | 
			
		|||
  rb_define_method(cls, "rb_set_end_proc", kernel_spec_rb_set_end_proc, 1);
 | 
			
		||||
  rb_define_method(cls, "rb_f_sprintf", kernel_spec_rb_f_sprintf, 1);
 | 
			
		||||
  rb_define_method(cls, "rb_make_backtrace", kernel_spec_rb_make_backtrace, 0);
 | 
			
		||||
  rb_define_method(cls, "rb_funcall3", kernel_spec_rb_funcall3, 2);
 | 
			
		||||
  rb_define_method(cls, "rb_funcallv", kernel_spec_rb_funcallv, 3);
 | 
			
		||||
#ifdef RUBY_VERSION_IS_3_0
 | 
			
		||||
  rb_define_method(cls, "rb_funcallv_kw", kernel_spec_rb_funcallv_kw, 3);
 | 
			
		||||
  rb_define_method(cls, "rb_keyword_given_p", kernel_spec_rb_keyword_given_p, -1);
 | 
			
		||||
#endif
 | 
			
		||||
  rb_define_method(cls, "rb_funcallv_public", kernel_spec_rb_funcallv_public, 2);
 | 
			
		||||
  rb_define_method(cls, "rb_funcall_many_args", kernel_spec_rb_funcall_many_args, 2);
 | 
			
		||||
  rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 3);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,10 +47,19 @@ VALUE symbol_spec_rb_id2name(VALUE self, VALUE symbol) {
 | 
			
		|||
  return rb_str_new(c_str, strlen(c_str));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VALUE symbol_spec_rb_id2name_id_zero(VALUE self) {
 | 
			
		||||
  const char* c_str = rb_id2name((ID) 0);
 | 
			
		||||
  return c_str ? rb_str_new(c_str, strlen(c_str)) : Qnil;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VALUE symbol_spec_rb_id2str(VALUE self, VALUE symbol) {
 | 
			
		||||
  return rb_id2str(SYM2ID(symbol));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VALUE symbol_spec_rb_id2str_id_zero(VALUE self) {
 | 
			
		||||
  return rb_id2str((ID) 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VALUE symbol_spec_rb_intern_str(VALUE self, VALUE str) {
 | 
			
		||||
  return ID2SYM(rb_intern_str(str));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +99,9 @@ void Init_symbol_spec(void) {
 | 
			
		|||
  rb_define_method(cls, "rb_intern3", symbol_spec_rb_intern3, 3);
 | 
			
		||||
  rb_define_method(cls, "rb_intern3_c_compare", symbol_spec_rb_intern3_c_compare, 4);
 | 
			
		||||
  rb_define_method(cls, "rb_id2name", symbol_spec_rb_id2name, 1);
 | 
			
		||||
  rb_define_method(cls, "rb_id2name_id_zero", symbol_spec_rb_id2name_id_zero, 0);
 | 
			
		||||
  rb_define_method(cls, "rb_id2str", symbol_spec_rb_id2str, 1);
 | 
			
		||||
  rb_define_method(cls, "rb_id2str_id_zero", symbol_spec_rb_id2str_id_zero, 0);
 | 
			
		||||
  rb_define_method(cls, "rb_intern_str", symbol_spec_rb_intern_str, 1);
 | 
			
		||||
  rb_define_method(cls, "rb_check_symbol_cstr", symbol_spec_rb_check_symbol_cstr, 1);
 | 
			
		||||
  rb_define_method(cls, "rb_is_class_id", symbol_spec_rb_is_class_id, 1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,16 @@ class CApiClassSpecs
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  class KeywordAlloc
 | 
			
		||||
    attr_reader :initialized, :args, :kwargs
 | 
			
		||||
 | 
			
		||||
    def initialize(*args, **kwargs)
 | 
			
		||||
      @initialized = true
 | 
			
		||||
      @args = args
 | 
			
		||||
      @kwargs = kwargs
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  class Attr
 | 
			
		||||
    def initialize
 | 
			
		||||
      @foo, @bar, @baz = 1, 2, 3
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -567,7 +567,64 @@ describe "C-API Kernel function" do
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "rb_funcall3" do
 | 
			
		||||
  describe "rb_funcallv" do
 | 
			
		||||
    def empty
 | 
			
		||||
      42
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def sum(a, b)
 | 
			
		||||
      a + b
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "calls a method" do
 | 
			
		||||
      @s.rb_funcallv(self, :empty, []).should == 42
 | 
			
		||||
      @s.rb_funcallv(self, :sum, [1, 2]).should == 3
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ruby_version_is "3.0" do
 | 
			
		||||
    describe "rb_funcallv_kw" do
 | 
			
		||||
      it "passes keyword arguments to the callee" do
 | 
			
		||||
        def m(*args, **kwargs)
 | 
			
		||||
          [args, kwargs]
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        @s.rb_funcallv_kw(self, :m, [{}]).should == [[], {}]
 | 
			
		||||
        @s.rb_funcallv_kw(self, :m, [{a: 1}]).should == [[], {a: 1}]
 | 
			
		||||
        @s.rb_funcallv_kw(self, :m, [{b: 2}, {a: 1}]).should == [[{b: 2}], {a: 1}]
 | 
			
		||||
        @s.rb_funcallv_kw(self, :m, [{b: 2}, {}]).should == [[{b: 2}], {}]
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it "raises TypeError if the last argument is not a Hash" do
 | 
			
		||||
        def m(*args, **kwargs)
 | 
			
		||||
          [args, kwargs]
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        -> {
 | 
			
		||||
          @s.rb_funcallv_kw(self, :m, [42])
 | 
			
		||||
        }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    describe "rb_keyword_given_p" do
 | 
			
		||||
      it "returns whether keywords were given to the C extension method" do
 | 
			
		||||
        h = {a: 1}
 | 
			
		||||
        empty = {}
 | 
			
		||||
        @s.rb_keyword_given_p(a: 1).should == true
 | 
			
		||||
        @s.rb_keyword_given_p("foo" => "bar").should == true
 | 
			
		||||
        @s.rb_keyword_given_p(**h).should == true
 | 
			
		||||
 | 
			
		||||
        @s.rb_keyword_given_p(h).should == false
 | 
			
		||||
        @s.rb_keyword_given_p().should == false
 | 
			
		||||
        @s.rb_keyword_given_p(**empty).should == false
 | 
			
		||||
 | 
			
		||||
        @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{a: 1}]).should == true
 | 
			
		||||
        @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{}]).should == false
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "rb_funcallv_public" do
 | 
			
		||||
    before :each do
 | 
			
		||||
      @obj = Object.new
 | 
			
		||||
      class << @obj
 | 
			
		||||
| 
						 | 
				
			
			@ -578,10 +635,11 @@ describe "C-API Kernel function" do
 | 
			
		|||
    end
 | 
			
		||||
 | 
			
		||||
    it "calls a public method" do
 | 
			
		||||
      @s.rb_funcall3(@obj, :method_public).should == :method_public
 | 
			
		||||
      @s.rb_funcallv_public(@obj, :method_public).should == :method_public
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "does not call a private method" do
 | 
			
		||||
      -> { @s.rb_funcall3(@obj, :method_private) }.should raise_error(NoMethodError, /private/)
 | 
			
		||||
      -> { @s.rb_funcallv_public(@obj, :method_private) }.should raise_error(NoMethodError, /private/)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -599,6 +657,7 @@ describe "C-API Kernel function" do
 | 
			
		|||
      @s.rb_funcall_many_args(@obj, :many_args).should == 15.downto(1).to_a
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe 'rb_funcall_with_block' do
 | 
			
		||||
    before :each do
 | 
			
		||||
      @obj = Object.new
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,6 +61,10 @@ describe "C-API Symbol function" do
 | 
			
		|||
    it "converts a symbol to a C char array" do
 | 
			
		||||
      @s.rb_id2name(:test_symbol).should == "test_symbol"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns (char*) NULL for (ID) 0" do
 | 
			
		||||
      @s.rb_id2name_id_zero.should == nil
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "rb_id2str" do
 | 
			
		||||
| 
						 | 
				
			
			@ -72,6 +76,10 @@ describe "C-API Symbol function" do
 | 
			
		|||
      str = "test_symbol".encode(Encoding::UTF_16LE)
 | 
			
		||||
      @s.rb_id2str(str.to_sym).encoding.should == Encoding::UTF_16LE
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "returns (VALUE) 0 = Qfalse for (ID) 0" do
 | 
			
		||||
      @s.rb_id2str_id_zero.should == false
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe "rb_intern_str" do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,11 +29,41 @@ describe :kernel_raise, shared: true do
 | 
			
		|||
        @data = data
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
    -> { @object.raise(data_error, {:data => 42}) }.should raise_error(data_error) do |ex|
 | 
			
		||||
      ex.data.should == {:data => 42}
 | 
			
		||||
 | 
			
		||||
    -> { @object.raise(data_error, {data: 42}) }.should raise_error(data_error) do |ex|
 | 
			
		||||
      ex.data.should == {data: 42}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # https://bugs.ruby-lang.org/issues/8257#note-36
 | 
			
		||||
  it "allows extra keyword arguments for compatibility" do
 | 
			
		||||
    data_error = Class.new(StandardError) do
 | 
			
		||||
      attr_reader :data
 | 
			
		||||
      def initialize(data)
 | 
			
		||||
        @data = data
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    -> { @object.raise(data_error, data: 42) }.should raise_error(data_error) do |ex|
 | 
			
		||||
      ex.data.should == {data: 42}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "does not allow message and extra keyword arguments" do
 | 
			
		||||
    data_error = Class.new(StandardError) do
 | 
			
		||||
      attr_reader :data
 | 
			
		||||
      def initialize(data)
 | 
			
		||||
        @data = data
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    -> { @object.raise(data_error, {a: 1}, b: 2) }.should raise_error(StandardError) do |e|
 | 
			
		||||
      [TypeError, ArgumentError].should.include?(e.class)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    -> { @object.raise(data_error, {a: 1}, [], b: 2) }.should raise_error(ArgumentError)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "raises RuntimeError if no exception class is given" do
 | 
			
		||||
    -> { @object.raise }.should raise_error(RuntimeError, "")
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,12 @@ describe :process_exit, shared: true do
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "raises a SystemExit with message 'exit'" do
 | 
			
		||||
    -> { @object.exit }.should raise_error(SystemExit) { |e|
 | 
			
		||||
      e.message.should == "exit"
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "tries to convert the passed argument to an Integer using #to_int" do
 | 
			
		||||
    obj = mock('5')
 | 
			
		||||
    obj.should_receive(:to_int).and_return(5)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue