1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66622 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
eregon 2018-12-29 00:22:52 +00:00
parent 548defb608
commit 2076c2c3c4
34 changed files with 759 additions and 328 deletions

View file

@ -10,13 +10,16 @@ matrix:
env: MSPEC_OPTS="-R2 -ff" env: MSPEC_OPTS="-R2 -ff"
- rvm: 2.3.8 - rvm: 2.3.8
- rvm: 2.4.5 - rvm: 2.4.5
env: CHECK_LEAKS=true
- rvm: 2.5.3 - rvm: 2.5.3
env: CHECK_LEAKS=true env: CHECK_LEAKS=true
- rvm: 2.6.0
env: CHECK_LEAKS=true
- rvm: ruby-head - rvm: ruby-head
- env: RUBOCOP=true - env: RUBOCOP=true
rvm: 2.4.5 rvm: 2.4.5
script: script:
- gem install rubocop -v 0.54.0 - gem install rubocop:0.61.0
- rubocop - rubocop
allow_failures: allow_failures:
- rvm: ruby-head - rvm: ruby-head

View file

@ -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

View file

@ -1,87 +1,7 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/difference'
describe "Array#-" do describe "Array#-" do
it "creates an array minus any items from other array" do it_behaves_like :array_binary_difference, :-
([] - [ 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
end end

View file

@ -29,7 +29,7 @@ describe "Array#pack with format 'L'" do
it_behaves_like :array_pack_32bit_be, 'L>' it_behaves_like :array_pack_32bit_be, 'L>'
end end
guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do platform_is wordsize: 32 do
describe "with modifier '<' and '_'" do describe "with modifier '<' and '_'" do
it_behaves_like :array_pack_32bit_le, 'L<_' it_behaves_like :array_pack_32bit_le, 'L<_'
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
end end
guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do platform_is wordsize: 64 do
describe "with modifier '<' and '_'" do describe "with modifier '<' and '_'" do
it_behaves_like :array_pack_64bit_le, 'L<_' it_behaves_like :array_pack_64bit_le, 'L<_'
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>' it_behaves_like :array_pack_32bit_be, 'l>'
end end
guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do platform_is wordsize: 32 do
describe "with modifier '<' and '_'" do describe "with modifier '<' and '_'" do
it_behaves_like :array_pack_32bit_le, 'l<_' it_behaves_like :array_pack_32bit_le, 'l<_'
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
end end
guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do platform_is wordsize: 64 do
describe "with modifier '<' and '_'" do describe "with modifier '<' and '_'" do
it_behaves_like :array_pack_64bit_le, 'l<_' it_behaves_like :array_pack_64bit_le, 'l<_'
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' it_behaves_like :array_pack_32bit_le, 'l'
end 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 describe "Array#pack with format 'L' with modifier '_'" do
it_behaves_like :array_pack_32bit_le, 'L_' it_behaves_like :array_pack_32bit_le, 'L_'
end end
@ -155,7 +155,7 @@ little_endian do
end end
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 describe "Array#pack with format 'L' with modifier '_'" do
it_behaves_like :array_pack_64bit_le, 'L_' it_behaves_like :array_pack_64bit_le, 'L_'
end end
@ -183,7 +183,7 @@ big_endian do
it_behaves_like :array_pack_32bit_be, 'l' it_behaves_like :array_pack_32bit_be, 'l'
end 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 describe "Array#pack with format 'L' with modifier '_'" do
it_behaves_like :array_pack_32bit_be, 'L_' it_behaves_like :array_pack_32bit_be, 'L_'
end end
@ -201,7 +201,7 @@ big_endian do
end end
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 describe "Array#pack with format 'L' with modifier '_'" do
it_behaves_like :array_pack_64bit_be, 'L_' it_behaves_like :array_pack_64bit_be, 'L_'
end end

View file

@ -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

View file

@ -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

View file

@ -1,82 +1,27 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
require_relative 'shared/union'
describe "Array#|" do describe "Array#|" do
it "returns an array of elements that appear in either array (union)" do it_behaves_like :array_binary_union, :|
([] | []).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 end
it "creates an array with no duplicates" do ruby_version_is "2.6" do
([ 1, 2, 3, 1, 4, 5 ] | [ 1, 3, 4, 5, 3, 6 ]).should == [1, 2, 3, 4, 5, 6] describe "Array#union" do
end it_behaves_like :array_binary_union, :union
it "creates an array with elements in order they are first encountered" do it "returns unique elements when given no argument" do
([ 1, 2, 3, 1 ] | [ 1, 3, 4, 5 ]).should == [1, 2, 3, 4, 5] x = [1, 2, 3, 2]
end x.union.should == [1, 2, 3]
it "properly handles recursive arrays" do
empty = ArraySpecs.empty_recursive_array
(empty | empty).should == empty
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
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]
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 end
it "does not return subclass instances for Array subclasses" do 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].union.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 end
it "does not call to_ary on array subclasses" do it "accepts multiple arguments" do
([1, 2] | ArraySpecs::ToAryArray[5, 6]).should == [1, 2, 5, 6] x = [1, 2, 3]
end x.union(x, x, x, x, [3, 4], x).should == [1, 2, 3, 4]
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] | [x]).should == [x]
end end
end end

