diff options
author | Benoit Daloze <eregontp@gmail.com> | 2021-10-20 21:41:46 +0200 |
---|---|---|
committer | Benoit Daloze <eregontp@gmail.com> | 2021-10-20 21:41:46 +0200 |
commit | a6c6eef04aaa075f4bbd0eef740d011737afec91 (patch) | |
tree | e8779ca2ddc044899caabca88c16abc9a9054fff /spec/ruby/core | |
parent | 207a5a5bc13018344dc2ab7913fdcaeaeca01292 (diff) | |
download | ruby-a6c6eef04aaa075f4bbd0eef740d011737afec91.tar.gz |
Update to ruby/spec@d6921ef
Diffstat (limited to 'spec/ruby/core')
-rw-r--r-- | spec/ruby/core/array/shared/slice.rb | 217 | ||||
-rw-r--r-- | spec/ruby/core/binding/eval_spec.rb | 16 | ||||
-rw-r--r-- | spec/ruby/core/conditionvariable/broadcast_spec.rb | 40 | ||||
-rw-r--r-- | spec/ruby/core/conditionvariable/marshal_dump_spec.rb | 9 | ||||
-rw-r--r-- | spec/ruby/core/conditionvariable/signal_spec.rb | 77 | ||||
-rw-r--r-- | spec/ruby/core/conditionvariable/wait_spec.rb | 175 | ||||
-rw-r--r-- | spec/ruby/core/dir/shared/glob.rb | 17 | ||||
-rw-r--r-- | spec/ruby/core/env/shift_spec.rb | 39 | ||||
-rw-r--r-- | spec/ruby/core/file/utime_spec.rb | 12 | ||||
-rw-r--r-- | spec/ruby/core/float/comparison_spec.rb | 15 | ||||
-rw-r--r-- | spec/ruby/core/hash/transform_keys_spec.rb | 27 | ||||
-rw-r--r-- | spec/ruby/core/integer/zero_spec.rb | 21 | ||||
-rw-r--r-- | spec/ruby/core/kernel/clone_spec.rb | 92 | ||||
-rw-r--r-- | spec/ruby/core/kernel/eval_spec.rb | 10 | ||||
-rw-r--r-- | spec/ruby/core/kernel/fixtures/classes.rb | 8 | ||||
-rw-r--r-- | spec/ruby/core/module/autoload_spec.rb | 100 | ||||
-rw-r--r-- | spec/ruby/core/module/const_set_spec.rb | 3 | ||||
-rw-r--r-- | spec/ruby/core/module/constants_spec.rb | 6 | ||||
-rw-r--r-- | spec/ruby/core/range/each_spec.rb | 24 |
19 files changed, 778 insertions, 130 deletions
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/core/conditionvariable/broadcast_spec.rb b/spec/ruby/core/conditionvariable/broadcast_spec.rb new file mode 100644 index 0000000000..d88159df23 --- /dev/null +++ b/spec/ruby/core/conditionvariable/broadcast_spec.rb @@ -0,0 +1,40 @@ +require_relative '../../spec_helper' +require 'thread' + +describe "ConditionVariable#broadcast" do + it "releases all threads waiting in line for this resource" do + m = Mutex.new + cv = ConditionVariable.new + threads = [] + r1 = [] + r2 = [] + + # large number to attempt to cause race conditions + 100.times do |i| + threads << Thread.new(i) do |tid| + m.synchronize do + r1 << tid + cv.wait(m) + r2 << tid + end + end + end + + # wait for all threads to acquire the mutex the first time + Thread.pass until m.synchronize { r1.size == threads.size } + # wait until all threads are sleeping (ie waiting) + Thread.pass until threads.all?(&:stop?) + + r2.should be_empty + m.synchronize do + cv.broadcast + end + + threads.each {|t| t.join } + + # ensure that all threads that enter cv.wait are released + r2.sort.should == r1.sort + # note that order is not specified as broadcast results in a race + # condition on regaining the lock m + end +end diff --git a/spec/ruby/core/conditionvariable/marshal_dump_spec.rb b/spec/ruby/core/conditionvariable/marshal_dump_spec.rb new file mode 100644 index 0000000000..f951a13e28 --- /dev/null +++ b/spec/ruby/core/conditionvariable/marshal_dump_spec.rb @@ -0,0 +1,9 @@ +require_relative '../../spec_helper' +require 'thread' + +describe "ConditionVariable#marshal_dump" do + it "raises a TypeError" do + cv = ConditionVariable.new + -> { cv.marshal_dump }.should raise_error(TypeError, /can't dump/) + end +end diff --git a/spec/ruby/core/conditionvariable/signal_spec.rb b/spec/ruby/core/conditionvariable/signal_spec.rb new file mode 100644 index 0000000000..86383073f1 --- /dev/null +++ b/spec/ruby/core/conditionvariable/signal_spec.rb @@ -0,0 +1,77 @@ +require_relative '../../spec_helper' +require 'thread' + +describe "ConditionVariable#signal" do + it "releases the first thread waiting in line for this resource" do + m = Mutex.new + cv = ConditionVariable.new + threads = [] + r1 = [] + r2 = [] + + # large number to attempt to cause race conditions + 100.times do |i| + threads << Thread.new(i) do |tid| + m.synchronize do + r1 << tid + cv.wait(m) + r2 << tid + end + end + end + + # wait for all threads to acquire the mutex the first time + Thread.pass until m.synchronize { r1.size == threads.size } + # wait until all threads are sleeping (ie waiting) + Thread.pass until threads.all?(&:stop?) + + r2.should be_empty + 100.times do |i| + m.synchronize do + cv.signal + end + Thread.pass until r2.size == i+1 + end + + threads.each {|t| t.join } + + # ensure that all the threads that went into the cv.wait are + # released in the same order + r2.should == r1 + end + + it "allows control to be passed between a pair of threads" do + m = Mutex.new + cv = ConditionVariable.new + repeats = 100 + in_synchronize = false + + t1 = Thread.new do + m.synchronize do + in_synchronize = true + repeats.times do + cv.wait(m) + cv.signal + end + end + end + + # Make sure t1 is waiting for a signal before launching t2. + Thread.pass until in_synchronize + Thread.pass until t1.stop? + + t2 = Thread.new do + m.synchronize do + repeats.times do + cv.signal + cv.wait(m) + end + end + end + + # Check that both threads terminated without exception + t1.join + t2.join + m.should_not.locked? + end +end diff --git a/spec/ruby/core/conditionvariable/wait_spec.rb b/spec/ruby/core/conditionvariable/wait_spec.rb new file mode 100644 index 0000000000..9a68c2b5a1 --- /dev/null +++ b/spec/ruby/core/conditionvariable/wait_spec.rb @@ -0,0 +1,175 @@ +require_relative '../../spec_helper' +require 'thread' + +describe "ConditionVariable#wait" do + it "calls #sleep on the given object" do + o = Object.new + o.should_receive(:sleep).with(1234) + + cv = ConditionVariable.new + + cv.wait(o, 1234) + end + + it "can be woken up by ConditionVariable#signal" do + m = Mutex.new + cv = ConditionVariable.new + in_synchronize = false + + th = Thread.new do + m.synchronize do + in_synchronize = true + cv.wait(m) + end + :success + end + + # wait for m to acquire the mutex + Thread.pass until in_synchronize + # wait until th is sleeping (ie waiting) + Thread.pass until th.stop? + + m.synchronize { cv.signal } + th.value.should == :success + end + + it "can be interrupted by Thread#run" do + m = Mutex.new + cv = ConditionVariable.new + in_synchronize = false + + th = Thread.new do + m.synchronize do + in_synchronize = true + cv.wait(m) + end + :success + end + + # wait for m to acquire the mutex + Thread.pass until in_synchronize + # wait until th is sleeping (ie waiting) + Thread.pass until th.stop? + + th.run + th.value.should == :success + end + + it "can be interrupted by Thread#wakeup" do + m = Mutex.new + cv = ConditionVariable.new + in_synchronize = false + + th = Thread.new do + m.synchronize do + in_synchronize = true + cv.wait(m) + end + :success + end + + # wait for m to acquire the mutex + Thread.pass until in_synchronize + # wait until th is sleeping (ie waiting) + Thread.pass until th.stop? + + th.wakeup + th.value.should == :success + end + + it "reacquires the lock even if the thread is killed" do + m = Mutex.new + cv = ConditionVariable.new + in_synchronize = false + owned = nil + + th = Thread.new do + m.synchronize do + in_synchronize = true + begin + cv.wait(m) + ensure + owned = m.owned? + $stderr.puts "\nThe Thread doesn't own the Mutex!" unless owned + end + end + end + + # wait for m to acquire the mutex + Thread.pass until in_synchronize + # wait until th is sleeping (ie waiting) + Thread.pass until th.stop? + + th.kill + th.join + + owned.should == true + end + + it "reacquires the lock even if the thread is killed after being signaled" do + m = Mutex.new + cv = ConditionVariable.new + in_synchronize = false + owned = nil + + th = Thread.new do + m.synchronize do + in_synchronize = true + begin + cv.wait(m) + ensure + owned = m.owned? + $stderr.puts "\nThe Thread doesn't own the Mutex!" unless owned + end + end + end + + # wait for m to acquire the mutex + Thread.pass until in_synchronize + # wait until th is sleeping (ie waiting) + Thread.pass until th.stop? + + m.synchronize { + cv.signal + # Wait that the thread is blocked on acquiring the Mutex + sleep 0.001 + # Kill the thread, yet the thread should first acquire the Mutex before going on + th.kill + } + + th.join + owned.should == true + end + + it "supports multiple Threads waiting on the same ConditionVariable and Mutex" do + m = Mutex.new + cv = ConditionVariable.new + n_threads = 4 + events = [] + + threads = n_threads.times.map { + Thread.new { + m.synchronize { + events << :t_in_synchronize + cv.wait(m) + } + } + } + + Thread.pass until m.synchronize { events.size } == n_threads + Thread.pass until threads.any?(&:stop?) + m.synchronize do + threads.each { |t| + # Cause interactions with the waiting threads. + # On TruffleRuby, this causes a safepoint which has interesting + # interactions with the ConditionVariable. + bt = t.backtrace + bt.should be_kind_of(Array) + bt.size.should >= 2 + } + end + + cv.broadcast + threads.each(&:join) + end +end 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 @@ -2,36 +2,13 @@ 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 @external = Encoding.default_external @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 + describe "with no arguments" do + it "copies frozen state from the original" do + o2 = @obj.clone + @obj.freeze + o3 = @obj.clone + + o2.should_not.frozen? + o3.should.frozen? + end - o2.should_not.frozen? - o3.should.frozen? + it 'copies frozen?' do + o = ''.freeze.clone + o.frozen?.should be_true + end 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 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 - 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: 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 - - 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_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 + 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 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 - } - - 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 + 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 + } - 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 |