1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
This commit is contained in:
Benoit Daloze 2021-10-20 21:41:46 +02:00
parent 207a5a5bc1
commit a6c6eef04a
44 changed files with 1141 additions and 250 deletions

View file

@ -0,0 +1,48 @@
require_relative '../spec_helper'
ruby_version_is "3.0" do
describe "The --backtrace-limit command line option" do
it "limits top-level backtraces to a given number of entries" do
file = fixture(__FILE__ , "backtrace.rb")
out = ruby_exe(file, options: "--backtrace-limit=2", args: "top 2>&1", exit_status: 1)
out = out.gsub(__dir__, '')
out.should == <<-MSG
top
/fixtures/backtrace.rb:2:in `a': unhandled exception
\tfrom /fixtures/backtrace.rb:6:in `b'
\tfrom /fixtures/backtrace.rb:10:in `c'
\t ... 2 levels...
MSG
end
it "affects Exception#full_message" do
file = fixture(__FILE__ , "backtrace.rb")
out = ruby_exe(file, options: "--backtrace-limit=2", args: "full_message 2>&1")
out = out.gsub(__dir__, '')
out.should == <<-MSG
full_message
/fixtures/backtrace.rb:2:in `a': unhandled exception
\tfrom /fixtures/backtrace.rb:6:in `b'
\tfrom /fixtures/backtrace.rb:10:in `c'
\t ... 2 levels...
MSG
end
it "does not affect Exception#backtrace" do
file = fixture(__FILE__ , "backtrace.rb")
out = ruby_exe(file, options: "--backtrace-limit=2", args: "backtrace 2>&1")
out = out.gsub(__dir__, '')
out.should == <<-MSG
backtrace
/fixtures/backtrace.rb:2:in `a'
/fixtures/backtrace.rb:6:in `b'
/fixtures/backtrace.rb:10:in `c'
/fixtures/backtrace.rb:14:in `d'
/fixtures/backtrace.rb:29:in `<main>'
MSG
end
end
end

View file

@ -0,0 +1,35 @@
def a
raise
end
def b
a
end
def c
b
end
def d
c
end
arg = ARGV.first
$stderr.puts arg
case arg
when 'full_message'
begin
d
rescue => exc
puts exc.full_message
end
when 'backtrace'
begin
d
rescue => exc
puts exc.backtrace
end
else
d
end

View file

@ -517,8 +517,9 @@ describe :array_slice, shared: true do
end end
it "raises a RangeError if passed a range with a bound that is too large" do it "raises a RangeError if passed a range with a bound that is too large" do
-> { "hello".send(@method, bignum_value..(bignum_value + 1)) }.should raise_error(RangeError) array = [1, 2, 3, 4, 5, 6]
-> { "hello".send(@method, 0..bignum_value) }.should raise_error(RangeError) -> { array.send(@method, bignum_value..(bignum_value + 1)) }.should raise_error(RangeError)
-> { array.send(@method, 0..bignum_value) }.should raise_error(RangeError)
end end
it "can accept endless ranges" do it "can accept endless ranges" do
@ -533,6 +534,218 @@ describe :array_slice, shared: true do
a.send(@method, eval("(-9...)")).should == nil a.send(@method, eval("(-9...)")).should == nil
end end
ruby_version_is "3.0" do
describe "can be sliced with Enumerator::ArithmeticSequence" do
before :each do
@array = [0, 1, 2, 3, 4, 5]
end
it "has endless range and positive steps" do
@array.send(@method, eval("(0..).step(1)")).should == [0, 1, 2, 3, 4, 5]
@array.send(@method, eval("(0..).step(2)")).should == [0, 2, 4]
@array.send(@method, eval("(0..).step(10)")).should == [0]
@array.send(@method, eval("(2..).step(1)")).should == [2, 3, 4, 5]
@array.send(@method, eval("(2..).step(2)")).should == [2, 4]
@array.send(@method, eval("(2..).step(10)")).should == [2]
@array.send(@method, eval("(-3..).step(1)")).should == [3, 4, 5]
@array.send(@method, eval("(-3..).step(2)")).should == [3, 5]
@array.send(@method, eval("(-3..).step(10)")).should == [3]
end
it "has beginless range and positive steps" do
# end with zero index
@array.send(@method, eval("(..0).step(1)")).should == [0]
@array.send(@method, eval("(...0).step(1)")).should == []
@array.send(@method, eval("(..0).step(2)")).should == [0]
@array.send(@method, eval("(...0).step(2)")).should == []
@array.send(@method, eval("(..0).step(10)")).should == [0]
@array.send(@method, eval("(...0).step(10)")).should == []
# end with positive index
@array.send(@method, eval("(..3).step(1)")).should == [0, 1, 2, 3]
@array.send(@method, eval("(...3).step(1)")).should == [0, 1, 2]
@array.send(@method, eval("(..3).step(2)")).should == [0, 2]
@array.send(@method, eval("(...3).step(2)")).should == [0, 2]
@array.send(@method, eval("(..3).step(10)")).should == [0]
@array.send(@method, eval("(...3).step(10)")).should == [0]
# end with negative index
@array.send(@method, eval("(..-2).step(1)")).should == [0, 1, 2, 3, 4,]
@array.send(@method, eval("(...-2).step(1)")).should == [0, 1, 2, 3]
@array.send(@method, eval("(..-2).step(2)")).should == [0, 2, 4]
@array.send(@method, eval("(...-2).step(2)")).should == [0, 2]
@array.send(@method, eval("(..-2).step(10)")).should == [0]
@array.send(@method, eval("(...-2).step(10)")).should == [0]
end
it "has endless range and negative steps" do
@array.send(@method, eval("(0..).step(-1)")).should == [0]
@array.send(@method, eval("(0..).step(-2)")).should == [0]
@array.send(@method, eval("(0..).step(-10)")).should == [0]
@array.send(@method, eval("(2..).step(-1)")).should == [2, 1, 0]
@array.send(@method, eval("(2..).step(-2)")).should == [2, 0]
@array.send(@method, eval("(-3..).step(-1)")).should == [3, 2, 1, 0]
@array.send(@method, eval("(-3..).step(-2)")).should == [3, 1]
end
it "has closed range and positive steps" do
# start and end with 0
@array.send(@method, eval("(0..0).step(1)")).should == [0]
@array.send(@method, eval("(0...0).step(1)")).should == []
@array.send(@method, eval("(0..0).step(2)")).should == [0]
@array.send(@method, eval("(0...0).step(2)")).should == []
@array.send(@method, eval("(0..0).step(10)")).should == [0]
@array.send(@method, eval("(0...0).step(10)")).should == []
# start and end with positive index
@array.send(@method, eval("(1..3).step(1)")).should == [1, 2, 3]
@array.send(@method, eval("(1...3).step(1)")).should == [1, 2]
@array.send(@method, eval("(1..3).step(2)")).should == [1, 3]
@array.send(@method, eval("(1...3).step(2)")).should == [1]
@array.send(@method, eval("(1..3).step(10)")).should == [1]
@array.send(@method, eval("(1...3).step(10)")).should == [1]
# start with positive index, end with negative index
@array.send(@method, eval("(1..-2).step(1)")).should == [1, 2, 3, 4]
@array.send(@method, eval("(1...-2).step(1)")).should == [1, 2, 3]
@array.send(@method, eval("(1..-2).step(2)")).should == [1, 3]
@array.send(@method, eval("(1...-2).step(2)")).should == [1, 3]
@array.send(@method, eval("(1..-2).step(10)")).should == [1]
@array.send(@method, eval("(1...-2).step(10)")).should == [1]
# start with negative index, end with positive index
@array.send(@method, eval("(-4..4).step(1)")).should == [2, 3, 4]
@array.send(@method, eval("(-4...4).step(1)")).should == [2, 3]
@array.send(@method, eval("(-4..4).step(2)")).should == [2, 4]
@array.send(@method, eval("(-4...4).step(2)")).should == [2]
@array.send(@method, eval("(-4..4).step(10)")).should == [2]
@array.send(@method, eval("(-4...4).step(10)")).should == [2]
# start with negative index, end with negative index
@array.send(@method, eval("(-4..-2).step(1)")).should == [2, 3, 4]
@array.send(@method, eval("(-4...-2).step(1)")).should == [2, 3]
@array.send(@method, eval("(-4..-2).step(2)")).should == [2, 4]
@array.send(@method, eval("(-4...-2).step(2)")).should == [2]
@array.send(@method, eval("(-4..-2).step(10)")).should == [2]
@array.send(@method, eval("(-4...-2).step(10)")).should == [2]
end
it "has closed range and negative steps" do
# start and end with 0
@array.send(@method, eval("(0..0).step(-1)")).should == [0]
@array.send(@method, eval("(0...0).step(-1)")).should == []
@array.send(@method, eval("(0..0).step(-2)")).should == [0]
@array.send(@method, eval("(0...0).step(-2)")).should == []
@array.send(@method, eval("(0..0).step(-10)")).should == [0]
@array.send(@method, eval("(0...0).step(-10)")).should == []
# start and end with positive index
@array.send(@method, eval("(1..3).step(-1)")).should == []
@array.send(@method, eval("(1...3).step(-1)")).should == []
@array.send(@method, eval("(1..3).step(-2)")).should == []
@array.send(@method, eval("(1...3).step(-2)")).should == []
@array.send(@method, eval("(1..3).step(-10)")).should == []
@array.send(@method, eval("(1...3).step(-10)")).should == []
# start with positive index, end with negative index
@array.send(@method, eval("(1..-2).step(-1)")).should == []
@array.send(@method, eval("(1...-2).step(-1)")).should == []
@array.send(@method, eval("(1..-2).step(-2)")).should == []
@array.send(@method, eval("(1...-2).step(-2)")).should == []
@array.send(@method, eval("(1..-2).step(-10)")).should == []
@array.send(@method, eval("(1...-2).step(-10)")).should == []
# start with negative index, end with positive index
@array.send(@method, eval("(-4..4).step(-1)")).should == []
@array.send(@method, eval("(-4...4).step(-1)")).should == []
@array.send(@method, eval("(-4..4).step(-2)")).should == []
@array.send(@method, eval("(-4...4).step(-2)")).should == []
@array.send(@method, eval("(-4..4).step(-10)")).should == []
@array.send(@method, eval("(-4...4).step(-10)")).should == []
# start with negative index, end with negative index
@array.send(@method, eval("(-4..-2).step(-1)")).should == []
@array.send(@method, eval("(-4...-2).step(-1)")).should == []
@array.send(@method, eval("(-4..-2).step(-2)")).should == []
@array.send(@method, eval("(-4...-2).step(-2)")).should == []
@array.send(@method, eval("(-4..-2).step(-10)")).should == []
@array.send(@method, eval("(-4...-2).step(-10)")).should == []
end
it "has inverted closed range and positive steps" do
# start and end with positive index
@array.send(@method, eval("(3..1).step(1)")).should == []
@array.send(@method, eval("(3...1).step(1)")).should == []
@array.send(@method, eval("(3..1).step(2)")).should == []
@array.send(@method, eval("(3...1).step(2)")).should == []
@array.send(@method, eval("(3..1).step(10)")).should == []
@array.send(@method, eval("(3...1).step(10)")).should == []
# start with negative index, end with positive index
@array.send(@method, eval("(-2..1).step(1)")).should == []
@array.send(@method, eval("(-2...1).step(1)")).should == []
@array.send(@method, eval("(-2..1).step(2)")).should == []
@array.send(@method, eval("(-2...1).step(2)")).should == []
@array.send(@method, eval("(-2..1).step(10)")).should == []
@array.send(@method, eval("(-2...1).step(10)")).should == []
# start with positive index, end with negative index
@array.send(@method, eval("(4..-4).step(1)")).should == []
@array.send(@method, eval("(4...-4).step(1)")).should == []
@array.send(@method, eval("(4..-4).step(2)")).should == []
@array.send(@method, eval("(4...-4).step(2)")).should == []
@array.send(@method, eval("(4..-4).step(10)")).should == []
@array.send(@method, eval("(4...-4).step(10)")).should == []
# start with negative index, end with negative index
@array.send(@method, eval("(-2..-4).step(1)")).should == []
@array.send(@method, eval("(-2...-4).step(1)")).should == []
@array.send(@method, eval("(-2..-4).step(2)")).should == []
@array.send(@method, eval("(-2...-4).step(2)")).should == []
@array.send(@method, eval("(-2..-4).step(10)")).should == []
@array.send(@method, eval("(-2...-4).step(10)")).should == []
end
end
end
ruby_version_is "2.7" do ruby_version_is "2.7" do
it "can accept beginless ranges" do it "can accept beginless ranges" do
a = [0, 1, 2, 3, 4, 5] a = [0, 1, 2, 3, 4, 5]

View file

@ -93,16 +93,28 @@ describe "Binding#eval" do
it "inherits __FILE__ from the enclosing scope" do it "inherits __FILE__ from the enclosing scope" do
obj = BindingSpecs::Demo.new(1) obj = BindingSpecs::Demo.new(1)
bind = obj.get_binding bind = obj.get_binding
suppress_warning {bind.eval("__FILE__")}.should == obj.get_file_of_binding suppress_warning { bind.eval("__FILE__") }.should == obj.get_file_of_binding
end
it "inherits __LINE__ from the enclosing scope" do
obj = BindingSpecs::Demo.new(1)
bind, line = obj.get_binding_and_line
suppress_warning { bind.eval("__LINE__") }.should == line
end end
end end
ruby_version_is "3.0" do ruby_version_is "3.0" do
it "Uses (eval) as __FILE__ if single argument given" do it "uses (eval) as __FILE__ if single argument given" do
obj = BindingSpecs::Demo.new(1) obj = BindingSpecs::Demo.new(1)
bind = obj.get_binding bind = obj.get_binding
bind.eval("__FILE__").should == '(eval)' bind.eval("__FILE__").should == '(eval)'
end end
it "uses 1 as __LINE__" do
obj = BindingSpecs::Demo.new(1)
bind = obj.get_binding
suppress_warning { bind.eval("__LINE__") }.should == 1
end
end end
it "uses the __FILE__ that is passed in" do it "uses the __FILE__ that is passed in" do

View file

@ -38,6 +38,23 @@ describe :dir_glob, shared: true do
end end
end end
ruby_version_is "3.0" do
it "result is sorted by default" do
result = Dir.send(@method, '*')
result.should == result.sort
end
it "result is sorted with sort: true" do
result = Dir.send(@method, '*', sort: true)
result.should == result.sort
end
it "sort: false returns same files" do
result = Dir.send(@method,'*', sort: false)
result.sort.should == Dir.send(@method, '*').sort
end
end
it "matches non-dotfiles with '*'" do it "matches non-dotfiles with '*'" do
expected = %w[ expected = %w[
brace brace

View file

@ -1,30 +1,6 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/common' require_relative 'fixtures/common'
describe "ENV.shift" do
it "returns a pair and deletes it" do
ENV.should_not.empty?
orig = ENV.to_hash
begin
pair = ENV.shift
ENV.has_key?(pair.first).should == false
ensure
ENV.replace orig
end
ENV.has_key?(pair.first).should == true
end
it "returns nil if ENV.empty?" do
orig = ENV.to_hash
begin
ENV.clear
ENV.shift.should == nil
ensure
ENV.replace orig
end
end
end
describe "ENV.shift" do describe "ENV.shift" do
before :each do before :each do
@orig = ENV.to_hash @orig = ENV.to_hash
@ -32,6 +8,7 @@ describe "ENV.shift" do
@internal = Encoding.default_internal @internal = Encoding.default_internal
Encoding.default_external = Encoding::BINARY Encoding.default_external = Encoding::BINARY
ENV.replace({"FOO"=>"BAR"})
end end
after :each do after :each do
@ -40,6 +17,18 @@ describe "ENV.shift" do
ENV.replace @orig ENV.replace @orig
end end
it "returns a pair and deletes it" do
ENV.should.has_key?("FOO")
pair = ENV.shift
pair.should == ["FOO", "BAR"]
ENV.should_not.has_key?("FOO")
end
it "returns nil if ENV.empty?" do
ENV.clear
ENV.shift.should == nil
end
it "uses the locale encoding if Encoding.default_internal is nil" do it "uses the locale encoding if Encoding.default_internal is nil" do
Encoding.default_internal = nil Encoding.default_internal = nil
@ -52,9 +41,7 @@ describe "ENV.shift" do
Encoding.default_internal = Encoding::IBM437 Encoding.default_internal = Encoding::IBM437
pair = ENV.shift pair = ENV.shift
pair.first.encode(Encoding::IBM437) rescue next
pair.first.encoding.should equal(Encoding::IBM437) pair.first.encoding.should equal(Encoding::IBM437)
pair.last.encode(Encoding::IBM437) rescue next
pair.last.encoding.should equal(Encoding::IBM437) pair.last.encoding.should equal(Encoding::IBM437)
end end
end end

View file

@ -70,11 +70,13 @@ describe "File.utime" do
end end
end end
it "may set nanosecond precision" do platform_is_not :windows do
t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r) it "sets nanosecond precision" do
File.utime(t, t, @file1) t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
File.atime(@file1).nsec.should.between?(0, 123500000) File.utime(t, t, @file1)
File.mtime(@file1).nsec.should.between?(0, 123500000) File.atime(@file1).nsec.should == 123456789
File.mtime(@file1).nsec.should == 123456789
end
end end
platform_is :linux do platform_is :linux do

View file

@ -90,4 +90,19 @@ describe "Float#<=>" do
end end
(infinity_value <=> obj).should == 1 (infinity_value <=> obj).should == 1
end end
it "returns 0 for -0.0 and 0.0" do
(-0.0 <=> 0.0).should == 0
(0.0 <=> -0.0).should == 0
end
it "returns 0 for -0.0 and 0" do
(-0.0 <=> 0).should == 0
(0 <=> -0.0).should == 0
end
it "returns 0 for 0.0 and 0" do
(0.0 <=> 0).should == 0
(0 <=> 0.0).should == 0
end
end end

View file

@ -42,6 +42,20 @@ describe "Hash#transform_keys" do
r.keys.should == [:xfoo] r.keys.should == [:xfoo]
r.class.should == Hash r.class.should == Hash
end end
ruby_version_is "3.0" do
it "allows a hash argument" do
@hash.transform_keys({ a: :A, b: :B, c: :C }).should == { A: 1, B: 2, C: 3 }
end
it "allows a partial transformation of keys when using a hash argument" do
@hash.transform_keys({ a: :A, c: :C }).should == { A: 1, b: 2, C: 3 }
end
it "allows a combination of hash and block argument" do
@hash.transform_keys({ a: :A }, &:to_s).should == { A: 1, 'b' => 2, 'c' => 3 }
end
end
end end
describe "Hash#transform_keys!" do describe "Hash#transform_keys!" do
@ -98,6 +112,13 @@ describe "Hash#transform_keys!" do
end end
end end
ruby_version_is "3.0" do
it "allows a hash argument" do
@hash.transform_keys!({ a: :A, b: :B, c: :C, d: :D })
@hash.should == { A: 1, B: 2, C: 3, D: 4 }
end
end
describe "on frozen instance" do describe "on frozen instance" do
before :each do before :each do
@hash.freeze @hash.freeze
@ -112,6 +133,12 @@ describe "Hash#transform_keys!" do
@hash.should == @initial_pairs @hash.should == @initial_pairs
end end
ruby_version_is "3.0" do
it "raises a FrozenError on hash argument" do
->{ @hash.transform_keys!({ a: :A, b: :B, c: :C }) }.should raise_error(FrozenError)
end
end
context "when no block is given" do context "when no block is given" do
it "does not raise an exception" do it "does not raise an exception" do
@hash.transform_keys!.should be_an_instance_of(Enumerator) @hash.transform_keys!.should be_an_instance_of(Enumerator)

View file

@ -0,0 +1,21 @@
require_relative '../../spec_helper'
describe "Integer#zero?" do
it "returns true if self is 0" do
0.should.zero?
1.should_not.zero?
-1.should_not.zero?
end
ruby_version_is "3.0" do
it "Integer#zero? overrides Numeric#zero?" do
42.method(:zero?).owner.should == Integer
end
end
ruby_version_is ""..."3.0" do
it "Integer#zero? uses Numeric#zero?" do
42.method(:zero?).owner.should == Numeric
end
end
end

View file

@ -28,27 +28,88 @@ describe "Kernel#clone" do
clone.class.should equal klass clone.class.should equal klass
end end
it "copies frozen state from the original" do describe "with no arguments" do
o2 = @obj.clone it "copies frozen state from the original" do
@obj.freeze o2 = @obj.clone
o3 = @obj.clone
o2.should_not.frozen?
o3.should.frozen?
end
ruby_version_is '3.0' do
it 'takes an freeze: true option to frozen copy' do
@obj.clone(freeze: true).should.frozen?
@obj.freeze @obj.freeze
@obj.clone(freeze: true).should.frozen? o3 = @obj.clone
o2.should_not.frozen?
o3.should.frozen?
end
it 'copies frozen?' do
o = ''.freeze.clone
o.frozen?.should be_true
end end
end end
it 'takes an freeze: false option to not return frozen copy' do describe "with freeze: true" do
@obj.clone(freeze: false).should_not.frozen? it 'makes a frozen copy if the original is frozen' do
@obj.freeze @obj.freeze
@obj.clone(freeze: false).should_not.frozen? @obj.clone(freeze: true).should.frozen?
end
ruby_version_is ''...'3.0' do
it 'does not freeze the copy even if the original is not frozen' do
@obj.clone(freeze: true).should_not.frozen?
end
it "calls #initialize_clone with no kwargs" do
obj = KernelSpecs::CloneFreeze.new
obj.clone(freeze: true)
ScratchPad.recorded.should == [obj, {}]
end
end
ruby_version_is '3.0' do
it 'freezes the copy even if the original was not frozen' do
@obj.clone(freeze: true).should.frozen?
end
it "calls #initialize_clone with kwargs freeze: true" do
obj = KernelSpecs::CloneFreeze.new
obj.clone(freeze: true)
ScratchPad.recorded.should == [obj, { freeze: true }]
end
it "calls #initialize_clone with kwargs freeze: true even if #initialize_clone only takes a single argument" do
obj = KernelSpecs::Clone.new
-> { obj.clone(freeze: true) }.should raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
end
end
end
describe "with freeze: false" do
it 'does not freeze the copy if the original is frozen' do
@obj.freeze
@obj.clone(freeze: false).should_not.frozen?
end
it 'does not freeze the copy if the original is not frozen' do
@obj.clone(freeze: false).should_not.frozen?
end
ruby_version_is ''...'3.0' do
it "calls #initialize_clone with no kwargs" do
obj = KernelSpecs::CloneFreeze.new
obj.clone(freeze: false)
ScratchPad.recorded.should == [obj, {}]
end
end
ruby_version_is '3.0' do
it "calls #initialize_clone with kwargs freeze: false" do
obj = KernelSpecs::CloneFreeze.new
obj.clone(freeze: false)
ScratchPad.recorded.should == [obj, { freeze: false }]
end
it "calls #initialize_clone with kwargs freeze: false even if #initialize_clone only takes a single argument" do
obj = KernelSpecs::Clone.new
-> { obj.clone(freeze: false) }.should raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
end
end
end end
it "copies instance variables" do it "copies instance variables" do
@ -114,11 +175,6 @@ describe "Kernel#clone" do
cloned.bar.should == ['a'] cloned.bar.should == ['a']
end end
it 'copies frozen?' do
o = ''.freeze.clone
o.frozen?.should be_true
end
ruby_version_is ''...'2.7' do ruby_version_is ''...'2.7' do
it 'copies tainted?' do it 'copies tainted?' do
o = ''.taint.clone o = ''.taint.clone

View file

@ -168,6 +168,12 @@ describe "Kernel#eval" do
suppress_warning {eval("eval '__FILE__', binding", binding)}.should == __FILE__ suppress_warning {eval("eval '__FILE__', binding", binding)}.should == __FILE__
suppress_warning {eval("eval '__FILE__', binding", binding, 'success')}.should == 'success' suppress_warning {eval("eval '__FILE__', binding", binding, 'success')}.should == 'success'
end end
it 'uses the given binding file and line for __FILE__ and __LINE__' do
suppress_warning {
eval("[__FILE__, __LINE__]", binding).should == [__FILE__, __LINE__]
}
end
end end
ruby_version_is "3.0" do ruby_version_is "3.0" do
@ -180,6 +186,10 @@ describe "Kernel#eval" do
eval("eval '__FILE__', binding", binding, 'success').should == '(eval)' eval("eval '__FILE__', binding", binding, 'success').should == '(eval)'
eval("eval '__FILE__', binding, 'success'", binding).should == 'success' eval("eval '__FILE__', binding, 'success'", binding).should == 'success'
end end
it 'uses (eval) for __FILE__ and 1 for __LINE__ with a binding argument' do
eval("[__FILE__, __LINE__]", binding).should == ["(eval)", 1]
end
end end
# Found via Rubinius bug github:#149 # Found via Rubinius bug github:#149

View file

@ -288,7 +288,13 @@ module KernelSpecs
class Clone class Clone
def initialize_clone(other) def initialize_clone(other)
ScratchPad.record other.object_id ScratchPad.record other
end
end
class CloneFreeze
def initialize_clone(other, **kwargs)
ScratchPad.record([other, kwargs])
end end
end end

View file

@ -441,42 +441,21 @@ describe "Module#autoload" do
ScratchPad.recorded.should == [:raise, :raise] ScratchPad.recorded.should == [:raise, :raise]
end end
ruby_version_is "3.1" do it "does not remove the constant from Module#constants if the loaded file does not define it, but leaves it as 'undefined'" do
it "removes the constant from Module#constants if the loaded file does not define it" do path = fixture(__FILE__, "autoload_o.rb")
path = fixture(__FILE__, "autoload_o.rb") ScratchPad.record []
ScratchPad.record [] ModuleSpecs::Autoload.autoload :O, path
ModuleSpecs::Autoload.autoload :O, path
ModuleSpecs::Autoload.const_defined?(:O).should == true ModuleSpecs::Autoload.const_defined?(:O).should == true
ModuleSpecs::Autoload.should have_constant(:O) ModuleSpecs::Autoload.should have_constant(:O)
ModuleSpecs::Autoload.autoload?(:O).should == path ModuleSpecs::Autoload.autoload?(:O).should == path
-> { ModuleSpecs::Autoload::O }.should raise_error(NameError) -> { ModuleSpecs::Autoload::O }.should raise_error(NameError)
ModuleSpecs::Autoload.const_defined?(:O).should == false ModuleSpecs::Autoload.should have_constant(:O)
ModuleSpecs::Autoload.should_not have_constant(:O) ModuleSpecs::Autoload.const_defined?(:O).should == false
ModuleSpecs::Autoload.autoload?(:O).should == nil ModuleSpecs::Autoload.autoload?(:O).should == nil
-> { ModuleSpecs::Autoload.const_get(:O) }.should raise_error(NameError) -> { ModuleSpecs::Autoload.const_get(:O) }.should raise_error(NameError)
end
end
ruby_version_is ""..."3.1" do
it "does not remove the constant from Module#constants if the loaded file does not define it, but leaves it as 'undefined'" do
path = fixture(__FILE__, "autoload_o.rb")
ScratchPad.record []
ModuleSpecs::Autoload.autoload :O, path
ModuleSpecs::Autoload.const_defined?(:O).should == true
ModuleSpecs::Autoload.should have_constant(:O)
ModuleSpecs::Autoload.autoload?(:O).should == path
-> { ModuleSpecs::Autoload::O }.should raise_error(NameError)
ModuleSpecs::Autoload.const_defined?(:O).should == false
ModuleSpecs::Autoload.should have_constant(:O)
ModuleSpecs::Autoload.autoload?(:O).should == nil
-> { ModuleSpecs::Autoload.const_get(:O) }.should raise_error(NameError)
end
end end
it "does not try to load the file again if the loaded file did not define the constant" do it "does not try to load the file again if the loaded file did not define the constant" do
@ -575,54 +554,31 @@ describe "Module#autoload" do
# Basically, the parent autoload constant remains in a "undefined" state # Basically, the parent autoload constant remains in a "undefined" state
self.autoload?(:DeclaredInParentDefinedInCurrent).should == nil self.autoload?(:DeclaredInParentDefinedInCurrent).should == nil
const_defined?(:DeclaredInParentDefinedInCurrent).should == false const_defined?(:DeclaredInParentDefinedInCurrent).should == false
self.should have_constant(:DeclaredInParentDefinedInCurrent)
-> { DeclaredInParentDefinedInCurrent }.should raise_error(NameError) -> { DeclaredInParentDefinedInCurrent }.should raise_error(NameError)
ModuleSpecs::Autoload::LexicalScope.send(:remove_const, :DeclaredInParentDefinedInCurrent) ModuleSpecs::Autoload::LexicalScope.send(:remove_const, :DeclaredInParentDefinedInCurrent)
end end
end end
ruby_version_is "3.1" do it "and fails when finding the undefined autoload constant in the current scope when declared in current and defined in parent" do
it "looks up in parent scope after failed autoload" do @remove << :DeclaredInCurrentDefinedInParent
@remove << :DeclaredInCurrentDefinedInParent module ModuleSpecs::Autoload
module ModuleSpecs::Autoload ScratchPad.record -> {
ScratchPad.record -> { DeclaredInCurrentDefinedInParent = :declared_in_current_defined_in_parent
DeclaredInCurrentDefinedInParent = :declared_in_current_defined_in_parent }
}
class LexicalScope class LexicalScope
autoload :DeclaredInCurrentDefinedInParent, fixture(__FILE__, "autoload_callback.rb") autoload :DeclaredInCurrentDefinedInParent, fixture(__FILE__, "autoload_callback.rb")
-> { DeclaredInCurrentDefinedInParent }.should_not raise_error(NameError) -> { DeclaredInCurrentDefinedInParent }.should raise_error(NameError)
# Basically, the autoload constant remains in a "undefined" state # Basically, the autoload constant remains in a "undefined" state
self.autoload?(:DeclaredInCurrentDefinedInParent).should == nil self.autoload?(:DeclaredInCurrentDefinedInParent).should == nil
const_defined?(:DeclaredInCurrentDefinedInParent).should == false const_defined?(:DeclaredInCurrentDefinedInParent).should == false
-> { const_get(:DeclaredInCurrentDefinedInParent) }.should raise_error(NameError) self.should have_constant(:DeclaredInCurrentDefinedInParent)
end -> { const_get(:DeclaredInCurrentDefinedInParent) }.should raise_error(NameError)
DeclaredInCurrentDefinedInParent.should == :declared_in_current_defined_in_parent
end end
end
end
ruby_version_is ""..."3.1" do DeclaredInCurrentDefinedInParent.should == :declared_in_current_defined_in_parent
it "and fails when finding the undefined autoload constant in the current scope when declared in current and defined in parent" do
@remove << :DeclaredInCurrentDefinedInParent
module ModuleSpecs::Autoload
ScratchPad.record -> {
DeclaredInCurrentDefinedInParent = :declared_in_current_defined_in_parent
}
class LexicalScope
autoload :DeclaredInCurrentDefinedInParent, fixture(__FILE__, "autoload_callback.rb")
-> { DeclaredInCurrentDefinedInParent }.should raise_error(NameError)
# Basically, the autoload constant remains in a "undefined" state
self.autoload?(:DeclaredInCurrentDefinedInParent).should == nil
const_defined?(:DeclaredInCurrentDefinedInParent).should == false
self.should have_constant(:DeclaredInCurrentDefinedInParent)
-> { const_get(:DeclaredInCurrentDefinedInParent) }.should raise_error(NameError)
end
DeclaredInCurrentDefinedInParent.should == :declared_in_current_defined_in_parent
end
end end
end end

View file

@ -101,7 +101,7 @@ describe "Module#const_set" do
mod.const_get(:Foo).should == 1 mod.const_get(:Foo).should == 1
end end
it "does not warn after a failed autoload" do it "does not warn if the previous value was undefined" do
path = fixture(__FILE__, "autoload_o.rb") path = fixture(__FILE__, "autoload_o.rb")
ScratchPad.record [] ScratchPad.record []
mod = Module.new mod = Module.new
@ -109,6 +109,7 @@ describe "Module#const_set" do
mod.autoload :Foo, path mod.autoload :Foo, path
-> { mod::Foo }.should raise_error(NameError) -> { mod::Foo }.should raise_error(NameError)
mod.should have_constant(:Foo)
mod.const_defined?(:Foo).should == false mod.const_defined?(:Foo).should == false
mod.autoload?(:Foo).should == nil mod.autoload?(:Foo).should == nil

View file

@ -74,6 +74,12 @@ describe "Module#constants" do
it "returns only public constants" do it "returns only public constants" do
ModuleSpecs::PrivConstModule.constants.should == [:PUBLIC_CONSTANT] ModuleSpecs::PrivConstModule.constants.should == [:PUBLIC_CONSTANT]
end end
it "returns only constants starting with an uppercase letter" do
# e.g. fatal, IO::generic_readable and IO::generic_writable should not be returned by Module#constants
Object.constants.each { |c| c[0].should == c[0].upcase }
IO.constants.each { |c| c[0].should == c[0].upcase }
end
end end
describe "Module#constants" do describe "Module#constants" do

View file

@ -84,9 +84,27 @@ describe "Range#each" do
enum.to_a.should == [1, 2, 3] enum.to_a.should == [1, 2, 3]
end end
it "raises a TypeError if the first element is a Time object" do ruby_version_is "3.1" do
t = Time.now it "supports Time objects that respond to #succ" do
-> { (t..t+1).each { |i| i } }.should raise_error(TypeError) t = Time.utc(1970)
def t.succ; self + 1 end
t_succ = t.succ
def t_succ.succ; self + 1; end
(t..t_succ).to_a.should == [Time.utc(1970), Time.utc(1970, nil, nil, nil, nil, 1)]
(t...t_succ).to_a.should == [Time.utc(1970)]
end
end
ruby_version_is ""..."3.1" do
it "raises a TypeError if the first element is a Time object even if it responds to #succ" do
t = Time.utc(1970)
def t.succ; self + 1 end
t_succ = t.succ
def t_succ.succ; self + 1; end
-> { (t..t_succ).each { |i| i } }.should raise_error(TypeError)
end
end end
it "passes each Symbol element by using #succ" do it "passes each Symbol element by using #succ" do

View file

@ -225,4 +225,43 @@ describe "The ** operator" do
h.should == { two: 2 } h.should == { two: 2 }
end end
end end
ruby_version_is "3.1" do
describe "hash with omitted value" do
it "accepts short notation 'key' for 'key: value' syntax" do
a, b, c = 1, 2, 3
h = eval('{a:}')
{a: 1}.should == h
h = eval('{a:, b:, c:}')
{a: 1, b: 2, c: 3}.should == h
end
it "ignores hanging comma on short notation" do
a, b, c = 1, 2, 3
h = eval('{a:, b:, c:,}')
{a: 1, b: 2, c: 3}.should == h
end
it "accepts mixed syntax" do
a, e = 1, 5
h = eval('{a:, b: 2, "c" => 3, :d => 4, e:}')
eval('{a: 1, :b => 2, "c" => 3, "d": 4, e: 5}').should == h
end
it "works with methods and local vars" do
a = Class.new
a.class_eval(<<-RUBY)
def bar
"baz"
end
def foo(val)
{bar:, val:}
end
RUBY
a.new.foo(1).should == {bar: "baz", val: 1}
end
end
end
end end

View file

@ -1861,15 +1861,158 @@ describe "An array-dereference method ([])" do
end end
end end
ruby_version_is '3.0' do ruby_version_is "3.0" do
describe "An endless method definition" do describe "An endless method definition" do
evaluate <<-ruby do context "without arguments" do
def m(a) = a evaluate <<-ruby do
ruby def m() = 42
ruby
a = b = m 1 m.should == 42
a.should == 1 end
b.should == 1 end
context "with arguments" do
evaluate <<-ruby do
def m(a, b) = a + b
ruby
m(1, 4).should == 5
end
end
context "with multiline body" do
evaluate <<-ruby do
def m(n) =
if n > 2
m(n - 2) + m(n - 1)
else
1
end
ruby
m(6).should == 8
end
end
context "with args forwarding" do
evaluate <<-ruby do
def mm(word, num:)
word * num
end
def m(...) = mm(...) + mm(...)
ruby
m("meow", num: 2).should == "meow" * 4
end
end
end
describe "Keyword arguments are now separated from positional arguments" do
context "when the method has only positional parameters" do
it "treats incoming keyword arguments as positional for compatibility" do
def foo(a, b, c, hsh)
hsh[:key]
end
foo(1, 2, 3, key: 42).should == 42
end
end
context "when the method takes a ** parameter" do
it "captures the passed literal keyword arguments" do
def foo(a, b, c, **hsh)
hsh[:key]
end
foo(1, 2, 3, key: 42).should == 42
end
it "captures the passed ** keyword arguments" do
def foo(a, b, c, **hsh)
hsh[:key]
end
h = { key: 42 }
foo(1, 2, 3, **h).should == 42
end
it "does not convert a positional Hash to keyword arguments" do
def foo(a, b, c, **hsh)
hsh[:key]
end
-> {
foo(1, 2, 3, { key: 42 })
}.should raise_error(ArgumentError, 'wrong number of arguments (given 4, expected 3)')
end
end
context "when the method takes a key: parameter" do
context "when it's called with a positional Hash and no **" do
it "raises ArgumentError" do
def foo(a, b, c, key: 1)
key
end
-> {
foo(1, 2, 3, { key: 42 })
}.should raise_error(ArgumentError, 'wrong number of arguments (given 4, expected 3)')
end
end
context "when it's called with **" do
it "captures the passed keyword arguments" do
def foo(a, b, c, key: 1)
key
end
h = { key: 42 }
foo(1, 2, 3, **h).should == 42
end
end
end
end
end
ruby_version_is "3.1" do
describe "kwarg with omitted value in a method call" do
context "accepts short notation 'kwarg' in method call" do
evaluate <<-ruby do
def call(*args, **kwargs) = [args, kwargs]
ruby
a, b, c = 1, 2, 3
arr, h = eval('call a:')
h.should == {a: 1}
arr.should == []
arr, h = eval('call(a:, b:, c:)')
h.should == {a: 1, b: 2, c: 3}
arr.should == []
arr, h = eval('call(a:, b: 10, c:)')
h.should == {a: 1, b: 10, c: 3}
arr.should == []
end
end
context "with methods and local variables" do
evaluate <<-ruby do
def call(*args, **kwargs) = [args, kwargs]
def bar
"baz"
end
def foo(val)
call bar:, val:
end
ruby
foo(1).should == [[], {bar: "baz", val: 1}]
end
end end
end end
end end

View file

@ -18,6 +18,63 @@ ruby_version_is "2.7" do
RUBY RUBY
end end
end end
describe "find pattern" do
it "captures preceding elements to the pattern" do
eval(<<~RUBY).should == [0, 1]
case [0, 1, 2, 3]
in [*pre, 2, 3]
pre
else
false
end
RUBY
end
it "captures following elements to the pattern" do
eval(<<~RUBY).should == [2, 3]
case [0, 1, 2, 3]
in [0, 1, *post]
post
else
false
end
RUBY
end
it "captures both preceding and following elements to the pattern" do
eval(<<~RUBY).should == [[0, 1], [3, 4]]
case [0, 1, 2, 3, 4]
in [*pre, 2, *post]
[pre, post]
else
false
end
RUBY
end
it "can capture the entirety of the pattern" do
eval(<<~RUBY).should == [0, 1, 2, 3, 4]
case [0, 1, 2, 3, 4]
in [*everything]
everything
else
false
end
RUBY
end
it "will match an empty Array-like structure" do
eval(<<~RUBY).should == []
case []
in [*everything]
everything
else
false
end
RUBY
end
end
end end
it "extends case expression with case/in construction" do it "extends case expression with case/in construction" do
@ -41,25 +98,49 @@ ruby_version_is "2.7" do
end end
describe "warning" do describe "warning" do
ruby_version_is ""..."3.1" do before :each do
before :each do @experimental, Warning[:experimental] = Warning[:experimental], true
ruby_version_is ""..."3.0" do end
@src = 'case [0, 1]; in [a, b]; end'
end
ruby_version_is "3.0" do after :each do
Warning[:experimental] = @experimental
end
context 'when regular form' do
before :each do
@src = 'case [0, 1]; in [a, b]; end'
end
ruby_version_is ""..."3.0" do
it "warns about pattern matching is experimental feature" do
-> { eval @src }.should complain(/pattern matching is experimental, and the behavior may change in future versions of Ruby!/i)
end
end
ruby_version_is "3.0" do
it "does not warn about pattern matching is experimental feature" do
-> { eval @src }.should_not complain
end
end
end
context 'when one-line form' do
ruby_version_is '3.0' do
before :each do
@src = '[0, 1] => [a, b]' @src = '[0, 1] => [a, b]'
end end
@experimental, Warning[:experimental] = Warning[:experimental], true ruby_version_is ""..."3.1" do
end it "warns about pattern matching is experimental feature" do
-> { eval @src }.should complain(/pattern matching is experimental, and the behavior may change in future versions of Ruby!/i)
end
end
after :each do ruby_version_is "3.1" do
Warning[:experimental] = @experimental it "does not warn about pattern matching is experimental feature" do
end -> { eval @src }.should_not complain
end
it "warns about pattern matching is experimental feature" do end
-> { eval @src }.should complain(/pattern matching is experimental, and the behavior may change in future versions of Ruby!/i)
end end
end end
end end
@ -444,6 +525,28 @@ ruby_version_is "2.7" do
end end
RUBY RUBY
end end
it "can be used as a nested pattern" do
eval(<<~RUBY).should == true
case [[1], ["2"]]
in [[0] | nil, _]
false
in [[1], [1]]
false
in [[1], [2 | "2"]]
true
end
RUBY
eval(<<~RUBY).should == true
case [1, 2]
in [0, _] | {a: 0}
false
in {a: 1, b: 2} | [1, 2]
true
end
RUBY
end
end end
describe "AS pattern" do describe "AS pattern" do
@ -705,6 +808,28 @@ ruby_version_is "2.7" do
end end
RUBY RUBY
end end
it "can be used as a nested pattern" do
eval(<<~RUBY).should == true
case [[1], ["2"]]
in [[0] | nil, _]
false
in [[1], [1]]
false
in [[1], [2 | "2"]]
true
end
RUBY
eval(<<~RUBY).should == true
case [1, 2]
in [0, _] | {a: 0}
false
in {a: 1, b: 2} | [1, 2]
true
end
RUBY
end
end end
describe "Hash pattern" do describe "Hash pattern" do
@ -1060,6 +1185,30 @@ ruby_version_is "2.7" do
end end
RUBY RUBY
end end
it "can be used as a nested pattern" do
eval(<<~RUBY).should == true
case {a: {a: 1, b: 1}, b: {a: 1, b: 2}}
in {a: {a: 0}}
false
in {a: {a: 1}, b: {b: 1}}
false
in {a: {a: 1}, b: {b: 2}}
true
end
RUBY
eval(<<~RUBY).should == true
case [{a: 1, b: [1]}, {a: 1, c: ["2"]}]
in [{a:, c:},]
false
in [{a: 1, b:}, {a: 1, c: [Integer]}]
false
in [_, {a: 1, c: [String]}]
true
end
RUBY
end
end end
describe "refinements" do describe "refinements" do

View file

@ -217,4 +217,30 @@ describe "A Proc" do
lambda { @l.call(obj) }.should raise_error(TypeError) lambda { @l.call(obj) }.should raise_error(TypeError)
end end
end end
describe "taking |*a, **kw| arguments" do
before :each do
@p = proc { |*a, **kw| [a, kw] }
end
ruby_version_is ""..."2.7" do
it 'autosplats keyword arguments' do
@p.call([1, {a: 1}]).should == [[1], {a: 1}]
end
end
ruby_version_is "2.7"..."3.0" do
it 'autosplats keyword arguments and warns' do
-> {
@p.call([1, {a: 1}]).should == [[1], {a: 1}]
}.should complain(/warning: Using the last argument as keyword parameters is deprecated; maybe \*\* should be added to the call/)
end
end
ruby_version_is "3.0" do
it 'does not autosplat keyword arguments' do
@p.call([1, {a: 1}]).should == [[[1, {a: 1}]], {}]
end
end
end
end end

View file

@ -15,6 +15,12 @@ describe "Literal Ranges" do
(1...).should == Range.new(1, nil, true) (1...).should == Range.new(1, nil, true)
end end
ruby_version_is "3.0" do
it "is frozen" do
(42..).should.frozen?
end
end
ruby_version_is "2.7" do ruby_version_is "2.7" do
it "creates beginless ranges" do it "creates beginless ranges" do
eval("(..1)").should == Range.new(nil, 1) eval("(..1)").should == Range.new(nil, 1)

View file

@ -157,23 +157,6 @@ describe "A constant on a singleton class" do
end end
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 describe "Defining instance methods on a singleton class" do
before :each do before :each do
@k = ClassSpecs::K.new @k = ClassSpecs::K.new

View file

@ -183,5 +183,33 @@ describe "The yield call" do
it "uses captured block of a block used in define_method" do it "uses captured block of a block used in define_method" do
@y.deep(2).should == 4 @y.deep(2).should == 4
end end
end
describe "Using yield in a singleton class literal" 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
ruby_version_is "3.0" do
it 'raises a SyntaxError' do
code = <<~RUBY
class << Object.new
yield
end
RUBY
-> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/)
end
end
end end

View file

@ -77,34 +77,17 @@ describe "IPAddr#new" do
a.family.should == Socket::AF_INET6 a.family.should == Socket::AF_INET6
end end
ruby_version_is ""..."3.1" do it "raises on incorrect IPAddr strings" do
it "raises on incorrect IPAddr strings" do [
[ ["fe80::1%fxp0"],
["fe80::1%fxp0"], ["::1/255.255.255.0"],
["::1/255.255.255.0"], [IPAddr.new("::1").to_i],
[IPAddr.new("::1").to_i], ["::ffff:192.168.1.2/120", Socket::AF_INET],
["::ffff:192.168.1.2/120", Socket::AF_INET], ["[192.168.1.2]/120"],
["[192.168.1.2]/120"], ].each { |args|
].each { |args| ->{
->{ IPAddr.new(*args)
IPAddr.new(*args) }.should raise_error(ArgumentError)
}.should raise_error(ArgumentError) }
}
end
end
ruby_version_is "3.1" do
it "raises on incorrect IPAddr strings" do
[
["::1/255.255.255.0"],
[IPAddr.new("::1").to_i],
["::ffff:192.168.1.2/120", Socket::AF_INET],
["[192.168.1.2]/120"],
].each { |args|
->{
IPAddr.new(*args)
}.should raise_error(ArgumentError)
}
end
end end
end end

View file

@ -0,0 +1,29 @@
require_relative '../../spec_helper'
require 'set'
ruby_version_is "3.0" do
describe "Set#<=>" do
it "returns 0 if the sets are equal" do
(Set[] <=> Set[]).should == 0
(Set[:a, :b, :c] <=> Set[:a, :b, :c]).should == 0
end
it "returns -1 if the set is a proper subset of the other set" do
(Set[] <=> Set[1]).should == -1
(Set[1, 2] <=> Set[1, 2, 3]).should == -1
end
it "returns +1 if the set is a proper superset of other set" do
(Set[1] <=> Set[]).should == +1
(Set[1, 2, 3] <=> Set[1, 2]).should == +1
end
it "returns nil if the set has unique elements" do
(Set[1, 2, 3] <=> Set[:a, :b, :c]).should be_nil
end
it "returns nil when the argument is not set-like" do
(Set[] <=> false).should be_nil
end
end
end

View file

@ -0,0 +1,18 @@
require_relative '../../spec_helper'
require 'set'
describe "Set#initialize_clone" do
ruby_version_is "3.0" do
# See https://bugs.ruby-lang.org/issues/14266
it "does not freeze the new Set when called from clone(freeze: false)" do
set1 = Set[1, 2]
set1.freeze
set2 = set1.clone(freeze: false)
set1.frozen?.should == true
set2.frozen?.should == false
set2.add 3
set1.should == Set[1, 2]
set2.should == Set[1, 2, 3]
end
end
end

View file

@ -0,0 +1,31 @@
require_relative '../../spec_helper'
require 'set'
ruby_version_is "3.0" do
describe "Set#join" do
it "returns an empty string if the Set is empty" do
Set[].join.should == ''
end
it "returns a new string formed by joining elements after conversion" do
set = Set[:a, :b, :c]
set.join.should == "abc"
end
it "does not separate elements when the passed separator is nil" do
set = Set[:a, :b, :c]
set.join(nil).should == "abc"
end
it "returns a string formed by concatenating each element separated by the separator" do
set = Set[:a, :b, :c]
set.join(' | ').should == "a | b | c"
end
it "calls #to_a to convert the Set in to an Array" do
set = Set[:a, :b, :c]
set.should_receive(:to_a).and_return([:a, :b, :c])
set.join.should == "abc"
end
end
end

View file

@ -0,0 +1,22 @@
require_relative '../../../spec_helper'
require 'set'
ruby_version_is "3.0" do
describe "SortedSet" do
it "raises error including message that it has been extracted from the set stdlib" do
-> {
SortedSet
}.should raise_error(RuntimeError) { |e|
e.message.should.include?("The `SortedSet` class has been extracted from the `set` library")
}
end
end
end
ruby_version_is ""..."3.0" do
describe "SortedSet" do
it "is part of the set stdlib" do
SortedSet.superclass.should == Set
end
end
end

View file

@ -1,5 +1,10 @@
require_relative '../spec_helper' require_relative '../spec_helper'
require_relative '../fixtures/classes' require_relative '../fixtures/classes'
require_relative 'shared/new'
describe 'TCPSocket#initialize' do
it_behaves_like :tcpsocket_new, :new
end
describe 'TCPSocket#initialize' do describe 'TCPSocket#initialize' do
SocketSpecs.each_ip_protocol do |family, ip_address| SocketSpecs.each_ip_protocol do |family, ip_address|

View file

@ -1,5 +0,0 @@
require_relative 'shared/new'
describe "TCPSocket.new" do
it_behaves_like :tcpsocket_new, :new
end

View file

@ -14,6 +14,14 @@ describe :tcpsocket_new, shared: true do
} }
end end
ruby_version_is "3.0" do
it 'raises Errno::ETIMEDOUT with :connect_timeout when no server is listening on the given address' do
-> {
TCPSocket.send(@method, "192.0.2.1", 80, connect_timeout: 0)
}.should raise_error(Errno::ETIMEDOUT)
end
end
describe "with a running server" do describe "with a running server" do
before :each do before :each do
@server = SocketSpecs::SpecTCPServer.new @server = SocketSpecs::SpecTCPServer.new
@ -75,5 +83,12 @@ describe :tcpsocket_new, shared: true do
@socket.addr[1].should be_kind_of(Integer) @socket.addr[1].should be_kind_of(Integer)
@socket.addr[2].should =~ /^#{@hostname}/ @socket.addr[2].should =~ /^#{@hostname}/
end end
ruby_version_is "3.0" do
it "connects to a server when passed connect_timeout argument" do
@socket = TCPSocket.send(@method, @hostname, @server.port, connect_timeout: 1)
@socket.should be_an_instance_of(TCPSocket)
end
end
end end
end end

View file

@ -30,7 +30,7 @@ describe "StringIO#ungetbyte" do
io.string.should == 'Shis is a simple string.' io.string.should == 'Shis is a simple string.'
end end
it "ungets the bytes of a string if given a string as an argument" do it "ungets the bytes of a string if given a string as an arugment" do
str = "\u01a9" str = "\u01a9"
io = StringIO.new(str) io = StringIO.new(str)
b = io.getbyte b = io.getbyte

View file

@ -1,7 +1,6 @@
require_relative '../spec_helper' require_relative '../spec_helper'
describe "String#gsub" do describe "String#gsub" do
it "resists CVE-2010-1330 by raising an exception on invalid UTF-8 bytes" do it "resists CVE-2010-1330 by raising an exception on invalid UTF-8 bytes" do
# This original vulnerability talked about KCODE, which is no longer # This original vulnerability talked about KCODE, which is no longer
# used. Instead we are forcing encodings here. But I think the idea is the # used. Instead we are forcing encodings here. But I think the idea is the
@ -17,5 +16,4 @@ describe "String#gsub" do
str.gsub(/</, "&lt;") str.gsub(/</, "&lt;")
}.should raise_error(ArgumentError, /invalid byte sequence in UTF-8/) }.should raise_error(ArgumentError, /invalid byte sequence in UTF-8/)
end end
end end

View file

@ -3,17 +3,13 @@ require_relative '../spec_helper'
require 'json' require 'json'
describe "String#to_f" do describe "String#to_f" do
it "resists CVE-2013-4164 by converting very long Strings to a Float" do it "resists CVE-2013-4164 by converting very long Strings to a Float" do
"1.#{'1'*1000000}".to_f.should be_close(1.1111111111111112, TOLERANCE) "1.#{'1'*1000000}".to_f.should be_close(1.1111111111111112, TOLERANCE)
end end
end end
describe "JSON.parse" do describe "JSON.parse" do
it "resists CVE-2013-4164 by converting very long Strings to a Float" do it "resists CVE-2013-4164 by converting very long Strings to a Float" do
JSON.parse("[1.#{'1'*1000000}]").first.should be_close(1.1111111111111112, TOLERANCE) JSON.parse("[1.#{'1'*1000000}]").first.should be_close(1.1111111111111112, TOLERANCE)
end end
end end

View file

@ -1,6 +1,5 @@
require_relative '../spec_helper' require_relative '../spec_helper'
ruby_version_is ''...'3.0' do ruby_version_is ''...'3.0' do
require 'rexml/document' require 'rexml/document'

View file

@ -1,7 +1,6 @@
require_relative '../spec_helper' require_relative '../spec_helper'
describe "Array#pack" do describe "Array#pack" do
ruby_version_is ''...'2.7' do ruby_version_is ''...'2.7' do
it "resists CVE-2018-16396 by tainting output based on input" do it "resists CVE-2018-16396 by tainting output based on input" do
"aAZBbHhuMmPp".each_char do |f| "aAZBbHhuMmPp".each_char do |f|
@ -9,11 +8,9 @@ describe "Array#pack" do
end end
end end
end end
end end
describe "String#unpack" do describe "String#unpack" do
ruby_version_is ''...'2.7' do ruby_version_is ''...'2.7' do
it "resists CVE-2018-16396 by tainting output based on input" do it "resists CVE-2018-16396 by tainting output based on input" do
"aAZBbHhuMm".each_char do |f| "aAZBbHhuMm".each_char do |f|
@ -21,5 +18,4 @@ describe "String#unpack" do
end end
end end
end end
end end

View file

@ -1,12 +1,10 @@
require_relative '../spec_helper' require_relative '../spec_helper'
describe "String#unpack" do describe "String#unpack" do
it "resists CVE-2018-8778 by raising an exception when a position indicator is larger than a native integer" do it "resists CVE-2018-8778 by raising an exception when a position indicator is larger than a native integer" do
pos = (1 << PlatformGuard::POINTER_SIZE) - 99 pos = (1 << PlatformGuard::POINTER_SIZE) - 99
-> { -> {
"0123456789".unpack("@#{pos}C10") "0123456789".unpack("@#{pos}C10")
}.should raise_error(RangeError, /pack length too big/) }.should raise_error(RangeError, /pack length too big/)
end end
end end

View file

@ -1,36 +1,38 @@
require_relative '../spec_helper' require_relative '../spec_helper'
require 'optparse' platform_is_not :darwin do # frequent timeout/hang on macOS
require 'optparse'
require 'rubygems' require 'rubygems'
require 'rubygems/gemcutter_utilities' require 'rubygems/gemcutter_utilities'
describe "CVE-2019-8323 is resisted by" do describe "CVE-2019-8323 is resisted by" do
describe "sanitising the body" do describe "sanitising the body" do
it "for success codes" do it "for success codes" do
cutter = Class.new { cutter = Class.new {
include Gem::GemcutterUtilities include Gem::GemcutterUtilities
}.new }.new
response = Net::HTTPSuccess.new(nil, nil, nil) response = Net::HTTPSuccess.new(nil, nil, nil)
def response.body def response.body
"\e]2;nyan\a" "\e]2;nyan\a"
end
cutter.should_receive(:say).with(".]2;nyan.")
cutter.with_response response
end end
cutter.should_receive(:say).with(".]2;nyan.")
cutter.with_response response
end
it "for error codes" do it "for error codes" do
cutter = Class.new { cutter = Class.new {
include Gem::GemcutterUtilities include Gem::GemcutterUtilities
}.new }.new
def cutter.terminate_interaction(n) def cutter.terminate_interaction(n)
end
response = Net::HTTPNotFound.new(nil, nil, nil)
def response.body
"\e]2;nyan\a"
end
cutter.should_receive(:say).with(".]2;nyan.")
cutter.with_response response
end end
response = Net::HTTPNotFound.new(nil, nil, nil)
def response.body
"\e]2;nyan\a"
end
cutter.should_receive(:say).with(".]2;nyan.")
cutter.with_response response
end end
end end
end end

View file

@ -1,9 +1,9 @@
require_relative '../spec_helper' require_relative '../spec_helper'
require 'rubygems'
require 'rubygems/command_manager'
platform_is_not :darwin do # frequent timeout/hang on macOS platform_is_not :darwin do # frequent timeout/hang on macOS
require 'rubygems'
require 'rubygems/command_manager'
describe "CVE-2019-8325 is resisted by" do describe "CVE-2019-8325 is resisted by" do
describe "sanitising error message components" do describe "sanitising error message components" do
it "for the 'while executing' message" do it "for the 'while executing' message" do