View file

@ -60,4 +60,13 @@ describe :dir_open, shared: true do
dir = Dir.send(@method, DirSpecs.mock_dir, encoding: nil) {|d| d } dir = Dir.send(@method, DirSpecs.mock_dir, encoding: nil) {|d| d }
dir.should be_kind_of(Dir) dir.should be_kind_of(Dir)
end 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 end

View file

@ -15,7 +15,7 @@ describe "File.atime" do
File.atime(@file).should be_kind_of(Time) File.atime(@file).should be_kind_of(Time)
end 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. ## 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 it "returns the last access time for the named file with microseconds" do
supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d+)/, 1], 10) supports_subseconds = Integer(`stat -c%x '#{__FILE__}'`[/\.(\d+)/, 1], 10)

View file

@ -14,7 +14,7 @@ describe "File.ctime" do
File.ctime(@file).should be_kind_of(Time) File.ctime(@file).should be_kind_of(Time)
end 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 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) supports_subseconds = Integer(`stat -c%z '#{__FILE__}'`[/\.(\d+)/, 1], 10)
if supports_subseconds != 0 if supports_subseconds != 0

View file

@ -15,7 +15,7 @@ describe "File.mtime" do
File.mtime(@filename).should be_close(@mtime, 2.0) File.mtime(@filename).should be_close(@mtime, 2.0)
end 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 it "returns the modification Time of the file with microseconds" do
supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d+)/, 1], 10) supports_subseconds = Integer(`stat -c%y '#{__FILE__}'`[/\.(\d+)/, 1], 10)
if supports_subseconds != 0 if supports_subseconds != 0

View file

@ -25,7 +25,7 @@ describe "File#reopen" do
@file.read.should == @content_b @file.read.should == @content_b
end 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 = File.new(@name_a).reopen(mock_to_path(@name_b), "r")
@file.read.should == @content_b @file.read.should == @content_b
end end

View file

@ -1,6 +1,11 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
describe "File.utime" do describe "File.utime" do
before :all do
@time_is_float = /mswin|mingw/ =~ RUBY_PLATFORM && RUBY_VERSION >= '2.5'
end
before :each do before :each do
@atime = Time.now @atime = Time.now
@mtime = Time.now @mtime = Time.now
@ -16,31 +21,54 @@ describe "File.utime" do
it "sets the access and modification time of each file" do it "sets the access and modification time of each file" do
File.utime(@atime, @mtime, @file1, @file2) File.utime(@atime, @mtime, @file1, @file2)
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.atime(@file1).to_i.should be_close(@atime.to_i, 2)
File.mtime(@file1).to_i.should be_close(@mtime.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.atime(@file2).to_i.should be_close(@atime.to_i, 2)
File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2) File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2)
end end
end
it "uses the current times if two nil values are passed" do it "uses the current times if two nil values are passed" do
tn = Time.now
File.utime(nil, nil, @file1, @file2) File.utime(nil, nil, @file1, @file2)
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.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.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.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) File.mtime(@file2).to_i.should be_close(Time.now.to_i, 2)
end end
end
it "accepts an object that has a #to_path method" do it "accepts an object that has a #to_path method" do
File.utime(@atime, @mtime, mock_to_path(@file1), mock_to_path(@file2)) File.utime(@atime, @mtime, mock_to_path(@file1), mock_to_path(@file2))
end end
it "accepts numeric atime and mtime arguments" do it "accepts numeric atime and mtime arguments" do
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.utime(@atime.to_i, @mtime.to_i, @file1, @file2)
File.atime(@file1).to_i.should be_close(@atime.to_i, 2) 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.mtime(@file1).to_i.should be_close(@mtime.to_i, 2)
File.atime(@file2).to_i.should be_close(@atime.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) File.mtime(@file2).to_i.should be_close(@mtime.to_i, 2)
end end
end
platform_is :linux do platform_is :linux do
platform_is wordsize: 64 do platform_is wordsize: 64 do

