diff --git a/spec/ruby/command_line/backtrace_limit_spec.rb b/spec/ruby/command_line/backtrace_limit_spec.rb new file mode 100644 index 0000000000..c0b243841e --- /dev/null +++ b/spec/ruby/command_line/backtrace_limit_spec.rb @@ -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 `
' + MSG + end + end +end diff --git a/spec/ruby/command_line/fixtures/backtrace.rb b/spec/ruby/command_line/fixtures/backtrace.rb new file mode 100644 index 0000000000..19ad638d35 --- /dev/null +++ b/spec/ruby/command_line/fixtures/backtrace.rb @@ -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 diff --git a/spec/ruby/core/array/shared/slice.rb b/spec/ruby/core/array/shared/slice.rb index 19e1a3ee2b..cf195ad7a4 100644 --- a/spec/ruby/core/array/shared/slice.rb +++ b/spec/ruby/core/array/shared/slice.rb @@ -517,8 +517,9 @@ describe :array_slice, shared: true do end 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) - -> { "hello".send(@method, 0..bignum_value) }.should raise_error(RangeError) + array = [1, 2, 3, 4, 5, 6] + -> { array.send(@method, bignum_value..(bignum_value + 1)) }.should raise_error(RangeError) + -> { array.send(@method, 0..bignum_value) }.should raise_error(RangeError) end it "can accept endless ranges" do @@ -533,6 +534,218 @@ describe :array_slice, shared: true do a.send(@method, eval("(-9...)")).should == nil 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 it "can accept beginless ranges" do a = [0, 1, 2, 3, 4, 5] diff --git a/spec/ruby/core/binding/eval_spec.rb b/spec/ruby/core/binding/eval_spec.rb index b36bec799e..4bb3da7a6c 100644 --- a/spec/ruby/core/binding/eval_spec.rb +++ b/spec/ruby/core/binding/eval_spec.rb @@ -93,16 +93,28 @@ describe "Binding#eval" do it "inherits __FILE__ from the enclosing scope" do obj = BindingSpecs::Demo.new(1) 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 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) bind = obj.get_binding bind.eval("__FILE__").should == '(eval)' 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 it "uses the __FILE__ that is passed in" do diff --git a/spec/ruby/library/conditionvariable/broadcast_spec.rb b/spec/ruby/core/conditionvariable/broadcast_spec.rb similarity index 100% rename from spec/ruby/library/conditionvariable/broadcast_spec.rb rename to spec/ruby/core/conditionvariable/broadcast_spec.rb diff --git a/spec/ruby/library/conditionvariable/marshal_dump_spec.rb b/spec/ruby/core/conditionvariable/marshal_dump_spec.rb similarity index 100% rename from spec/ruby/library/conditionvariable/marshal_dump_spec.rb rename to spec/ruby/core/conditionvariable/marshal_dump_spec.rb diff --git a/spec/ruby/library/conditionvariable/signal_spec.rb b/spec/ruby/core/conditionvariable/signal_spec.rb similarity index 100% rename from spec/ruby/library/conditionvariable/signal_spec.rb rename to spec/ruby/core/conditionvariable/signal_spec.rb diff --git a/spec/ruby/library/conditionvariable/wait_spec.rb b/spec/ruby/core/conditionvariable/wait_spec.rb similarity index 100% rename from spec/ruby/library/conditionvariable/wait_spec.rb rename to spec/ruby/core/conditionvariable/wait_spec.rb diff --git a/spec/ruby/core/dir/shared/glob.rb b/spec/ruby/core/dir/shared/glob.rb index d2824df446..173038ee24 100644 --- a/spec/ruby/core/dir/shared/glob.rb +++ b/spec/ruby/core/dir/shared/glob.rb @@ -38,6 +38,23 @@ describe :dir_glob, shared: true do 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 expected = %w[ brace diff --git a/spec/ruby/core/env/shift_spec.rb b/spec/ruby/core/env/shift_spec.rb index 8717b7220e..1b92e5d1e4 100644 --- a/spec/ruby/core/env/shift_spec.rb +++ b/spec/ruby/core/env/shift_spec.rb @@ -1,30 +1,6 @@ require_relative '../../spec_helper' 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 before :each do @orig = ENV.to_hash @@ -32,6 +8,7 @@ describe "ENV.shift" do @internal = Encoding.default_internal Encoding.default_external = Encoding::BINARY + ENV.replace({"FOO"=>"BAR"}) end after :each do @@ -40,6 +17,18 @@ describe "ENV.shift" do ENV.replace @orig 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 Encoding.default_internal = nil @@ -52,9 +41,7 @@ describe "ENV.shift" do Encoding.default_internal = Encoding::IBM437 pair = ENV.shift - pair.first.encode(Encoding::IBM437) rescue next pair.first.encoding.should equal(Encoding::IBM437) - pair.last.encode(Encoding::IBM437) rescue next pair.last.encoding.should equal(Encoding::IBM437) end end diff --git a/spec/ruby/core/file/utime_spec.rb b/spec/ruby/core/file/utime_spec.rb index 59eef20c66..45a51490e4 100644 --- a/spec/ruby/core/file/utime_spec.rb +++ b/spec/ruby/core/file/utime_spec.rb @@ -70,11 +70,13 @@ describe "File.utime" do end end - it "may set nanosecond precision" do - t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r) - File.utime(t, t, @file1) - File.atime(@file1).nsec.should.between?(0, 123500000) - File.mtime(@file1).nsec.should.between?(0, 123500000) + platform_is_not :windows do + it "sets nanosecond precision" do + t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r) + File.utime(t, t, @file1) + File.atime(@file1).nsec.should == 123456789 + File.mtime(@file1).nsec.should == 123456789 + end end platform_is :linux do diff --git a/spec/ruby/core/float/comparison_spec.rb b/spec/ruby/core/float/comparison_spec.rb index a46841ffbf..53e7ec332a 100644 --- a/spec/ruby/core/float/comparison_spec.rb +++ b/spec/ruby/core/float/comparison_spec.rb @@ -90,4 +90,19 @@ describe "Float#<=>" do end (infinity_value <=> obj).should == 1 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 diff --git a/spec/ruby/core/hash/transform_keys_spec.rb b/spec/ruby/core/hash/transform_keys_spec.rb index 70a4cdde36..8ee1a2cd6d 100644 --- a/spec/ruby/core/hash/transform_keys_spec.rb +++ b/spec/ruby/core/hash/transform_keys_spec.rb @@ -42,6 +42,20 @@ describe "Hash#transform_keys" do r.keys.should == [:xfoo] r.class.should == Hash 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 describe "Hash#transform_keys!" do @@ -98,6 +112,13 @@ describe "Hash#transform_keys!" do 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 before :each do @hash.freeze @@ -112,6 +133,12 @@ describe "Hash#transform_keys!" do @hash.should == @initial_pairs 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 it "does not raise an exception" do @hash.transform_keys!.should be_an_instance_of(Enumerator) diff --git a/spec/ruby/core/integer/zero_spec.rb b/spec/ruby/core/integer/zero_spec.rb new file mode 100644 index 0000000000..2dac50c406 --- /dev/null +++ b/spec/ruby/core/integer/zero_spec.rb @@ -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 diff --git a/spec/ruby/core/kernel/clone_spec.rb b/spec/ruby/core/kernel/clone_spec.rb index c18af4a490..f9daa2badc 100644 --- a/spec/ruby/core/kernel/clone_spec.rb +++ b/spec/ruby/core/kernel/clone_spec.rb @@ -28,27 +28,88 @@ describe "Kernel#clone" do clone.class.should equal klass end - it "copies frozen state from the original" do - o2 = @obj.clone - @obj.freeze - 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? + describe "with no arguments" do + it "copies frozen state from the original" do + o2 = @obj.clone @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 - it 'takes an freeze: false option to not return frozen copy' do - @obj.clone(freeze: false).should_not.frozen? - @obj.freeze - @obj.clone(freeze: false).should_not.frozen? + describe "with freeze: true" do + it 'makes a frozen copy if the original is frozen' do + @obj.freeze + @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 it "copies instance variables" do @@ -114,11 +175,6 @@ describe "Kernel#clone" do cloned.bar.should == ['a'] end - it 'copies frozen?' do - o = ''.freeze.clone - o.frozen?.should be_true - end - ruby_version_is ''...'2.7' do it 'copies tainted?' do o = ''.taint.clone diff --git a/spec/ruby/core/kernel/eval_spec.rb b/spec/ruby/core/kernel/eval_spec.rb index 1e2764a4de..9be0f2dfd3 100644 --- a/spec/ruby/core/kernel/eval_spec.rb +++ b/spec/ruby/core/kernel/eval_spec.rb @@ -168,6 +168,12 @@ describe "Kernel#eval" do suppress_warning {eval("eval '__FILE__', binding", binding)}.should == __FILE__ suppress_warning {eval("eval '__FILE__', binding", binding, 'success')}.should == 'success' 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 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, 'success'", binding).should == 'success' end + + it 'uses (eval) for __FILE__ and 1 for __LINE__ with a binding argument' do + eval("[__FILE__, __LINE__]", binding).should == ["(eval)", 1] + end end # Found via Rubinius bug github:#149 diff --git a/spec/ruby/core/kernel/fixtures/classes.rb b/spec/ruby/core/kernel/fixtures/classes.rb index 1bf5715c50..8de1407b92 100644 --- a/spec/ruby/core/kernel/fixtures/classes.rb +++ b/spec/ruby/core/kernel/fixtures/classes.rb @@ -288,7 +288,13 @@ module KernelSpecs class Clone 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 diff --git a/spec/ruby/core/module/autoload_spec.rb b/spec/ruby/core/module/autoload_spec.rb index 1d61646db5..f17675846b 100644 --- a/spec/ruby/core/module/autoload_spec.rb +++ b/spec/ruby/core/module/autoload_spec.rb @@ -441,42 +441,21 @@ describe "Module#autoload" do ScratchPad.recorded.should == [:raise, :raise] end - ruby_version_is "3.1" do - it "removes the constant from Module#constants if the loaded file does not define it" do - path = fixture(__FILE__, "autoload_o.rb") - ScratchPad.record [] - ModuleSpecs::Autoload.autoload :O, path + 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.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::O }.should raise_error(NameError) - ModuleSpecs::Autoload.const_defined?(:O).should == false - ModuleSpecs::Autoload.should_not have_constant(:O) - ModuleSpecs::Autoload.autoload?(:O).should == nil - -> { 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 + ModuleSpecs::Autoload.should have_constant(:O) + ModuleSpecs::Autoload.const_defined?(:O).should == false + ModuleSpecs::Autoload.autoload?(:O).should == nil + -> { ModuleSpecs::Autoload.const_get(:O) }.should raise_error(NameError) end 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 self.autoload?(:DeclaredInParentDefinedInCurrent).should == nil const_defined?(:DeclaredInParentDefinedInCurrent).should == false + self.should have_constant(:DeclaredInParentDefinedInCurrent) -> { DeclaredInParentDefinedInCurrent }.should raise_error(NameError) ModuleSpecs::Autoload::LexicalScope.send(:remove_const, :DeclaredInParentDefinedInCurrent) end end - ruby_version_is "3.1" do - it "looks up in parent scope after failed autoload" do - @remove << :DeclaredInCurrentDefinedInParent - module ModuleSpecs::Autoload - ScratchPad.record -> { - DeclaredInCurrentDefinedInParent = :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_not raise_error(NameError) - # Basically, the autoload constant remains in a "undefined" state - self.autoload?(:DeclaredInCurrentDefinedInParent).should == nil - const_defined?(:DeclaredInCurrentDefinedInParent).should == false - -> { const_get(:DeclaredInCurrentDefinedInParent) }.should raise_error(NameError) - end - - DeclaredInCurrentDefinedInParent.should == :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 - 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 - @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 + DeclaredInCurrentDefinedInParent.should == :declared_in_current_defined_in_parent end end diff --git a/spec/ruby/core/module/const_set_spec.rb b/spec/ruby/core/module/const_set_spec.rb index ba7810d17b..b537d3f133 100644 --- a/spec/ruby/core/module/const_set_spec.rb +++ b/spec/ruby/core/module/const_set_spec.rb @@ -101,7 +101,7 @@ describe "Module#const_set" do mod.const_get(:Foo).should == 1 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") ScratchPad.record [] mod = Module.new @@ -109,6 +109,7 @@ describe "Module#const_set" do mod.autoload :Foo, path -> { mod::Foo }.should raise_error(NameError) + mod.should have_constant(:Foo) mod.const_defined?(:Foo).should == false mod.autoload?(:Foo).should == nil diff --git a/spec/ruby/core/module/constants_spec.rb b/spec/ruby/core/module/constants_spec.rb index fe95143872..330da1cc88 100644 --- a/spec/ruby/core/module/constants_spec.rb +++ b/spec/ruby/core/module/constants_spec.rb @@ -74,6 +74,12 @@ describe "Module#constants" do it "returns only public constants" do ModuleSpecs::PrivConstModule.constants.should == [:PUBLIC_CONSTANT] 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 describe "Module#constants" do diff --git a/spec/ruby/core/range/each_spec.rb b/spec/ruby/core/range/each_spec.rb index bd4bbb82e5..6b33f57737 100644 --- a/spec/ruby/core/range/each_spec.rb +++ b/spec/ruby/core/range/each_spec.rb @@ -84,9 +84,27 @@ describe "Range#each" do enum.to_a.should == [1, 2, 3] end - it "raises a TypeError if the first element is a Time object" do - t = Time.now - -> { (t..t+1).each { |i| i } }.should raise_error(TypeError) + ruby_version_is "3.1" do + it "supports Time objects that respond 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).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 it "passes each Symbol element by using #succ" do diff --git a/spec/ruby/language/hash_spec.rb b/spec/ruby/language/hash_spec.rb index f99ff8ab3f..2f8b97199a 100644 --- a/spec/ruby/language/hash_spec.rb +++ b/spec/ruby/language/hash_spec.rb @@ -225,4 +225,43 @@ describe "The ** operator" do h.should == { two: 2 } 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 diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb index 449d5e6f1b..0a5bb99d0b 100644 --- a/spec/ruby/language/method_spec.rb +++ b/spec/ruby/language/method_spec.rb @@ -1861,15 +1861,158 @@ describe "An array-dereference method ([])" do end end -ruby_version_is '3.0' do +ruby_version_is "3.0" do describe "An endless method definition" do - evaluate <<-ruby do - def m(a) = a - ruby + context "without arguments" do + evaluate <<-ruby do + def m() = 42 + ruby - a = b = m 1 - a.should == 1 - b.should == 1 + m.should == 42 + end + 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 diff --git a/spec/ruby/language/pattern_matching_spec.rb b/spec/ruby/language/pattern_matching_spec.rb index c6a3008458..e4abae9412 100644 --- a/spec/ruby/language/pattern_matching_spec.rb +++ b/spec/ruby/language/pattern_matching_spec.rb @@ -18,6 +18,63 @@ ruby_version_is "2.7" do RUBY 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 it "extends case expression with case/in construction" do @@ -41,25 +98,49 @@ ruby_version_is "2.7" do end describe "warning" do - ruby_version_is ""..."3.1" do - before :each do - ruby_version_is ""..."3.0" do - @src = 'case [0, 1]; in [a, b]; end' - end + before :each do + @experimental, Warning[:experimental] = Warning[:experimental], true + 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]' end - @experimental, Warning[:experimental] = Warning[:experimental], true - end + ruby_version_is ""..."3.1" 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 - after :each do - Warning[:experimental] = @experimental - 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) + ruby_version_is "3.1" do + it "does not warn about pattern matching is experimental feature" do + -> { eval @src }.should_not complain + end + end end end end @@ -444,6 +525,28 @@ ruby_version_is "2.7" do end RUBY 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 describe "AS pattern" do @@ -705,6 +808,28 @@ ruby_version_is "2.7" do end RUBY 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 describe "Hash pattern" do @@ -1060,6 +1185,30 @@ ruby_version_is "2.7" do end RUBY 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 describe "refinements" do diff --git a/spec/ruby/language/proc_spec.rb b/spec/ruby/language/proc_spec.rb index c44e711d2b..ef4a43bed6 100644 --- a/spec/ruby/language/proc_spec.rb +++ b/spec/ruby/language/proc_spec.rb @@ -217,4 +217,30 @@ describe "A Proc" do lambda { @l.call(obj) }.should raise_error(TypeError) 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 diff --git a/spec/ruby/language/range_spec.rb b/spec/ruby/language/range_spec.rb index 79500c6b33..4cde7e9488 100644 --- a/spec/ruby/language/range_spec.rb +++ b/spec/ruby/language/range_spec.rb @@ -15,6 +15,12 @@ describe "Literal Ranges" do (1...).should == Range.new(1, nil, true) end + ruby_version_is "3.0" do + it "is frozen" do + (42..).should.frozen? + end + end + ruby_version_is "2.7" do it "creates beginless ranges" do eval("(..1)").should == Range.new(nil, 1) diff --git a/spec/ruby/language/singleton_class_spec.rb b/spec/ruby/language/singleton_class_spec.rb index 7e9370d6f9..c1fb682ea0 100644 --- a/spec/ruby/language/singleton_class_spec.rb +++ b/spec/ruby/language/singleton_class_spec.rb @@ -157,23 +157,6 @@ describe "A constant on a singleton class" do end end -describe "Defining yield in singleton class" do - ruby_version_is "2.7"..."3.0" do - it 'emits a deprecation warning' do - code = <<~RUBY - def m - class << Object.new - yield - end - end - m { :ok } - RUBY - - -> { eval(code) }.should complain(/warning: `yield' in class syntax will not be supported from Ruby 3.0/) - end - end -end - describe "Defining instance methods on a singleton class" do before :each do @k = ClassSpecs::K.new diff --git a/spec/ruby/language/yield_spec.rb b/spec/ruby/language/yield_spec.rb index 5fad7cb176..3db6d353a9 100644 --- a/spec/ruby/language/yield_spec.rb +++ b/spec/ruby/language/yield_spec.rb @@ -183,5 +183,33 @@ describe "The yield call" do it "uses captured block of a block used in define_method" do @y.deep(2).should == 4 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 diff --git a/spec/ruby/library/ipaddr/new_spec.rb b/spec/ruby/library/ipaddr/new_spec.rb index 053928c3cf..3148d6e39c 100644 --- a/spec/ruby/library/ipaddr/new_spec.rb +++ b/spec/ruby/library/ipaddr/new_spec.rb @@ -77,34 +77,17 @@ describe "IPAddr#new" do a.family.should == Socket::AF_INET6 end - ruby_version_is ""..."3.1" do - it "raises on incorrect IPAddr strings" do - [ - ["fe80::1%fxp0"], - ["::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 - - 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 + it "raises on incorrect IPAddr strings" do + [ + ["fe80::1%fxp0"], + ["::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 diff --git a/spec/ruby/library/set/comparison_spec.rb b/spec/ruby/library/set/comparison_spec.rb new file mode 100644 index 0000000000..b851ea3d57 --- /dev/null +++ b/spec/ruby/library/set/comparison_spec.rb @@ -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 diff --git a/spec/ruby/library/set/initialize_clone_spec.rb b/spec/ruby/library/set/initialize_clone_spec.rb new file mode 100644 index 0000000000..62985987fa --- /dev/null +++ b/spec/ruby/library/set/initialize_clone_spec.rb @@ -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 diff --git a/spec/ruby/library/set/join_spec.rb b/spec/ruby/library/set/join_spec.rb new file mode 100644 index 0000000000..7498a91d98 --- /dev/null +++ b/spec/ruby/library/set/join_spec.rb @@ -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 diff --git a/spec/ruby/library/set/sortedset/sortedset_spec.rb b/spec/ruby/library/set/sortedset/sortedset_spec.rb new file mode 100644 index 0000000000..3ead5495fc --- /dev/null +++ b/spec/ruby/library/set/sortedset/sortedset_spec.rb @@ -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 diff --git a/spec/ruby/library/socket/tcpsocket/initialize_spec.rb b/spec/ruby/library/socket/tcpsocket/initialize_spec.rb index a3cee05412..065c8f4190 100644 --- a/spec/ruby/library/socket/tcpsocket/initialize_spec.rb +++ b/spec/ruby/library/socket/tcpsocket/initialize_spec.rb @@ -1,5 +1,10 @@ require_relative '../spec_helper' require_relative '../fixtures/classes' +require_relative 'shared/new' + +describe 'TCPSocket#initialize' do + it_behaves_like :tcpsocket_new, :new +end describe 'TCPSocket#initialize' do SocketSpecs.each_ip_protocol do |family, ip_address| diff --git a/spec/ruby/library/socket/tcpsocket/new_spec.rb b/spec/ruby/library/socket/tcpsocket/new_spec.rb deleted file mode 100644 index 4924468be7..0000000000 --- a/spec/ruby/library/socket/tcpsocket/new_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require_relative 'shared/new' - -describe "TCPSocket.new" do - it_behaves_like :tcpsocket_new, :new -end diff --git a/spec/ruby/library/socket/tcpsocket/shared/new.rb b/spec/ruby/library/socket/tcpsocket/shared/new.rb index 5ca3a0e6cc..4189acc2f8 100644 --- a/spec/ruby/library/socket/tcpsocket/shared/new.rb +++ b/spec/ruby/library/socket/tcpsocket/shared/new.rb @@ -14,6 +14,14 @@ describe :tcpsocket_new, shared: true do } 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 before :each do @server = SocketSpecs::SpecTCPServer.new @@ -75,5 +83,12 @@ describe :tcpsocket_new, shared: true do @socket.addr[1].should be_kind_of(Integer) @socket.addr[2].should =~ /^#{@hostname}/ 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 diff --git a/spec/ruby/library/stringio/ungetbyte_spec.rb b/spec/ruby/library/stringio/ungetbyte_spec.rb index 2f082acbf6..eb1cc55220 100644 --- a/spec/ruby/library/stringio/ungetbyte_spec.rb +++ b/spec/ruby/library/stringio/ungetbyte_spec.rb @@ -30,7 +30,7 @@ describe "StringIO#ungetbyte" do io.string.should == 'Shis is a simple string.' 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" io = StringIO.new(str) b = io.getbyte diff --git a/spec/ruby/security/cve_2010_1330_spec.rb b/spec/ruby/security/cve_2010_1330_spec.rb index fa4c756c6d..33e88d652e 100644 --- a/spec/ruby/security/cve_2010_1330_spec.rb +++ b/spec/ruby/security/cve_2010_1330_spec.rb @@ -1,7 +1,6 @@ require_relative '../spec_helper' describe "String#gsub" 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 # used. Instead we are forcing encodings here. But I think the idea is the @@ -17,5 +16,4 @@ describe "String#gsub" do str.gsub(/ { "0123456789".unpack("@#{pos}C10") }.should raise_error(RangeError, /pack length too big/) end - end diff --git a/spec/ruby/security/cve_2019_8323_spec.rb b/spec/ruby/security/cve_2019_8323_spec.rb index d4606de054..3632d3b028 100644 --- a/spec/ruby/security/cve_2019_8323_spec.rb +++ b/spec/ruby/security/cve_2019_8323_spec.rb @@ -1,36 +1,38 @@ require_relative '../spec_helper' -require 'optparse' +platform_is_not :darwin do # frequent timeout/hang on macOS + require 'optparse' -require 'rubygems' -require 'rubygems/gemcutter_utilities' + require 'rubygems' + require 'rubygems/gemcutter_utilities' -describe "CVE-2019-8323 is resisted by" do - describe "sanitising the body" do - it "for success codes" do - cutter = Class.new { - include Gem::GemcutterUtilities - }.new - response = Net::HTTPSuccess.new(nil, nil, nil) - def response.body - "\e]2;nyan\a" + describe "CVE-2019-8323 is resisted by" do + describe "sanitising the body" do + it "for success codes" do + cutter = Class.new { + include Gem::GemcutterUtilities + }.new + response = Net::HTTPSuccess.new(nil, nil, nil) + def response.body + "\e]2;nyan\a" + end + cutter.should_receive(:say).with(".]2;nyan.") + cutter.with_response response end - cutter.should_receive(:say).with(".]2;nyan.") - cutter.with_response response - end - it "for error codes" do - cutter = Class.new { - include Gem::GemcutterUtilities - }.new - def cutter.terminate_interaction(n) + it "for error codes" do + cutter = Class.new { + include Gem::GemcutterUtilities + }.new + 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 - 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 diff --git a/spec/ruby/security/cve_2019_8325_spec.rb b/spec/ruby/security/cve_2019_8325_spec.rb index 935f127d7f..309445a50f 100644 --- a/spec/ruby/security/cve_2019_8325_spec.rb +++ b/spec/ruby/security/cve_2019_8325_spec.rb @@ -1,9 +1,9 @@ require_relative '../spec_helper' -require 'rubygems' -require 'rubygems/command_manager' - 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 "sanitising error message components" do it "for the 'while executing' message" do