diff options
author | eregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2019-01-20 20:38:57 +0000 |
---|---|---|
committer | eregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2019-01-20 20:38:57 +0000 |
commit | 6204e0804b24f1675b49d5880da014411bcfb831 (patch) | |
tree | ce6c00bf078fc416936ca3cdc972b9b3c1c78dae /spec/ruby/core | |
parent | 58573c33e4720315ed27491e31dcc22892e1ce95 (diff) | |
download | ruby-6204e0804b24f1675b49d5880da014411bcfb831.tar.gz |
Update to ruby/spec@35a9fba
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66888 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'spec/ruby/core')
75 files changed, 2049 insertions, 109 deletions
diff --git a/spec/ruby/core/array/difference_spec.rb b/spec/ruby/core/array/difference_spec.rb index 33ea8e1f3c..a357657967 100644 --- a/spec/ruby/core/array/difference_spec.rb +++ b/spec/ruby/core/array/difference_spec.rb @@ -4,7 +4,7 @@ require_relative 'shared/difference' ruby_version_is "2.6" do describe "Array#difference" do - it_behaves_like :array_binary_difference, :- + it_behaves_like :array_binary_difference, :difference it "returns a copy when called without any parameter" do x = [1, 2, 3, 2] diff --git a/spec/ruby/core/array/to_h_spec.rb b/spec/ruby/core/array/to_h_spec.rb index e845d2c950..27a0b6112f 100644 --- a/spec/ruby/core/array/to_h_spec.rb +++ b/spec/ruby/core/array/to_h_spec.rb @@ -36,9 +36,42 @@ describe "Array#to_h" do end ruby_version_is "2.6" do - it "converts [key, value] pairs returned by the block to a hash" do - i = 0 - [:a, :b].to_h {|k| [k, i += 1]}.should == { a: 1, b: 2 } + context "with block" do + it "converts [key, value] pairs returned by the block to a Hash" do + [:a, :b].to_h { |k| [k, k.to_s] }.should == { a: 'a', b: 'b' } + end + + it "raises ArgumentError if block returns longer or shorter array" do + -> do + [:a, :b].to_h { |k| [k, k.to_s, 1] } + end.should raise_error(ArgumentError, /wrong array length at 0/) + + -> do + [:a, :b].to_h { |k| [k] } + end.should raise_error(ArgumentError, /wrong array length at 0/) + end + + it "raises TypeError if block returns something other than Array" do + -> do + [:a, :b].to_h { |k| "not-array" } + end.should raise_error(TypeError, /wrong element type String at 0/) + end + + it "coerces returned pair to Array with #to_ary" do + x = mock('x') + x.stub!(:to_ary).and_return([:b, 'b']) + + [:a].to_h { |k| x }.should == { :b => 'b' } + end + + it "does not coerce returned pair to Array with #to_a" do + x = mock('x') + x.stub!(:to_a).and_return([:b, 'b']) + + -> do + [:a].to_h { |k| x } + end.should raise_error(TypeError, /wrong element type MockObject at 0/) + end end end end diff --git a/spec/ruby/core/binding/fixtures/location.rb b/spec/ruby/core/binding/fixtures/location.rb new file mode 100644 index 0000000000..a78ae75731 --- /dev/null +++ b/spec/ruby/core/binding/fixtures/location.rb @@ -0,0 +1,6 @@ +module BindingSpecs + module LocationMethod + FILE_PATH = __FILE__ + TEST_BINDING = binding + end +end diff --git a/spec/ruby/core/binding/source_location_spec.rb b/spec/ruby/core/binding/source_location_spec.rb new file mode 100644 index 0000000000..e562bc65c8 --- /dev/null +++ b/spec/ruby/core/binding/source_location_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/location' + +ruby_version_is "2.6" do + describe "Binding#source_location" do + it "returns an [file, line] pair" do + b = BindingSpecs::LocationMethod::TEST_BINDING + b.source_location.should == [BindingSpecs::LocationMethod::FILE_PATH, 4] + end + end +end diff --git a/spec/ruby/core/dir/children_spec.rb b/spec/ruby/core/dir/children_spec.rb index 4bc64934b3..c6329b416f 100644 --- a/spec/ruby/core/dir/children_spec.rb +++ b/spec/ruby/core/dir/children_spec.rb @@ -70,3 +70,69 @@ ruby_version_is "2.5" do end end end + +ruby_version_is "2.6" do + describe "Dir#children" do + before :all do + DirSpecs.create_mock_dirs + end + + before :each do + @internal = Encoding.default_internal + end + + after :all do + DirSpecs.delete_mock_dirs + end + + after :each do + Encoding.default_internal = @internal + @dir.close if @dir + end + + it "returns an Array of filenames in an existing directory including dotfiles" do + @dir = Dir.new(DirSpecs.mock_dir) + a = @dir.children.sort + @dir.close + + a.should == DirSpecs.expected_paths - %w[. ..] + + @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested") + a = @dir.children.sort + a.should == %w|.dotfile.ext directory| + end + + it "accepts an options Hash" do + @dir = Dir.new("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8") + a = @dir.children.sort + a.should == %w|.dotfile.ext directory| + end + + it "returns children encoded with the filesystem encoding by default" do + # This spec depends on the locale not being US-ASCII because if it is, the + # children that are not ascii_only? will be ASCII-8BIT encoded. + @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special')) + children = @dir.children.sort + encoding = Encoding.find("filesystem") + encoding = Encoding::ASCII_8BIT if encoding == Encoding::US_ASCII + platform_is_not :windows do + children.should include("こんにちは.txt".force_encoding(encoding)) + end + children.first.encoding.should equal(Encoding.find("filesystem")) + end + + it "returns children encoded with the specified encoding" do + path = File.join(DirSpecs.mock_dir, 'special') + @dir = Dir.new(path, encoding: "euc-jp") + children = @dir.children.sort + children.first.encoding.should equal(Encoding::EUC_JP) + end + + it "returns children transcoded to the default internal encoding" do + Encoding.default_internal = Encoding::EUC_KR + @dir = Dir.new(File.join(DirSpecs.mock_dir, 'special')) + children = @dir.children.sort + children.first.encoding.should equal(Encoding::EUC_KR) + end + end +end diff --git a/spec/ruby/core/dir/each_child_spec.rb b/spec/ruby/core/dir/each_child_spec.rb index 913b9cb96f..c04d212bfa 100644 --- a/spec/ruby/core/dir/each_child_spec.rb +++ b/spec/ruby/core/dir/each_child_spec.rb @@ -51,3 +51,55 @@ ruby_version_is "2.5" do end end end + +ruby_version_is "2.6" do + describe "Dir#each_child" do + before :all do + DirSpecs.create_mock_dirs + end + + after :all do + DirSpecs.delete_mock_dirs + end + + after :each do + @dir.close if @dir + end + + it "yields all names in an existing directory to the provided block" do + a, b = [], [] + @dir = Dir.new(DirSpecs.mock_dir) + @dir2 = Dir.new("#{DirSpecs.mock_dir}/deeply/nested") + + @dir.each_child { |f| a << f } + @dir2.each_child { |f| b << f } + @dir2.close + + a.sort.should == DirSpecs.expected_paths - %w|. ..| + b.sort.should == %w|.dotfile.ext directory| + end + + it "returns self when successful" do + @dir = Dir.new(DirSpecs.mock_dir) + @dir.each_child { |f| f }.should == @dir + end + + describe "when no block is given" do + it "returns an Enumerator" do + @dir = Dir.new(DirSpecs.mock_dir) + + @dir.each_child.should be_an_instance_of(Enumerator) + @dir.each_child.to_a.sort.should == DirSpecs.expected_paths - %w|. ..| + end + + describe "returned Enumerator" do + describe "size" do + it "should return nil" do + @dir = Dir.new(DirSpecs.mock_dir) + @dir.each_child.size.should == nil + end + end + end + end + end +end diff --git a/spec/ruby/core/enumerable/chain_spec.rb b/spec/ruby/core/enumerable/chain_spec.rb new file mode 100644 index 0000000000..0e86a02905 --- /dev/null +++ b/spec/ruby/core/enumerable/chain_spec.rb @@ -0,0 +1,22 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +ruby_version_is "2.6" do + describe "Enumerable#chain" do + before :each do + ScratchPad.record [] + end + + it "returns a chain of self and provided enumerables" do + one = EnumerableSpecs::Numerous.new(1) + two = EnumerableSpecs::Numerous.new(2) + three = EnumerableSpecs::Numerous.new(3) + + chain = one.chain(two, three) + + chain.should be_an_instance_of(Enumerator::Chain) + chain.each { |item| ScratchPad << item } + ScratchPad.recorded.should == [1, 2, 3] + end + end +end diff --git a/spec/ruby/core/enumerable/to_h_spec.rb b/spec/ruby/core/enumerable/to_h_spec.rb index 966325c736..f15ced122e 100644 --- a/spec/ruby/core/enumerable/to_h_spec.rb +++ b/spec/ruby/core/enumerable/to_h_spec.rb @@ -45,10 +45,46 @@ describe "Enumerable#to_h" do end ruby_version_is "2.6" do - it "converts [key, value] pairs returned by the block to a hash" do - enum = EnumerableSpecs::EachDefiner.new(:a, :b) - i = 0 - enum.to_h {|k| [k, i += 1]}.should == { a: 1, b: 2 } + context "with block" do + before do + @enum = EnumerableSpecs::EachDefiner.new(:a, :b) + end + + it "converts [key, value] pairs returned by the block to a hash" do + @enum.to_h { |k| [k, k.to_s] }.should == { a: 'a', b: 'b' } + end + + it "raises ArgumentError if block returns longer or shorter array" do + -> do + @enum.to_h { |k| [k, k.to_s, 1] } + end.should raise_error(ArgumentError, /element has wrong array length/) + + -> do + @enum.to_h { |k| [k] } + end.should raise_error(ArgumentError, /element has wrong array length/) + end + + it "raises TypeError if block returns something other than Array" do + -> do + @enum.to_h { |k| "not-array" } + end.should raise_error(TypeError, /wrong element type String/) + end + + it "coerces returned pair to Array with #to_ary" do + x = mock('x') + x.stub!(:to_ary).and_return([:b, 'b']) + + @enum.to_h { |k| x }.should == { :b => 'b' } + end + + it "does not coerce returned pair to Array with #to_a" do + x = mock('x') + x.stub!(:to_a).and_return([:b, 'b']) + + -> do + @enum.to_h { |k| x } + end.should raise_error(TypeError, /wrong element type MockObject/) + end end end end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/begin_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/begin_spec.rb new file mode 100644 index 0000000000..c8d91ebaec --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/begin_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence#begin" do + it "returns the begin of the sequence" do + 1.step(10).begin.should == 1 + (1..10).step.begin.should == 1 + (1...10).step.begin.should == 1 + end + end +end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/each_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/each_spec.rb new file mode 100644 index 0000000000..d7edf3a21f --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/each_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence#each" do + before :each do + ScratchPad.record [] + @seq = 1.step(10, 4) + end + + it "calls given block on each item of the sequence" do + @seq.each { |item| ScratchPad << item } + ScratchPad.recorded.should == [1, 5, 9] + end + + it "returns self" do + @seq.each { |item| }.should equal(@seq) + end + end +end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/end_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/end_spec.rb new file mode 100644 index 0000000000..5a436e8167 --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/end_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence#end" do + it "returns the end of the sequence" do + 1.step(10).end.should == 10 + (1..10).step.end.should == 10 + (1...10).step(17).end.should == 10 + end + end +end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/eq_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/eq_spec.rb new file mode 100644 index 0000000000..7895f98047 --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/eq_spec.rb @@ -0,0 +1,20 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence#==" do + it "returns true if begin, end, step and exclude_end? are equal" do + 1.step(10).should == 1.step(10) + 1.step(10, 5).should == 1.step(10, 5) + + (1..10).step.should == (1..10).step + (1...10).step(8).should == (1...10).step(8) + + # both have exclude_end? == false + (1..10).step(100).should == 1.step(10, 100) + + ((1..10).step == (1..11).step).should == false + ((1..10).step == (1...10).step).should == false + ((1..10).step == (1..10).step(2)).should == false + end + end +end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/exclude_end_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/exclude_end_spec.rb new file mode 100644 index 0000000000..8ce0ce0cd9 --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/exclude_end_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence#exclude_end?" do + context "when created using Numeric#step" do + it "always returns false" do + 1.step(10).exclude_end?.should == false + 10.step(1).exclude_end?.should == false + end + end + + context "when created using Range#step" do + it "mirrors range.exclude_end?" do + (1...10).step.exclude_end?.should == true + (1..10).step.exclude_end?.should == false + end + end + end +end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/first_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/first_spec.rb new file mode 100644 index 0000000000..43c520d1f0 --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/first_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence#first" do + it "returns the first element of the sequence" do + 1.step(10).first.should == 1 + (1..10).step.first.should == 1 + (1...10).step.first.should == 1 + end + end +end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/hash_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/hash_spec.rb new file mode 100644 index 0000000000..236f845f41 --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/hash_spec.rb @@ -0,0 +1,22 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence#hash" do + it "is based on begin, end, step and exclude_end?" do + 1.step(10).hash.should be_an_instance_of(Integer) + + 1.step(10).hash.should == 1.step(10).hash + 1.step(10, 5).hash.should == 1.step(10, 5).hash + + (1..10).step.hash.should == (1..10).step.hash + (1...10).step(8).hash.should == (1...10).step(8).hash + + # both have exclude_end? == false + (1..10).step(100).hash.should == 1.step(10, 100).hash + + ((1..10).step.hash == (1..11).step.hash).should == false + ((1..10).step.hash == (1...10).step.hash).should == false + ((1..10).step.hash == (1..10).step(2).hash).should == false + end + end +end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/inspect_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/inspect_spec.rb new file mode 100644 index 0000000000..21e64a6b58 --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/inspect_spec.rb @@ -0,0 +1,22 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence#inspect" do + context 'when Numeric#step is used' do + it "returns '(begin.step(end{, step}))'" do + 1.step(10).inspect.should == "(1.step(10))" + 1.step(10, 3).inspect.should == "(1.step(10, 3))" + end + end + + context 'when Range#step is used' do + it "returns '((range).step{(step)})'" do + (1..10).step.inspect.should == "((1..10).step)" + (1..10).step(3).inspect.should == "((1..10).step(3))" + + (1...10).step.inspect.should == "((1...10).step)" + (1...10).step(3).inspect.should == "((1...10).step(3))" + end + end + end +end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/last_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/last_spec.rb new file mode 100644 index 0000000000..ebb20090fc --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/last_spec.rb @@ -0,0 +1,11 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence#last" do + it "returns the last element of the sequence" do + 1.step(10).last.should == 10 + (1..10).step.last.should == 10 + (1...10).step(4).last.should == 9 + end + end +end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/new_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/new_spec.rb new file mode 100644 index 0000000000..5a62d3f346 --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/new_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence.new" do + it "is not defined" do + lambda { + Enumerator::ArithmeticSequence.new + }.should raise_error(NoMethodError) + end + end + + describe "Enumerator::ArithmeticSequence.allocate" do + it "is not defined" do + lambda { + Enumerator::ArithmeticSequence.allocate + }.should raise_error(TypeError, 'allocator undefined for Enumerator::ArithmeticSequence') + end + end +end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/size_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/size_spec.rb new file mode 100644 index 0000000000..00403b0238 --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/size_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence#size" do + context "for finite sequence" do + it "returns the number of elements in this arithmetic sequence" do + 1.step(10).size.should == 10 + (1...10).step.size.should == 9 + end + end + + context "for infinite sequence" do + it "returns Infinity" do + 1.step(Float::INFINITY).size.should == Float::INFINITY + (1..Float::INFINITY).step.size.should == Float::INFINITY + end + end + end +end diff --git a/spec/ruby/core/enumerator/arithmetic_sequence/step_spec.rb b/spec/ruby/core/enumerator/arithmetic_sequence/step_spec.rb new file mode 100644 index 0000000000..20a5cb6e7b --- /dev/null +++ b/spec/ruby/core/enumerator/arithmetic_sequence/step_spec.rb @@ -0,0 +1,15 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::ArithmeticSequence#step" do + it "returns the original value given to step method" do + (1..10).step.step.should == 1 + (1..10).step(3).step.should == 3 + (1..10).step(0).step.should == 0 + + 1.step(10).step.should == 1 + 1.step(10, 3).step.should == 3 + 1.step(10, 0).step.should == 0 + end + end +end diff --git a/spec/ruby/core/enumerator/chain/each_spec.rb b/spec/ruby/core/enumerator/chain/each_spec.rb new file mode 100644 index 0000000000..dab2c1cf1d --- /dev/null +++ b/spec/ruby/core/enumerator/chain/each_spec.rb @@ -0,0 +1,17 @@ +require_relative '../../../spec_helper' +require_relative '../../enumerable/fixtures/classes' + +ruby_version_is "2.6" do + describe "Enumerator::Chain#each" do + it "calls each on its consistuents as needed" do + a = EnumerableSpecs::EachCounter.new(:a, :b) + b = EnumerableSpecs::EachCounter.new(:c, :d) + + ScratchPad.record [] + Enumerator::Chain.new(a, b).each do |elem| + ScratchPad << elem << b.times_yielded + end + ScratchPad.recorded.should == [:a, 0, :b, 0, :c, 1, :d, 2] + end + end +end diff --git a/spec/ruby/core/enumerator/chain/initialize_spec.rb b/spec/ruby/core/enumerator/chain/initialize_spec.rb new file mode 100644 index 0000000000..4ede1b8670 --- /dev/null +++ b/spec/ruby/core/enumerator/chain/initialize_spec.rb @@ -0,0 +1,33 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::Chain#initialize" do + before :each do + @uninitialized = Enumerator::Chain.allocate + end + + it "is a private method" do + Enumerator::Chain.should have_private_instance_method(:initialize, false) + end + + it "returns self" do + @uninitialized.send(:initialize).should equal(@uninitialized) + end + + it "accepts many arguments" do + @uninitialized.send(:initialize, 0..1, 2..3, 4..5).should equal(@uninitialized) + end + + it "accepts arguments that are not Enumerable nor responding to :each" do + @uninitialized.send(:initialize, Object.new).should equal(@uninitialized) + end + + describe "on frozen instance" do + it "raises a RuntimeError" do + lambda { + @uninitialized.freeze.send(:initialize) + }.should raise_error(RuntimeError) + end + end + end +end diff --git a/spec/ruby/core/enumerator/chain/inspect_spec.rb b/spec/ruby/core/enumerator/chain/inspect_spec.rb new file mode 100644 index 0000000000..a644d88c6f --- /dev/null +++ b/spec/ruby/core/enumerator/chain/inspect_spec.rb @@ -0,0 +1,16 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::Chain#inspect" do + it "shows a representation of the Enumerator" do + Enumerator::Chain.new.inspect.should == "#<Enumerator::Chain: []>" + Enumerator::Chain.new(1..2, 3..4).inspect.should == "#<Enumerator::Chain: [1..2, 3..4]>" + end + + it "calls inspect on its chain elements" do + obj = mock('inspect') + obj.should_receive(:inspect).and_return('some desc') + Enumerator::Chain.new(obj).inspect.should == "#<Enumerator::Chain: [some desc]>" + end + end +end diff --git a/spec/ruby/core/enumerator/chain/rewind_spec.rb b/spec/ruby/core/enumerator/chain/rewind_spec.rb new file mode 100644 index 0000000000..61b07b8c0a --- /dev/null +++ b/spec/ruby/core/enumerator/chain/rewind_spec.rb @@ -0,0 +1,53 @@ +require_relative '../../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator::Chain#rewind" do + before(:each) do + @obj = mock('obj') + @obj.should_receive(:each).any_number_of_times.and_yield(42) + @second = mock('obj') + @second.should_receive(:each).any_number_of_times.and_yield(:second) + @enum = Enumerator::Chain.new(@obj, @second) + end + + it "returns self" do + @enum.rewind.should equal @enum + end + + it "does nothing if receiver has not been iterated" do + @obj.should_not_receive(:rewind) + @obj.respond_to?(:rewind).should == true # sanity check + @enum.rewind + end + + it "does nothing on objects that don't respond_to rewind" do + @obj.respond_to?(:rewind).should == false # sanity check + @enum.each {} + @enum.rewind + end + + it "calls_rewind its objects" do + @obj.should_receive(:rewind) + @enum.each {} + @enum.rewind + end + + it "calls_rewind in reverse order" do + @obj.should_not_receive(:rewind) + @second.should_receive(:rewind).and_raise(RuntimeError) + @enum.each {} + lambda { @enum.rewind }.should raise_error(RuntimeError) + end + + it "calls rewind only for objects that have actually been iterated on" do + @obj = mock('obj') + @obj.should_receive(:each).any_number_of_times.and_raise(RuntimeError) + @enum = Enumerator::Chain.new(@obj, @second) + + @obj.should_receive(:rewind) + @second.should_not_receive(:rewind) + lambda { @enum.each {} }.should raise_error(RuntimeError) + @enum.rewind + end + end +end diff --git a/spec/ruby/core/enumerator/chain/size_spec.rb b/spec/ruby/core/enumerator/chain/size_spec.rb new file mode 100644 index 0000000000..42c31ac10b --- /dev/null +++ b/spec/ruby/core/enumerator/chain/size_spec.rb @@ -0,0 +1,24 @@ +require_relative '../../../spec_helper' +require_relative '../../enumerable/fixtures/classes' + +ruby_version_is "2.6" do + describe "Enumerator::Chain#size" do + it "returns the sum of the sizes of the elements" do + a = mock('size') + a.should_receive(:size).and_return(40) + Enumerator::Chain.new(a, [:a, :b]).size.should == 42 + end + + it "returns nil or Infinity for the first element of such a size" do + [nil, Float::INFINITY].each do |special| + a = mock('size') + a.should_receive(:size).and_return(40) + b = mock('special') + b.should_receive(:size).and_return(special) + c = mock('not called') + c.should_not_receive(:size) + Enumerator::Chain.new(a, b, c).size.should == special + end + end + end +end diff --git a/spec/ruby/core/enumerator/each_spec.rb b/spec/ruby/core/enumerator/each_spec.rb index e2a78a6a99..f66951851e 100644 --- a/spec/ruby/core/enumerator/each_spec.rb +++ b/spec/ruby/core/enumerator/each_spec.rb @@ -1,5 +1,89 @@ -require_relative '../../shared/enumerator/each' +require_relative '../../spec_helper' describe "Enumerator#each" do - it_behaves_like :enum_each, :each + before :each do + object_each_with_arguments = Object.new + def object_each_with_arguments.each_with_arguments(arg, *args) + yield arg, *args + :method_returned + end + + @enum_with_arguments = object_each_with_arguments.to_enum(:each_with_arguments, :arg0, :arg1, :arg2) + + @enum_with_yielder = Enumerator.new {|y| y.yield :ok} + end + + it "yields each element of self to the given block" do + acc = [] + [1,2,3].to_enum.each {|e| acc << e } + acc.should == [1,2,3] + end + + it "calls #each on the object given in the constructor by default" do + each = mock('each') + each.should_receive(:each) + each.to_enum.each {|e| e } + end + + it "calls #each on the underlying object until it's exhausted" do + each = mock('each') + each.should_receive(:each).and_yield(1).and_yield(2).and_yield(3) + acc = [] + each.to_enum.each {|e| acc << e } + acc.should == [1,2,3] + end + + it "calls the method given in the constructor instead of #each" do + each = mock('peach') + each.should_receive(:peach) + each.to_enum(:peach).each {|e| e } + end + + it "calls the method given in the constructor until it's exhausted" do + each = mock('each') + each.should_receive(:each).and_yield(1).and_yield(2).and_yield(3) + acc = [] + each.to_enum.each {|e| acc << e } + acc.should == [1,2,3] + end + + it "raises a NoMethodError if the object doesn't respond to #each" do + enum = Object.new.to_enum + lambda do + enum.each { |e| e } + end.should raise_error(NoMethodError) + end + + it "returns self if not given arguments and not given a block" do + @enum_with_arguments.each.should equal(@enum_with_arguments) + + @enum_with_yielder.each.should equal(@enum_with_yielder) + end + + it "returns the same value from receiver.each if block is given" do + @enum_with_arguments.each {}.should equal(:method_returned) + end + + it "passes given arguments at initialized to receiver.each" do + @enum_with_arguments.each.to_a.should == [[:arg0, :arg1, :arg2]] + end + + it "requires multiple arguments" do + Enumerator.instance_method(:each).arity.should < 0 + end + + it "appends given arguments to receiver.each" do + @enum_with_arguments.each(:each0, :each1).to_a.should == [[:arg0, :arg1, :arg2, :each0, :each1]] + @enum_with_arguments.each(:each2, :each3).to_a.should == [[:arg0, :arg1, :arg2, :each2, :each3]] + end + + it "returns the same value from receiver.each if block and arguments are given" do + @enum_with_arguments.each(:each1, :each2) {}.should equal(:method_returned) + end + + it "returns new Enumerator if given arguments but not given a block" do + ret = @enum_with_arguments.each 1 + ret.should be_an_instance_of(Enumerator) + ret.should_not equal(@enum_with_arguments) + end end diff --git a/spec/ruby/core/enumerator/inject_spec.rb b/spec/ruby/core/enumerator/inject_spec.rb deleted file mode 100644 index 326c7ed581..0000000000 --- a/spec/ruby/core/enumerator/inject_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require_relative '../../shared/enumerator/each' - -describe "Enumerator#inject" do - it_behaves_like :enum_each, :each - - it "works when chained against each_with_index" do - passed_values = [] - [:a].each_with_index.inject(0) do |accumulator,value| - passed_values << value - accumulator + 1 - end.should == 1 - passed_values.should == [[:a,0]] - end - -end diff --git a/spec/ruby/core/enumerator/lazy/filter_spec.rb b/spec/ruby/core/enumerator/lazy/filter_spec.rb new file mode 100644 index 0000000000..2ababa69cc --- /dev/null +++ b/spec/ruby/core/enumerator/lazy/filter_spec.rb @@ -0,0 +1,8 @@ +require_relative '../../../spec_helper' +require_relative 'shared/select' + +ruby_version_is "2.6" do + describe "Enumerator::Lazy#filter" do + it_behaves_like :enumerator_lazy_select, :filter + end +end diff --git a/spec/ruby/core/enumerator/new_spec.rb b/spec/ruby/core/enumerator/new_spec.rb index 86d2d38022..170809dbc1 100644 --- a/spec/ruby/core/enumerator/new_spec.rb +++ b/spec/ruby/core/enumerator/new_spec.rb @@ -1,6 +1,41 @@ require_relative '../../spec_helper' -require_relative '../../shared/enumerator/new' describe "Enumerator.new" do - it_behaves_like :enum_new, :new + it "creates a new custom enumerator with the given object, iterator and arguments" do + enum = Enumerator.new(1, :upto, 3) + enum.should be_an_instance_of(Enumerator) + end + + it "creates a new custom enumerator that responds to #each" do + enum = Enumerator.new(1, :upto, 3) + enum.respond_to?(:each).should == true + end + + it "creates a new custom enumerator that runs correctly" do + Enumerator.new(1, :upto, 3).map{|x|x}.should == [1,2,3] + end + + it "aliases the second argument to :each" do + Enumerator.new(1..2).to_a.should == Enumerator.new(1..2, :each).to_a + end + + it "doesn't check for the presence of the iterator method" do + Enumerator.new(nil).should be_an_instance_of(Enumerator) + end + + it "uses the latest define iterator method" do + class StrangeEach + def each + yield :foo + end + end + enum = Enumerator.new(StrangeEach.new) + enum.to_a.should == [:foo] + class StrangeEach + def each + yield :bar + end + end + enum.to_a.should == [:bar] + end end diff --git a/spec/ruby/core/enumerator/next_spec.rb b/spec/ruby/core/enumerator/next_spec.rb index 1d3baf54d7..e0d3c0a39b 100644 --- a/spec/ruby/core/enumerator/next_spec.rb +++ b/spec/ruby/core/enumerator/next_spec.rb @@ -1,6 +1,27 @@ require_relative '../../spec_helper' -require_relative '../../shared/enumerator/next' describe "Enumerator#next" do - it_behaves_like :enum_next,:next + before :each do + @enum = 1.upto(3) + end + + it "returns the next element of the enumeration" do + @enum.next.should == 1 + @enum.next.should == 2 + @enum.next.should == 3 + end + + it "raises a StopIteration exception at the end of the stream" do + 3.times { @enum.next } + lambda { @enum.next }.should raise_error(StopIteration) + end + + it "cannot be called again until the enumerator is rewound" do + 3.times { @enum.next } + lambda { @enum.next }.should raise_error(StopIteration) + lambda { @enum.next }.should raise_error(StopIteration) + lambda { @enum.next }.should raise_error(StopIteration) + @enum.rewind + @enum.next.should == 1 + end end diff --git a/spec/ruby/core/enumerator/plus_spec.rb b/spec/ruby/core/enumerator/plus_spec.rb new file mode 100644 index 0000000000..c9bae08b04 --- /dev/null +++ b/spec/ruby/core/enumerator/plus_spec.rb @@ -0,0 +1,35 @@ +require_relative '../../spec_helper' + +ruby_version_is "2.6" do + describe "Enumerator#+" do + before :each do + ScratchPad.record [] + end + + it "returns a chain of self and provided enumerators" do + one = Enumerator.new { |y| y << 1 } + two = Enumerator.new { |y| y << 2 } + three = Enumerator.new { |y| y << 3 } + + chain = one + two + three + + chain.should be_an_instance_of(Enumerator::Chain) + chain.each { |item| ScratchPad << item } + ScratchPad.recorded.should == [1, 2, 3] + end + + it "calls #each on each argument" do + enum = Enumerator.new { |y| y << "one" } + + obj1 = mock("obj1") + obj1.should_receive(:each).once.and_yield("two") + + obj2 = mock("obj2") + obj2.should_receive(:each).once.and_yield("three") + + chain = enum + obj1 + obj2 + chain.each { |item| ScratchPad << item } + ScratchPad.recorded.should == ["one", "two", "three"] + end + end +end diff --git a/spec/ruby/core/enumerator/rewind_spec.rb b/spec/ruby/core/enumerator/rewind_spec.rb index e846f63c49..2a83b7c6ee 100644 --- a/spec/ruby/core/enumerator/rewind_spec.rb +++ b/spec/ruby/core/enumerator/rewind_spec.rb @@ -1,9 +1,41 @@ require_relative '../../spec_helper' -require_relative '../../shared/enumerator/rewind' require_relative 'fixtures/common' describe "Enumerator#rewind" do - it_behaves_like :enum_rewind, :rewind + before :each do + @enum = 1.upto(3) + end + + it "resets the enumerator to its initial state" do + @enum.next.should == 1 + @enum.next.should == 2 + @enum.rewind + @enum.next.should == 1 + end + + it "returns self" do + @enum.rewind.should == @enum + end + + it "has no effect on a new enumerator" do + @enum.rewind + @enum.next.should == 1 + end + + it "has no effect if called multiple, consecutive times" do + @enum.next.should == 1 + @enum.rewind + @enum.rewind + @enum.next.should == 1 + end + + it "works with peek to reset the position" do + @enum.next + @enum.next + @enum.rewind + @enum.next + @enum.peek.should == 2 + end it "calls the enclosed object's rewind method if one exists" do obj = mock('rewinder') diff --git a/spec/ruby/core/env/filter_spec.rb b/spec/ruby/core/env/filter_spec.rb new file mode 100644 index 0000000000..ba18a3b33b --- /dev/null +++ b/spec/ruby/core/env/filter_spec.rb @@ -0,0 +1,15 @@ +require_relative '../../spec_helper' +require_relative '../enumerable/shared/enumeratorized' +require_relative 'shared/select' + +ruby_version_is "2.6" do + describe "ENV.filter!" do + it_behaves_like :env_select!, :filter! + it_behaves_like :enumeratorized_with_origin_size, :filter!, ENV + end + + describe "ENV.filter" do + it_behaves_like :env_select, :filter + it_behaves_like :enumeratorized_with_origin_size, :filter, ENV + end +end diff --git a/spec/ruby/core/env/select_spec.rb b/spec/ruby/core/env/select_spec.rb index 9ccd27f3e7..c3a76f4434 100644 --- a/spec/ruby/core/env/select_spec.rb +++ b/spec/ruby/core/env/select_spec.rb @@ -1,39 +1,13 @@ require_relative '../../spec_helper' require_relative '../enumerable/shared/enumeratorized' +require_relative 'shared/select' describe "ENV.select!" do - it "removes environment variables for which the block returns true" do - ENV["foo"] = "bar" - ENV.select! { |k, v| k != "foo" } - ENV["foo"].should == nil - end - - it "returns self if any changes were made" do - ENV["foo"] = "bar" - ENV.select! { |k, v| k != "foo" }.should == ENV - end - - it "returns nil if no changes were made" do - ENV.select! { true }.should == nil - end - - it "returns an Enumerator if called without a block" do - ENV.select!.should be_an_instance_of(Enumerator) - end - + it_behaves_like :env_select!, :select! it_behaves_like :enumeratorized_with_origin_size, :select!, ENV end describe "ENV.select" do - it "returns a Hash of names and values for which block return true" do - ENV["foo"] = "bar" - ENV.select { |k, v| k == "foo" }.should == {"foo" => "bar"} - ENV.delete "foo" - end - - it "returns an Enumerator when no block is given" do - ENV.select.should be_an_instance_of(Enumerator) - end - + it_behaves_like :env_select, :select it_behaves_like :enumeratorized_with_origin_size, :select, ENV end diff --git a/spec/ruby/core/env/shared/select.rb b/spec/ruby/core/env/shared/select.rb new file mode 100644 index 0000000000..a0b46a775a --- /dev/null +++ b/spec/ruby/core/env/shared/select.rb @@ -0,0 +1,32 @@ +describe :env_select, shared: true do + it "returns a Hash of names and values for which block return true" do + ENV["foo"] = "bar" + (ENV.send(@method) { |k, v| k == "foo" }).should == { "foo" => "bar" } + ENV.delete "foo" + end + + it "returns an Enumerator when no block is given" do + ENV.send(@method).should be_an_instance_of(Enumerator) + end +end + +describe :env_select!, shared: true do + it "removes environment variables for which the block returns true" do + ENV["foo"] = "bar" + ENV.send(@method) { |k, v| k != "foo" } + ENV["foo"].should == nil + end + + it "returns self if any changes were made" do + ENV["foo"] = "bar" + (ENV.send(@method) { |k, v| k != "foo" }).should == ENV + end + + it "returns nil if no changes were made" do + (ENV.send(@method) { true }).should == nil + end + + it "returns an Enumerator if called without a block" do + ENV.send(@method).should be_an_instance_of(Enumerator) + end +end diff --git a/spec/ruby/core/env/to_h_spec.rb b/spec/ruby/core/env/to_h_spec.rb index f6c796b4d6..81a17700e9 100644 --- a/spec/ruby/core/env/to_h_spec.rb +++ b/spec/ruby/core/env/to_h_spec.rb @@ -1,18 +1,54 @@ require_relative '../../spec_helper' require_relative 'shared/to_hash' -describe "ENV.to_hash" do +describe "ENV.to_h" do it_behaves_like :env_to_hash, :to_h ruby_version_is "2.6" do - it "converts [key, value] pairs returned by the block to a hash" do - orig = ENV.to_hash - begin - ENV.replace "a" => "b", "c" => "d" - i = 0 - ENV.to_h {|k, v| [k.to_sym, v.upcase]}.should == {a:"B", c:"D"} - ensure - ENV.replace orig + context "with block" do + before do + @orig_hash = ENV.to_hash + end + + after do + ENV.replace @orig_hash + end + + it "converts [key, value] pairs returned by the block to a hash" do + ENV.replace("a" => "b", "c" => "d") + ENV.to_h { |k, v| [k, v.upcase] }.should == { 'a' => "B", 'c' => "D" } + end + + it "raises ArgumentError if block returns longer or shorter array" do + -> do + ENV.to_h { |k, v| [k, v.upcase, 1] } + end.should raise_error(ArgumentError, /element has wrong array length/) + + -> do + ENV.to_h { |k, v| [k] } + end.should raise_error(ArgumentError, /element has wrong array length/) + end + + it "raises TypeError if block returns something other than Array" do + -> do + ENV.to_h { |k, v| "not-array" } + end.should raise_error(TypeError, /wrong element type String/) + end + + it "coerces returned pair to Array with #to_ary" do + x = mock('x') + x.stub!(:to_ary).and_return([:b, 'b']) + + ENV.to_h { |k| x }.should == { :b => 'b' } + end + + it "does not coerce returned pair to Array with #to_a" do + x = mock('x') + x.stub!(:to_a).and_return([:b, 'b']) + + -> do + ENV.to_h { |k| x } + end.should raise_error(TypeError, /wrong element type MockObject/) end end end diff --git a/spec/ruby/core/exception/full_message_spec.rb b/spec/ruby/core/exception/full_message_spec.rb index f56282d67b..bc611c165b 100644 --- a/spec/ruby/core/exception/full_message_spec.rb +++ b/spec/ruby/core/exception/full_message_spec.rb @@ -34,5 +34,42 @@ ruby_version_is "2.5" do e.full_message(order: :bottom, highlight: false).should =~ /b.rb:2.*a.rb:1/m end end + + ruby_version_is "2.6" do + it "contains cause of exception" do + begin + begin + raise 'the cause' + rescue + raise 'main exception' + end + rescue => e + exception = e + end + + exception.full_message.should include "main exception" + exception.full_message.should include "the cause" + end + + it 'contains all the chain of exceptions' do + begin + begin + begin + raise 'origin exception' + rescue + raise 'intermediate exception' + end + rescue + raise 'last exception' + end + rescue => e + exception = e + end + + exception.full_message.should include "last exception" + exception.full_message.should include "intermediate exception" + exception.full_message.should include "origin exception" + end + end end end diff --git a/spec/ruby/core/exception/key_error_spec.rb b/spec/ruby/core/exception/key_error_spec.rb new file mode 100644 index 0000000000..ad280279d8 --- /dev/null +++ b/spec/ruby/core/exception/key_error_spec.rb @@ -0,0 +1,15 @@ +require_relative '../../spec_helper' + +describe "KeyError" do + ruby_version_is "2.6" do + it "accepts :receiver and :key options" do + receiver = mock("receiver") + key = mock("key") + + error = KeyError.new(receiver: receiver, key: key) + + error.receiver.should == receiver + error.key.should == key + end + end +end diff --git a/spec/ruby/core/exception/name_error_spec.rb b/spec/ruby/core/exception/name_error_spec.rb index a5810f178f..d0a810029b 100644 --- a/spec/ruby/core/exception/name_error_spec.rb +++ b/spec/ruby/core/exception/name_error_spec.rb @@ -10,4 +10,15 @@ describe "NameError.new" do it "should take optional name argument" do NameError.new("msg","name").name.should == "name" end + + ruby_version_is "2.6" do + it "accepts a :receiver keyword argument" do + receiver = mock("receiver") + + error = NameError.new("msg", :name, receiver: receiver) + + error.receiver.should == receiver + error.name.should == :name + end + end end diff --git a/spec/ruby/core/exception/no_method_error_spec.rb b/spec/ruby/core/exception/no_method_error_spec.rb index 7839c080a8..28c3549562 100644 --- a/spec/ruby/core/exception/no_method_error_spec.rb +++ b/spec/ruby/core/exception/no_method_error_spec.rb @@ -3,12 +3,23 @@ require_relative 'fixtures/common' describe "NoMethodError.new" do it "allows passing method args" do - NoMethodError.new("msg","name","args").args.should == "args" + NoMethodError.new("msg", "name", ["args"]).args.should == ["args"] end it "does not require a name" do NoMethodError.new("msg").message.should == "msg" end + + ruby_version_is "2.6" do + it "accepts a :receiver keyword argument" do + receiver = mock("receiver") + + error = NoMethodError.new("msg", :name, receiver: receiver) + + error.receiver.should == receiver + error.name.should == :name + end + end end describe "NoMethodError#args" do diff --git a/spec/ruby/core/file/open_spec.rb b/spec/ruby/core/file/open_spec.rb index c6a0b68233..c37e7316ba 100644 --- a/spec/ruby/core/file/open_spec.rb +++ b/spec/ruby/core/file/open_spec.rb @@ -638,6 +638,33 @@ describe "File.open" do end end + ruby_version_is "2.6" do + context "'x' flag" do + before :each do + @xfile = tmp("x-flag") + rm_r @xfile + end + + after :each do + rm_r @xfile + end + + it "does nothing if the file doesn't exist" do + File.open(@xfile, "wx") { |f| f.write("content") } + File.read(@xfile).should == "content" + end + + it "throws a Errno::EEXIST error if the file exists" do + touch @xfile + lambda { File.open(@xfile, "wx") }.should raise_error(Errno::EEXIST) + end + + it "can't be used with 'r' and 'a' flags" do + lambda { File.open(@xfile, "rx") }.should raise_error(ArgumentError, 'invalid access mode rx') + lambda { File.open(@xfile, "ax") }.should raise_error(ArgumentError, 'invalid access mode ax') + end + end + end end describe "File.open when passed a file descriptor" do diff --git a/spec/ruby/core/hash/merge_spec.rb b/spec/ruby/core/hash/merge_spec.rb index 3af1ea6d4f..5ea70610be 100644 --- a/spec/ruby/core/hash/merge_spec.rb +++ b/spec/ruby/core/hash/merge_spec.rb @@ -63,6 +63,17 @@ describe "Hash#merge" do merge_pairs.should == each_pairs end + ruby_version_is "2.6" do + it "accepts multiple hashes" do + result = { a: 1 }.merge({ b: 2 }, { c: 3 }, { d: 4 }) + result.should == { a: 1, b: 2, c: 3, d: 4 } + end + + it "accepts zero arguments and returns self" do + hash = { a: 1 } + hash.merge.should eql(hash) + end + end end describe "Hash#merge!" do diff --git a/spec/ruby/core/hash/shared/update.rb b/spec/ruby/core/hash/shared/update.rb index be03ac019e..250c5ab440 100644 --- a/spec/ruby/core/hash/shared/update.rb +++ b/spec/ruby/core/hash/shared/update.rb @@ -56,4 +56,16 @@ describe :hash_update, shared: true do HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash) end.should raise_error(frozen_error_class) end + + ruby_version_is "2.6" do + it "accepts multiple hashes" do + result = { a: 1 }.send(@method, { b: 2 }, { c: 3 }, { d: 4 }) + result.should == { a: 1, b: 2, c: 3, d: 4 } + end + + it "accepts zero arguments" do + hash = { a: 1 } + hash.send(@method).should eql(hash) + end + end end diff --git a/spec/ruby/core/hash/to_h_spec.rb b/spec/ruby/core/hash/to_h_spec.rb index 40bd0d06d2..d6eaac9f33 100644 --- a/spec/ruby/core/hash/to_h_spec.rb +++ b/spec/ruby/core/hash/to_h_spec.rb @@ -7,12 +7,6 @@ describe "Hash#to_h" do h.to_h.should equal(h) end - ruby_version_is "2.6" do - it "converts [key, value] pairs returned by the block to a hash" do - {a: 1, b: 2}.to_h {|k, v| [k.to_s, v*v]}.should == { "a" => 1, "b" => 4 } - end - end - describe "when called on a subclass of Hash" do before :each do @h = HashSpecs::MyHash.new @@ -37,4 +31,44 @@ describe "Hash#to_h" do @h[42].should == 84 end end + + ruby_version_is "2.6" do + context "with block" do + it "converts [key, value] pairs returned by the block to a hash" do + { a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v]}.should == { "a" => 1, "b" => 4 } + end + + it "raises ArgumentError if block returns longer or shorter array" do + -> do + { a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v, 1] } + end.should raise_error(ArgumentError, /element has wrong array length/) + + -> do + { a: 1, b: 2 }.to_h { |k, v| [k] } + end.should raise_error(ArgumentError, /element has wrong array length/) + end + + it "raises TypeError if block returns something other than Array" do + -> do + { a: 1, b: 2 }.to_h { |k, v| "not-array" } + end.should raise_error(TypeError, /wrong element type String/) + end + + it "coerces returned pair to Array with #to_ary" do + x = mock('x') + x.stub!(:to_ary).and_return([:b, 'b']) + + { a: 1 }.to_h { |k| x }.should == { :b => 'b' } + end + + it "does not coerce returned pair to Array with #to_a" do + x = mock('x') + x.stub!(:to_a).and_return([:b, 'b']) + + -> do + { a: 1 }.to_h { |k| x } + end.should raise_error(TypeError, /wrong element type MockObject/) + end + end + end end diff --git a/spec/ruby/core/io/ungetbyte_spec.rb b/spec/ruby/core/io/ungetbyte_spec.rb index 2e51fb0298..f5f9a11be1 100644 --- a/spec/ruby/core/io/ungetbyte_spec.rb +++ b/spec/ruby/core/io/ungetbyte_spec.rb @@ -49,7 +49,7 @@ describe "IO#ungetbyte" do end end - ruby_version_is '2.6'...'2.7' do + ruby_version_is '2.6'...'2.6.1' do it "is an RangeError if the integer is not in 8bit" do for i in [4095, 0x4f7574206f6620636861722072616e6765] do lambda { @io.ungetbyte(i) }.should raise_error(RangeError) @@ -57,10 +57,11 @@ describe "IO#ungetbyte" do end end - ruby_version_is '2.7' do + ruby_version_is '2.6.1' do it "never raises RangeError" do - for i in [4095, 0x4f7574206f6620636861722072616e6765] do - lambda { @io.ungetbyte(i) }.should_not raise_error + for i in [4095, 0x4f7574206f6620636861722072616e67ff] do + @io.ungetbyte(i).should be_nil + @io.getbyte.should == 255 end end end diff --git a/spec/ruby/core/kernel/Complex_spec.rb b/spec/ruby/core/kernel/Complex_spec.rb index 44e4f44ada..e5435a56e6 100644 --- a/spec/ruby/core/kernel/Complex_spec.rb +++ b/spec/ruby/core/kernel/Complex_spec.rb @@ -138,4 +138,52 @@ describe "Kernel.Complex()" do lambda { Complex(nil, 0) }.should raise_error(TypeError, "can't convert nil into Complex") end end + + ruby_version_is "2.6" do + describe "when passed exception: false" do + describe "and [Numeric]" do + it "returns a complex number" do + Complex("123", exception: false).should == Complex(123) + end + end + + describe "and [non-Numeric]" do + it "swallows an error" do + Complex(:sym, exception: false).should == nil + end + end + + describe "and [non-Numeric, Numeric] argument" do + it "throws a TypeError" do + lambda { Complex(:sym, 0, exception: false) }.should raise_error(TypeError, "not a real") + end + end + + describe "and [anything, non-Numeric] argument" do + it "swallows an error" do + Complex("a", :sym, exception: false).should == nil + Complex(:sym, :sym, exception: false).should == nil + Complex(0, :sym, exception: false).should == nil + end + end + + describe "and non-numeric String arguments" do + it "swallows an error" do + Complex("a", "b", exception: false).should == nil + Complex("a", 0, exception: false).should == nil + Complex(0, "b", exception: false).should == nil + end + end + + ruby_bug "#15525", "2.6"..."2.6.1" do + describe "and nil arguments" do + it "swallows an error" do + Complex(nil, exception: false).should == nil + Complex(0, nil, exception: false).should == nil + Complex(nil, 0, exception: false).should == nil + end + end + end + end + end end diff --git a/spec/ruby/core/kernel/Float_spec.rb b/spec/ruby/core/kernel/Float_spec.rb index 47d7d0816f..43daefa6aa 100644 --- a/spec/ruby/core/kernel/Float_spec.rb +++ b/spec/ruby/core/kernel/Float_spec.rb @@ -299,6 +299,31 @@ describe :kernel_float, shared: true do c = Complex(2, 3) lambda { @object.send(:Float, c) }.should raise_error(RangeError) end + + ruby_version_is "2.6" do + describe "when passed exception: false" do + describe "and valid input" do + it "returns a Float number" do + @object.send(:Float, 1, exception: false).should == 1.0 + @object.send(:Float, "1", exception: false).should == 1.0 + @object.send(:Float, "1.23", exception: false).should == 1.23 + end + end + + describe "and invalid input" do + it "swallows an error" do + @object.send(:Float, "abc", exception: false).should == nil + @object.send(:Float, :sym, exception: false).should == nil + end + end + + describe "and nil" do + it "swallows it" do + @object.send(:Float, nil, exception: false).should == nil + end + end + end + end end describe "Kernel.Float" do diff --git a/spec/ruby/core/kernel/Integer_spec.rb b/spec/ruby/core/kernel/Integer_spec.rb index b79c827d31..72e33fc737 100644 --- a/spec/ruby/core/kernel/Integer_spec.rb +++ b/spec/ruby/core/kernel/Integer_spec.rb @@ -99,6 +99,65 @@ describe :kernel_integer, shared: true do it "raises a FloatDomainError when passed Infinity" do lambda { Integer(infinity_value) }.should raise_error(FloatDomainError) end + + ruby_version_is "2.6" do + describe "when passed exception: false" do + describe "and to_i returns a value that is not an Integer" do + it "swallows an error" do + obj = mock("object") + obj.should_receive(:to_i).and_return("1") + Integer(obj, exception: false).should == nil + end + end + + describe "and no to_int or to_i methods exist" do + it "swallows an error" do + obj = mock("object") + Integer(obj, exception: false).should == nil + end + end + + describe "and to_int returns nil and no to_i exists" do + it "swallows an error" do + obj = mock("object") + obj.should_receive(:to_i).and_return(nil) + Integer(obj, exception: false).should == nil + end + end + + ruby_bug "#15525", "2.6"..."2.6.1" do + describe "and passed NaN" do + it "swallows an error" do + Integer(nan_value, exception: false).should == nil + end + end + + describe "and passed Infinity" do + it "swallows an error" do + Integer(infinity_value, exception: false).should == nil + end + end + end + + describe "and passed nil" do + it "swallows an error" do + Integer(nil, exception: false).should == nil + end + end + + describe "and passed a String that contains numbers" do + it "normally parses it and returns an Integer" do + Integer("42", exception: false).should == 42 + end + end + + describe "and passed a String that can't be converted to an Integer" do + it "swallows an error" do + Integer("abc", exception: false).should == nil + end + end + end + end end describe "Integer() given a String", shared: true do @@ -189,6 +248,34 @@ describe "Integer() given a String", shared: true do lambda { Integer("") }.should raise_error(ArgumentError) end + ruby_version_is "2.6" do + describe "when passed exception: false" do + describe "and multiple leading -s" do + it "swallows an error" do + Integer("---1", exception: false).should == nil + end + end + + describe "and multiple trailing -s" do + it "swallows an error" do + Integer("1---", exception: false).should == nil + end + end + + describe "and an argument that contains a period" do + it "swallows an error" do + Integer("0.0", exception: false).should == nil + end + end + + describe "and an empty string" do + it "swallows an error" do + Integer("", exception: false).should == nil + end + end + end + end + it "parses the value as 0 if the string consists of a single zero character" do Integer("0").should == 0 end @@ -508,6 +595,24 @@ describe "Integer() given a String and base", shared: true do lambda { Integer(98, 15) }.should raise_error(ArgumentError) end end + + ruby_version_is "2.6" do + describe "when passed exception: false" do + describe "and valid argument" do + it "returns an Integer number" do + Integer("100", 10, exception: false).should == 100 + Integer("100", 2, exception: false).should == 4 + end + end + + describe "and invalid argument" do + it "swallows an error" do + Integer("999", 2, exception: false).should == nil + Integer("abc", 10, exception: false).should == nil + end + end + end + end end describe :kernel_Integer, shared: true do diff --git a/spec/ruby/core/kernel/match_spec.rb b/spec/ruby/core/kernel/match_spec.rb index d5808b6ede..6dc1eb7de8 100644 --- a/spec/ruby/core/kernel/match_spec.rb +++ b/spec/ruby/core/kernel/match_spec.rb @@ -13,4 +13,12 @@ describe "Kernel#=~" do (o =~ true).should be_nil end end + + ruby_version_is "2.6" do + it "is deprecated" do + -> do + Object.new =~ /regexp/ + end.should complain(/deprecated Object#=~ is called on Object/, verbose: true) + end + end end diff --git a/spec/ruby/core/kernel/shared/load.rb b/spec/ruby/core/kernel/shared/load.rb index 0ce7d58d2c..b479b29399 100644 --- a/spec/ruby/core/kernel/shared/load.rb +++ b/spec/ruby/core/kernel/shared/load.rb @@ -30,9 +30,8 @@ describe :kernel_load, shared: true do it "loads a file that recursively requires itself" do path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR -> { - $VERBOSE = true @object.load(path).should be_true - }.should complain(/circular require considered harmful/) + }.should complain(/circular require considered harmful/, verbose: true) ScratchPad.recorded.should == [:loaded, :loaded] end diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb index a81a68088a..b502476bc3 100644 --- a/spec/ruby/core/kernel/shared/require.rb +++ b/spec/ruby/core/kernel/shared/require.rb @@ -225,9 +225,8 @@ describe :kernel_require, shared: true do it "loads a file that recursively requires itself" do path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR -> { - $VERBOSE = true @object.require(path).should be_true - }.should complain(/circular require considered harmful/) + }.should complain(/circular require considered harmful/, verbose: true) ScratchPad.recorded.should == [:loaded] end end diff --git a/spec/ruby/core/method/compose_spec.rb b/spec/ruby/core/method/compose_spec.rb new file mode 100644 index 0000000000..aa0aded775 --- /dev/null +++ b/spec/ruby/core/method/compose_spec.rb @@ -0,0 +1,101 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative '../proc/shared/compose' + +ruby_version_is "2.6" do + describe "Method#<<" do + it "returns a Proc that is the composition of self and the passed Proc" do + succ = MethodSpecs::Composition.new.method(:succ) + upcase = proc { |s| s.upcase } + + (succ << upcase).call('Ruby').should == "RUBZ" + end + + it "calls passed Proc with arguments and then calls self with result" do + pow_2_proc = proc { |x| x * x } + double_proc = proc { |x| x + x } + + pow_2_method = MethodSpecs::Composition.new.method(:pow_2) + double_method = MethodSpecs::Composition.new.method(:double) + + (pow_2_method << double_proc).call(2).should == 16 + (double_method << pow_2_proc).call(2).should == 8 + end + + it "accepts any callable object" do + inc = MethodSpecs::Composition.new.method(:inc) + + double = Object.new + def double.call(n); n * 2; end + + (inc << double).call(3).should == 7 + end + + it_behaves_like :proc_compose, :<<, -> { MethodSpecs::Composition.new.method(:upcase) } + + describe "composition" do + it "is a lambda" do + pow_2 = MethodSpecs::Composition.new.method(:pow_2) + double = proc { |x| x + x } + + (pow_2 << double).is_a?(Proc).should == true + (pow_2 << double).lambda?.should == true + end + + it "may accept multiple arguments" do + inc = MethodSpecs::Composition.new.method(:inc) + mul = proc { |n, m| n * m } + + (inc << mul).call(2, 3).should == 7 + end + end + end + + describe "Method#>>" do + it "returns a Proc that is the composition of self and the passed Proc" do + upcase = proc { |s| s.upcase } + succ = MethodSpecs::Composition.new.method(:succ) + + (succ >> upcase).call('Ruby').should == "RUBZ" + end + + it "calls passed Proc with arguments and then calls self with result" do + pow_2_proc = proc { |x| x * x } + double_proc = proc { |x| x + x } + + pow_2_method = MethodSpecs::Composition.new.method(:pow_2) + double_method = MethodSpecs::Composition.new.method(:double) + + (pow_2_method >> double_proc).call(2).should == 8 + (double_method >> pow_2_proc).call(2).should == 16 + end + + it "accepts any callable object" do + inc = MethodSpecs::Composition.new.method(:inc) + + double = Object.new + def double.call(n); n * 2; end + + (inc >> double).call(3).should == 8 + end + + it_behaves_like :proc_compose, :>>, -> { MethodSpecs::Composition.new.method(:upcase) } + + describe "composition" do + it "is a lambda" do + pow_2 = MethodSpecs::Composition.new.method(:pow_2) + double = proc { |x| x + x } + + (pow_2 >> double).is_a?(Proc).should == true + (pow_2 >> double).lambda?.should == true + end + + it "may accept multiple arguments" do + mul = MethodSpecs::Composition.new.method(:mul) + inc = proc { |n| n + 1 } + + (mul >> inc).call(2, 3).should == 7 + end + end + end +end diff --git a/spec/ruby/core/method/fixtures/classes.rb b/spec/ruby/core/method/fixtures/classes.rb index 142cbd1bec..315672c6e0 100644 --- a/spec/ruby/core/method/fixtures/classes.rb +++ b/spec/ruby/core/method/fixtures/classes.rb @@ -181,4 +181,29 @@ module MethodSpecs end end + class Composition + def upcase(s) + s.upcase + end + + def succ(s) + s.succ + end + + def pow_2(n) + n * n + end + + def double(n) + n + n + end + + def inc(n) + n + 1 + end + + def mul(n, m) + n * m + end + end end diff --git a/spec/ruby/core/module/attr_spec.rb b/spec/ruby/core/module/attr_spec.rb index 87bf8ea57f..b114b06b6e 100644 --- a/spec/ruby/core/module/attr_spec.rb +++ b/spec/ruby/core/module/attr_spec.rb @@ -138,9 +138,8 @@ describe "Module#attr" do it "with a boolean argument emits a warning when $VERBOSE is true" do lambda { - $VERBOSE = true Class.new { attr :foo, true } - }.should complain(/boolean argument is obsoleted/) + }.should complain(/boolean argument is obsoleted/, verbose: true) end ruby_version_is ''...'2.5' do diff --git a/spec/ruby/core/module/autoload_spec.rb b/spec/ruby/core/module/autoload_spec.rb index d6af169cff..b028dbc56c 100644 --- a/spec/ruby/core/module/autoload_spec.rb +++ b/spec/ruby/core/module/autoload_spec.rb @@ -582,9 +582,8 @@ describe "Module#autoload" do end -> { - $VERBOSE = true Kernel.require fixture(__FILE__, "autoload_during_require.rb") - }.should_not complain + }.should_not complain(verbose: true) ModuleSpecs::Autoload::AutoloadDuringRequire.should be_kind_of(Class) end diff --git a/spec/ruby/core/module/method_defined_spec.rb b/spec/ruby/core/module/method_defined_spec.rb index 62e0db8ee6..f5203917b4 100644 --- a/spec/ruby/core/module/method_defined_spec.rb +++ b/spec/ruby/core/module/method_defined_spec.rb @@ -46,4 +46,55 @@ describe "Module#method_defined?" do c.method_defined?(o).should == true end + + ruby_version_is "2.6" do + # works as method_defined?(method_name) + describe "when passed true as a second optional argument" do + it "performs a lookup in ancestors" do + ModuleSpecs::Child.method_defined?(:public_child, true).should == true + ModuleSpecs::Child.method_defined?(:protected_child, true).should == true + ModuleSpecs::Child.method_defined?(:accessor_method, true).should == true + ModuleSpecs::Child.method_defined?(:private_child, true).should == false + + # Defined in Parent + ModuleSpecs::Child.method_defined?(:public_parent, true).should == true + ModuleSpecs::Child.method_defined?(:protected_parent, true).should == true + ModuleSpecs::Child.method_defined?(:private_parent, true).should == false + + # Defined in Module + ModuleSpecs::Child.method_defined?(:public_module, true).should == true + ModuleSpecs::Child.method_defined?(:protected_module, true).should == true + ModuleSpecs::Child.method_defined?(:private_module, true).should == false + + # Defined in SuperModule + ModuleSpecs::Child.method_defined?(:public_super_module, true).should == true + ModuleSpecs::Child.method_defined?(:protected_super_module, true).should == true + ModuleSpecs::Child.method_defined?(:private_super_module, true).should == false + end + end + + describe "when passed false as a second optional argument" do + it "checks only the class itself" do + ModuleSpecs::Child.method_defined?(:public_child, false).should == true + ModuleSpecs::Child.method_defined?(:protected_child, false).should == true + ModuleSpecs::Child.method_defined?(:accessor_method, false).should == true + ModuleSpecs::Child.method_defined?(:private_child, false).should == false + + # Defined in Parent + ModuleSpecs::Child.method_defined?(:public_parent, false).should == false + ModuleSpecs::Child.method_defined?(:protected_parent, false).should == false + ModuleSpecs::Child.method_defined?(:private_parent, false).should == false + + # Defined in Module + ModuleSpecs::Child.method_defined?(:public_module, false).should == false + ModuleSpecs::Child.method_defined?(:protected_module, false).should == false + ModuleSpecs::Child.method_defined?(:private_module, false).should == false + + # Defined in SuperModule + ModuleSpecs::Child.method_defined?(:public_super_module, false).should == false + ModuleSpecs::Child.method_defined?(:protected_super_module, false).should == false + ModuleSpecs::Child.method_defined?(:private_super_module, false).should == false + end + end + end end diff --git a/spec/ruby/core/module/private_method_defined_spec.rb b/spec/ruby/core/module/private_method_defined_spec.rb index e00a8c8a9b..a3cf55852e 100644 --- a/spec/ruby/core/module/private_method_defined_spec.rb +++ b/spec/ruby/core/module/private_method_defined_spec.rb @@ -69,4 +69,54 @@ describe "Module#private_method_defined?" do def str.to_str() 'private_3' end ModuleSpecs::CountsMixin.private_method_defined?(str).should == true end + + ruby_version_is "2.6" do + describe "when passed true as a second optional argument" do + it "performs a lookup in ancestors" do + ModuleSpecs::Child.private_method_defined?(:public_child, true).should == false + ModuleSpecs::Child.private_method_defined?(:protected_child, true).should == false + ModuleSpecs::Child.private_method_defined?(:accessor_method, true).should == false + ModuleSpecs::Child.private_method_defined?(:private_child, true).should == true + + # Defined in Parent + ModuleSpecs::Child.private_method_defined?(:public_parent, true).should == false + ModuleSpecs::Child.private_method_defined?(:protected_parent, true).should == false + ModuleSpecs::Child.private_method_defined?(:private_parent, true).should == true + + # Defined in Module + ModuleSpecs::Child.private_method_defined?(:public_module, true).should == false + ModuleSpecs::Child.private_method_defined?(:protected_module, true).should == false + ModuleSpecs::Child.private_method_defined?(:private_module, true).should == true + + # Defined in SuperModule + ModuleSpecs::Child.private_method_defined?(:public_super_module, true).should == false + ModuleSpecs::Child.private_method_defined?(:protected_super_module, true).should == false + ModuleSpecs::Child.private_method_defined?(:private_super_module, true).should == true + end + end + + describe "when passed false as a second optional argument" do + it "checks only the class itself" do + ModuleSpecs::Child.private_method_defined?(:public_child, false).should == false + ModuleSpecs::Child.private_method_defined?(:protected_child, false).should == false + ModuleSpecs::Child.private_method_defined?(:accessor_method, false).should == false + ModuleSpecs::Child.private_method_defined?(:private_child, false).should == true + + # Defined in Parent + ModuleSpecs::Child.private_method_defined?(:public_parent, false).should == false + ModuleSpecs::Child.private_method_defined?(:protected_parent, false).should == false + ModuleSpecs::Child.private_method_defined?(:private_parent, false).should == false + + # Defined in Module + ModuleSpecs::Child.private_method_defined?(:public_module, false).should == false + ModuleSpecs::Child.private_method_defined?(:protected_module, false).should == false + ModuleSpecs::Child.private_method_defined?(:private_module, false).should == false + + # Defined in SuperModule + ModuleSpecs::Child.private_method_defined?(:public_super_module, false).should == false + ModuleSpecs::Child.private_method_defined?(:protected_super_module, false).should == false + ModuleSpecs::Child.private_method_defined?(:private_super_module, false).should == false + end + end + end end diff --git a/spec/ruby/core/module/protected_method_defined_spec.rb b/spec/ruby/core/module/protected_method_defined_spec.rb index 8492a497d3..75e02e0433 100644 --- a/spec/ruby/core/module/protected_method_defined_spec.rb +++ b/spec/ruby/core/module/protected_method_defined_spec.rb @@ -69,4 +69,54 @@ describe "Module#protected_method_defined?" do str.should_receive(:to_str).and_return("protected_3") ModuleSpecs::CountsMixin.protected_method_defined?(str).should == true end + + ruby_version_is "2.6" do + describe "when passed true as a second optional argument" do + it "performs a lookup in ancestors" do + ModuleSpecs::Child.protected_method_defined?(:public_child, true).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_child, true).should == true + ModuleSpecs::Child.protected_method_defined?(:accessor_method, true).should == false + ModuleSpecs::Child.protected_method_defined?(:private_child, true).should == false + + # Defined in Parent + ModuleSpecs::Child.protected_method_defined?(:public_parent, true).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_parent, true).should == true + ModuleSpecs::Child.protected_method_defined?(:private_parent, true).should == false + + # Defined in Module + ModuleSpecs::Child.protected_method_defined?(:public_module, true).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_module, true).should == true + ModuleSpecs::Child.protected_method_defined?(:private_module, true).should == false + + # Defined in SuperModule + ModuleSpecs::Child.protected_method_defined?(:public_super_module, true).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_super_module, true).should == true + ModuleSpecs::Child.protected_method_defined?(:private_super_module, true).should == false + end + end + + describe "when passed false as a second optional argument" do + it "checks only the class itself" do + ModuleSpecs::Child.protected_method_defined?(:public_child, false).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_child, false).should == true + ModuleSpecs::Child.protected_method_defined?(:accessor_method, false).should == false + ModuleSpecs::Child.protected_method_defined?(:private_child, false).should == false + + # Defined in Parent + ModuleSpecs::Child.protected_method_defined?(:public_parent, false).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_parent, false).should == false + ModuleSpecs::Child.protected_method_defined?(:private_parent, false).should == false + + # Defined in Module + ModuleSpecs::Child.protected_method_defined?(:public_module, false).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_module, false).should == false + ModuleSpecs::Child.protected_method_defined?(:private_module, false).should == false + + # Defined in SuperModule + ModuleSpecs::Child.protected_method_defined?(:public_super_module, false).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_super_module, false).should == false + ModuleSpecs::Child.protected_method_defined?(:private_super_module, false).should == false + end + end + end end diff --git a/spec/ruby/core/module/refine_spec.rb b/spec/ruby/core/module/refine_spec.rb index 97ea0a706d..662d49205d 100644 --- a/spec/ruby/core/module/refine_spec.rb +++ b/spec/ruby/core/module/refine_spec.rb @@ -425,6 +425,24 @@ describe "Module#refine" do end end + ruby_version_is "" ... "2.6" do + it "is not honored by Kernel#public_send" do + refinement = Module.new do + refine ModuleSpecs::ClassWithFoo do + def foo; "foo from refinement"; end + end + end + + result = nil + Module.new do + using refinement + result = ModuleSpecs::ClassWithFoo.new.public_send :foo + end + + result.should == "foo" + end + end + ruby_version_is "2.6" do it "is honored by Kernel#public_send" do refinement = Module.new do @@ -561,6 +579,45 @@ describe "Module#refine" do result.should == true end end + + ruby_version_is ""..."2.6" do + it "is not honored by &" do + refinement = Module.new do + refine String do + def to_proc(*args) + -> (*) { 'foo' } + end + end + end + + -> do + Module.new do + using refinement + ["hola"].map(&"upcase") + end + end.should raise_error(TypeError, /wrong argument type String \(expected Proc\)/) + end + end + + ruby_version_is "2.6" do + it "is honored by &" do + refinement = Module.new do + refine String do + def to_proc(*args) + -> (*) { 'foo' } + end + end + end + + result = nil + Module.new do + using refinement + result = ["hola"].map(&"upcase") + end + + result.should == ['foo'] + end + end end context "when super is called in a refinement" do diff --git a/spec/ruby/core/nil/match_spec.rb b/spec/ruby/core/nil/match_spec.rb new file mode 100644 index 0000000000..3f69312bfe --- /dev/null +++ b/spec/ruby/core/nil/match_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../spec_helper' + +ruby_version_is "2.6" do + describe "NilClass#=~" do + it "returns nil matching any object" do + o = Object.new + + 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 =~ false).should be_nil + (o =~ true).should be_nil + end + end + end +end diff --git a/spec/ruby/core/numeric/step_spec.rb b/spec/ruby/core/numeric/step_spec.rb index 1eb91aa1e5..96439927a4 100644 --- a/spec/ruby/core/numeric/step_spec.rb +++ b/spec/ruby/core/numeric/step_spec.rb @@ -61,6 +61,20 @@ describe "Numeric#step" do end end end + + describe "type" do + ruby_version_is ""..."2.6" do + it "returns an instance of Enumerator" do + 1.step(10).class.should == Enumerator + end + end + + ruby_version_is "2.6" do + it "returns an instance of Enumerator::ArithmeticSequence" do + 1.step(10).class.should == Enumerator::ArithmeticSequence + end + end + end end end diff --git a/spec/ruby/core/proc/compose_spec.rb b/spec/ruby/core/proc/compose_spec.rb new file mode 100644 index 0000000000..f1befe658b --- /dev/null +++ b/spec/ruby/core/proc/compose_spec.rb @@ -0,0 +1,94 @@ +require_relative '../../spec_helper' +require_relative 'shared/compose' + +ruby_version_is "2.6" do + describe "Proc#<<" do + it "returns a Proc that is the composition of self and the passed Proc" do + upcase = proc { |s| s.upcase } + succ = proc { |s| s.succ } + + (succ << upcase).call('Ruby').should == "RUBZ" + end + + it "calls passed Proc with arguments and then calls self with result" do + f = proc { |x| x * x } + g = proc { |x| x + x } + + (f << g).call(2).should == 16 + (g << f).call(2).should == 8 + end + + it "accepts any callable object" do + inc = proc { |n| n + 1 } + + double = Object.new + def double.call(n); n * 2; end + + (inc << double).call(3).should == 7 + end + + it_behaves_like :proc_compose, :<<, -> { proc { |s| s.upcase } } + + describe "composition" do + it "is a Proc" do + f = proc { |x| x * x } + g = proc { |x| x + x } + + (f << g).is_a?(Proc).should == true + (f << g).lambda?.should == false + end + + it "may accept multiple arguments" do + inc = proc { |n| n + 1 } + mul = proc { |n, m| n * m } + + (inc << mul).call(2, 3).should == 7 + end + end + end + + describe "Proc#>>" do + it "returns a Proc that is the composition of self and the passed Proc" do + upcase = proc { |s| s.upcase } + succ = proc { |s| s.succ } + + (succ >> upcase).call('Ruby').should == "RUBZ" + end + + it "calls passed Proc with arguments and then calls self with result" do + f = proc { |x| x * x } + g = proc { |x| x + x } + + (f >> g).call(2).should == 8 + (g >> f).call(2).should == 16 + end + + it "accepts any callable object" do + inc = proc { |n| n + 1 } + + double = Object.new + def double.call(n); n * 2; end + + (inc >> double).call(3).should == 8 + end + + it_behaves_like :proc_compose, :>>, -> { proc { |s| s.upcase } } + + describe "composition" do + it "is a Proc" do + f = proc { |x| x * x } + g = proc { |x| x + x } + + (f >> g).is_a?(Proc).should == true + (f >> g).lambda?.should == false + end + + it "may accept multiple arguments" do + inc = proc { |n| n + 1 } + mul = proc { |n, m| n * m } + + (mul >> inc).call(2, 3).should == 7 + end + end + end +end diff --git a/spec/ruby/core/proc/shared/compose.rb b/spec/ruby/core/proc/shared/compose.rb new file mode 100644 index 0000000000..64338cada8 --- /dev/null +++ b/spec/ruby/core/proc/shared/compose.rb @@ -0,0 +1,47 @@ +describe :proc_compose, shared: true do + ruby_version_is "2.6"..."2.7" do + it "raises NoMethodError when called if passed not callable object" do + not_callable = Object.new + composed = @object.call.send(@method, not_callable) + + -> { + composed.call('a') + }.should raise_error(NoMethodError, /undefined method `call' for/) + + end + + it "when called does not try to coerce argument with #to_proc" do + succ = Object.new + def succ.to_proc(s); s.succ; end + + composed = @object.call.send(@method, succ) + + -> { + composed.call('a') + }.should raise_error(NoMethodError, /undefined method `call' for/) + end + end + + ruby_version_is "2.7" do # https://bugs.ruby-lang.org/issues/15428 + it "raises TypeError if passed not callable object" do + lhs = @object.call + not_callable = Object.new + + -> { + lhs.send(@method, not_callable) + }.should raise_error(TypeError, "callable object is expected") + + end + + it "does not try to coerce argument with #to_proc" do + lhs = @object.call + + succ = Object.new + def succ.to_proc(s); s.succ; end + + -> { + lhs.send(@method, succ) + }.should raise_error(TypeError, "callable object is expected") + end + end +end diff --git a/spec/ruby/core/process/fixtures/in.txt b/spec/ruby/core/process/fixtures/in.txt new file mode 100644 index 0000000000..cf52303bdc --- /dev/null +++ b/spec/ruby/core/process/fixtures/in.txt @@ -0,0 +1 @@ +stdin diff --git a/spec/ruby/core/process/spawn_spec.rb b/spec/ruby/core/process/spawn_spec.rb index 951941888b..bb55fe299b 100644 --- a/spec/ruby/core/process/spawn_spec.rb +++ b/spec/ruby/core/process/spawn_spec.rb @@ -8,10 +8,10 @@ end describe :process_spawn_does_not_close_std_streams, shared: true do it "does not close STDIN" do - code = "STDOUT.puts STDIN.read(0).inspect" + code = "puts STDIN.read" cmd = "Process.wait Process.spawn(#{ruby_cmd(code).inspect}, #{@options.inspect})" - ruby_exe(cmd, args: "> #{@name}") - File.binread(@name).should == %[""#{newline}] + ruby_exe(cmd, args: "< #{fixture(__FILE__, "in.txt")} > #{@name}") + File.binread(@name).should == %[stdin#{newline}] end it "does not close STDOUT" do @@ -530,7 +530,35 @@ describe "Process.spawn" do File.read(@name).should == "glarkbang" end + # :close_others + platform_is_not :windows do + context "defaults :close_others to" do + ruby_version_is ""..."2.6" do + it "true" do + IO.pipe do |r, w| + w.close_on_exec = false + code = "begin; IO.new(#{w.fileno}).close; rescue Errno::EBADF; puts 'not inherited'; end" + Process.wait Process.spawn(ruby_cmd(code), :out => @name) + File.read(@name).should == "not inherited\n" + end + end + end + + ruby_version_is "2.6" do + it "false" do + IO.pipe do |r, w| + w.close_on_exec = false + code = "io = IO.new(#{w.fileno}); io.puts('inherited'); io.close" + pid = Process.spawn(ruby_cmd(code)) + w.close + Process.wait(pid) + r.read.should == "inherited\n" + end + end + end + end + context "when passed close_others: true" do before :each do @options = { close_others: true } diff --git a/spec/ruby/core/range/case_compare_spec.rb b/spec/ruby/core/range/case_compare_spec.rb index 9a33c5b73b..37d8cc4677 100644 --- a/spec/ruby/core/range/case_compare_spec.rb +++ b/spec/ruby/core/range/case_compare_spec.rb @@ -9,12 +9,20 @@ describe "Range#===" do range.should_receive(:include?).with(2).and_return(:true) (range === 2).should == :true end + + it "requires #succ method to be implemented" do + range = RangeSpecs::WithoutSucc.new(0)..RangeSpecs::WithoutSucc.new(10) + + lambda do + range === RangeSpecs::WithoutSucc.new(2) + end.should raise_error(TypeError, /can't iterate from/) + end end ruby_version_is "2.6" do it "returns the result of calling #cover? on self" do - range = RangeSpecs::Custom.new(0)..RangeSpecs::Custom.new(10) - (range === RangeSpecs::Custom.new(2)).should == true + range = RangeSpecs::WithoutSucc.new(0)..RangeSpecs::WithoutSucc.new(10) + (range === RangeSpecs::WithoutSucc.new(2)).should == true end end end diff --git a/spec/ruby/core/range/fixtures/classes.rb b/spec/ruby/core/range/fixtures/classes.rb index b62704ca39..3a1df010b2 100644 --- a/spec/ruby/core/range/fixtures/classes.rb +++ b/spec/ruby/core/range/fixtures/classes.rb @@ -40,6 +40,28 @@ module RangeSpecs end end + class WithoutSucc + include Comparable + attr_reader :n + + def initialize(n) + @n = n + end + + def eql?(other) + inspect.eql? other.inspect + end + alias :== :eql? + + def inspect + "WithoutSucc(#{@n})" + end + + def <=>(other) + @n <=> other.n + end + end + class Xs < Custom # represent a string of 'x's def succ Xs.new(@length + 1) diff --git a/spec/ruby/core/range/new_spec.rb b/spec/ruby/core/range/new_spec.rb index 26ea12867d..c110687163 100644 --- a/spec/ruby/core/range/new_spec.rb +++ b/spec/ruby/core/range/new_spec.rb @@ -42,4 +42,30 @@ describe "Range.new" do -> { Range.new(a, b) }.should raise_error(RangeSpecs::ComparisonError) end end + + describe "endless range" do + it "does not allow range without left boundary" do + -> { Range.new(nil, 1) }.should raise_error(ArgumentError, /bad value for range/) + end + + ruby_version_is ""..."2.6" do + it "does not allow range without right boundary" do + -> { Range.new(1, nil) }.should raise_error(ArgumentError, /bad value for range/) + end + end + + ruby_version_is "2.6" do + it "allows endless right boundary" do + range = Range.new(1, nil) + range.end.should == nil + end + + it "distinguishes ranges with included and excluded right boundary" do + range_exclude = Range.new(1, nil, true) + range_include = Range.new(1, nil, false) + + range_exclude.should_not == range_include + end + end + end end diff --git a/spec/ruby/core/range/percent_spec.rb b/spec/ruby/core/range/percent_spec.rb new file mode 100644 index 0000000000..41badd4f72 --- /dev/null +++ b/spec/ruby/core/range/percent_spec.rb @@ -0,0 +1,18 @@ +require_relative '../../spec_helper' + +ruby_version_is "2.6" do + describe "Range#%" do + it "works as a Range#step" do + aseq = (1..10) % 2 + aseq.class.should == Enumerator::ArithmeticSequence + aseq.begin.should == 1 + aseq.end.should == 10 + aseq.step.should == 2 + aseq.to_a.should == [1, 3, 5, 7, 9] + end + + it "produces an arithmetic sequence with a percent sign in #inspect" do + ((1..10) % 2).inspect.should == "((1..10).%(2))" + end + end +end diff --git a/spec/ruby/core/range/shared/cover.rb b/spec/ruby/core/range/shared/cover.rb index b2de86531d..7d2367d712 100644 --- a/spec/ruby/core/range/shared/cover.rb +++ b/spec/ruby/core/range/shared/cover.rb @@ -90,4 +90,64 @@ describe :range_cover, shared: true do end end end + + ruby_version_is "2.6" do + context "range argument" do + it "accepts range argument" do + (0..10).send(@method, (3..7)).should be_true + (0..10).send(@method, (3..15)).should be_false + (0..10).send(@method, (-2..7)).should be_false + + (1.1..7.9).send(@method, (2.5..6.5)).should be_true + (1.1..7.9).send(@method, (2.5..8.5)).should be_false + (1.1..7.9).send(@method, (0.5..6.5)).should be_false + + ('c'..'i').send(@method, ('d'..'f')).should be_true + ('c'..'i').send(@method, ('d'..'z')).should be_false + ('c'..'i').send(@method, ('a'..'f')).should be_false + + range_10_100 = RangeSpecs::TenfoldSucc.new(10)..RangeSpecs::TenfoldSucc.new(100) + range_20_90 = RangeSpecs::TenfoldSucc.new(20)..RangeSpecs::TenfoldSucc.new(90) + range_20_110 = RangeSpecs::TenfoldSucc.new(20)..RangeSpecs::TenfoldSucc.new(110) + range_0_90 = RangeSpecs::TenfoldSucc.new(0)..RangeSpecs::TenfoldSucc.new(90) + + range_10_100.send(@method, range_20_90).should be_true + range_10_100.send(@method, range_20_110).should be_false + range_10_100.send(@method, range_0_90).should be_false + end + + it "supports boundaries of different comparable types" do + (0..10).send(@method, (3.1..7.9)).should be_true + (0..10).send(@method, (3.1..15.9)).should be_false + (0..10).send(@method, (-2.1..7.9)).should be_false + end + + it "returns false if types are not comparable" do + (0..10).send(@method, ('a'..'z')).should be_false + (0..10).send(@method, (RangeSpecs::TenfoldSucc.new(0)..RangeSpecs::TenfoldSucc.new(100))).should be_false + end + + it "honors exclusion of right boundary (:exclude_end option)" do + # Integer + (0..10).send(@method, (0..10)).should be_true + (0...10).send(@method, (0...10)).should be_true + + (0..10).send(@method, (0...10)).should be_true + (0...10).send(@method, (0..10)).should be_false + + (0...11).send(@method, (0..10)).should be_true + (0..10).send(@method, (0...11)).should be_true + + # Float + (0..10.1).send(@method, (0..10.1)).should be_true + (0...10.1).send(@method, (0...10.1)).should be_true + + (0..10.1).send(@method, (0...10.1)).should be_true + (0...10.1).send(@method, (0..10.1)).should be_false + + (0...11.1).send(@method, (0..10.1)).should be_true + (0..10.1).send(@method, (0...11.1)).should be_false + end + end + end end diff --git a/spec/ruby/core/range/step_spec.rb b/spec/ruby/core/range/step_spec.rb index cdf66e4565..818207974a 100644 --- a/spec/ruby/core/range/step_spec.rb +++ b/spec/ruby/core/range/step_spec.rb @@ -1,21 +1,10 @@ require_relative '../../spec_helper' describe "Range#step" do - step_enum_class = Enumerator - ruby_version_is "2.6" do - step_enum_class = Enumerator::ArithmeticSequence - end - before :each do ScratchPad.record [] end - it "returns an #{step_enum_class} when no block is given" do - enum = (1..10).step(4) - enum.should be_an_instance_of(step_enum_class) - enum.to_a.should eql([1, 5, 9]) - end - it "returns self" do r = 1..2 r.step { }.should equal(r) @@ -268,7 +257,7 @@ describe "Range#step" do end describe "when no block is given" do - describe "returned #{step_enum_class}" do + describe "returned Enumerator" do describe "size" do it "raises a TypeError if step does not respond to #to_int" do obj = mock("Range#step non-integer") @@ -363,6 +352,28 @@ describe "Range#step" do enum.size.should == nil end end + + describe "type" do + ruby_version_is ""..."2.6" do + it "returns an instance of Enumerator" do + (1..10).step.class.should == Enumerator + end + end + + ruby_version_is "2.6" do + context "when both begin and end are numerics" do + it "returns an instance of Enumerator::ArithmeticSequence" do + (1..10).step.class.should == Enumerator::ArithmeticSequence + end + end + + context "when begin and end are not numerics" do + it "returns an instance of Enumerator" do + ("a".."z").step.class.should == Enumerator + end + end + end + end end end end diff --git a/spec/ruby/core/string/split_spec.rb b/spec/ruby/core/string/split_spec.rb index b89a28c149..b451921c66 100644 --- a/spec/ruby/core/string/split_spec.rb +++ b/spec/ruby/core/string/split_spec.rb @@ -411,5 +411,17 @@ describe "String#split with Regexp" do returned_object.should == "chunky bacon" a.should == ["Chunky", "Bacon"] end + + describe "for a String subclass" do + a = [] + StringSpecs::MyString.new("a|b").split("|") { |str| a << str } + first, last = a + + first.should be_an_instance_of(StringSpecs::MyString) + first.should == "a" + + last.should be_an_instance_of(StringSpecs::MyString) + last.should == "b" + end end end diff --git a/spec/ruby/core/struct/to_h_spec.rb b/spec/ruby/core/struct/to_h_spec.rb index ddfbfbca61..b5f4e40701 100644 --- a/spec/ruby/core/struct/to_h_spec.rb +++ b/spec/ruby/core/struct/to_h_spec.rb @@ -14,10 +14,45 @@ describe "Struct#to_h" do end ruby_version_is "2.6" do - it "converts [key, value] pairs returned by the block to a hash" do - car = StructClasses::Car.new('Ford', 'Ranger') - h = car.to_h {|k, v| [k.to_s, "#{v}".downcase]} - h.should == {"make" => "ford", "model" => "ranger", "year" => ""} + context "with block" do + it "converts [key, value] pairs returned by the block to a hash" do + car = StructClasses::Car.new('Ford', 'Ranger') + + h = car.to_h { |k, v| [k.to_s, "#{v}".downcase] } + h.should == { "make" => "ford", "model" => "ranger", "year" => "" } + end + + it "raises ArgumentError if block returns longer or shorter array" do + -> do + StructClasses::Car.new.to_h { |k, v| [k.to_s, "#{v}".downcase, 1] } + end.should raise_error(ArgumentError, /element has wrong array length/) + + -> do + StructClasses::Car.new.to_h { |k, v| [k] } + end.should raise_error(ArgumentError, /element has wrong array length/) + end + + it "raises TypeError if block returns something other than Array" do + -> do + StructClasses::Car.new.to_h { |k, v| "not-array" } + end.should raise_error(TypeError, /wrong element type String/) + end + + it "coerces returned pair to Array with #to_ary" do + x = mock('x') + x.stub!(:to_ary).and_return([:b, 'b']) + + StructClasses::Car.new.to_h { |k| x }.should == { :b => 'b' } + end + + it "does not coerce returned pair to Array with #to_a" do + x = mock('x') + x.stub!(:to_a).and_return([:b, 'b']) + + -> do + StructClasses::Car.new.to_h { |k| x } + end.should raise_error(TypeError, /wrong element type MockObject/) + end end end end diff --git a/spec/ruby/core/tracepoint/eval_script_spec.rb b/spec/ruby/core/tracepoint/eval_script_spec.rb new file mode 100644 index 0000000000..8a153156d1 --- /dev/null +++ b/spec/ruby/core/tracepoint/eval_script_spec.rb @@ -0,0 +1,22 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +ruby_version_is "2.6" do + describe "#eval_script" do + ScratchPad.record [] + + script = <<-CODE + def foo + p :hello + end + CODE + + TracePoint.new(:script_compiled) do |e| + ScratchPad << e.eval_script + end.enable do + eval script + end + + ScratchPad.recorded.should == [script] + end +end diff --git a/spec/ruby/core/tracepoint/instruction_sequence_spec.rb b/spec/ruby/core/tracepoint/instruction_sequence_spec.rb new file mode 100644 index 0000000000..50a9cd5722 --- /dev/null +++ b/spec/ruby/core/tracepoint/instruction_sequence_spec.rb @@ -0,0 +1,23 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +ruby_version_is "2.6" do + describe "#instruction_sequence" do + ScratchPad.record [] + + script = <<-CODE + def foo + p :hello + end + CODE + + TracePoint.new(:script_compiled) do |e| + ScratchPad << e.instruction_sequence + end.enable do + eval script + end + + ScratchPad.recorded.size.should == 1 + ScratchPad.recorded[0].class.should == RubyVM::InstructionSequence + end +end |