View file

@ -10,12 +10,10 @@ describe "Float#round" do
0.0.round.should == 0 0.0.round.should == 0
end end
platform_is_not :mingw32 do
it "returns the nearest Integer for Float near the limit" do it "returns the nearest Integer for Float near the limit" do
0.49999999999999994.round.should == 0 0.49999999999999994.round.should == 0
-0.49999999999999994.round.should == 0 -0.49999999999999994.round.should == 0
end end
end
it "raises FloatDomainError for exceptional values" do it "raises FloatDomainError for exceptional values" do
lambda { (+infinity_value).round }.should raise_error(FloatDomainError) lambda { (+infinity_value).round }.should raise_error(FloatDomainError)

View file

@ -3,7 +3,7 @@ require_relative 'fixtures/classes'
describe "IO#dup" do describe "IO#dup" do
before :each do before :each do
@file = tmp("rubinius_spec_io_dup_#{$$}_#{Time.now.to_f}") @file = tmp("spec_io_dup")
@f = File.open @file, 'w+' @f = File.open @file, 'w+'
@i = @f.dup @i = @f.dup
@ -66,4 +66,22 @@ end
it "raises IOError on closed stream" do it "raises IOError on closed stream" do
lambda { IOSpecs.closed_io.dup }.should raise_error(IOError) lambda { IOSpecs.closed_io.dup }.should raise_error(IOError)
end 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 end

View file

@ -162,6 +162,18 @@ describe "IO#reopen with a String" do
end end
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 it "creates the file if it doesn't exist if the IO is opened in write mode" do
@io = new_io @name, "w" @io = new_io @name, "w"
@ -294,6 +306,18 @@ describe "IO#reopen with an IO" do
File.read(@other_name).should == "io data" File.read(@other_name).should == "io data"
end 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 it "may change the class of the instance" do
@io.reopen @other_io @io.reopen @other_io
@io.should be_an_instance_of(File) @io.should be_an_instance_of(File)

View file

@ -1,17 +1,10 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
describe "Kernel#=~" do 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 it "returns nil matching any object" do
o = Object.new o = Object.new
suppress_warning do
(o =~ /Object/).should be_nil (o =~ /Object/).should be_nil
(o =~ 'Object').should be_nil (o =~ 'Object').should be_nil
(o =~ Object).should be_nil (o =~ Object).should be_nil
@ -20,3 +13,4 @@ describe "Kernel#=~" do
(o =~ true).should be_nil (o =~ true).should be_nil
end end
end end
end

View file

