diff options
Diffstat (limited to 'spec')
34 files changed, 747 insertions, 316 deletions
diff --git a/spec/ruby/.travis.yml b/spec/ruby/.travis.yml index 35fea82656..a66b393d4c 100644 --- a/spec/ruby/.travis.yml +++ b/spec/ruby/.travis.yml @@ -10,13 +10,16 @@ matrix: env: MSPEC_OPTS="-R2 -ff" - rvm: 2.3.8 - rvm: 2.4.5 + env: CHECK_LEAKS=true - rvm: 2.5.3 env: CHECK_LEAKS=true + - rvm: 2.6.0 + env: CHECK_LEAKS=true - rvm: ruby-head - env: RUBOCOP=true rvm: 2.4.5 script: - - gem install rubocop -v 0.54.0 + - gem install rubocop:0.61.0 - rubocop allow_failures: - rvm: ruby-head diff --git a/spec/ruby/core/array/difference_spec.rb b/spec/ruby/core/array/difference_spec.rb new file mode 100644 index 0000000000..33ea8e1f3c --- /dev/null +++ b/spec/ruby/core/array/difference_spec.rb @@ -0,0 +1,24 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'shared/difference' + +ruby_version_is "2.6" do + describe "Array#difference" do + it_behaves_like :array_binary_difference, :- + + it "returns a copy when called without any parameter" do + x = [1, 2, 3, 2] + x.difference.should == x + x.difference.should_not equal x + end + + it "does not return subclass instances for Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].difference.should be_an_instance_of(Array) + end + + it "accepts multiple arguments" do + x = [1, 2, 3, 1] + x.difference([], [0, 1], [3, 4], [3]).should == [2] + end + end +end diff --git a/spec/ruby/core/array/minus_spec.rb b/spec/ruby/core/array/minus_spec.rb index 8cb6bb323c..cb1bf56d76 100644 --- a/spec/ruby/core/array/minus_spec.rb +++ b/spec/ruby/core/array/minus_spec.rb @@ -1,87 +1,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/difference' describe "Array#-" do - it "creates an array minus any items from other array" do - ([] - [ 1, 2, 4 ]).should == [] - ([1, 2, 4] - []).should == [1, 2, 4] - ([ 1, 2, 3, 4, 5 ] - [ 1, 2, 4 ]).should == [3, 5] - end - - it "removes multiple items on the lhs equal to one on the rhs" do - ([1, 1, 2, 2, 3, 3, 4, 5] - [1, 2, 4]).should == [3, 3, 5] - end - - it "properly handles recursive arrays" do - empty = ArraySpecs.empty_recursive_array - (empty - empty).should == [] - - ([] - ArraySpecs.recursive_array).should == [] - - array = ArraySpecs.recursive_array - (array - array).should == [] - end - - it "tries to convert the passed arguments to Arrays using #to_ary" do - obj = mock('[2,3,3,4]') - obj.should_receive(:to_ary).and_return([2, 3, 3, 4]) - ([1, 1, 2, 2, 3, 4] - obj).should == [1, 1] - end - - it "raises a TypeError if the argument cannot be coerced to an Array by calling #to_ary" do - obj = mock('not an array') - lambda { [1, 2, 3] - obj }.should raise_error(TypeError) - end - - it "does not return subclass instance for Array subclasses" do - (ArraySpecs::MyArray[1, 2, 3] - []).should be_an_instance_of(Array) - (ArraySpecs::MyArray[1, 2, 3] - ArraySpecs::MyArray[]).should be_an_instance_of(Array) - ([1, 2, 3] - ArraySpecs::MyArray[]).should be_an_instance_of(Array) - end - - it "does not call to_ary on array subclasses" do - ([5, 6, 7] - ArraySpecs::ToAryArray[7]).should == [5, 6] - end - - it "removes an item identified as equivalent via #hash and #eql?" do - obj1 = mock('1') - obj2 = mock('2') - obj1.stub!(:hash).and_return(0) - obj2.stub!(:hash).and_return(0) - obj1.should_receive(:eql?).at_least(1).and_return(true) - - ([obj1] - [obj2]).should == [] - ([obj1, obj1, obj2, obj2] - [obj2]).should == [] - end - - it "doesn't remove an item with the same hash but not #eql?" do - obj1 = mock('1') - obj2 = mock('2') - obj1.stub!(:hash).and_return(0) - obj2.stub!(:hash).and_return(0) - obj1.should_receive(:eql?).at_least(1).and_return(false) - - ([obj1] - [obj2]).should == [obj1] - ([obj1, obj1, obj2, obj2] - [obj2]).should == [obj1, obj1] - end - - it "removes an identical item even when its #eql? isn't reflexive" do - x = mock('x') - x.stub!(:hash).and_return(42) - x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. - - ([x] - [x]).should == [] - end - - it "is not destructive" do - a = [1, 2, 3] - a - [] - a.should == [1, 2, 3] - a - [1] - a.should == [1, 2, 3] - a - [1,2,3] - a.should == [1, 2, 3] - a - [:a, :b, :c] - a.should == [1, 2, 3] - end + it_behaves_like :array_binary_difference, :- end diff --git a/spec/ruby/core/array/pack/l_spec.rb b/spec/ruby/core/array/pack/l_spec.rb index 0a5552b984..b446a7a36a 100644 --- a/spec/ruby/core/array/pack/l_spec.rb +++ b/spec/ruby/core/array/pack/l_spec.rb @@ -29,7 +29,7 @@ describe "Array#pack with format 'L'" do it_behaves_like :array_pack_32bit_be, 'L>' end - guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + platform_is wordsize: 32 do describe "with modifier '<' and '_'" do it_behaves_like :array_pack_32bit_le, 'L<_' it_behaves_like :array_pack_32bit_le, 'L_<' @@ -51,7 +51,7 @@ describe "Array#pack with format 'L'" do end end - guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + platform_is wordsize: 64 do describe "with modifier '<' and '_'" do it_behaves_like :array_pack_64bit_le, 'L<_' it_behaves_like :array_pack_64bit_le, 'L_<' @@ -83,7 +83,7 @@ describe "Array#pack with format 'l'" do it_behaves_like :array_pack_32bit_be, 'l>' end - guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + platform_is wordsize: 32 do describe "with modifier '<' and '_'" do it_behaves_like :array_pack_32bit_le, 'l<_' it_behaves_like :array_pack_32bit_le, 'l_<' @@ -105,7 +105,7 @@ describe "Array#pack with format 'l'" do end end - guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + platform_is wordsize: 64 do describe "with modifier '<' and '_'" do it_behaves_like :array_pack_64bit_le, 'l<_' it_behaves_like :array_pack_64bit_le, 'l_<' @@ -137,7 +137,7 @@ little_endian do it_behaves_like :array_pack_32bit_le, 'l' end - guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + platform_is wordsize: 32 do describe "Array#pack with format 'L' with modifier '_'" do it_behaves_like :array_pack_32bit_le, 'L_' end @@ -155,7 +155,7 @@ little_endian do end end - guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + platform_is wordsize: 64 do describe "Array#pack with format 'L' with modifier '_'" do it_behaves_like :array_pack_64bit_le, 'L_' end @@ -183,7 +183,7 @@ big_endian do it_behaves_like :array_pack_32bit_be, 'l' end - guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + platform_is wordsize: 32 do describe "Array#pack with format 'L' with modifier '_'" do it_behaves_like :array_pack_32bit_be, 'L_' end @@ -201,7 +201,7 @@ big_endian do end end - guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + platform_is wordsize: 64 do describe "Array#pack with format 'L' with modifier '_'" do it_behaves_like :array_pack_64bit_be, 'L_' end diff --git a/spec/ruby/core/array/shared/difference.rb b/spec/ruby/core/array/shared/difference.rb new file mode 100644 index 0000000000..c43a6375e0 --- /dev/null +++ b/spec/ruby/core/array/shared/difference.rb @@ -0,0 +1,78 @@ +describe :array_binary_difference, shared: true do + it "creates an array minus any items from other array" do + [].send(@method, [ 1, 2, 4 ]).should == [] + [1, 2, 4].send(@method, []).should == [1, 2, 4] + [ 1, 2, 3, 4, 5 ].send(@method, [ 1, 2, 4 ]).should == [3, 5] + end + + it "removes multiple items on the lhs equal to one on the rhs" do + [1, 1, 2, 2, 3, 3, 4, 5].send(@method, [1, 2, 4]).should == [3, 3, 5] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.send(@method, empty).should == [] + + [].send(@method, ArraySpecs.recursive_array).should == [] + + array = ArraySpecs.recursive_array + array.send(@method, array).should == [] + end + + it "tries to convert the passed arguments to Arrays using #to_ary" do + obj = mock('[2,3,3,4]') + obj.should_receive(:to_ary).and_return([2, 3, 3, 4]) + [1, 1, 2, 2, 3, 4].send(@method, obj).should == [1, 1] + end + + it "raises a TypeError if the argument cannot be coerced to an Array by calling #to_ary" do + obj = mock('not an array') + lambda { [1, 2, 3].send(@method, obj) }.should raise_error(TypeError) + end + + it "does not return subclass instance for Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].send(@method, []).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[]).should be_an_instance_of(Array) + [1, 2, 3].send(@method, ArraySpecs::MyArray[]).should be_an_instance_of(Array) + end + + it "does not call to_ary on array subclasses" do + [5, 6, 7].send(@method, ArraySpecs::ToAryArray[7]).should == [5, 6] + end + + it "removes an item identified as equivalent via #hash and #eql?" do + obj1 = mock('1') + obj2 = mock('2') + obj1.stub!(:hash).and_return(0) + obj2.stub!(:hash).and_return(0) + obj1.should_receive(:eql?).at_least(1).and_return(true) + + [obj1].send(@method, [obj2]).should == [] + [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [] + end + + it "doesn't remove an item with the same hash but not #eql?" do + obj1 = mock('1') + obj2 = mock('2') + obj1.stub!(:hash).and_return(0) + obj2.stub!(:hash).and_return(0) + obj1.should_receive(:eql?).at_least(1).and_return(false) + + [obj1].send(@method, [obj2]).should == [obj1] + [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj1, obj1] + end + + it "removes an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.stub!(:hash).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + [x].send(@method, [x]).should == [] + end + + it "is not destructive" do + a = [1, 2, 3] + a.send(@method, [1]) + a.should == [1, 2, 3] + end +end diff --git a/spec/ruby/core/array/shared/union.rb b/spec/ruby/core/array/shared/union.rb new file mode 100644 index 0000000000..12a98cc9fe --- /dev/null +++ b/spec/ruby/core/array/shared/union.rb @@ -0,0 +1,79 @@ +describe :array_binary_union, shared: true do + it "returns an array of elements that appear in either array (union)" do + [].send(@method, []).should == [] + [1, 2].send(@method, []).should == [1, 2] + [].send(@method, [1, 2]).should == [1, 2] + [ 1, 2, 3, 4 ].send(@method, [ 3, 4, 5 ]).should == [1, 2, 3, 4, 5] + end + + it "creates an array with no duplicates" do + [ 1, 2, 3, 1, 4, 5 ].send(@method, [ 1, 3, 4, 5, 3, 6 ]).should == [1, 2, 3, 4, 5, 6] + end + + it "creates an array with elements in order they are first encountered" do + [ 1, 2, 3, 1 ].send(@method, [ 1, 3, 4, 5 ]).should == [1, 2, 3, 4, 5] + end + + it "properly handles recursive arrays" do + empty = ArraySpecs.empty_recursive_array + empty.send(@method, empty).should == empty + + array = ArraySpecs.recursive_array + array.send(@method, []).should == [1, 'two', 3.0, array] + [].send(@method, array).should == [1, 'two', 3.0, array] + array.send(@method, array).should == [1, 'two', 3.0, array] + array.send(@method, empty).should == [1, 'two', 3.0, array, empty] + end + + it "tries to convert the passed argument to an Array using #to_ary" do + obj = mock('[1,2,3]') + obj.should_receive(:to_ary).and_return([1, 2, 3]) + [0].send(@method, obj).should == ([0] | [1, 2, 3]) + end + + # MRI follows hashing semantics here, so doesn't actually call eql?/hash for Fixnum/Symbol + it "acts as if using an intermediate hash to collect values" do + not_supported_on :opal do + [5.0, 4.0].send(@method, [5, 4]).should == [5.0, 4.0, 5, 4] + end + + str = "x" + [str].send(@method, [str.dup]).should == [str] + + obj1 = mock('1') + obj2 = mock('2') + obj1.stub!(:hash).and_return(0) + obj2.stub!(:hash).and_return(0) + obj2.should_receive(:eql?).at_least(1).and_return(true) + + [obj1].send(@method, [obj2]).should == [obj1] + [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj1] + + obj1 = mock('3') + obj2 = mock('4') + obj1.stub!(:hash).and_return(0) + obj2.stub!(:hash).and_return(0) + obj2.should_receive(:eql?).at_least(1).and_return(false) + + [obj1].send(@method, [obj2]).should == [obj1, obj2] + [obj1, obj1, obj2, obj2].send(@method, [obj2]).should == [obj1, obj2] + end + + it "does not return subclass instances for Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].send(@method, []).should be_an_instance_of(Array) + ArraySpecs::MyArray[1, 2, 3].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) + [].send(@method, ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) + end + + it "does not call to_ary on array subclasses" do + [1, 2].send(@method, ArraySpecs::ToAryArray[5, 6]).should == [1, 2, 5, 6] + end + + it "properly handles an identical item even when its #eql? isn't reflexive" do + x = mock('x') + x.stub!(:hash).and_return(42) + x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + + [x].send(@method, [x]).should == [x] + end +end diff --git a/spec/ruby/core/array/union_spec.rb b/spec/ruby/core/array/union_spec.rb index 0e177715e5..1dca47696d 100644 --- a/spec/ruby/core/array/union_spec.rb +++ b/spec/ruby/core/array/union_spec.rb @@ -1,82 +1,27 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/union' describe "Array#|" do - it "returns an array of elements that appear in either array (union)" do - ([] | []).should == [] - ([1, 2] | []).should == [1, 2] - ([] | [1, 2]).should == [1, 2] - ([ 1, 2, 3, 4 ] | [ 3, 4, 5 ]).should == [1, 2, 3, 4, 5] - end - - it "creates an array with no duplicates" do - ([ 1, 2, 3, 1, 4, 5 ] | [ 1, 3, 4, 5, 3, 6 ]).should == [1, 2, 3, 4, 5, 6] - end - - it "creates an array with elements in order they are first encountered" do - ([ 1, 2, 3, 1 ] | [ 1, 3, 4, 5 ]).should == [1, 2, 3, 4, 5] - end - - it "properly handles recursive arrays" do - empty = ArraySpecs.empty_recursive_array - (empty | empty).should == empty + it_behaves_like :array_binary_union, :| +end - array = ArraySpecs.recursive_array - (array | []).should == [1, 'two', 3.0, array] - ([] | array).should == [1, 'two', 3.0, array] - (array | array).should == [1, 'two', 3.0, array] - (array | empty).should == [1, 'two', 3.0, array, empty] - end +ruby_version_is "2.6" do + describe "Array#union" do + it_behaves_like :array_binary_union, :union - it "tries to convert the passed argument to an Array using #to_ary" do - obj = mock('[1,2,3]') - obj.should_receive(:to_ary).and_return([1, 2, 3]) - ([0] | obj).should == ([0] | [1, 2, 3]) - end - - # MRI follows hashing semantics here, so doesn't actually call eql?/hash for Fixnum/Symbol - it "acts as if using an intermediate hash to collect values" do - not_supported_on :opal do - ([5.0, 4.0] | [5, 4]).should == [5.0, 4.0, 5, 4] + it "returns unique elements when given no argument" do + x = [1, 2, 3, 2] + x.union.should == [1, 2, 3] end - str = "x" - ([str] | [str.dup]).should == [str] - - obj1 = mock('1') - obj2 = mock('2') - obj1.stub!(:hash).and_return(0) - obj2.stub!(:hash).and_return(0) - obj2.should_receive(:eql?).at_least(1).and_return(true) - - ([obj1] | [obj2]).should == [obj1] - ([obj1, obj1, obj2, obj2] | [obj2]).should == [obj1] - - obj1 = mock('3') - obj2 = mock('4') - obj1.stub!(:hash).and_return(0) - obj2.stub!(:hash).and_return(0) - obj2.should_receive(:eql?).at_least(1).and_return(false) - - ([obj1] | [obj2]).should == [obj1, obj2] - ([obj1, obj1, obj2, obj2] | [obj2]).should == [obj1, obj2] - end - - it "does not return subclass instances for Array subclasses" do - (ArraySpecs::MyArray[1, 2, 3] | []).should be_an_instance_of(Array) - (ArraySpecs::MyArray[1, 2, 3] | ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) - ([] | ArraySpecs::MyArray[1, 2, 3]).should be_an_instance_of(Array) - end - - it "does not call to_ary on array subclasses" do - ([1, 2] | ArraySpecs::ToAryArray[5, 6]).should == [1, 2, 5, 6] - end - - it "properly handles an identical item even when its #eql? isn't reflexive" do - x = mock('x') - x.stub!(:hash).and_return(42) - x.stub!(:eql?).and_return(false) # Stubbed for clarity and latitude in implementation; not actually sent by MRI. + it "does not return subclass instances for Array subclasses" do + ArraySpecs::MyArray[1, 2, 3].union.should be_an_instance_of(Array) + end - ([x] | [x]).should == [x] + it "accepts multiple arguments" do + x = [1, 2, 3] + x.union(x, x, x, x, [3, 4], x).should == [1, 2, 3, 4] + end end end diff --git a/spec/ruby/core/dir/shared/open.rb b/spec/ruby/core/dir/shared/open.rb index 7f4fe5c2a6..6df361e452 100644 --- a/spec/ruby/core/dir/shared/open.rb +++ b/spec/ruby/core/dir/shared/open.rb @@ -60,4 +60,13 @@ describe :dir_open, shared: true do dir = Dir.send(@method, DirSpecs.mock_dir, encoding: nil) {|d| d } dir.should be_kind_of(Dir) end + + platform_is_not :windows do + it 'sets the close-on-exec flag for the directory file descriptor' do + Dir.send(@method, DirSpecs.mock_dir) do |dir| + io = IO.for_fd(dir.fileno) + io.close_on_exec?.should == true + end + end + end end diff --git a/spec/ruby/core/file/atime_spec.rb b/spec/ruby/core/file/atime_spec.rb index d4106c0411..c791aa6d74 100644 --- a/spec/ruby/core/file/atime_spec.rb +++ b/spec/ruby/core/file/atime_spec.rb @@ -15,7 +15,7 @@ describe "File.atime" do File.atime(@file).should be_kind_of(Time) end - platform_is :linux do + guard -> { platform_is :linux or (platform_is :windows and ruby_version_is '2.5') } do ## NOTE also that some Linux systems disable atime (e.g. via mount params) for better filesystem speed. it "returns the last access time for the named file with microseconds" do supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d+)/, 1], 10) diff --git a/spec/ruby/core/file/ctime_spec.rb b/spec/ruby/core/file/ctime_spec.rb index 50ffbe6a79..5753e0d90e 100644 --- a/spec/ruby/core/file/ctime_spec.rb +++ b/spec/ruby/core/file/ctime_spec.rb @@ -14,7 +14,7 @@ describe "File.ctime" do File.ctime(@file).should be_kind_of(Time) end - platform_is :linux do + guard -> { platform_is :linux or (platform_is :windows and ruby_version_is '2.5') } do it "returns the change time for the named file (the time at which directory information about the file was changed, not the file itself) with microseconds." do supports_subseconds = Integer(`stat -c%z '#{__FILE__}'`[/\.(\d+)/, 1], 10) if supports_subseconds != 0 diff --git a/spec/ruby/core/file/mtime_spec.rb b/spec/ruby/core/file/mtime_spec.rb index 4c26cb5dac..d5769c0584 100644 --- a/spec/ruby/core/file/mtime_spec.rb +++ b/spec/ruby/core/file/mtime_spec.rb @@ -15,7 +15,7 @@ describe "File.mtime" do File.mtime(@filename).should be_close(@mtime, 2.0) end - platform_is :linux do + guard -> { platform_is :linux or (platform_is :windows and ruby_version_is '2.5') } do it "returns the modification Time of the file with microseconds" do supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d+)/, 1], 10) if supports_subseconds != 0 diff --git a/spec/ruby/core/file/reopen_spec.rb b/spec/ruby/core/file/reopen_spec.rb index 2d79129320..858d424c67 100644 --- a/spec/ruby/core/file/reopen_spec.rb +++ b/spec/ruby/core/file/reopen_spec.rb @@ -25,7 +25,7 @@ describe "File#reopen" do @file.read.should == @content_b end - it "calls #to_path to convern an Object" do + it "calls #to_path to convert an Object" do @file = File.new(@name_a).reopen(mock_to_path(@name_b), "r") @file.read.should == @content_b end diff --git a/spec/ruby/core/file/utime_spec.rb b/spec/ruby/core/file/utime_spec.rb index 542681ea93..64af82ef19 100644 --- a/spec/ruby/core/file/utime_spec.rb +++ b/spec/ruby/core/file/utime_spec.rb @@ -1,6 +1,11 @@ require_relative '../../spec_helper' describe "File.utime" do + + before :all do + @time_is_float = /mswin|mingw/ =~ RUBY_PLATFORM && RUBY_VERSION >= '2.5' + end + before :each do @atime = Time.now @mtime = Time.now @@ -16,18 +21,33 @@ describe "File.utime" do it "sets the access and modification time of each file" do File.utime(@atime, @mtime, @file1, @file2) - File.atime(@file1).to_i.should be_close(@atime.to_i, 2) - File.mtime(@file1).to_i.should be_close(@mtime.to_i, 2) - File.atime(@file2).to_i.should be_close(@atime.to_i, 2) - File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2) + if @time_is_float + File.atime(@file1).should be_close(@atime, 0.0001) + File.mtime(@file1).should be_close(@mtime, 0.0001) + File.atime(@file2).should be_close(@atime, 0.0001) + File.mtime(@file2).should be_close(@mtime, 0.0001) + else + File.atime(@file1).to_i.should be_close(@atime.to_i, 2) + File.mtime(@file1).to_i.should be_close(@mtime.to_i, 2) + File.atime(@file2).to_i.should be_close(@atime.to_i, 2) + File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2) + end end it "uses the current times if two nil values are passed" do + tn = Time.now File.utime(nil, nil, @file1, @file2) - File.atime(@file1).to_i.should be_close(Time.now.to_i, 2) - File.mtime(@file1).to_i.should be_close(Time.now.to_i, 2) - File.atime(@file2).to_i.should be_close(Time.now.to_i, 2) - File.mtime(@file2).to_i.should be_close(Time.now.to_i, 2) + if @time_is_float + File.atime(@file1).should be_close(tn, 0.050) + File.mtime(@file1).should be_close(tn, 0.050) + File.atime(@file2).should be_close(tn, 0.050) + File.mtime(@file2).should be_close(tn, 0.050) + else + File.atime(@file1).to_i.should be_close(Time.now.to_i, 2) + File.mtime(@file1).to_i.should be_close(Time.now.to_i, 2) + File.atime(@file2).to_i.should be_close(Time.now.to_i, 2) + File.mtime(@file2).to_i.should be_close(Time.now.to_i, 2) + end end it "accepts an object that has a #to_path method" do @@ -35,11 +55,19 @@ describe "File.utime" do end it "accepts numeric atime and mtime arguments" do - File.utime(@atime.to_i, @mtime.to_i, @file1, @file2) - File.atime(@file1).to_i.should be_close(@atime.to_i, 2) - File.mtime(@file1).to_i.should be_close(@mtime.to_i, 2) - File.atime(@file2).to_i.should be_close(@atime.to_i, 2) - File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2) + if @time_is_float + File.utime(@atime.to_f, @mtime.to_f, @file1, @file2) + File.atime(@file1).should be_close(@atime, 0.0001) + File.mtime(@file1).should be_close(@mtime, 0.0001) + File.atime(@file2).should be_close(@atime, 0.0001) + File.mtime(@file2).should be_close(@mtime, 0.0001) + else + File.utime(@atime.to_i, @mtime.to_i, @file1, @file2) + File.atime(@file1).to_i.should be_close(@atime.to_i, 2) + File.mtime(@file1).to_i.should be_close(@mtime.to_i, 2) + File.atime(@file2).to_i.should be_close(@atime.to_i, 2) + File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2) + end end platform_is :linux do diff --git a/spec/ruby/core/float/round_spec.rb b/spec/ruby/core/float/round_spec.rb index e04b376c36..df113f97b1 100644 --- a/spec/ruby/core/float/round_spec.rb +++ b/spec/ruby/core/float/round_spec.rb @@ -10,11 +10,9 @@ describe "Float#round" do 0.0.round.should == 0 end - platform_is_not :mingw32 do - it "returns the nearest Integer for Float near the limit" do - 0.49999999999999994.round.should == 0 - -0.49999999999999994.round.should == 0 - end + it "returns the nearest Integer for Float near the limit" do + 0.49999999999999994.round.should == 0 + -0.49999999999999994.round.should == 0 end it "raises FloatDomainError for exceptional values" do diff --git a/spec/ruby/core/io/dup_spec.rb b/spec/ruby/core/io/dup_spec.rb index 421ae27f24..c88d109ec9 100644 --- a/spec/ruby/core/io/dup_spec.rb +++ b/spec/ruby/core/io/dup_spec.rb @@ -3,7 +3,7 @@ require_relative 'fixtures/classes' describe "IO#dup" do before :each do - @file = tmp("rubinius_spec_io_dup_#{$$}_#{Time.now.to_f}") + @file = tmp("spec_io_dup") @f = File.open @file, 'w+' @i = @f.dup @@ -66,4 +66,22 @@ end it "raises IOError on closed stream" do lambda { IOSpecs.closed_io.dup }.should raise_error(IOError) end + + it "always sets the close-on-exec flag for the new IO object" do + @f.close_on_exec = true + dup = @f.dup + begin + dup.close_on_exec?.should == true + ensure + dup.close + end + + @f.close_on_exec = false + dup = @f.dup + begin + dup.close_on_exec?.should == true + ensure + dup.close + end + end end diff --git a/spec/ruby/core/io/reopen_spec.rb b/spec/ruby/core/io/reopen_spec.rb index e769991554..53fcc9dede 100644 --- a/spec/ruby/core/io/reopen_spec.rb +++ b/spec/ruby/core/io/reopen_spec.rb @@ -162,6 +162,18 @@ describe "IO#reopen with a String" do end end + it "always resets the close-on-exec flag to true on non-STDIO objects" do + @io = new_io @name, "w" + + @io.close_on_exec = true + @io.reopen @other_name + @io.close_on_exec?.should == true + + @io.close_on_exec = false + @io.reopen @other_name + @io.close_on_exec?.should == true + end + it "creates the file if it doesn't exist if the IO is opened in write mode" do @io = new_io @name, "w" @@ -294,6 +306,18 @@ describe "IO#reopen with an IO" do File.read(@other_name).should == "io data" end + it "always resets the close-on-exec flag to true on non-STDIO objects" do + @other_io.close_on_exec = true + @io.close_on_exec = true + @io.reopen @other_io + @io.close_on_exec?.should == true + + @other_io.close_on_exec = false + @io.close_on_exec = false + @io.reopen @other_io + @io.close_on_exec?.should == true + end + it "may change the class of the instance" do @io.reopen @other_io @io.should be_an_instance_of(File) diff --git a/spec/ruby/core/kernel/match_spec.rb b/spec/ruby/core/kernel/match_spec.rb index bc6cb3d9dd..d5808b6ede 100644 --- a/spec/ruby/core/kernel/match_spec.rb +++ b/spec/ruby/core/kernel/match_spec.rb @@ -1,22 +1,16 @@ require_relative '../../spec_helper' describe "Kernel#=~" do - verbose = $VERBOSE - before :each do - verbose, $VERBOSE = $VERBOSE, nil - end - after :each do - $VERBOSE = verbose - end - it "returns nil matching any object" do o = Object.new - (o =~ /Object/).should be_nil - (o =~ 'Object').should be_nil - (o =~ Object).should be_nil - (o =~ Object.new).should be_nil - (o =~ nil).should be_nil - (o =~ true).should be_nil + suppress_warning do + (o =~ /Object/).should be_nil + (o =~ 'Object').should be_nil + (o =~ Object).should be_nil + (o =~ Object.new).should be_nil + (o =~ nil).should be_nil + (o =~ true).should be_nil + end end end diff --git a/spec/ruby/core/marshal/dump_spec.rb b/spec/ruby/core/marshal/dump_spec.rb index 644a88b4a3..c53840962c 100644 --- a/spec/ruby/core/marshal/dump_spec.rb +++ b/spec/ruby/core/marshal/dump_spec.rb @@ -63,6 +63,10 @@ describe "Marshal.dump" do "\x04\bI:\b\xE2\x86\x92\x06:\x06ET"], [Marshal, s.encode("utf-16").to_sym, "\x04\bI:\t\xFE\xFF!\x92\x06:\rencoding\"\vUTF-16"], + [Marshal, s.encode("utf-16le").to_sym, + "\x04\bI:\a\x92!\x06:\rencoding\"\rUTF-16LE"], + [Marshal, s.encode("utf-16be").to_sym, + "\x04\bI:\a!\x92\x06:\rencoding\"\rUTF-16BE"], [Marshal, s.encode("euc-jp").to_sym, "\x04\bI:\a\xA2\xAA\x06:\rencoding\"\vEUC-JP"], [Marshal, s.encode("sjis").to_sym, @@ -74,20 +78,6 @@ describe "Marshal.dump" do s = "\u2192".force_encoding("binary").to_sym Marshal.dump(s).should == "\x04\b:\b\xE2\x86\x92" end - - end - - it "dumps an extended_object" do - Marshal.dump(Object.new.extend(Meths)).should == "\x04\be:\nMethso:\vObject\x00" - end - - it "dumps an object that has had an ivar added and removed as though the ivar never was set" do - obj = Object.new - initial = Marshal.dump(obj) - obj.instance_variable_set(:@ivar, 1) - Marshal.dump(obj).should == "\004\bo:\vObject\006:\n@ivari\006" - obj.send :remove_instance_variable, :@ivar - Marshal.dump(obj).should == initial end describe "with an object responding to #marshal_dump" do @@ -376,6 +366,13 @@ describe "Marshal.dump" do Marshal.dump(obj).should == "\004\bo:\vObject\006:\n@ivari\006" end + it "dumps an Object with a non-US-ASCII instance variable" do + obj = Object.new + ivar = "@é".force_encoding(Encoding::UTF_8).to_sym + obj.instance_variable_set(ivar, 1) + Marshal.dump(obj).should == "\x04\bo:\vObject\x06I:\b@\xC3\xA9\x06:\x06ETi\x06" + end + it "dumps an Object that has had an instance variable added and removed as though it was never set" do obj = Object.new obj.instance_variable_set(:@ivar, 1) diff --git a/spec/ruby/core/marshal/shared/load.rb b/spec/ruby/core/marshal/shared/load.rb index 8b68384738..0f1d49b115 100644 --- a/spec/ruby/core/marshal/shared/load.rb +++ b/spec/ruby/core/marshal/shared/load.rb @@ -352,6 +352,54 @@ describe :marshal_load, shared: true do end end + describe "for a Symbol" do + it "loads a Symbol" do + sym = Marshal.send(@method, "\004\b:\vsymbol") + sym.should == :symbol + sym.encoding.should == Encoding::US_ASCII + end + + it "loads a big Symbol" do + sym = ('big' * 100).to_sym + Marshal.send(@method, "\004\b:\002,\001#{'big' * 100}").should == sym + end + + it "loads an encoded Symbol" do + s = "\u2192" + + sym = Marshal.send(@method, "\x04\bI:\b\xE2\x86\x92\x06:\x06ET") + sym.should == s.encode("utf-8").to_sym + sym.encoding.should == Encoding::UTF_8 + + sym = Marshal.send(@method, "\x04\bI:\t\xFE\xFF!\x92\x06:\rencoding\"\vUTF-16") + sym.should == s.encode("utf-16").to_sym + sym.encoding.should == Encoding::UTF_16 + + sym = Marshal.send(@method, "\x04\bI:\a\x92!\x06:\rencoding\"\rUTF-16LE") + sym.should == s.encode("utf-16le").to_sym + sym.encoding.should == Encoding::UTF_16LE + + sym = Marshal.send(@method, "\x04\bI:\a!\x92\x06:\rencoding\"\rUTF-16BE") + sym.should == s.encode("utf-16be").to_sym + sym.encoding.should == Encoding::UTF_16BE + + sym = Marshal.send(@method, "\x04\bI:\a\xA2\xAA\x06:\rencoding\"\vEUC-JP") + sym.should == s.encode("euc-jp").to_sym + sym.encoding.should == Encoding::EUC_JP + + sym = Marshal.send(@method, "\x04\bI:\a\x81\xA8\x06:\rencoding\"\x10Windows-31J") + sym.should == s.encode("sjis").to_sym + sym.encoding.should == Encoding::SJIS + end + + it "loads a binary encoded Symbol" do + s = "\u2192".force_encoding("binary").to_sym + sym = Marshal.send(@method, "\x04\b:\b\xE2\x86\x92") + sym.should == s + sym.encoding.should == Encoding::BINARY + end + end + describe "for a String" do it "loads a string having ivar with ref to self" do obj = 'hi' @@ -485,24 +533,39 @@ describe :marshal_load, shared: true do end end - describe "for a user Class" do - it "loads a user-marshaled extended object" do - obj = UserMarshal.new.extend(Meths) + describe "for an Object" do + it "loads an object" do + Marshal.send(@method, "\004\bo:\vObject\000").should be_kind_of(Object) + end - new_obj = Marshal.send(@method, "\004\bU:\020UserMarshal\"\nstuff") + it "loads an extended Object" do + obj = Object.new.extend(Meths) - new_obj.should == obj + new_obj = Marshal.send(@method, "\004\be:\nMethso:\vObject\000") + + new_obj.class.should == obj.class new_obj_metaclass_ancestors = class << new_obj; ancestors; end - new_obj_metaclass_ancestors[@num_self_class].should == UserMarshal + new_obj_metaclass_ancestors[@num_self_class, 2].should == [Meths, Object] end - it "loads a user_object" do - UserObject.new - Marshal.send(@method, "\004\bo:\017UserObject\000").should be_kind_of(UserObject) + it "loads an object having ivar" do + s = 'hi' + arr = [:so, :so, s, s] + obj = Object.new + obj.instance_variable_set :@str, arr + + new_obj = Marshal.send(@method, "\004\bo:\vObject\006:\t@str[\t:\aso;\a\"\ahi@\a") + new_str = new_obj.instance_variable_get :@str + + new_str.should == arr end - it "loads an object" do - Marshal.send(@method, "\004\bo:\vObject\000").should be_kind_of(Object) + it "loads an Object with a non-US-ASCII instance variable" do + ivar = "@é".force_encoding(Encoding::UTF_8).to_sym + obj = Marshal.send(@method, "\x04\bo:\vObject\x06I:\b@\xC3\xA9\x06:\x06ETi\x06") + obj.instance_variables.should == [ivar] + obj.instance_variables[0].encoding.should == Encoding::UTF_8 + obj.instance_variable_get(ivar).should == 1 end it "raises ArgumentError if the object from an 'o' stream is not dumpable as 'o' type user class" do @@ -510,15 +573,21 @@ describe :marshal_load, shared: true do Marshal.send(@method, "\x04\bo:\tFile\001\001:\001\005@path\"\x10/etc/passwd") end.should raise_error(ArgumentError) end + end - it "loads an extended Object" do - obj = Object.new.extend(Meths) + describe "for a user object" do + it "loads a user-marshaled extended object" do + obj = UserMarshal.new.extend(Meths) - new_obj = Marshal.send(@method, "\004\be:\nMethso:\vObject\000") + new_obj = Marshal.send(@method, "\004\bU:\020UserMarshal\"\nstuff") - new_obj.class.should == obj.class + new_obj.should == obj new_obj_metaclass_ancestors = class << new_obj; ancestors; end - new_obj_metaclass_ancestors[@num_self_class, 2].should == [Meths, Object] + new_obj_metaclass_ancestors[@num_self_class].should == UserMarshal + end + + it "loads a UserObject" do + Marshal.send(@method, "\004\bo:\017UserObject\000").should be_kind_of(UserObject) end describe "that extends a core type other than Object or BasicObject" do @@ -537,18 +606,6 @@ describe :marshal_load, shared: true do lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError) end end - - it "loads an object having ivar" do - s = 'hi' - arr = [:so, :so, s, s] - obj = Object.new - obj.instance_variable_set :@str, arr - - new_obj = Marshal.send(@method, "\004\bo:\vObject\006:\t@str[\t:\aso;\a\"\ahi@\a") - new_str = new_obj.instance_variable_get :@str - - new_str.should == arr - end end describe "for a Regexp" do diff --git a/spec/ruby/core/matchdata/begin_spec.rb b/spec/ruby/core/matchdata/begin_spec.rb index b791018633..85c454da56 100644 --- a/spec/ruby/core/matchdata/begin_spec.rb +++ b/spec/ruby/core/matchdata/begin_spec.rb @@ -3,28 +3,102 @@ require_relative '../../spec_helper' describe "MatchData#begin" do - it "returns the offset of the start of the nth element" do - match_data = /(.)(.)(\d+)(\d)/.match("THX1138.") - match_data.begin(0).should == 1 - match_data.begin(2).should == 2 - end + context "when passed an integer argument" do + it "returns the character offset of the start of the nth element" do + match_data = /(.)(.)(\d+)(\d)/.match("THX1138.") + match_data.begin(0).should == 1 + match_data.begin(2).should == 2 + end - it "returns nil when the nth match isn't found" do - match_data = /something is( not)? (right)/.match("something is right") - match_data.begin(1).should be_nil + it "returns nil when the nth match isn't found" do + match_data = /something is( not)? (right)/.match("something is right") + match_data.begin(1).should be_nil + end + + it "returns the character offset for multi-byte strings" do + match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.") + match_data.begin(0).should == 1 + match_data.begin(2).should == 2 + end + + not_supported_on :opal do + it "returns the character offset for multi-byte strings with unicode regexp" do + match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.") + match_data.begin(0).should == 1 + match_data.begin(2).should == 2 + end + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2) + + match_data = /(.)(.)(\d+)(\d)/.match("THX1138.") + match_data.begin(obj).should == 2 + end end - it "returns the offset for multi byte strings" do - match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.") - match_data.begin(0).should == 1 - match_data.begin(2).should == 2 + context "when passed a String argument" do + it "return the character offset of the start of the named capture" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.") + match_data.begin("a").should == 1 + match_data.begin("b").should == 3 + end + + it "returns the character offset for multi byte strings" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.") + match_data.begin("a").should == 1 + match_data.begin("b").should == 3 + end + + not_supported_on :opal do + it "returns the character offset for multi byte strings with unicode regexp" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.") + match_data.begin("a").should == 1 + match_data.begin("b").should == 3 + end + end + + it "returns the character offset for the farthest match when multiple named captures use the same name" do + match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.") + match_data.begin("a").should == 3 + end + + it "returns the character offset for multi-byte names" do + match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.") + match_data.begin("æ").should == 1 + end end - not_supported_on :opal do - it "returns the offset for multi byte strings with unicode regexp" do - match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.") - match_data.begin(0).should == 1 - match_data.begin(2).should == 2 + context "when passed a Symbol argument" do + it "return the character offset of the start of the named capture" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.") + match_data.begin(:a).should == 1 + match_data.begin(:b).should == 3 + end + + it "returns the character offset for multi byte strings" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.") + match_data.begin(:a).should == 1 + match_data.begin(:b).should == 3 + end + + not_supported_on :opal do + it "returns the character offset for multi byte strings with unicode regexp" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.") + match_data.begin(:a).should == 1 + match_data.begin(:b).should == 3 + end + end + + it "returns the character offset for the farthest match when multiple named captures use the same name" do + match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.") + match_data.begin(:a).should == 3 + end + + it "returns the character offset for multi-byte names" do + match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.") + match_data.begin(:æ).should == 1 end end end diff --git a/spec/ruby/core/matchdata/end_spec.rb b/spec/ruby/core/matchdata/end_spec.rb index f6f3e1a281..d01b0a8b30 100644 --- a/spec/ruby/core/matchdata/end_spec.rb +++ b/spec/ruby/core/matchdata/end_spec.rb @@ -3,28 +3,102 @@ require_relative '../../spec_helper' describe "MatchData#end" do - it "returns the offset of the end of the nth element" do - match_data = /(.)(.)(\d+)(\d)/.match("THX1138.") - match_data.end(0).should == 7 - match_data.end(2).should == 3 - end + context "when passed an integer argument" do + it "returns the character offset of the end of the nth element" do + match_data = /(.)(.)(\d+)(\d)/.match("THX1138.") + match_data.end(0).should == 7 + match_data.end(2).should == 3 + end - it "returns nil when the nth match isn't found" do - match_data = /something is( not)? (right)/.match("something is right") - match_data.end(1).should be_nil + it "returns nil when the nth match isn't found" do + match_data = /something is( not)? (right)/.match("something is right") + match_data.end(1).should be_nil + end + + it "returns the character offset for multi-byte strings" do + match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.") + match_data.end(0).should == 7 + match_data.end(2).should == 3 + end + + not_supported_on :opal do + it "returns the character offset for multi-byte strings with unicode regexp" do + match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.") + match_data.end(0).should == 7 + match_data.end(2).should == 3 + end + end + + it "tries to convert the passed argument to an Integer using #to_int" do + obj = mock('to_int') + obj.should_receive(:to_int).and_return(2) + + match_data = /(.)(.)(\d+)(\d)/.match("THX1138.") + match_data.end(obj).should == 3 + end end - it "returns the offset for multi byte strings" do - match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.") - match_data.end(0).should == 7 - match_data.end(2).should == 3 + context "when passed a String argument" do + it "return the character offset of the start of the named capture" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.") + match_data.end("a").should == 2 + match_data.end("b").should == 6 + end + + it "returns the character offset for multi byte strings" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.") + match_data.end("a").should == 2 + match_data.end("b").should == 6 + end + + not_supported_on :opal do + it "returns the character offset for multi byte strings with unicode regexp" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.") + match_data.end("a").should == 2 + match_data.end("b").should == 6 + end + end + + it "returns the character offset for the farthest match when multiple named captures use the same name" do + match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.") + match_data.end("a").should == 6 + end + + it "returns the character offset for multi-byte names" do + match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.") + match_data.end("æ").should == 2 + end end - not_supported_on :opal do - it "returns the offset for multi byte strings with unicode regexp" do - match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.") - match_data.end(0).should == 7 - match_data.end(2).should == 3 + context "when passed a Symbol argument" do + it "return the character offset of the start of the named capture" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("THX1138.") + match_data.end(:a).should == 2 + match_data.end(:b).should == 6 + end + + it "returns the character offset for multi byte strings" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/.match("TñX1138.") + match_data.end(:a).should == 2 + match_data.end(:b).should == 6 + end + + not_supported_on :opal do + it "returns the character offset for multi byte strings with unicode regexp" do + match_data = /(?<a>.)(.)(?<b>\d+)(\d)/u.match("TñX1138.") + match_data.end(:a).should == 2 + match_data.end(:b).should == 6 + end + end + + it "returns the character offset for the farthest match when multiple named captures use the same name" do + match_data = /(?<a>.)(.)(?<a>\d+)(\d)/.match("THX1138.") + match_data.end(:a).should == 6 + end + + it "returns the character offset for multi-byte names" do + match_data = /(?<æ>.)(.)(?<b>\d+)(\d)/.match("THX1138.") + match_data.end(:æ).should == 2 end end end diff --git a/spec/ruby/core/matchdata/named_captures_spec.rb b/spec/ruby/core/matchdata/named_captures_spec.rb index 588b607a44..0b0771355f 100644 --- a/spec/ruby/core/matchdata/named_captures_spec.rb +++ b/spec/ruby/core/matchdata/named_captures_spec.rb @@ -9,5 +9,9 @@ ruby_version_is '2.4' do it 'prefers later captures' do /\A(?<a>.)(?<b>.)(?<b>.)(?<a>.)\z/.match('0123').named_captures.should == { 'a' => '3', 'b' => '2' } end + + it 'returns the latest matched capture, even if a later one that does not match exists' do + /\A(?<a>.)(?<b>.)(?<b>.)(?<a>.)?\z/.match('012').named_captures.should == { 'a' => '0', 'b' => '2' } + end end end diff --git a/spec/ruby/core/module/name_spec.rb b/spec/ruby/core/module/name_spec.rb index 5b59b6adaa..f56e35f204 100644 --- a/spec/ruby/core/module/name_spec.rb +++ b/spec/ruby/core/module/name_spec.rb @@ -65,4 +65,14 @@ describe "Module#name" do ModuleSpecs::Anonymous::E = m m::N.name.should == "ModuleSpecs::Anonymous::E::N" end + + it "returns a mutable string" do + ModuleSpecs.name.frozen?.should be_false + end + + it "returns a mutable string that when mutated does not modify the original module name" do + ModuleSpecs.name << "foo" + + ModuleSpecs.name.should == "ModuleSpecs" + end end diff --git a/spec/ruby/core/process/clock_gettime_spec.rb b/spec/ruby/core/process/clock_gettime_spec.rb index 165f0db730..558e4fe717 100644 --- a/spec/ruby/core/process/clock_gettime_spec.rb +++ b/spec/ruby/core/process/clock_gettime_spec.rb @@ -1,6 +1,14 @@ require_relative '../../spec_helper' describe "Process.clock_gettime" do + platform_is_not :windows do + it 'can be called with all declared clocks' do + Process.constants.select { |c| c.to_s.start_with?('CLOCK_') }.each do |c| + Process.clock_gettime(Process.const_get(c)).should be_an_instance_of(Float) + end + end + end + describe 'time units' do it 'handles a fixed set of time units' do [:nanosecond, :microsecond, :millisecond, :second].each do |unit| diff --git a/spec/ruby/core/process/kill_spec.rb b/spec/ruby/core/process/kill_spec.rb index f7665ba087..14deba2451 100644 --- a/spec/ruby/core/process/kill_spec.rb +++ b/spec/ruby/core/process/kill_spec.rb @@ -31,6 +31,10 @@ describe "Process.kill" do Process.kill("SIGKILL", pid) }.should raise_error(Errno::ESRCH) end + + it "checks for existence and permissions to signal a process, but does not actually signal it, when using signal 0" do + Process.kill(0, @pid).should == 1 + end end platform_is_not :windows do @@ -65,7 +69,7 @@ platform_is_not :windows do @sp.result.should == "signaled" end - it "acceps an Integer as a signal value" do + it "accepts an Integer as a signal value" do Process.kill(15, @sp.pid) @sp.result.should == "signaled" end diff --git a/spec/ruby/core/process/spawn_spec.rb b/spec/ruby/core/process/spawn_spec.rb index 1bd1dfac83..43e389ad7f 100644 --- a/spec/ruby/core/process/spawn_spec.rb +++ b/spec/ruby/core/process/spawn_spec.rb @@ -11,22 +11,22 @@ describe :process_spawn_does_not_close_std_streams, shared: true do it "does not close STDIN" do code = "STDOUT.puts STDIN.read(0).inspect" cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})" - ruby_exe(cmd, args: "> #{@output}") - File.binread(@output).should == %[""#{newline}] + ruby_exe(cmd, args: "> #{@name}") + File.binread(@name).should == %[""#{newline}] end it "does not close STDOUT" do code = "STDOUT.puts 'hello'" cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})" - ruby_exe(cmd, args: "> #{@output}") - File.binread(@output).should == "hello#{newline}" + ruby_exe(cmd, args: "> #{@name}") + File.binread(@name).should == "hello#{newline}" end it "does not close STDERR" do code = "STDERR.puts 'hello'" cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})" - ruby_exe(cmd, args: "2> #{@output}") - File.binread(@output).should =~ /hello#{newline}/ + ruby_exe(cmd, args: "2> #{@name}") + File.binread(@name).should =~ /hello#{newline}/ end end end @@ -391,6 +391,50 @@ describe "Process.spawn" do end end + # chdir + + platform_is :linux do + describe "inside Dir.chdir" do + def child_pids(pid) + `pgrep -P #{pid}`.lines.map { |child| Integer(child) } + end + + it "does not create extra process without chdir" do + pid = Process.spawn("sleep 10") + begin + child_pids(pid).size.should == 0 + ensure + Process.kill("TERM", pid) + Process.wait(pid) + end + end + + it "kills extra chdir processes" do + pid = nil + Dir.chdir("/tmp") do + pid = Process.spawn("sleep 10") + end + + children = child_pids(pid) + children.size.should <= 1 + + Process.kill("TERM", pid) + Process.wait(pid) + + if children.size > 0 + # wait a bit for children to die + sleep(1) + + children.each do |child| + lambda do + Process.kill("TERM", child) + end.should raise_error(Errno::ESRCH) + end + end + end + end + end + # :umask it "uses the current umask by default" do @@ -490,20 +534,19 @@ describe "Process.spawn" do context "when passed close_others: true" do before :each do - @output = tmp("spawn_close_others_true") @options = { close_others: true } end - after :each do - rm_r @output - end - - it "closes file descriptors >= 3 in the child process" do + it "closes file descriptors >= 3 in the child process even if fds are set close_on_exec=false" do + touch @name IO.pipe do |r, w| + r.close_on_exec = false + w.close_on_exec = false + begin pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options) w.close - lambda { r.read_nonblock(1) }.should raise_error(EOFError) + r.read(1).should == nil ensure rm_r @name Process.wait(pid) if pid @@ -516,20 +559,16 @@ describe "Process.spawn" do context "when passed close_others: false" do before :each do - @output = tmp("spawn_close_others_false") @options = { close_others: false } end - after :each do - rm_r @output - end - it "closes file descriptors >= 3 in the child process because they are set close_on_exec by default" do + touch @name IO.pipe do |r, w| begin pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options) w.close - lambda { r.read_nonblock(1) }.should raise_error(EOFError) + r.read(1).should == nil ensure rm_r @name Process.wait(pid) if pid @@ -542,13 +581,14 @@ describe "Process.spawn" do IO.pipe do |r, w| r.close_on_exec = false w.close_on_exec = false + + code = "fd = IO.for_fd(#{w.fileno}); fd.write 'abc'; fd.close" + pid = Process.spawn(ruby_cmd(code), @options) begin - pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options) w.close - lambda { r.read_nonblock(1) }.should raise_error(Errno::EAGAIN) + r.read.should == 'abc' ensure - rm_r @name - Process.wait(pid) if pid + Process.wait(pid) end end end @@ -623,7 +663,7 @@ describe "Process.spawn" do end it "maps the key to a file descriptor in the child that inherits the file descriptor from the parent specified by the value" do - child_fd = @io.fileno + 1 + child_fd = find_unused_fd args = ruby_cmd(fixture(__FILE__, "map_fd.rb"), args: [child_fd.to_s]) pid = Process.spawn(*args, { child_fd => @io }) Process.waitpid pid diff --git a/spec/ruby/core/string/scan_spec.rb b/spec/ruby/core/string/scan_spec.rb index 64a1c31443..ac82f5e236 100644 --- a/spec/ruby/core/string/scan_spec.rb +++ b/spec/ruby/core/string/scan_spec.rb @@ -84,6 +84,11 @@ describe "String#scan" do a = "hello".taint.scan(/./) a.each { |m| m.tainted?.should be_true } end + + # jruby/jruby#5513 + it "does not raise any errors when passed a multi-byte string" do + "あああaaaあああ".scan("あああ").should == ["あああ", "あああ"] + end end describe "String#scan with pattern and block" do diff --git a/spec/ruby/core/string/unpack/l_spec.rb b/spec/ruby/core/string/unpack/l_spec.rb index 6f9fcd4fd0..18bb68b8d0 100644 --- a/spec/ruby/core/string/unpack/l_spec.rb +++ b/spec/ruby/core/string/unpack/l_spec.rb @@ -14,7 +14,7 @@ describe "String#unpack with format 'L'" do it_behaves_like :string_unpack_32bit_be_unsigned, 'L>' end - guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + platform_is wordsize: 32 do describe "with modifier '<' and '_'" do it_behaves_like :string_unpack_32bit_le, 'L<_' it_behaves_like :string_unpack_32bit_le, 'L_<' @@ -44,7 +44,7 @@ describe "String#unpack with format 'L'" do end end - guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + platform_is wordsize: 64 do describe "with modifier '<' and '_'" do it_behaves_like :string_unpack_64bit_le, 'L<_' it_behaves_like :string_unpack_64bit_le, 'L_<' @@ -86,7 +86,7 @@ describe "String#unpack with format 'l'" do it_behaves_like :string_unpack_32bit_be_signed, 'l>' end - guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + platform_is wordsize: 32 do describe "with modifier '<' and '_'" do it_behaves_like :string_unpack_32bit_le, 'l<_' it_behaves_like :string_unpack_32bit_le, 'l_<' @@ -116,7 +116,7 @@ describe "String#unpack with format 'l'" do end end - guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + platform_is wordsize: 64 do describe "with modifier '<' and '_'" do it_behaves_like :string_unpack_64bit_le, 'l<_' it_behaves_like :string_unpack_64bit_le, 'l_<' @@ -160,7 +160,7 @@ little_endian do it_behaves_like :string_unpack_32bit_le_signed, 'l' end - guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + platform_is wordsize: 32 do describe "String#unpack with format 'L' with modifier '_'" do it_behaves_like :string_unpack_32bit_le, 'L_' it_behaves_like :string_unpack_32bit_le_unsigned, 'L_' @@ -182,7 +182,7 @@ little_endian do end end - guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + platform_is wordsize: 64 do describe "String#unpack with format 'L' with modifier '_'" do it_behaves_like :string_unpack_64bit_le, 'L_' it_behaves_like :string_unpack_64bit_le_unsigned, 'L_' @@ -218,7 +218,7 @@ big_endian do it_behaves_like :string_unpack_32bit_be_signed, 'l' end - guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do + platform_is wordsize: 32 do describe "String#unpack with format 'L' with modifier '_'" do it_behaves_like :string_unpack_32bit_be, 'L_' it_behaves_like :string_unpack_32bit_be_unsigned, 'L_' @@ -240,7 +240,7 @@ big_endian do end end - guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do + platform_is wordsize: 64 do describe "String#unpack with format 'L' with modifier '_'" do it_behaves_like :string_unpack_64bit_be, 'L_' it_behaves_like :string_unpack_64bit_be_unsigned, 'L_' diff --git a/spec/ruby/language/return_spec.rb b/spec/ruby/language/return_spec.rb index 0a05914545..0d7e74a6a1 100644 --- a/spec/ruby/language/return_spec.rb +++ b/spec/ruby/language/return_spec.rb @@ -442,6 +442,21 @@ describe "The return keyword" do end end + describe "within a block within a class" do + it "is allowed" do + File.write(@filename, <<-END_OF_CODE) + class A + ScratchPad << "before return" + 1.times { return } + ScratchPad << "after return" + end + END_OF_CODE + + load @filename + ScratchPad.recorded.should == ["before return"] + end + end + describe "file loading" do it "stops file loading and execution" do File.write(@filename, <<-END_OF_CODE) diff --git a/spec/ruby/library/datetime/add_spec.rb b/spec/ruby/library/datetime/add_spec.rb index a6d98914bd..20288e2105 100644 --- a/spec/ruby/library/datetime/add_spec.rb +++ b/spec/ruby/library/datetime/add_spec.rb @@ -4,6 +4,6 @@ require 'date' describe "DateTime#+" do it "is able to add sub-millisecond precision values" do datetime = DateTime.new(2017) - (datetime + 0.00001).to_time.usec.should == 864000 + (datetime + 0.00001001).to_time.usec.should == 864864 end end diff --git a/spec/ruby/library/datetime/subtract_spec.rb b/spec/ruby/library/datetime/subtract_spec.rb index ed88533246..ba01f4eff6 100644 --- a/spec/ruby/library/datetime/subtract_spec.rb +++ b/spec/ruby/library/datetime/subtract_spec.rb @@ -4,6 +4,16 @@ require 'date' describe "DateTime#-" do it "is able to subtract sub-millisecond precision values" do date = DateTime.new(2017) - ((date + 0.00001) - date).should == Rational(1, 100000) + diff = Rational(123456789, 24*60*60*1000*1000) + ((date + diff) - date).should == diff + (date - (date + diff)).should == -diff + (date - (date - diff)).should == diff + ((date - diff) - date).should == -diff + end + + it "correctly calculates sub-millisecond time differences" do #5493 + dt1 = DateTime.new(2018, 1, 1, 0, 0, 30) + dt2 = DateTime.new(2018, 1, 1, 0, 1, 29.000001) + ((dt2 - dt1) * 24 * 60 * 60).should == 59.000001 end end diff --git a/spec/ruby/library/stringio/set_encoding_spec.rb b/spec/ruby/library/stringio/set_encoding_spec.rb index 17aa7df23e..21d45750f3 100644 --- a/spec/ruby/library/stringio/set_encoding_spec.rb +++ b/spec/ruby/library/stringio/set_encoding_spec.rb @@ -2,9 +2,19 @@ require 'stringio' require_relative '../../spec_helper' describe "StringIO#set_encoding" do - it "sets the encoding of the underlying String" do - io = StringIO.new + it "sets the encoding of the underlying String if the String is not frozen" do + str = "".encode(Encoding::US_ASCII) + + io = StringIO.new(str) io.set_encoding Encoding::UTF_8 io.string.encoding.should == Encoding::UTF_8 end + + it "does not set the encoding of the underlying String if the String is frozen" do + str = "".encode(Encoding::US_ASCII).freeze + + io = StringIO.new(str) + io.set_encoding Encoding::UTF_8 + io.string.encoding.should == Encoding::US_ASCII + end end diff --git a/spec/ruby/optional/capi/encoding_spec.rb b/spec/ruby/optional/capi/encoding_spec.rb index 583b33d736..ce3bb9fa54 100644 --- a/spec/ruby/optional/capi/encoding_spec.rb +++ b/spec/ruby/optional/capi/encoding_spec.rb @@ -42,6 +42,8 @@ describe :rb_enc_set_index, shared: true do end describe "C-API Encoding function" do + @n = 0 + before :each do @s = CApiEncodingSpecs.new end @@ -49,8 +51,9 @@ describe "C-API Encoding function" do ruby_version_is "2.6" do describe "rb_enc_alias" do it "creates an alias for an existing Encoding" do - @s.rb_enc_alias("ZOMGWTFBBQ", "UTF-8").should >= 0 - Encoding.find("ZOMGWTFBBQ").name.should == "UTF-8" + name = "ZOMGWTFBBQ#{@n += 1}" + @s.rb_enc_alias(name, "UTF-8").should >= 0 + Encoding.find(name).name.should == "UTF-8" end end end diff --git a/spec/ruby/optional/capi/time_spec.rb b/spec/ruby/optional/capi/time_spec.rb index 4a59c98100..1191ceabd2 100644 --- a/spec/ruby/optional/capi/time_spec.rb +++ b/spec/ruby/optional/capi/time_spec.rb @@ -165,7 +165,7 @@ describe "CApiTimeSpecs" do usec.should == 500000 end - platform_is_not :mingw32 do + guard -> { platform_is_not :mingw or ruby_version_is '2.5' } do it "creates a timeval for a negative Fixnum" do sec, usec = @s.rb_time_timeval(-1232141421) sec.should be_kind_of(Integer) @@ -224,7 +224,7 @@ describe "CApiTimeSpecs" do nsec.should == 500000000 end - platform_is_not :mingw32 do + guard -> { platform_is_not :mingw or ruby_version_is '2.5' } do it "creates a timespec for a negative Fixnum" do sec, nsec = @s.rb_time_timespec(-1232141421) sec.should be_kind_of(Integer) |