@ -63,6 +63,10 @@ describe "Marshal.dump" do
"\x04\bI:\b\xE2\x86\x92\x06:\x06ET"], "\x04\bI:\b\xE2\x86\x92\x06:\x06ET"],
[Marshal, s.encode("utf-16").to_sym, [Marshal, s.encode("utf-16").to_sym,
"\x04\bI:\t\xFE\xFF!\x92\x06:\rencoding\"\vUTF-16"], "\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, [Marshal, s.encode("euc-jp").to_sym,
"\x04\bI:\a\xA2\xAA\x06:\rencoding\"\vEUC-JP"], "\x04\bI:\a\xA2\xAA\x06:\rencoding\"\vEUC-JP"],
[Marshal, s.encode("sjis").to_sym, [Marshal, s.encode("sjis").to_sym,
@ -74,20 +78,6 @@ describe "Marshal.dump" do
s = "\u2192".force_encoding("binary").to_sym s = "\u2192".force_encoding("binary").to_sym
Marshal.dump(s).should == "\x04\b:\b\xE2\x86\x92" Marshal.dump(s).should == "\x04\b:\b\xE2\x86\x92"
end 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 end
describe "with an object responding to #marshal_dump" do 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" Marshal.dump(obj).should == "\004\bo:\vObject\006:\n@ivari\006"
end 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 it "dumps an Object that has had an instance variable added and removed as though it was never set" do
obj = Object.new obj = Object.new
obj.instance_variable_set(:@ivar, 1) obj.instance_variable_set(:@ivar, 1)

View file

@ -352,6 +352,54 @@ describe :marshal_load, shared: true do
end end
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 describe "for a String" do
it "loads a string having ivar with ref to self" do it "loads a string having ivar with ref to self" do
obj = 'hi' obj = 'hi'
@ -485,32 +533,11 @@ describe :marshal_load, shared: true do
end end
end end
describe "for a user Class" do describe "for an Object" do
it "loads a user-marshaled extended object" do
obj = UserMarshal.new.extend(Meths)
new_obj = Marshal.send(@method, "\004\bU:\020UserMarshal\"\nstuff")
new_obj.should == obj
new_obj_metaclass_ancestors = class << new_obj; ancestors; end
new_obj_metaclass_ancestors[@num_self_class].should == UserMarshal
end
it "loads a user_object" do
UserObject.new
Marshal.send(@method, "\004\bo:\017UserObject\000").should be_kind_of(UserObject)
end
it "loads an object" do it "loads an object" do
Marshal.send(@method, "\004\bo:\vObject\000").should be_kind_of(Object) Marshal.send(@method, "\004\bo:\vObject\000").should be_kind_of(Object)
end end
it "raises ArgumentError if the object from an 'o' stream is not dumpable as 'o' type user class" do
lambda do
Marshal.send(@method, "\x04\bo:\tFile\001\001:\001\005@path\"\x10/etc/passwd")
end.should raise_error(ArgumentError)
end
it "loads an extended Object" do it "loads an extended Object" do
obj = Object.new.extend(Meths) obj = Object.new.extend(Meths)
@ -521,6 +548,48 @@ describe :marshal_load, shared: true do
new_obj_metaclass_ancestors[@num_self_class, 2].should == [Meths, Object] new_obj_metaclass_ancestors[@num_self_class, 2].should == [Meths, Object]
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
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
lambda do
Marshal.send(@method, "\x04\bo:\tFile\001\001:\001\005@path\"\x10/etc/passwd")
end.should raise_error(ArgumentError)
end
end
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\bU:\020UserMarshal\"\nstuff")
new_obj.should == obj
new_obj_metaclass_ancestors = class << new_obj; ancestors; end
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 describe "that extends a core type other than Object or BasicObject" do
after :each do after :each do
MarshalSpec.reset_swapped_class MarshalSpec.reset_swapped_class
@ -537,18 +606,6 @@ describe :marshal_load, shared: true do
lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError) lambda { Marshal.send(@method, data) }.should raise_error(ArgumentError)
end end
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 end
describe "for a Regexp" do describe "for a Regexp" do

View file

@ -3,7 +3,8 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
describe "MatchData#begin" do describe "MatchData#begin" do
it "returns the offset of the start of the nth element" do 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 = /(.)(.)(\d+)(\d)/.match("THX1138.")
match_data.begin(0).should == 1 match_data.begin(0).should == 1
match_data.begin(2).should == 2 match_data.begin(2).should == 2
@ -14,17 +15,90 @@ describe "MatchData#begin" do
match_data.begin(1).should be_nil match_data.begin(1).should be_nil
end end
it "returns the offset for multi byte strings" do it "returns the character offset for multi-byte strings" do
match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.") match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.")
match_data.begin(0).should == 1 match_data.begin(0).should == 1
match_data.begin(2).should == 2 match_data.begin(2).should == 2
end end
not_supported_on :opal do not_supported_on :opal do
it "returns the offset for multi byte strings with unicode regexp" do it "returns the character offset for multi-byte strings with unicode regexp" do
match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.") match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.")
match_data.begin(0).should == 1 match_data.begin(0).should == 1
match_data.begin(2).should == 2 match_data.begin(2).should == 2
end end
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
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
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 end

View file

@ -3,7 +3,8 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
describe "MatchData#end" do describe "MatchData#end" do
it "returns the offset of the end of the nth element" do 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 = /(.)(.)(\d+)(\d)/.match("THX1138.")
match_data.end(0).should == 7 match_data.end(0).should == 7
match_data.end(2).should == 3 match_data.end(2).should == 3
@ -14,17 +15,90 @@ describe "MatchData#end" do
match_data.end(1).should be_nil match_data.end(1).should be_nil
end end
it "returns the offset for multi byte strings" do it "returns the character offset for multi-byte strings" do
match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.") match_data = /(.)(.)(\d+)(\d)/.match("TñX1138.")
match_data.end(0).should == 7 match_data.end(0).should == 7
match_data.end(2).should == 3 match_data.end(2).should == 3
end end
not_supported_on :opal do not_supported_on :opal do
it "returns the offset for multi byte strings with unicode regexp" do it "returns the character offset for multi-byte strings with unicode regexp" do
match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.") match_data = /(.)(.)(\d+)(\d)/u.match("TñX1138.")
match_data.end(0).should == 7 match_data.end(0).should == 7
match_data.end(2).should == 3 match_data.end(2).should == 3
end end
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
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
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 end

View file

@ -9,5 +9,9 @@ ruby_version_is '2.4' do
it 'prefers later captures' do it 'prefers later captures' do
/\A(?<a>.)(?<b>.)(?<b>.)(?<a>.)\z/.match('0123').named_captures.should == { 'a' => '3', 'b' => '2' } /\A(?<a>.)(?<b>.)(?<b>.)(?<a>.)\z/.match('0123').named_captures.should == { 'a' => '3', 'b' => '2' }
end 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
end end

View file

@ -65,4 +65,14 @@ describe "Module#name" do
ModuleSpecs::Anonymous::E = m ModuleSpecs::Anonymous::E = m
m::N.name.should == "ModuleSpecs::Anonymous::E::N" m::N.name.should == "ModuleSpecs::Anonymous::E::N"
end 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 end

View file

@ -1,6 +1,14 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
describe "Process.clock_gettime" do 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 describe 'time units' do
it 'handles a fixed set of time units' do it 'handles a fixed set of time units' do
[:nanosecond, :microsecond, :millisecond, :second].each do |unit| [:nanosecond, :microsecond, :millisecond, :second].each do |unit|

View file

@ -31,6 +31,10 @@ describe "Process.kill" do
Process.kill("SIGKILL", pid) Process.kill("SIGKILL", pid)
}.should raise_error(Errno::ESRCH) }.should raise_error(Errno::ESRCH)
end 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 end
platform_is_not :windows do platform_is_not :windows do
@ -65,7 +69,7 @@ platform_is_not :windows do
@sp.result.should == "signaled" @sp.result.should == "signaled"
end end
it "acceps an Integer as a signal value" do it "accepts an Integer as a signal value" do
Process.kill(15, @sp.pid) Process.kill(15, @sp.pid)
@sp.result.should == "signaled" @sp.result.should == "signaled"
end end

View file

@ -11,22 +11,22 @@ describe :process_spawn_does_not_close_std_streams, shared: true do
it "does not close STDIN" do it "does not close STDIN" do
code = "STDOUT.puts STDIN.read(0).inspect" code = "STDOUT.puts STDIN.read(0).inspect"
cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})" cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})"
ruby_exe(cmd, args: "> #{@output}") ruby_exe(cmd, args: "> #{@name}")
File.binread(@output).should == %[""#{newline}] File.binread(@name).should == %[""#{newline}]
end end
it "does not close STDOUT" do it "does not close STDOUT" do
code = "STDOUT.puts 'hello'" code = "STDOUT.puts 'hello'"
cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})" cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})"
ruby_exe(cmd, args: "> #{@output}") ruby_exe(cmd, args: "> #{@name}")
File.binread(@output).should == "hello#{newline}" File.binread(@name).should == "hello#{newline}"
end end
it "does not close STDERR" do it "does not close STDERR" do
code = "STDERR.puts 'hello'" code = "STDERR.puts 'hello'"
cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})" cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})"
ruby_exe(cmd, args: "2> #{@output}") ruby_exe(cmd, args: "2> #{@name}")
File.binread(@output).should =~ /hello#{newline}/ File.binread(@name).should =~ /hello#{newline}/
end end
end end
end end
@ -391,6 +391,50 @@ describe "Process.spawn" do
end end
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 # :umask
it "uses the current umask by default" do it "uses the current umask by default" do
@ -490,20 +534,19 @@ describe "Process.spawn" do
context "when passed close_others: true" do context "when passed close_others: true" do
before :each do before :each do
@output = tmp("spawn_close_others_true")
@options = { close_others: true } @options = { close_others: true }
end end
after :each do it "closes file descriptors >= 3 in the child process even if fds are set close_on_exec=false" do
rm_r @output touch @name
end
it "closes file descriptors >= 3 in the child process" do
IO.pipe do |r, w| IO.pipe do |r, w|
r.close_on_exec = false
w.close_on_exec = false
begin begin
pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options) pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options)
w.close w.close
lambda { r.read_nonblock(1) }.should raise_error(EOFError) r.read(1).should == nil
ensure ensure
rm_r @name rm_r @name
Process.wait(pid) if pid Process.wait(pid) if pid
@ -516,20 +559,16 @@ describe "Process.spawn" do
context "when passed close_others: false" do context "when passed close_others: false" do
before :each do before :each do
@output = tmp("spawn_close_others_false")
@options = { close_others: false } @options = { close_others: false }
end 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 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| IO.pipe do |r, w|
begin begin
pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options) pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options)
w.close w.close
lambda { r.read_nonblock(1) }.should raise_error(EOFError) r.read(1).should == nil
ensure ensure
rm_r @name rm_r @name
Process.wait(pid) if pid Process.wait(pid) if pid
@ -542,13 +581,14 @@ describe "Process.spawn" do
IO.pipe do |r, w| IO.pipe do |r, w|
r.close_on_exec = false r.close_on_exec = false
w.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 begin
pid = Process.spawn(ruby_cmd("while File.exist? '#{@name}'; sleep 0.1; end"), @options)
w.close w.close
lambda { r.read_nonblock(1) }.should raise_error(Errno::EAGAIN) r.read.should == 'abc'
ensure ensure
rm_r @name Process.wait(pid)
Process.wait(pid) if pid
end end
end end
end end
@ -623,7 +663,7 @@ describe "Process.spawn" do
end 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 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]) args = ruby_cmd(fixture(__FILE__, "map_fd.rb"), args: [child_fd.to_s])
pid = Process.spawn(*args, { child_fd => @io }) pid = Process.spawn(*args, { child_fd => @io })
Process.waitpid pid Process.waitpid pid

View file

@ -84,6 +84,11 @@ describe "String#scan" do
a = "hello".taint.scan(/./) a = "hello".taint.scan(/./)
a.each { |m| m.tainted?.should be_true } a.each { |m| m.tainted?.should be_true }
end end
# jruby/jruby#5513
it "does not raise any errors when passed a multi-byte string" do
"あああaaaあああ".scan("あああ").should == ["あああ", "あああ"]
end
end end
describe "String#scan with pattern and block" do describe "String#scan with pattern and block" do

View file

@ -14,7 +14,7 @@ describe "String#unpack with format 'L'" do
it_behaves_like :string_unpack_32bit_be_unsigned, 'L>' it_behaves_like :string_unpack_32bit_be_unsigned, 'L>'
end end
guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do platform_is wordsize: 32 do
describe "with modifier '<' and '_'" do describe "with modifier '<' and '_'" do
it_behaves_like :string_unpack_32bit_le, 'L<_' it_behaves_like :string_unpack_32bit_le, 'L<_'
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
end end
guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do platform_is wordsize: 64 do
describe "with modifier '<' and '_'" do describe "with modifier '<' and '_'" do
it_behaves_like :string_unpack_64bit_le, 'L<_' it_behaves_like :string_unpack_64bit_le, 'L<_'
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>' it_behaves_like :string_unpack_32bit_be_signed, 'l>'
end end
guard -> { platform_is wordsize: 32 or platform_is :mingw32 } do platform_is wordsize: 32 do
describe "with modifier '<' and '_'" do describe "with modifier '<' and '_'" do
it_behaves_like :string_unpack_32bit_le, 'l<_' it_behaves_like :string_unpack_32bit_le, 'l<_'
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
end end
guard -> { platform_is wordsize: 64 and platform_is_not :mingw32 } do platform_is wordsize: 64 do
describe "with modifier '<' and '_'" do describe "with modifier '<' and '_'" do
it_behaves_like :string_unpack_64bit_le, 'l<_' it_behaves_like :string_unpack_64bit_le, 'l<_'
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' it_behaves_like :string_unpack_32bit_le_signed, 'l'
end 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 describe "String#unpack with format 'L' with modifier '_'" do
it_behaves_like :string_unpack_32bit_le, 'L_' it_behaves_like :string_unpack_32bit_le, 'L_'
it_behaves_like :string_unpack_32bit_le_unsigned, 'L_' it_behaves_like :string_unpack_32bit_le_unsigned, 'L_'
@ -182,7 +182,7 @@ little_endian do
end end
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 describe "String#unpack with format 'L' with modifier '_'" do
it_behaves_like :string_unpack_64bit_le, 'L_' it_behaves_like :string_unpack_64bit_le, 'L_'
it_behaves_like :string_unpack_64bit_le_unsigned, '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' it_behaves_like :string_unpack_32bit_be_signed, 'l'
end 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 describe "String#unpack with format 'L' with modifier '_'" do
it_behaves_like :string_unpack_32bit_be, 'L_' it_behaves_like :string_unpack_32bit_be, 'L_'
it_behaves_like :string_unpack_32bit_be_unsigned, 'L_' it_behaves_like :string_unpack_32bit_be_unsigned, 'L_'
@ -240,7 +240,7 @@ big_endian do
end end
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 describe "String#unpack with format 'L' with modifier '_'" do
it_behaves_like :string_unpack_64bit_be, 'L_' it_behaves_like :string_unpack_64bit_be, 'L_'
it_behaves_like :string_unpack_64bit_be_unsigned, 'L_' it_behaves_like :string_unpack_64bit_be_unsigned, 'L_'

View file

@ -442,6 +442,21 @@ describe "The return keyword" do
end end
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 describe "file loading" do
it "stops file loading and execution" do it "stops file loading and execution" do
File.write(@filename, <<-END_OF_CODE) File.write(@filename, <<-END_OF_CODE)

View file

@ -4,6 +4,6 @@ require 'date'
describe "DateTime#+" do describe "DateTime#+" do
it "is able to add sub-millisecond precision values" do it "is able to add sub-millisecond precision values" do
datetime = DateTime.new(2017) datetime = DateTime.new(2017)
(datetime + 0.00001).to_time.usec.should == 864000 (datetime + 0.00001001).to_time.usec.should == 864864
end end
end end

View file

@ -4,6 +4,16 @@ require 'date'
describe "DateTime#-" do describe "DateTime#-" do
it "is able to subtract sub-millisecond precision values" do it "is able to subtract sub-millisecond precision values" do
date = DateTime.new(2017) 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
end end

View file

@ -2,9 +2,19 @@ require 'stringio'
require_relative '../../spec_helper' require_relative '../../spec_helper'
describe "StringIO#set_encoding" do describe "StringIO#set_encoding" do
it "sets the encoding of the underlying String" do it "sets the encoding of the underlying String if the String is not frozen" do
io = StringIO.new str = "".encode(Encoding::US_ASCII)
io = StringIO.new(str)
io.set_encoding Encoding::UTF_8 io.set_encoding Encoding::UTF_8
io.string.encoding.should == Encoding::UTF_8 io.string.encoding.should == Encoding::UTF_8
end 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 end

View file

@ -42,6 +42,8 @@ describe :rb_enc_set_index, shared: true do
end end
describe "C-API Encoding function" do describe "C-API Encoding function" do
@n = 0
before :each do before :each do
@s = CApiEncodingSpecs.new @s = CApiEncodingSpecs.new
end end
@ -49,8 +51,9 @@ describe "C-API Encoding function" do
ruby_version_is "2.6" do ruby_version_is "2.6" do
describe "rb_enc_alias" do describe "rb_enc_alias" do
it "creates an alias for an existing Encoding" do it "creates an alias for an existing Encoding" do
@s.rb_enc_alias("ZOMGWTFBBQ", "UTF-8").should >= 0 name = "ZOMGWTFBBQ#{@n += 1}"
Encoding.find("ZOMGWTFBBQ").name.should == "UTF-8" @s.rb_enc_alias(name, "UTF-8").should >= 0
Encoding.find(name).name.should == "UTF-8"
end end
end end
end end

View file

@ -165,7 +165,7 @@ describe "CApiTimeSpecs" do
usec.should == 500000 usec.should == 500000
end 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 it "creates a timeval for a negative Fixnum" do
sec, usec = @s.rb_time_timeval(-1232141421) sec, usec = @s.rb_time_timeval(-1232141421)
sec.should be_kind_of(Integer) sec.should be_kind_of(Integer)
@ -224,7 +224,7 @@ describe "CApiTimeSpecs" do
nsec.should == 500000000 nsec.should == 500000000
end 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 it "creates a timespec for a negative Fixnum" do
sec, nsec = @s.rb_time_timespec(-1232141421) sec, nsec = @s.rb_time_timespec(-1232141421)
sec.should be_kind_of(Integer) sec.should be_kind_of(Integer)